In our project we're trying to move from Spring standard AOP to AspectJ as explained in many places like this (we need to make transactional some private and protected methods).
We had been able to make this work fine with standard Spring annotations like #Transactional. But we face the problem that in our project there are some custom Annotations (not custom aspects) that are not recognized by AspectJ. For example, we have this annotation that 'extends' #Transactional (only modifies rollbackFor property):
#Target({ ElementType.METHOD, ElementType.TYPE })
#Retention(RetentionPolicy.CLASS)
#Transactional(rollbackFor = Exception.class)
public #interface TransactionalWithCheckedException {
}
Any idea on how to tell AspectJ to weaver also this custom annotations? Is it possible? If not, should i build custom Aspects extending Spring ones (i.e.: for #Transactional).
For more information this is the aop.xml:
<aspectj>
<aspects>
<!-- Aparently no need to explicit declare aspects like the ones used by Spring #Transactional or #Async, they work fine. -->
</aspects>
<weaver options="-XnoInline -Xreweavable -showWeaveInfo">
<include within="com.mycompany.myproject..*" />
</weaver>
And part of Spring's context config file:
<tx:annotation-driven mode="aspectj" />
<context:load-time-weaver />
Well, first of all, the Spring #Transactional annotation is not a meta-annotation, so it cannot be used on annotation types. Additionally, nothing in the spring transaction code would support this kind of usage. In order to support this kind of usage, you'll have to create a specialized aspect with the proper pointcuts to identify the join points at transaction boundaries, probably supporting both the original Spring #Transactional annotation and your custom annotation as well. You'll also need to provide the aspect with an implementation of TransactionAttributeSource to support you own source of metadata, in your case an implementation of that interface handling #TransactionalWithCheckedException annotation (your point of interest will be the TransactionAttribute.rollbackOn(Throwable ex) method). You can then use CompositeTransactionAttributeSource as the implementation of the TransactionAttributeSource so you can combine the metadata sources supporting Spring's #Transactional annotation and your annotation as well.
To sum it up, you'll need these two things to handle special transaction attributes:
a concrete Aspect supporting both your and Spring's annotations (probably a subclass of AbstractTransactionAspect, see AnnotationTransactionAspect for implementation ideas`
a TransactionAttributeSource implementation handling your transaction annotation. Use CompositeTransactionAttributeSource to combine support for your metadata with spring's metadata (AnnotationTransactionAttributeSource).
Related
I want to add API versions across all log statements. To achieve that, I have created a custom annotation and its corresponding interceptor.
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD, ElementType.TYPE, ElementType.PACKAGE})
#Around
#Documented
public #interface LogAPIVersion {
String apiVersion() default "";
}
Things are working fine if I place annotation on a method or a class.
But, I want to apply this annotation at a package level (for all classes and their methods). I've created a package-info.java file and decorated my custom annotation onthe package name.
#LogAPIVersion(apiVersion = "v1")
package com.example.controllers.v1;
Unfortunately, my logging interceptor is not getting invoked. How do I make this work?
The simple answer is: Neither native AspectJ nor simpler AOP frameworks such as Spring AOP provide any means to intercept package-level annotations.
What you could do is use native AspectJ's annotation processing support in order to generate annotations for all classes or methods you wish to target during your build and then intercept them using an aspect. Here are some of my old answers showing examples of how to use the feature:
https://stackoverflow.com/a/29877757/1082681
https://stackoverflow.com/a/40449796/1082681
https://stackoverflow.com/a/29437129/1082681
It might need to be annotated with #Inherited.
I've run into a strange problem that I am having difficulty tracking down. I have an class (ServiceErrorInterceptor) defined as an #Aspect which is instantiated via XML configuration as a singleton bean. The XML configuration allows me to inject its dependent beans.
In my normal workflow, everything works fine. The aspect class is properly instantiated, and whenever the advice is called, the injected beans are as I would expect.
However, when I run my JUnit test, all my injected beans are null. This leads me to the conclusion that the advice is called from a different bean - not the same singleton bean that was instantiated by Spring. To further validate my hypothesis, I put a breakpoint on a setter which is called during the instantiation, and see that the bean id is not the same as the bean id if I put a breakpoint in my advice.
Is there some special configuration I must enable in my JUnit class to rectify this? My test class is already annotate with:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {
"classpath:spring/applicationContext-base.xml",
"classpath:spring/applicationContext-calculateServices.xml",
"classpath:spring/applicationContext-dom.xml"})
public class LendingSimulationServiceImplTest {
...
...
}
I've looked through the logs (I enabled spring trace logs), but don't see anything that stands out. And posting the entire log here would likely be overkill. If there is value in a specific part of the log please let me know and I will post it.
I'm able to post my code for the aspect, my junit and my config if that is helpful.
application-context.xml snippet:
<!-- SPRING ASPECT BEAN. POINTCUT DEFINED IN BEAN WITH ANNOTATION -->
<bean id="serviceErrorInterceptor" class="com.cws.cs.lendingsimulationservice.error.ServiceErrorInterceptor" scope="singleton">
<property name="errorMessageProvider" ref="resourceBundleProviderImpl"/>
<property name="defaultLocale">
<util:constant static-field="java.util.Locale.ENGLISH" />
</property>
</bean>
Any suggestions would be appreciated.
EDIT
My bean is implemented as:
#Aspect
public class ServiceErrorInterceptor {
/**
* Logger
*/
private static final Logger logger = LoggerFactory.getLogger(ServiceErrorInterceptor.class);
/**
* SOAP Header data
*/
#Autowired
private SOAPHeaderData soapHeaderData;
public ServiceErrorInterceptor(){
int x = 0;
x=x+1;
}
/**
* Exception Interceptor.
* #param ex
*/
#AfterThrowing(pointcut = "execution(* com.cws.cs.lendingsimulationservice.process.CalculatorProcess.calculate (..))", throwing = "ex")
public void errorInterceptor(Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error Message Interceptor started");
}
}
The relevant portions of my pom:
<!-- Aspect Oriented Programming (AOP) Framework (depends on spring-core,
spring-beans) Define this if you use Spring AOP APIs (org.springframework.aop.*) -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<!-- Support for AspectJ Annotations -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${org.aspectj}</version>
</dependency>
I've done further debugging and putting a breakpoint in the dummy constructor, I get the following results:
with #Aspect and XML configuration, the constructor is called twice (different bean ids)
if I remove the #Aspect annotation then it is only called once.
If leave the #Aspect but remove the XML configuration, then the constructor isn't even called.
If I use an #Component annotation in combination with #Aspect (but without any XML configuration), then the bean is constructed twice.
Oddly enough, however, with both the #Component and #Aspect annotations AND the XML configuration, the constructor is still only called twice.
So then why would having both the XML configuration and the #Aspect annotation cause the constructor to be called twice with two different bean ids?
I have further validated that if I move the entire AOP definition into the XML configuration (removing the #Aspect and #Pointcut annotations) then the bean is only constructed once.
END EDIT
Thanks,
Eric
Does your aspect by any chance have any of the autodetect annotations (#Component, #Service) etc, apart from #Aspect - if it does, that could be one reason why a different bean seems to be present along with the one defined in your configuration.
Also, just scan through all your configuration to make sure that you are not declaring this bean elsewhere.
There is nothing special that needs to be done at the Junit level that I am aware of.
After a lot of discussion with the folks over at the SpringSource STS forum (see this thread), it turns out that the issue is related to the AJDT configuration. At the moment, AJ is weaving in the aspect, and Spring is locating the aspect on the Classpath, so they are both being executed.
Unfortunately, the AJ maven plugin is missing a configuration parameter to allow for exclusion of weaving; the current configuration excludes both LTW and CTW.
So, the workaround at the moment is to add -xmlConfigured to the AJ compiler flags and then specify an aop.xml file in aop.xml management which only lists the AJ aspects that you want to include in the project.
To get this to work, add '-xmlConfigured' to the Project Properties
'Non-standard compiler options' and then in AspectJBuild>'aop.xml
management' point it at a simple aop.xml file:
<aspectj>
<aspects>
<aspect name="com.fooMyNewNoneSpringAspect"/>
</aspects>
</aspectj>
Thanks to Andy Clement at the STS forum for this discovery and workaround. He will be raising JIRA issues to further address this shortcoming in the maven plugin.
A possible way you might find yourself in the same situation: you used the new operator rather than letting Spring inject your service.
Remember, we're not in AspectJ compile time weaving here. Nope, we're using Spring AOP proxies, so Spring must instantiate the object and dress it with proxies. If you were silly, like myself, and created a new service inside your tests, you won't get any AOP.
All the more reason to do compile time weaving with AspectJ and skip over all of the drawbacks of Spring AOP such as runtime weaving startup delay, inability to weave non-public and non-final classes.
Anyone knows why apparently it is not possible to use AOP with annotated MVC Controllers? (see Post).
I have a #Controller that stops working as soon as I add a pointcut to it.
The problem is not that the interceptor is not being called, but rather the #Controller simply stops working (in the log you can see that instead of "Mapped URL path [/xx] onto handler 'Yyy'" you get a "no URL paths identified").
I know there is a mechanism for adding interceptors to controllers via the handlerMapping but my question is specific to AOP interceptors. Aren't annotated controllers just pojos in the Spring container as any other pojo? What is the difference? Why?
#Controller
#RequestMapping("/user")
public class RestTestImpl implements RestTest {
#RequestMapping(value="/", method={RequestMethod.GET})
public #ResponseBody String deleteUsers(String arg) {
return "Xxxxx";
}
}
In my servlet-Context I have:
<context:component-scan base-package="org.xxx.yyy"></context:component-scan>
<mvc:annotation-driven />
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
. . .
</bean>
And everything works just great.
But when I add:
<aop:config>
<aop:pointcut expression="execution(* org.xxx.*(..))" id="pc1"/>
<aop:advisor advice-ref="hibernateInterceptor" pointcut-ref="pc1" order="2" />
</aop:config>
The controller stops being a controller (no errors, simply it stops binding to the specified URL)!
From the Spring MVC Reference:
Note
When using controller interfaces (e.g. for AOP proxying),
make sure to consistently put all your
mapping annotations - such as
#RequestMapping and #SessionAttributes
- on the controller interface rather than on the implementation class.
Granted, this note is well hidden :-)
I ran into the same issue and found out the solution.
Indeed your controller (annotated by #Controller) and your aspects (annotated by #Aspect) should be in the same Spring context.
Usually people define their controllers in the dispatch-servlet.xml or xxx-servlet.xml and their service beans (including the aspects) in the main applicationContext.xml. It will not work.
When Spring initializes the MVC context, it will create a proxy for your controller but if your aspects are not in the same context, Spring will not create interceptors for them.
The above ssertion does not depend
on the way your declare your controllers/aspects (by manual XML declaration or annotation style)
on the proxying style you choose (JDK proxy or CGLIB)
I've tested all the combinations and they all work as long as the controller & aspects are in the same Spring context
My best guess without doing some serious digging is because Spring's AOP mechanism that you are using is wrapping the target classes in proxy classes which end up loosing their annotation or the original annotation gets dropped after weaving.
I am sure there is a better answer and I'll expand on mine as I think of a better more clear way to present it.
How to enable the #Required annotation in Java (Spring 3.1) ? Not in a xml, but in through Java. Also under which annotation I put this enabling? Under #Feature (in #FutureConfiguration or #Bean (in #Configuration) ?
Edit:
#Feature
public MvcAnnotationDriven annotationDriven(ConversionService conversionService) {
return new MvcAnnotationDriven().conversionService(conversionService)
.argumentResolvers(new CustomArgumentResolver());
}
Does this enables all annotations?
#anubhava's answer works, but he's referenced the Spring 2.0 manual, which is 5 years old.
In XML config, Spring 3.x has a more elegant approach: <context:annotation-config/>. This also enabled a whole other bunch of features that you'll probably want, whereas RequiredAnnotationBeanPostProcessor only enables a few.
See Spring 3.x manual.
If you're using #Bean-style config, then annotations like #Required should already be enabled, since that's how #Bean works. However, it's possible that this is a bug - Spring 3.1 is still in early beta, and big chunks of it are likely to be broken.
Unless you really know what you're doing, I strongly recommend sticking to 3.0.x.
From the Spring manual:
There is one last little (small, tiny)
piece of Spring configuration that is
required to actually 'switch on' this
behavior. Simply annotating the
'setter' properties of your classes is
not enough to get this behavior. You
need to enable a component that is
aware of the #Required annotation and
that can process it appropriately.
This component is the
RequiredAnnotationBeanPostProcessor
class. This is a special
BeanPostProcessor implementation that
is #Required-aware and actually
provides the 'blow up if this required
property has not been set' logic. It
is very easy to configure; simply drop
the following bean definition into
your Spring XML configuration.
<bean class=
"org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor"/>
Please check: http://static.springsource.org/spring/docs/2.0.x/reference/metadata.html
Use AnnotationConfigApplicationContext if you don't want to use XML:
Standalone application context,
accepting annotated classes as input -
in particular #Configuration-annotated
classes, but also plain #Components
and JSR-330 compliant classes using
javax.inject annotations. Allows for
registering classes one by one
(register(java.lang.Class...)) as well
as for classpath scanning
(scan(java.lang.String...)).
In case of multiple Configuration
classes, Bean methods defined in later
classes will override those defined in
earlier classes. This can be leveraged
to deliberately override certain bean
definitions via an extra Configuration
class.
Sample Code:
ConfigurableApplicationContext applicationContext =
new AnnotationConfigApplicationContext(
"com.mycompany.package1",
"com.mycompany.package2",
"com.mycompany.package3"
// etc.
);
applicationContext.refresh();
I want to inject a plain java object using Spring programmatically without using any xml configuration. Want to inject fields/methods annotated with tags Like #EJB, #PostConstruct etc. Is that possible? Thanks!
Creating an ApplicationContext without XML (using AnnotationConfigApplicationContext)
With AnnotationConfigApplicationContext, you don't need any XML at all. You create the Application context programatically and either
a) manually register annotated classes
appContext.register( MyTypeA.class,
MyTypeB.class,
MyTypeC.class );
b) or scan the classpath for annotated classes
appContext.scan(
"com.mycompany.myproject.mypackagea",
"com.mycompany.myproject.mypackageb"
)
If you use one of the convenience constructors
AnnotationConfigApplicationContext(Class<?> ... annotatedClasses)
or
AnnotationConfigApplicationContext(String ... basePackages)
the context is created and refreshed automatically, otherwise you need to call the refresh() method manually after adding the classes or packages.
Autowiring existing non-Spring beans (using AutowireCapableBeanFactory)
For autowiring an existing bean I think the preferred idiom is to use
appContext.getAutowireCapableBeanFactory().autowireBean(existingBean)
Or, if you want more control, use
appContext.getAutowireCapableBeanFactory()
.autowireBeanProperties(
existingBean,
autowireMode,
// e.g. AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE
dependencyCheck
)
For further reference, see
ApplicationContext.getAutowireCapeableBeanFactory()
AutowireCapeableBeanFactory.autowireBean(Object existingBean)
AutowireCapeableBeanFactory.autowireBeanProperties(Object
existingBean, int autowireMode,
boolean dependencyCheck)
Yes, you can annotate any POJO with #Component, #Service, #Controller, or #Respository (depending on its responsibilities), and it becomes a spring bean. You just have to put this line into the applicationContext.xml:
<context:component-scan base-package="org.yourbasepackage" />
You can also use #PostConstruct and #PreDestroy instead of the xml init-method and destroy-method.
Update: There is a project called spring-javaconfig. Parts of it have become part of the core spring and you can see more details here. In short, it allows you to use java classes instead of xml for configuring your beans.
The documentation around annotation-based Spring configuration can be found here. You will need a minimal amount of XML, so that Spring knows to look for annotation-based configuration:
<context:component-scan base-package=".." />
Once that is done, you can use the #Autowired annotation to set up your beans.