I wanted to create a LoggingInterceptor for my current Stack:
Here is the Annotation to mark methods or types for interception.
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;
}
}
Here is the Interceptor logic:
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;
}
}
A simplified 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());
}
}
My interceptor for business methods works like a charm if called during runtime for example when pressing a button in the jsf view which calls the doSomething()
method. Both the doSomething()
and the setEntityList()
methods will be logged.
But all methods which are called in the @PostConstruct
method are not logged at all. That means the setEntityList()
method is not logged when called in the @PostConstruct
method.
Is there anything I can do to get the methods called from the @PostConstruct
method to be logged. I would appreciate some help. Thanks in advance.
Update due to the answer of HRgiger:
I also tried the @PostConstruct
method in my interceptor logic but with this method I can only log the invocation of the @PostConstruct
itself but the methods called in the @PostConstruct
method are not logged and that is still my main problem.
@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");
}
This is intended behaviour. Only calls from outside the bean to methods of the bean get intercepted, not calls from within the bean to its own methods.
From the 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.
This means that the behaviour you describe is fully intended. The @AroundInvoke
-annotated business method interceptor only intercepts "normal" methods called on the bean from outside the bean. This also means that if your bean has methods methodA()
and methodB()
, and methodA
calls methodB
, that when methodA
gets called from the outside only the invocation of methodA
gets logged, not the invocation of methodB
.
Say you have the following:
@Named
@ViewScoped
@Logging(Level.DEBUG)
public class SimpleBean {
public void methodA() {
methodB();
}
public void methodB() {
// do something
}
}
And in some other class you inject this bean, you're really injecting a proxy to the bean:
@Inject
private SimpleBean simpleBeanProxy;
When you call simpleBeanProxy.methodA();
, that methodA
invocation gets intercepted but not the invocation of methodB
from within methodA
. When you call simpleBeanProxy.methodB();
, that invocation of methodB
gets intercepted.
Something similar happens with the lifecycle callbacks, the interceptor intercepts the calls from outside (the container) to the lifecycle methods but not any methods on the same bean called from within that postconstruct method.
This is all because the intercepting these interceptors do is handled by the proxy. If a method on your bean is called "from the outside", it is actually called on the proxy and the proxy makes sure to call any registered interceptors before performing the actual method call on the actual bean object.
However, once you're "inside" a method on the actual bean and you call any other methods from that same bean you're not using the proxy (but calling methods directly on the same object) and therefore no intercepting happens.
I didnt try before but I think you need something like this asked in this 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);
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.