CDI Logging Interceptor无法在@PostConstruct中运行

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CDI Logging Interceptor无法在@PostConstruct中运行相关的知识,希望对你有一定的参考价值。

我想为我当前的Stack创建一个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()方法中调用时不会记录@PostConstruct方法。

我有什么办法可以记录从@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");
}
答案

这是预期的行为。只有从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调用被截获,但不会从methodB中调用methodA。当你调用simpleBeanProxy.methodB();时,methodB的调用被截获。

生命周期回调发生类似的事情,拦截器拦截从外部(容器)到生命周期方法的调用,但不拦截从postconstruct方法中调用的同一个bean上的任何方法。

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

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

另一答案

我没有尝试过,但我认为你需要这样的问题在这个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);
        }
    }

以上是关于CDI Logging Interceptor无法在@PostConstruct中运行的主要内容,如果未能解决你的问题,请参考以下文章

无法将 JSF + CDI 项目从 Tomcat 迁移到 Wildfly

CDI + JPA 多个 @OneToMany - LAZY/EAGER - LazyInitializationException 或无法实例化多个包

@Schedule 无法从 @SessionScoped CDI bean 获取数据

在 WildFly 上重新部署后,CDI 无法在 @Requestscoped REST 服务中将 @Singleton 设置为 @Provider

在较新版本的 JSF 中,@FacesValidator 和 @FacesConverter 中的 EJB 和 CDI 注入点无法通过 OmniFaces 工作

无法让CDI和JAX-RS在Glassfish中协同工作