CDI 日志记录拦截器在@PostConstruct 中不起作用

CDI Logging Interceptor not working in @PostConstruct

我想为我当前的 Stack 创建一个 LoggingInterceptor:

这是用于标记要拦截的方法或类型的注释。

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;
    }
}

拦截器逻辑如下:

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;      
    }
}

一个简化的 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()); 
    }
}

如果在运行时调用我的业务方法拦截器,例如在调用 doSomething() 方法的 jsf 视图中按下按钮时,它的工作就像一个魅力。 doSomething()setEntityList() 方法都将被记录。

但是在 @PostConstruct 方法中调用的所有方法都不会被记录。这意味着在 @PostConstruct 方法中调用时不会记录 setEntityList() 方法。

我可以做些什么来记录从 @PostConstruct 方法调用的方法。我会很感激一些帮助。提前致谢。

根据HRgiger的回答更新:

我还在我的拦截器逻辑中尝试了 @PostConstruct 方法但是使用这个方法我只能记录 @PostConstruct 本身的调用但是 @PostConstruct 方法中调用的方法是没有登录,这仍然是我的主要问题。

@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");
}

我之前没试过,但我想你需要这样的问题 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);
        }
    }

这是有意为之的行为。只有从 bean 外部调用 bean 的方法才会被拦截,而不是从 bean 内部调用它自己的方法。

来自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.

这意味着您描述的行为完全是有意为之。 @AroundInvoke-注释的业务方法拦截器只拦截从 bean 外部调用 bean 的“正常”方法。 这也意味着,如果您的 bean 具有方法 methodA()methodB(),并且 methodA 调用 methodB,那么当从外部调用 methodA 时,仅调用methodA 被记录,而不是 methodB.

的调用

假设您有以下内容:

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

    public void methodA() {
        methodB();
    }

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

而在其他一些 class 中,您注入了这个 bean,您实际上是在向 bean 注入一个代理:

@Inject
private SimpleBean simpleBeanProxy;

当您调用 simpleBeanProxy.methodA(); 时,methodA 调用会被拦截,但 不会methodA 中调用 methodB . 当您调用 simpleBeanProxy.methodB(); 时,methodB 的调用会被拦截。

生命周期回调也会发生类似的情况,拦截器会拦截从外部(容器)对生命周期方法的调用,但不会拦截从该后构造方法内部调用的同一 bean 上的任何方法。

这都是因为这些拦截器所做的拦截是由代理处理的。如果你的 bean 上的一个方法是“从外部”调用的,它实际上是在代理上调用的,并且代理确保在对实际 bean 对象执行实际方法调用之前调用任何已注册的拦截器。

但是,一旦您在实际 bean 的方法“内部”并且从同一个 bean 调用任何其他方法,您就没有使用代理(而是直接在同一个对象上调用方法),因此没有发生拦截。