Pointcut targeting a third party JAR class is not triggered - java

As a temporary fix of the bug https://github.com/spring-projects/spring-hateoas/issues/220, I would like modify the return value of org.springframework.hateoas.core.AnnotationMappingDiscoverer.getMapping methods so that I can resolve placeholders manually. Here is the aspect I tried:
<aop:aspectj-autoproxy />
#Component
#Aspect
public class AnnotationMappingDiscovererFix {
#Around("execution(* org.springframework.hateoas.core.AnnotationMappingDiscoverer.getMapping(..))")
public Object resolvePlaceholders(ProceedingJoinPoint joinPoint) throws Throwable {
Object mapping = joinPoint.proceed();
// resolve placeholders manually...
return mapping;
}
}
But this pointcut gets never triggered, any idea why?

With proxy-based Spring AOP you can only target Spring beans/components. I am not a Spring user, so I do not know for sure, but I do not think that you can actually intercept Spring framework classes via its own AOP framework due to "hen vs. egg" bootstrapping problems.
But if you use full AspectJ via load-time weaving (LTW), you should be able to achieve what you want because the AspectJ weaving agent (aspectjweaver.jar) is loaded before the Spring classes and thus can modify them during classloading phase. The Spring documentation explains how to use AspectJ in connection with Spring.

Related

How to use Spring #Component annotation with AspectJ compiler

I excluded part of my project for easier reproduce problem: GitHub repo.
When I compile it by Javac everything works as expected. I see logging in console when I open URLs /user/ and /user/2/:
Access: execution(List ru.krivochenko.demo.user.UserController.getAll())
Access: execution(User ru.krivochenko.demo.user.UserController.getOne(Integer))
But I wanna use AspectJ compiler. When I switch to it, error occurs:
java.lang.NoSuchMethodError: ru.krivochenko.demo.logging.LoggingAspect: method <init>()V not found
As I understood it happens because there is not no-args constructor in LoggingAspect. If I add it, I get another error, because logger is not injected:
java.lang.NullPointerException: null
at ru.krivochenko.demo.logging.LoggingAspect.beforeGettingUsers(LoggingAspect.java:28) ~[classes/:na]
So, how we can see, AspectJ ignores Autowired constructor with args.
In branch via-setter of my repo I implemented another solution. I removed #Component annotation of LoggingAspect and replaced constructor injection to setter injection. In DemoApplication.java I added #Bean configuration of LoggingAspect. It works fine, but in some situations it requires getting dependencies from application context. What is the best practice to resolve it?
Thanks for help.
Spring Aspects and compile time weaving don't automatically integrate. This is primary because aspectj and spring are fairly separate and I suspect Spring's recommended approach is not to use compile time weaving.
So thus by default Aspects are not spring magic and we need to add a little bit of plumbing to ensure they are.
In this regard, it is important to note that Aspects are not spring managed (they are managed by aspectj so we need to add something to ensure they are).
Thus the reason why you need a parameterless constructor on your aspect (so must use field injection).
Traditionally I have had to add the following piece of xml to my xml config files:
<bean id="securityAspect" class="com.<skip>.security.AuthorizationAspect"
factory-method="aspectOf" autowire="byType" />
So this works because the AspectJ compiler adds the static method aspectOf to the aspects and this method is available for acquiring the instance of the Aspect that aspectj creates (and uses).
This method is obviously not available in the source so we can't just add to our application class (DemoApplication):
#Bean
public LoggingAspect loggingAspect() {
return LoggingAspect.aspectOf();
}
Then what to do? My next option was to write some reflective code to call this method then having looked at this very helpful example that demonstrates exactly what you need - The Aspects class from AspectJ has a utilty method that does this work for us, so adding the following to our DemoApplication we have success:
#Bean
public LoggingAspect loggingAspect() {
return Aspects.aspectOf(LoggingAspect.class);
}
Btw, remove the #Component from the LoggingAspect as that will mean both Aspectj and Spring create an instance of the class...
Btw, I'd also suggest you add the following to your test class to demonstrate the problem in a test:
#Autowired
private UserController controller;
#Test
public void contextLoads() {
controller.getAll();
controller.getOne(1);
}
Btw, other suggestions to address this problem used #Configurable. I suspect this might work but you'll need to make sure you include the spring aspects java in your aspectj compile time config and I suspect it may still not work as I'm not sure the Spring context will be ready in time. i.e. if the Aspect is created before the spring context then #Configurable won't work as the beans to be injected will not yet be created.
Your approach to configure the aspect via setter injection looks valid to me. For more information about how to use AspectJ in combination with Spring check out the corresponding chapter in the Spring manual, specifically the description about how to configure AspectJ aspects by Spring IoC. It is mostly explained in the context of LTW, but it should work pretty much the same for CTW.

Spring AOP: Advice on annotation used over another advice

I have created my custom annotation:
#Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface Condition {
String value();
}
I want to use this annotation to determine whether or not to run advice, my try:
#Condition("some.config")
#Around("execution(public * someMethod())")
Object doSomething(ProceedingJoinPoint joinPoint) throws Throwable {
// some logic here
}
#Around("#annotation(condition)")
Object checkCondition(ProceedingJoinPoint joinPoint, Condition condition) throws Throwable {
String property = (String) configuration.getProperty(condition.value());
if (Boolean.valueOf(property)){
return joinPoint.proceed();
} else {
return null;
}
}
It works when I use #Condition on some other methods, i.e. the checkCondition is applied and then the method is executed or not based on config value. For advice doSomething it doesn't get applied though.
You said your aspect works for other components, just not the aspect itself. From this statement I gather that
your aspect is wired correctly (e.g. annotated with #Component and detected by component scan or wired manually via XML config) and
you use proxy-based Spring AOP.
In (2) is the source of your problem. According to the Spring manual aspects themselves are exempt from being aspect targets themselves:
Advising aspects with other aspects?
In Spring AOP, it is not possible to have aspects themselves be the target of advice from other aspects. The #Aspect annotation on a class marks it as an aspect, and hence excludes it from auto-proxying.
So M. Prokhorov is somewhat wrong when saying that aspects are not (or cannot be) Spring components, but he is right insofar as by design you cannot self-advise an aspect or advise other aspects. His assumption that it may work with AspectJ is also correct. It does work with AspectJ, so if you need it to you can configure Spring to use AspectJ via LTW instead of Spring AOP for this case.

#DeclareMixin with Spring AOP?

Is it possible to use #DeclareMixin with Spring AOP? Or do they only support #DeclareParents?
I want to write an aspect that mixes in the java.beans.PropertyChangeSupport into a java bean:
public class PropertyChangeSupportWithInterface extends PropertyChangeSupport
implements IObservableBean {
public PropertyChangeSupportWithInterface(Object sourceBean) {
super(sourceBean);
}
}
(IObservableBean simply contains all public methods from the PropertyChangeSupport)
#Aspect
#Named
public class ObservableAspect{
#DeclareMixin("#ObservableBean *")
public static IObservableBean createDelegate(Object object) {
return new PropertyChangeSupportWithInterface(object);
}
}
It seems that this aspect is never used, which makes me think that the #DeclareMixin is not supported by the runtime weaving done by Spring AOP.
Is there any way to get this working with Spring AOP?
You can find a (not) running example here (Maven multi module project):
https://github.com/BernhardBln/SpringAOPObservableBean
See the (only) test case in the springaop-observable-bean-aspect submodule.
No, it's not supported by Spring AOP out the box. I see two options:
Create a DeclareMixinIntroductionInterceptor for Spring AOP.
Switch to Aspectj
I think that PropertyChange interface fits better in Aspectj because usually you will create a lot of proxies for prototype beans and them could be created easily out of the framework, by an ORM for example.
Edit
However I'm interested in this feature too and I already done some work for use it:
A DelegateFactoryIntroductionInterceptor to support creating delegates from the aspect instance.
A DeclareMixinAdvisor to join the inteceptor with the type pattern.
A DeclareMixinAspectJAdvisorFactory to support the DeclareMixin annotation.
To use it you only need to declare a bean of type DeclareMixinAutoProxyCreatorConfigurer
for configuring the AnnotationAwareAspectJAutoProxyCreator with the AdvisorFactory above.
I'm just testing, but seem that work fine.

Spring pointcut for abstract class' protected method

I am using Spring 3.2 and AspectJ 1.7.1. (It is not likely that I can upgrade to later versions in the near future.)
I need to define a pointcut for a protected method in an abstract class. AFAIK I need AspectJ for methods that are not public, so I have only tried this with the (AspectJ) annotations:
package com.aspects;
#Aspect
public class Aspect{
#Before("execution(* com.x.y.x.MyClass.myMethod(..))")
public void beforeAspect(){
//do something here
}
}
In my beans.xml I have:
<aop:aspectj-autoproxy />
<bean id="myAspect" class="com.aspects.Aspect"/>
I have checked and my Aspect class is created (constructor is getting called), no exception is being thrown when the application is launched.
However I can not get the beforeAspect to be called. For public methods in non abstract classes this works. How can I make it work for protected method in abstract class?
You should add a + sign after the abstract class.
So
"execution(* com.x.y.x.MyClass.myMethod(..))"
should look like:
"execution(* com.x.y.x.MyClass+.myMethod(..))"
↑
The + is about inheritance either extending the given class (MyClass) or implementing an interface.
Actually your are using #AspectJ-style to configure Spring AOP. the <aop:aspectj-autoproxy> declares an AnnotationAwareAspectJAutoProxyCreator BeanPostProcessor but you need to run the aspectj compiler instead.
See 9.8 Using AspectJ with Spring applications in reference documentantion for more info about weaving aspects with Spring.
See Spring / #Transactional with AspectJ is totally ignored for more info about configure compile time weaving with maven.

Spring AOP does not intercept methods within Spring's container

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.

Categories