代码之家  ›  专栏  ›  技术社区  ›  Kamikazzze

CDI日志侦听器在@PostConstruct中不工作

  •  0
  • Kamikazzze  · 技术社区  · 7 年前

    我想为当前堆栈创建一个LoggingInterceptor:

    • Tomcat 8.5.24
    • 焊缝2.4.5-最终
    • JSF 2.3.3

    下面是标记要拦截的方法或类型的注释。

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

    如果在运行时调用我的业务方法拦截器,例如在jsf视图中按下按钮调用 doSomething() 方法两者 doSomething() 以及 setEntityList() 方法将被记录。

    但是在 @PostConstruct 方法根本没有记录。这意味着 setEntityList() 在中调用时,不会记录方法 @施工后 方法

    我可以做些什么来获取从 @施工后 要记录的方法。我会很感激你的帮助。提前谢谢。

    根据HRgiger的回答更新:

    我也试过 @施工后 方法,但使用此方法,我只能记录 @施工后 但在 @施工后 方法没有被记录,这仍然是我的主要问题。

    @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");
    }
    
    2 回复  |  直到 7 年前
        1
  •  1
  •   Community CDub    4 年前

    这是预期行为。只截获来自bean外部对bean方法的调用,而不截获来自bean内部对自身方法的调用。

    weld/CDI 1.0.0 specification

    业务方法拦截器应用于bean客户端对bean方法的调用。

    生命周期回调拦截器应用于容器对生命周期回调的调用。

    这意味着你所描述的行为是完全有意的。这个 @AroundInvoke -带注释的业务方法拦截器只截获从bean外部对bean调用的“普通”方法。 这也意味着如果您的bean有方法 methodA() methodB() methodA 电话 methodB ,当 方法a 仅在调用 方法a 记录,而不是调用 方法B .

    假设您有以下内容:

    @Named
    @ViewScoped
    @Logging(Level.DEBUG)
    public class SimpleBean {
    
        public void methodA() {
            methodB();
        }
    
        public void methodB() {
            // do something
        }
    }
    

    在另一个类中,您注入这个bean,实际上是向bean注入一个代理:

    @Inject
    private SimpleBean simpleBeanProxy;
    

    当你打电话的时候 simpleBeanProxy.methodA(); 那个 方法a 调用被拦截,但 调用 方法B 从内部 方法a . 当你打电话的时候 simpleBeanProxy.methodB(); ,调用 方法B 被截获。

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

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

    然而,一旦您“进入”实际bean上的一个方法并从该bean调用任何其他方法,您就不会使用代理(而是直接在同一对象上调用方法),因此不会发生拦截。

        2
  •  0
  •   HRgiger forhas    7 年前

    我以前没试过,但我想你需要这样的东西 question 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);
            }
        }