CDI 日志记录拦截器在@PostConstruct 中不起作用
CDI Logging Interceptor not working in @PostConstruct
我想为我当前的 Stack 创建一个 LoggingInterceptor:
- Tomcat 8.5.24
- 焊接 2.4.5-Final
- 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());
}
}
如果在运行时调用我的业务方法拦截器,例如在调用 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 调用任何其他方法,您就没有使用代理(而是直接在同一个对象上调用方法),因此没有发生拦截。
我想为我当前的 Stack 创建一个 LoggingInterceptor:
- Tomcat 8.5.24
- 焊接 2.4.5-Final
- 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());
}
}
如果在运行时调用我的业务方法拦截器,例如在调用 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 调用任何其他方法,您就没有使用代理(而是直接在同一个对象上调用方法),因此没有发生拦截。