My goal is to execute some code each time a method with a particular annotation completes its execution. I have the following:
#Aspect
public class MonitoringAspect {
#After("#annotation(MonitorExecution)")
public void onFinished(JoinPoint jp) {
System.out.println("called!");
}
}
The code of the MonitorExecution annotation is as follows:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface MonitorExecution {
}
This happens inside a Spring4 application and I have declared the MonitoringAspect as a bean like:
<bean id="monitoringAspect" class="com.....MonitoringAspect" />
<aop:aspectj-autoproxy proxy-target-class="true">
<aop:include name="monitoringAspect"/>
</aop:aspectj-autoproxy>
I have a public method inside a general-purpose class (i.e. not managed by spring / not a component) that is annotated with the #MonitorExecution annotation. I have successfully verified that the aforementioned method gets called, but the aspect is never triggered. Any ideas what might be the issue?
aspectj-autoproxy means that proxy classes will be created for each Spring managed bean (using JDK dynamic proxies or CGLIB) and using these proxies you are able to intercept methods invocation. Thus, if your annotated method is method of a class outside Spring Context, aspect will not work
Despite it if you still want to intercept it, you will have to use AspectJ only or in conjunction with Spring. For second option you will have to enable load-time weaving in your spring configuration
Have a look documentation for details:
https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#aop
I am trying to print logs for private and package scoped methods (from package scoped classes) in my application which is based on Spring. Since Spring's proxy based aspects don't work on private and package scoped methods(?), I tried to use AspectJ load time weaving as per the documentation. Below are the details:
LoggingAspect
#Component(Constants.LOGGING_ASPECT_BEAN)
#Aspect
public class LoggingAspect {
#Around("#annotation(my.pkg.Loggable)")
public Object doLogging(final ProceedingJoinPoint joinPoint) throws Throwable {
// ... logging code.
}
}
Spring Configuration
#Configuration
#EnableAspectJAutoProxy(proxyTargetClass = true)
#Import(AnotherBaseConfig.class)
#ComponentScan("my.pkg")
#EnableWebMvc
#EnableLoadTimeWeaving(aspectjWeaving = AspectJWeaving.AUTODETECT)
public class AppConfiguration extends WebMvcConfigurerAdapter {
// ... bean configs
}
src/main/webapp/META-INF/aop.xml
<aspectj>
<weaver>
<include within="my.pkg.*">
</weaver>
<aspects>
<aspect name="my.logger.LoggingAspect"/>
</aspects>
</aspectj>
JVM Config
-javaagent:"/path/to/spring-instrument-4.0.6.jar" -javaagent:"/path/to/aspectjweaver-1.8.1.jar"
With above configurations, I have my package scoped classes as:
#Component("someClass")
class SomeClass {
#Loggable
void doSomething(#Loggable final String s, #Loggable final Integer i) {
// ... do something here.
}
}
The weaving is not working for such package scoped classes. Am I doing anything wrong?
You are mixing up two AOP styles here:
Spring AOP ist a proxy-based "AOP lite" approach which is activated via #EnableAspectJAutoProxy. It only works for public, non-static methods of Spring components.
AspectJ, a full-blown AOP framework which also works on package-local or private methods, no matter if they are Spring components or not, is enabled from within Spring via #EnableLoadTimeWeaving and the weaving agent via -javaagent:"/path/to/aspectjweaver-1.8.1.jar". In this case your aspect does not need to be a Spring component either. P.S.: I recommend to use the latest AspectJ version 1.8.5, not 1.8.1.
As you want to work with package-locals, you need AspectJ LTW. Please configure Spring clearly in preference of one AOP type. More information can be found in the Spring Manual, chapter 9. Specific information about AspectJ LTW configuration is in chapter 9.8.
I want to run some code before every method in a Spring (3.2.3) #Controller. I have the following defined but it won't run. I suspect the pointcut expression is incorrect.
dispatcher-servlet.xml
<aop:aspectj-autoproxy/>
<bean class="com.example.web.controllers.ThingAspect"/>
c.e.w.c.ThingAspect
#Pointcut("execution(com.example.web.controllers.ThingController.*(..))")
public void thing() {
}
#Before("thing()")
public void doStuffBeforeThing(JoinPoint joinPoint) {
// do stuff here
}
Your pointcut expression is missing a return type like void, String or *, e.g.
execution(* com.example.web.controllers.ThingController.*(..))
The correct way to do it in current versions of Spring MVC is through a ControllerAdvice.
See: Advising controllers with the #ControllerAdvice annotation
For previous versions, refer to this answer of mine:
https://stackoverflow.com/a/5866960/342852
Besides #ControllerAdvice that is already mentioned in another answer, you should check out Spring MVC interceptors.
They basically simplify AOP for controllers and can be used in cases where #ControllerAdvice doesn't give you enough power.
In my web application(Spring based), i have simple layered architecture as Service->Manager->Dao->database. For logging purpose i want to log request coming to Service and then exit from Service at one go so that it is easy to debug issues. Otherwise logs contain various output from different threads intermingle with each other which is not easy to read. Is it possible with existing logging framework like log4j.
It is possible with any logging framework. You can use AOP to create a "logging" aspect around your service methods. Here is some example.
You can use Spring AOP to implement such logs. Here's an example:
#Aspect
public class LoggingAspect {
private static final Logger LOG = LoggerFactory.getLogger(LoggingAspect.class);
#Pointcut("call(* com.yourcompany.*.*(..))")
public void serviceMethod() {
}
#Before("serviceMethod()")
public void logMethodCalls(final JoinPoint joinPoint) {
if (LOG.isDebugEnabled())
LOG.debug("Calling method {} with args {}",
joinPoint.getSignature(), joinPoint.getArgs());
}
}
Just wire it as a Spring Bean:
<bean class="com.somepackage.LoggingAspect" />
<aop:aspectj-autoproxy/>
and calls to public methods of Spring beans in the matched packages should be logged.
I would like to make use of request scoped beans in my app. I use JUnit4 for testing. If I try to create one in a test like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:spring/TestScopedBeans-context.xml" })
public class TestScopedBeans {
protected final static Logger logger = Logger
.getLogger(TestScopedBeans.class);
#Resource
private Object tObj;
#Test
public void testBean() {
logger.debug(tObj);
}
#Test
public void testBean2() {
logger.debug(tObj);
}
With the following bean definition:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="java.lang.Object" id="tObj" scope="request" />
</beans>
And I get:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'gov.nasa.arc.cx.sor.query.TestScopedBeans': Injection of resource fields failed; nested exception is java.lang.IllegalStateException: No Scope registered for scope 'request'
<...SNIP...>
Caused by: java.lang.IllegalStateException: No Scope registered for scope 'request'
So I found this blog that seemed helpful:
http://www.javathinking.com/2009/06/no-scope-registered-for-scope-request_5.html
But I noticed he uses AbstractDependencyInjectionSpringContextTests which seems to be deprecated in Spring 3.0.
I use Spring 2.5 at this time, but thought it shouldn't be too hard to switch this method to use AbstractJUnit4SpringContextTests
as the docs suggest (ok the docs link to the 3.8 version but I'm using 4.4). So I change the
test to extend AbstractJUnit4SpringContextTests... same message. Same problem. And now the prepareTestInstance() method I want
to override is not defined. OK, maybe I'll put those registerScope calls somewhere else... So I read more about TestExecutionListeners and think that would be better since I don't want to have to inherit the spring package structure. So
I changed my Test to:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:spring/TestScopedBeans-context.xml" })
#TestExecutionListeners({})
public class TestScopedBeans {
expecting I would have to create a custom listener but I when I ran it. It works! Great, but why? I don't see where any of the stock listeners
are registering request scope or session scope, and why would they? there's nothing to say I want that yet, this might not be a Test for Spring MVC code...
Solution for Spring 3.2 or newer
Spring starting with version 3.2 provides support for session/request scoped beans for integration testing.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = TestConfig.class)
#WebAppConfiguration
public class SampleTest {
#Autowired WebApplicationContext wac;
#Autowired MockHttpServletRequest request;
#Autowired MockHttpSession session;
#Autowired MySessionBean mySessionBean;
#Autowired MyRequestBean myRequestBean;
#Test
public void requestScope() throws Exception {
assertThat(myRequestBean)
.isSameAs(request.getAttribute("myRequestBean"));
assertThat(myRequestBean)
.isSameAs(wac.getBean("myRequestBean", MyRequestBean.class));
}
#Test
public void sessionScope() throws Exception {
assertThat(mySessionBean)
.isSameAs(session.getAttribute("mySessionBean"));
assertThat(mySessionBean)
.isSameAs(wac.getBean("mySessionBean", MySessionBean.class));
}
}
Read more: Request and Session Scoped Beans
Solution for Spring before 3.2 with listener
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = TestConfig.class)
#TestExecutionListeners({WebContextTestExecutionListener.class,
DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class})
public class SampleTest {
...
}
WebContextTestExecutionListener.java
public class WebContextTestExecutionListener extends AbstractTestExecutionListener {
#Override
public void prepareTestInstance(TestContext testContext) {
if (testContext.getApplicationContext() instanceof GenericApplicationContext) {
GenericApplicationContext context = (GenericApplicationContext) testContext.getApplicationContext();
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST,
new SimpleThreadScope());
beanFactory.registerScope(WebApplicationContext.SCOPE_SESSION,
new SimpleThreadScope());
}
}
}
Solution for Spring before 3.2 with custom scopes
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = TestConfig.class, locations = "test-config.xml")
public class SampleTest {
...
}
TestConfig.java
#Configuration
#ComponentScan(...)
public class TestConfig {
#Bean
public CustomScopeConfigurer customScopeConfigurer(){
CustomScopeConfigurer scopeConfigurer = new CustomScopeConfigurer();
HashMap<String, Object> scopes = new HashMap<String, Object>();
scopes.put(WebApplicationContext.SCOPE_REQUEST,
new SimpleThreadScope());
scopes.put(WebApplicationContext.SCOPE_SESSION,
new SimpleThreadScope());
scopeConfigurer.setScopes(scopes);
return scopeConfigurer
}
or with xml configuration
test-config.xml
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="request">
<bean class="org.springframework.context.support.SimpleThreadScope"/>
</entry>
</map>
<map>
<entry key="session">
<bean class="org.springframework.context.support.SimpleThreadScope"/>
</entry>
</map>
</property>
</bean>
Source code
Source code for all presented solutions:
https://github.com/mariuszs/spring-test-web
I've tried several solutions, including #Marius's solution with the "WebContextTestExecutionListener", but it didn't work for me, as this code loaded the application context before creating the request scope.
The answer that helped me in the end is not a new one, but it's good:
http://tarunsapra.wordpress.com/2011/06/28/junit-spring-session-and-request-scope-beans/
I simply added the following snippet to my (test) application context:
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="request">
<bean class="org.springframework.context.support.SimpleThreadScope"/>
</entry>
</map>
</property>
</bean>
Good luck!
A solution, tested with Spring 4, for when you require request-scoped beans but aren't making any requests via MockMVC, etc.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(/* ... */)
public class Tests {
#Autowired
private GenericApplicationContext context;
#Before
public void defineRequestScope() {
context.getBeanFactory().registerScope(
WebApplicationContext.SCOPE_REQUEST, new RequestScope());
RequestContextHolder.setRequestAttributes(
new ServletRequestAttributes(new MockHttpServletRequest()));
}
// ...
The test passes because it isn't doing anything :)
When you omit the #TestExecutionListeners annotation, Spring registers 3 default listeners, including one called DependencyInjectionTestExecutionListener. This is the listener responsible for scanning your test class looking for things to inject, including #Resource annotations. This listener tried to inject tObj, and fails, because of the undefined scope.
When you declare #TestExecutionListeners({}), you suppress the registration of the DependencyInjectionTestExecutionListener, and so the test never gets tObj injected at all, and because your test is not checking for the existence of tObj, it passes.
Modify your test so that it does this, and it will fail:
#Test
public void testBean() {
assertNotNull("tObj is null", tObj);
}
So with your empty #TestExecutionListeners, the test passes because nothing happens.
Now, on to your original problem. If you want to try registering the request scope with your test context, then have a look at the source code for WebApplicationContextUtils.registerWebApplicationScopes(), you'll find the line:
beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope());
You could try that, and see how you go, but there might be odd side-effects, because you're not really meant to do this in a test.
Instead, I would recommend rephrasing your test so that you don't need request scoped beans. This shouldn't be difficult, the lifecycle of the #Test shouldn't be any longer than the lifecycle of a request-scoped bean, if you write self-contained tests. Remember, there's no need to test the scoping mechanism, it's part of Spring and you can assume it works.
This is still an open issue:
https://jira.springsource.org/browse/SPR-4588
I was able to get this to work (mostly) by defining a custom context loader as outlined in
http://forum.springsource.org/showthread.php?p=286280
Test Request-Scoped Beans with Spring explains very well how to register and create a custom scope with Spring.
In a nutshell, as Ido Cohn explained, it's enough to add the following to the text context configuration:
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="request">
<bean class="org.springframework.context.support.SimpleThreadScope"/>
</entry>
</map>
</property>
</bean>
Instead of using the predefined SimpleThreadScope, based on ThreadLocal, it's also easy to implement a Custom one, as explained in the article.
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;
public class CustomScope implements Scope {
private final Map<String , Object> beanMap = new HashMap<String , Object>();
public Object get(String name, ObjectFactory<?> factory) {
Object bean = beanMap.get(name);
if (null == bean) {
bean = factory.getObject();
beanMap.put(name, bean);
}
return bean;
}
public String getConversationId() {
// not needed
return null;
}
public void registerDestructionCallback(String arg0, Runnable arg1) {
// not needed
}
public Object remove(String obj) {
return beanMap.remove(obj);
}
public Object resolveContextualObject(String arg0) {
// not needed
return null;
}
}
MariuszS' solution works, except I couldn't get the transaction committed properly.
It seems the newly released 3.2 has finally made testing request/session scoped beans first class citizens. Here's a couple of blogs for more details.
Rossen Stoyanchev's Spring Framework 3.2 RC1: Spring MVC Test Framework
Sam Brannen's Spring Framework 3.2 RC1: New Testing Features
NOT reading the docs sometimes drives one crazy. Almost.
If you are using shorter-lived beans (request scope for example), you most likely also need to change your lazy init default! Otherwise the WebAppContext will fail to load and tell you something about missing request scope, which is of course missing, because the context is still loading!
http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/beans.html#beans-factory-lazy-init
The Spring guys should definitely put that hint into their exception message...
If you don't want to change the default, there is also the annotation way: put "#Lazy(true)" after #Component etc. to make singletons initialize lazy and avoid instantiating request-scoped beans too early.