简体   繁体   中英

CDI Logging Interceptor not working in @PostConstruct

I wanted to create a LoggingInterceptor for my current Stack:

  • Tomcat 8.5.24
  • Weld 2.4.5-Final
  • JSF 2.3.3

Here is the Annotation to mark methods or types for interception.

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.enterprise.util.Nonbinding;
import javax.interceptor.InterceptorBinding;

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@InterceptorBinding
public @interface Logging {

    @Nonbinding
    Level value() default Level.TRACE;

    public enum Level {
        NONE, ALL, TRACE, DEBUG, INFO, WARN, ERROR;
    }
}

Here is the Interceptor logic:

import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;

@Interceptor
@Logging
public class LoggingInterceptor {

    @AroundInvoke
    public Object interceptBusiness(final InvocationContext invocationContext) throws Exception {
        Logger log = LoggerFactory.getLogger(invocationContext.getMethod().getDeclaringClass().getName());
        log.trace("LOG start of method");
        Object result = invocationContext.proceed();
        log.trace("LOG end of method");
        return result;      
    }
}

A simplified Bean:

import javax.annotation.PostConstruct;
import javax.inject.Named;
import javax.inject.Inject;
import javax.faces.view.ViewScoped;

@Named
@ViewScoped
@Logging(Level.DEBUG)
public class ListController implements Serializable {

    private static final long serialVersionUID = 1L;

    @Inject
    EntityService entityService;

    private List<Entity> entityList;

    public List<Entity> getEntityList() {
        return this.entityList;
    }

    public void setEntityList(final List<Entity> entityList) {
        this.entityList= entityList;
    }

    public String doSomething(){
        List<Entity> entityList = new ArrayList<>();
        this.setEntityList(entityList);
        return "";
    }

    @PostConstruct
    public void setUp() {
        this.setEntityList(this.entityService.findAll()); 
    }
}

My interceptor for business methods works like a charm if called during runtime for example when pressing a button in the jsf view which calls the doSomething() method. Both the doSomething() and the setEntityList() methods will be logged.

But all methods which are called in the @PostConstruct method are not logged at all. That means the setEntityList() method is not logged when called in the @PostConstruct method.

Is there anything I can do to get the methods called from the @PostConstruct method to be logged. I would appreciate some help. Thanks in advance.

Update due to the answer of HRgiger:

I also tried the @PostConstruct method in my interceptor logic but with this method I can only log the invocation of the @PostConstruct itself but the methods called in the @PostConstruct method are not logged and that is still my main problem.

@PostConstruct
public void interceptLifecycle(final InvocationContext invocationContext) throws Exception {
    Logger log = LoggerFactory.getLogger(invocationContext.getTarget().getClass().getSimpleName());

    log.info("LOG start of POSTCONSTRUCT");
    invocationContext.proceed();
    log.info("LOG end of POSTCONSTRUCT");
}

This is intended behaviour. Only calls from outside the bean to methods of the bean get intercepted, not calls from within the bean to its own methods.

From the weld/CDI 1.0.0 specification

A business method interceptor applies to invocations of methods of the bean by clients of the bean.

A lifecycle callback interceptor applies to invocations of lifecycle callbacks by the container.

This means that the behaviour you describe is fully intended. The @AroundInvoke -annotated business method interceptor only intercepts "normal" methods called on the bean from outside the bean. This also means that if your bean has methods methodA() and methodB() , and methodA calls methodB , that when methodA gets called from the outside only the invocation of methodA gets logged, not the invocation of methodB .

Say you have the following:

@Named
@ViewScoped
@Logging(Level.DEBUG)
public class SimpleBean {

    public void methodA() {
        methodB();
    }

    public void methodB() {
        // do something
    }
}

And in some other class you inject this bean, you're really injecting a proxy to the bean:

@Inject
private SimpleBean simpleBeanProxy;

When you call simpleBeanProxy.methodA(); , that methodA invocation gets intercepted but not the invocation of methodB from within methodA . When you call simpleBeanProxy.methodB(); , that invocation of methodB gets intercepted.

Something similar happens with the lifecycle callbacks, the interceptor intercepts the calls from outside (the container) to the lifecycle methods but not any methods on the same bean called from within that postconstruct method.

This is all because the intercepting these interceptors do is handled by the proxy. If a method on your bean is called "from the outside", it is actually called on the proxy and the proxy makes sure to call any registered interceptors before performing the actual method call on the actual bean object.

However, once you're "inside" a method on the actual bean and you call any other methods from that same bean you're not using the proxy (but calling methods directly on the same object) and therefore no intercepting happens.

I didnt try before but I think you need something like this asked in this question or this :

@PostConstruct
    public void postConstruct(InvocationContext ctx) {
        try {
            System.out.println(PREFIX + " postConstruct");
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    @PreDestroy
    public void preDestroy(InvocationContext ctx) {
        try {
            System.out.println(PREFIX + " predestroy");
            System.out.println(PREFIX + "ctx.preceed=" + ctx.proceed());
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM