My project is using Spring web flow 2.4, and I need to use Spring AOP to advice flow scope bean to handle aspect requirement like logging.
Here is the aspect class:
#Aspect
#Component
public class LogFlowEventExecutor {
#Pointcut("execution(public * com.xyz.app.flow.*FlowBean.*(..))")
private void flowFunction() {}
#Before("flowOperation()")
public void logFlowEvent(JoinPoint jp) throws Throwable {
//logic ignored
...
}
}
And defines autoproxy in the root WebApplicationContext:
<aop:aspectj-autoproxy />
And each individual -flow.xml file
<flow xmlns="http://www.springframework.org/schema/webflow"...>
<var name="abcFlowBean" class="com.xyz.app.flow.AbcFlowBean" />
...
</flow>
I used the same pattern to easily advice Service beans and Controllers, but it didn't work on flow scoped beans, the aspect is never executed.
I thought it might be something with the JDK dynamic interface proxy, however it failed with CGLIB as well (proxy-target-class="true" attribute was set). The pointcut never got intercepted. So I doubted the flow-scope bean was never properly proxied when it's instantiated.
I tried to switch to LTW, but it threw an NoSuchMethodError (the bean was woven from the weaveinfo log). Maybe it's better to open another thread for that alone.
My question is that will it be possible to use Spring AOP to advice a flow scope bean and how to do it?
I worked around this issue by writing a Flow Execution Listener, basically the FlowExecutionListener defines a lot of callback methods that will be invoked when certain things happen in the course of a flow execution life cycle. So I created a custom listener class extends from FlowExecutionListenerAdapter that implements all methods by using empty method bodies and override what I am interested about, and this almost has the same kind of effect like AOP if you want global callbacks on all flow scope beans. But if you only need to intercept a few of them, then that's another story.
public class MyFlowExecutionListener extends FlowExecutionListenerAdapter{
#Override
public void eventSignaled(RequestContext context, Event event) {...}
#Override
public void transitionExecuting(RequestContext context, TransitionDefinition transition) {...}
#Override
public void viewRendered(RequestContext context, View view, StateDefinition viewState) {...}
#Override
public void exceptionThrown(RequestContext context, FlowExecutionException exception) {...}
...
}
Related
I need to run a method after the Spring Application Context of my web app has started up. I looked at this question but it refers to Java Servlet startup, and none of the Spring stuff has run at that point.
Is there a "SpringContext.onStartup()" method I can hook into?
Use something like the following code:
#Component
public class StartupListener implements ApplicationListener<ContextRefreshedEvent> {
#Override
public void onApplicationEvent(final ContextRefreshedEvent event) {
// do your stuff here
}
}
Of course StartupListener will need to be within the component scan's reach
Take note however that if your application uses multiple contexts (for example a root context and a web context) this method will be run once for each context.
You can write listener like this:
#Component
public class SpringContextListener implements ApplicationListener<ApplicationEvent> {
public void onApplicationEvent(ApplicationEvent arg0) {
System.out.println("ApplicationListener");
};
}
Just add component scan path like this:
<context:component-scan base-package="com.controller" />
Have a look at Better application events in Spring Framework 4.2
#Component
public class MyListener {
#EventListener
public void handleContextRefresh(ContextRefreshedEvent event) {
...
}
}
Annotate a method of a managed-bean with #EventListener to automatically register an ApplicationListener matching the signature of the method. #EventListener is a core annotation that is handled transparently in a similar fashion as #Autowired and others: no extra configuration is necessary with java config and the existing < context:annotation-driven/> element enables full support for it.
I am having trouble getting multiple aspects to fire in a specific order. I am using the RequestProcessor to do certain things on every incoming request on my controllers, which have a certain parameter
Then I have some specific annotations that I will be adding to only certain methods within my controllers.
FYI I am using Eclipse, Tomcat, Maven and spring with java/annotation based configs. I use Tomcat and the WebApplicationInitializer to load my context, dispatcher, listeners etc. I have no web.xml. I can post that or the pom.xml if needed too.
The problem I am getting is that a method that satisfies both the ProcessRequest pointcut and the someAnnotation pointcut is firing the someAnnotation method first even though the order is specified to fire ProcessRequest first. There is some properties set in the ProcessRequest that is needed in some other annotations.
Here is a simplified version of my code.
Spring Config Class
#Configuration // Enable Spring Annotation Configuration. Equivalent to <context:annotation-config/>
#EnableAspectJAutoProxy
#EnableCaching // Enable Spring caching
#EnableWebMvc // Enable Spring MVC Annotation. Equivalent to <mvc:annotation-driven />.
#ComponentScan(basePackages = {"xxx.yyy.zzz"}) // Scan for Spring Components. Equivalent to <context:component-scan>
public class WebAppConfig extends WebMvcConfigurerAdapter {
// Other Bean logic here
#Bean
public RequestProcessor requestProcessor() {
return new RequestProcessor();
}
#Bean
public AnnotationAspect annotationAspect() {
return new AnnotationAspect();
}
}
Aspect #1
#Aspect
#Order(0)
public class RequestProcessor {
#Pointcut("execution(* xxx.yyy.zzz.api..*.*(xxx.yyy.zzz.objects.api.Request,..)) && args(request)")
public void pointcut(Request<?> request) {}
#Before("pointcut(request)")
public void processRequest(Request<?> request) throws IOException, BadSignatureException {
// Some logic here that is independent of other and needs to run before other aspect which references annotation
}
}
Aspect #2
#Aspect
#Order(1)
public class AnnotationAspect {
#Before("#annotation(xxx.yyy.zzz.annotation.SomeAnnotation)")
public void someAnnotation() {
// Log for this annotation
}
// Some other annotation methods here
}
Also tried this format implements Ordered
#Aspect
public class RequestProcessor implements Ordered {
#Override
public int getOrder() {
return 0;
}
#Pointcut("execution(* xxx.yyy.zzz.api..*.*(xxx.yyy.zzz.objects.api.Request,..)) && args(request)")
public void pointcut(Request<?> request) {}
#Before("pointcut(request)")
public void processRequest(Request<?> request) throws IOException, BadSignatureException {
// Some logic here that is independent of other and needs to run before other aspect which references annotation
}
}
I read over this post and some others but couldn't find anything relevant that worked.
Ordering aspects with Spring AOP && MVC
****UPDATE****
So I have been reading through the AspectJ docs on declaring precedence so I thought I would give it a whirl. I created a simple aspect that only declares precedence and it works just fine.
Here is my Precedence Aspect:
public aspect AspectPrecedence {
declare precedence : RequestProcessor, SomeAspect;
}
I am not going to submit this as answer just yet because I would like to understand why the annotation or "implements Ordered" are not functioning properly in my project.
Any insight would be greatly appreciated. Thanks!
****UPDATE 2****
For reference this works locally in my eclipse environment and seemed to work when deployed to AWS via WAR file.
#Aspect
#DeclarePrecedence("RequestProcessor, SomeAspect")
public class RequestProcessor {
#Pointcut("execution(* xxx.yyy.zzz.api..*.*(xxx.yyy.zzz.objects.api.Request,..)) && args(request)")
public void pointcut(Request<?> request) {}
#Before("pointcut(request)")
public void processRequest(Request<?> request) throws IOException, BadSignatureException {
// Some logic here that is independent of other and needs to run before other aspect which references annotation
}
}
When using Eclipse with AspectJ support that automatically enables compile time weaving in your IDE. Which means the aspects get woven into your byte code, opposed to Spring which uses proxies to apply aspects.
Using an aspect to declare precedence or using #DeclarePrecedence will only work when using either compile or load time weaving (the latter can be enabled by specifying <context:load-time-weaver/> depending on your container might require some additional configuration). However both should work (you might need to specify the AspectJ compiler as a compiler for the #Aspect classes instead of the default java compiler).
#Order and Ordered only work for the proxy based solution and are ignored by AspectJ.
I think the problem could be that you're using LTW with AspectJ instead AOP with Spring, as #Order is defined for Spring, the container(AspectJ) is not able to determine the ordering of both the advices, so try one of these:
Try flipping the order of #Aspect and #Order annotations
You can try the annotation #DeclarePrecedence from AspectJ and then configuring your aspects into the aop.xml
<aspects>
<aspect name="your.package.RequestProcessor"/>
<aspect name="your.package.AnnotationAspect"/>
<concrete-aspect name="my_aspect_configuration_precedence"
precedence="*..*RequestProcessor, *"/>
</aspects>
I don't know if it's going to work but expect to give you a pointer on that
I am new to Spring AOP.
Using annotation based Spring configuration:
#Configuration
#EnableAspectJAutoProxy(proxyTargetClass=true)
#ComponentScan({"sk.lkrnac"})
Aspect:
#Aspect
#Component
public class TestAspect {
#Before("execution(* *(..))")
public void logJoinPoint(JoinPoint joinPoint){
....
}
}
Spring compoment:
package sk.lkrnac.testaop;
#Component
public class TestComponent{
#PostConstruct
public void init(){
testMethod();
}
public void testMethod() {
return;
}
}
How can I intercept all public methods that are called by Spring framework itself? (e.g. TestComponent.init() during creation of the TestComponent instance by Spring)
Currently I am able to intercept only TestComponent.testMethod() by invoking:
TestComponent testComponent = springContext.getBean(TestComponent.class);
testComponent.testMethod();
This is a common issue you run into with Spring AOP. Spring accomplishes AOP by proxying advised classes. In your case, your TestComponent instances will be wrapped in a run-time proxy class that provides the "hooks" for any aspect advice to be applied. This works very well when methods are called from outside the class, but as you have discovered it doesn't work on internal calls. The reason is that internal calls will not pass the proxy barrier, thus will not trigger the aspect.
There are primarily two ways around this. One is to fetch an instance of the (proxied) bean from the context. This is what you have already tried with success.
The other way is to use something called load-time weaving. When using this, AOP advices are added to a class ("weaved" into it) by a custom class-loader by injecting byte-code into the class definition. The Spring documentation has more on this.
There is a third way, which is called "compile time weaving". In this scenario, your AOP advices are statically weaved into each advised class when you compile it.
You can't intercept init() without any explicit means, please see the SpringSource Jira for details.
You can also try to call inner testMethod() from init() by self via proxy object like Don explained in https://stackoverflow.com/a/5786362/6786382.
I have a class that is using Spring AOP framework in my web app just like the code shown below. I was wondering why the Spring AOP was able to trace add() but not able trace multiple() if I implement the following code.
public interface calculator {
public void add();
public void multiple();
}
public class calculatorImpl implements calculator {
public void add() {
multiple();
}
public void multiple() {}
}
I've did an experiment and found out that the following code is working fine. Meaning that the Spring AOP able to trace both add and multiple function.
ICalculator calcProxy = (ICalculator) context.getBean("calculatorProxy");
calcProxy.add();
calcProxy.multiple();
I think that must be cause by the multiple() was inject by the proxy bean whereas the multiple() in calculatorImpl class wasn't, thus Spring AOP wasn't able to trace it. Correct me if I am wrong.
My next question. Is there a work around on this issue where the add() really need to execute multiple() and get trace by the Spring AOP?
Spring AOP doesn't change the actual method, but adds a proxy around the Object. Once you are inside the inner Object (inside the add() method), there is no proxy, you are underneath it.
I have explained this mechanism in more detail in this past answer:
https://stackoverflow.com/a/4171649/342852
There are three ways to deal with that situation:
Use AspectJ, not Spring AOP. AspectJ compiles the behavior into your code, whereas Spring AOP works with proxies that surround your code.
Access the Proxy from inside the Bean. This is reeeeaaally ugly, but it can be done.
Example code (here you make the class aware of it's being proxied, which is contrary to everything AOP stands for):
public void add() {
((Calculator)AopContext.currentProxy()).multiple();
}
Slightly less horrible (here you make the class aware that it's being managed by Spring):
#Autowired
private ApplicationContext context;
public void add() {
context.getBean(Calculator.class).multiple();
}
Change your design. Example: move the two methods to separate beans and inject each into the other bean.
I have the following ApplicationListener:
package org.mycompany.listeners;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextStartedEvent;
public class MyApplicationListener implements ApplicationListener<ContextStartedEvent> {
public MyApplicationListener() {
super();
System.out.println("Application context listener is created!");
}
/**
* {#inheritDoc}
*/
public void onApplicationEvent(final ContextStartedEvent event) {
System.out.println("Context '" + event.getApplicationContext().getDisplayName() + "' is started!");
}
}
And the following bean definition:
<bean name="myApplicationListener" class="org.mycompany.listeners.MyApplicationListener" />
I can see that bean is created as message from the constructor is printed, but context start event is never recieved. What am I missing?
ContextStartedEvent is published when you explicitly invoke ConfigurableApplicationContext.start() on the context. If you need an event that is published when context is initialized, use ContextRefreshedEvent.
See also:
3.13.2 Standard and Custom Events
Since you have no lazy loaded beans (according to you) then you are most likely using events for the wrong reason and probably should use something like InitializingBean interface instead:
public class MyBean implements InitializingBean {
#Override
public void afterPropertiesSet() throws Exception {
// ...
}
}
From Spring manual:
To interact with the container's management of the bean lifecycle, you
can implement the Spring InitializingBean and DisposableBean
interfaces. The container calls afterPropertiesSet() for the former
and destroy() for the latter to allow the bean to perform certain
actions upon initialization and destruction of your beans. You can
also achieve the same integration with the container without coupling
your classes to Spring interfaces through the use of init-method and
destroy method object definition metadata.
Source: Spring Framework - Lifecycle callbacks
Not sure if this helps, but I vaguely remember having a similar problem, which was solved by preloading and not lazy loading. Here's a quick overview of both