Is it posible to create a log.info pointcut? - java

I never tried AOP before and want to know if I could make a pointcut that captures the execution of a log.info(...) in Spring.
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
#Aspect
public class AfterFinallyExample {
#After("execution(* org.slf4j.*.*(..))")
public void sendLogParameterToServer() {
// ...
}
}
If it is posible, is it consider a good practice?

Could you use AOP to do this, yes you could. Can you do it with, the default, Spring AOP no you cannot.
Spring AOP, by default, is based on proxies and it will only create proxies for Spring-managed beans. As the loggers aren't managed by Spring (unless you inject all loggers) this won't work.
You will need to resort to fullblown AspectJ with either compile or loadtime weaving. Which will change the actual bytecode of the classes based on the pointcut and aspect.

Related

Accessing spring bean proxy reference itself

I have an issue with #Cacheable and #CacheEviction annotations. When I call these methods in the bean where they are declared, the aop part is not getting executed.
The underlying reason for this is that the bean access to its own instance itself, instead of accessing the spring proxy.
I have read this question where it is said that in most cases it should not be necessary a bean accessing the proxy.
Probably those answers work for me. The question is:
Is there any other way to make annotated methods work? Or does it sound like I found a good reason for a bean needing to access the proxy itself?
As is well documented in the Spring user manual, self-invocation cannot work with Spring AOP because Spring AOP uses proxies. So if you want to make self-invocation trigger an aspect, please switch to full AspectJ via LTW (load-time weaving). It works with the original beans and does not use any proxies.
Update: If you want to avoid using native AspectJ and instead as a (pretty lame and anti-AOP) workaround want to make your component proxy-aware, of course you can use self-injection and reference the cached method using the auto-wired proxy like this:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;
#Component
public class MyComponent {
#Autowired
MyComponent myComponent;
public void doSomething() {
System.out.println(myComponent.doCacheable());
System.out.println(myComponent.doCacheable());
System.out.println(myComponent.doCacheable());
}
#Cacheable("myCache")
public String doCacheable() {
return "" + System.nanoTime();
}
}
Calling doSomething() on the MyComponent bean should yield output like this:
247760543178800
247760543178800
247760543178800
This proves that caching works like this. If instead you just have three lines of either System.out.println(doCacheable()); or the weird, nonsensical variant from the other (now deleted) answer
System.out.println(MyComponent.this.doCacheable());, then you would get three different values on the console, i.e. nothing would be cached.

Pointcut or Aspect Around All Service Methods with Annotation #Transactional(readOnly = false)

Is it possible to use Spring AOP or AspectJ to intercept all Service methods (contained in classes in the com.app.service.* package) having the annotation
#Transactional(readOnly = false)
(other elements possible as well in Spring's #Transactional annotation, but we only care about readOnly = false).
I could only find examples pertaining to pointcuts with simple Annotations, or #Annotation(value).
My preference would be to use straight Spring, if possible.
It would probably be something like the below, but not sure about the syntax.
#Around("execution(* com.app.service..*.*(..))" && #Transactional[??])
You want to use a pointcut like this:
execution(#org.springframework.transaction.annotation.Transactional(readOnly = false) * com.app.service..*.*(..))
Unfortunately no easy way to do that. Even when we have an Annotation-based pointcut, e.g.
#Aspect
#Component
#EnableAspectJAutoProxy
public class WriteTransactionAspectBean {
#Before("#annotation(org.springframework.transaction.annotation.Transactional)")
public void test(org.springframework.transaction.annotation.Transactional t) {
System.out.println("TEST");
}
}
the issue is the Annotations aren't our own, they come from an external JAR (Hibernate). This would require Load-Time Weaving or some other difficult workaround.
Aspectj: intercept method from external jar
But to make things even worse, Annotations need RetentionPolicy=RUNTIME in order to be "discovered" by Pointcuts. And we would need to go thru every method and add this specification to every #Transactional. There's no way to automatically make all #Transactional's Runtime-retainable in the application.

Why AspectJ weaving on package scoped methods not working?

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.

Rest + Spring AOP + interface doesn't inject

I have a Spring AOP aspect used for logging, where a method can be included for logging by adding an annotation to it, like this:
#AspectLogging("do something")
public void doSomething() {
...
}
I've been using this on Spring beans and it's been working just fine. Now, I wanted to use it on a REST-service, but I ran into some problems. So, I have:
#Path("/path")
#Service
public class MyRestService {
#Inject
private Something something;
#GET
#AspectLogging("get some stuff")
public Response getSomeStuff() {
...
}
}
and this setup works just fine. The Rest-service that I'm trying to add the logging to now has an interface, and somehow that messes stuff up. As soon as I add the #AspectLogging annotation to one of the methods, no dependencies are injected in the bean, and also, the aspect is newer called!
I've tried adding an interface to the REST-service that works, and it gets the same error.
How can having an interface lead to this type of problems? The aspect-logger works on classes with interfaces elsewhere, seems it's only a problem when it's a REST-service..
Ref the below Spring documentation (para 2) -
To enable AspectJ annotation support in the Spring IoC container, you
only have to define an empty XML element aop:aspectj-autoproxy in your
bean configuration file. Then, Spring will automatically create
proxies for any of your beans that are matched by your AspectJ
aspects.
For cases in which interfaces are not available or not used in an
application’s design, it’s possible to create proxies by relying on
CGLIB. To enable CGLIB, you need to set the attribute
proxy-targetclass= true in aop:aspectj-autoproxy.
In case your class implements an interface, a JDK dynamic proxy will be used. However if your class does not implement any interfaces then a CGLIB proxy will be created. You can achieve this #EnableAspectJAutoProxy. Here is the sample
#Configuration
#EnableAspectJAutoProxy
public class AppConfig {
#Bean
public LoggingAspect logingAspect(){
return new LoggingAspect();
}
}
#Component
#Aspect
public class LoggingAspect {
...
...
}
In my opinion what you are actually trying to do is to add spring annotations to a class maintained by jersey. In the result you are receiving a proxy of proxy of proxy of somethng. I do not think so this is a good idea and this will work without any problems. I had a similar issue when I tried to implement bean based validation. For some reasons when there were #PahtParam and #Valid annotations in the same place validation annotations were not visible. My advice is to move your logging to a #Service layer instead of #Controller.

Whats the best way to inject same instance of service in service for Spring AOP

I'va a ServiceImpl with is annotated with #Service stereotype of Spring and have two methods in it each one is annotated with custom annotations which are intercepted by Spring.
#Service
public class ServiceImpl implements Service{
#CustomAnnotation
public void method1(){
...
}
#AnotherCustomAnnotation
public void method2(){
this.method1();
...
}
}
}
Now Spring uses proxy based AOP approach and hence as I'm using this.method1() interceptor for #CustomAnnotation will not able to intercept this call, We used to inject this service in another FactoryClass and in that way we were able to get the proxy instance like -
#AnotherCustomAnnotation
public void method2(){
someFactory.getService().method1();
...
}
I'm now using Spring 3.0.x, which is the best way to get the proxy instance?
The other alternative is to use AspectJ and #Configurable.
Spring seems to be going towards these days (favoring).
I would look into it if you are using Spring 3 as it is faster (performance) and more flexible than proxy based aop.
Both methods are inside the same proxy, whereas the AOP functionality just enriches calls from the outside (see Understanding AOP Proxies). There are three ways for you to deal with that restriction:
Change your design (that's what I would recommend)
Change proxy type from JDK-proxy to proxy-target-class (CGLib-based subclassing) Nope, that doesn't help, see #axtavt's comment, it would have to be static AspectJ compilation.
Use ((Service)AopContext.currentProxy()).method1() (Works, but is an awful violation of AOP, see the end of Understanding AOP Proxies)
You could make your ServiceImpl class implement the BeanFactoryAware interface, and lookup itself thanks to the provided bean factory. But this is not dependency injection anymore.
The best solution is to put method1 in another service bean, which would be injected in your existing service bean and to which your existing service bean would delegate.

Categories

Resources