【问题标题】:CDI Logging Interceptor not working in @PostConstructCDI 日志记录拦截器在 @PostConstruct 中不起作用
【发布时间】:2018-02-28 08:54:02
【问题描述】:

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

  • Tomcat 8.5.24
  • 焊接 2.4.5-Final
  • JSF 2.3.3

这是标记方法或类型以进行拦截的Annotation。

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

【问题讨论】:

    标签: java jakarta-ee cdi interceptor weld


    【解决方案1】:

    这是预期的行为。只有 bean 外部对 bean 方法的调用会被拦截,而不是 bean 内部对它自己的方法的调用。

    来自weld/CDI 1.0.0 specification

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

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

    这意味着您描述的行为完全是有意的。 @AroundInvoke-annotated 业务方法拦截器仅拦截从 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
        }
    }
    

    在你注入这个 bean 的其他类中,你实际上是在为 bean 注入一个代理:

    @Inject
    private SimpleBean simpleBeanProxy;
    

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

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

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

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

    【讨论】:

    • 感谢您的澄清。
    【解决方案2】:

    我之前没有尝试过,但我认为您需要在 questionthis 中提出这样的问题:

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

    【讨论】:

    • 我还在拦截器逻辑中尝试了@PostConstruct 方法,但使用此方法我只能记录@PostConstruct 方法的调用,但@PostConstruct 中调用的方法没有记录。这是我的主要问题。
    • @Kamikazzze 是的,还有 ctx.getMethod javadoc 说“对于生命周期回调方法,返回 null。”
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-14
    • 1970-01-01
    • 2020-06-17
    • 2014-11-14
    • 2013-03-20
    • 2014-03-10
    相关资源
    最近更新 更多