In my business, I have to execute a method (imagine an access controll-like method) before some special methods(e.g. save, update,etc) to check some prerequisites and then execute the intended method. It is obvious that the most simple way is to call that controlling method at the very beginning lines of those methods, but I'm looking for something like #Before. something clean.
Any answer/hints will be appreciated.
You can use Spring AOP + AspectJ
In simple, Spring AOP + AspectJ allow you to intercept method easily.
Common AspectJ annotations :
#Before – Run before the method execution #After – Run after the
method returned a result #AfterReturning – Run after the method
returned a result, intercept the returned result as well.
#AfterThrowing – Run after the method throws an exception #Around –
Run around the method execution, combine all three advices above.
Steps
1. First you should enable AspectJ
To enable AspectJ, you need aspectjrt.jar, aspectjweaver.jar and spring-aop.jar. See following Maven pom.xml file.
<!-- Spring AOP + AspectJ -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.11</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.11</version>
</dependency>
2. Enable AspectJ
In your context config file add
<aop:aspectj-autoproxy />
3. After that use AspectJ #Before
In below example, the method1() method will be executed before the execution of method2() method.
#Aspect
public class MyAspect {
#Before("execution(* your.package.method2(..))")
public void method1(JoinPoint joinPoint) {
System.out.println("method1() is running!");
System.out.println(" ---- " + joinPoint.getSignature().getName());
System.out.println("******");
}
}
Related
I'm trying to log which aspect instance is responsible for which proxied object. However, when I'm collecting proxy object context through this() PCD and using perthis() instantiation model I'm getting an error related to the variable name of proxy object that I use in the pointcut expression:
warning no match for this type name: bean [Xlint:invalidAbsoluteTypeName]
Maven dependencies that I use:
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>6.0.3</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.3</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.19</version>
</dependency>
</dependencies>
This is an aspect that I use to implement that I needed:
#Aspect("perthis(com.sj.aspects.AssociationsAspect.exampleBeans())")
#Component
#Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class AssociationsAspect {
public AssociationsAspect(){
System.out.println("Creating aspect instance!");
}
#Pointcut("execution(* com.sj.utilities.Example.*(..))")
public void exampleBeans(){};
#Pointcut("execution(* com.sj.utilities.Example.*(..)) && this(bean)")
public void exampleOperations(Object bean){};
#Before(value = "exampleOperations(bean)", argNames = "jp,bean")
public void beforeExampleMethodsExecution(JoinPoint jp, Object bean){
System.out.println(
"JoinPoint: " + jp.getStaticPart() +
"\n\taspect: " + this +
"\n\tobject: " + bean
);
}
}
I tried to change bean as variable name to concrete type, but from documentation it will give different from binding result:
Any join point (method execution only in Spring AOP) where the proxy implements the AccountService interface:
this(com.xyz.service.AccountService)
As well as it will be changing exiting error to another:
error at ::0 formal unbound in pointcut
Funny enough, if you put away ("perthis(com.sj.aspects.AssociationsAspect.exampleBeans())") then everything will work fine and Spring container will create a separate aspect instance for every proxy object in it. However that is not desirable and most likely a bug, because through #Scope() annotation I only say that there can be multiple instances of same aspect and that Spring container will need to create new instance when I told it to do, but not that it need to create them when it wants to.
The final solution to which I came was to use JoinPoint reflection API instead of collecting context through this() PCD. And it works fine, however I have preconceptions related to how #Scope() works without perthis() instantiation model and with it.
In the end I want to know, 'Is there a solution for collecting proxy object context with this() PCD and using perthis() instantiation model at the same time?'. As well as what are mistakes in the aspect that I described earlier, that give such an error.
I am assuming that you got the syntax example for perthis() from this part of the Spring manual. Unfortunately, the syntax is wrong. Inside perthis(), you need to specify a valid pointcut such as, not just a method name without even a return type. Examples for valid clauses would be:
perthis(myPointcut()), if myPointcut is specified as a separate, named pointcut
perthis(this(org.acme.MyClass))
perthis(execution(* doCommand(..))
perthis(execution(* doCommand(..)) && this(org.acme.MyClass))
See also the AspectJ documentation here and there. Unfortunately, documentation is sparse.
In your particular case, you might want:
perthis(execution(* com.sj.utilities.Example.*(..)))
Update: I created Spring pull request # 29998 in order to improve the documentation.
I am trying to intercept any method in my application which is annotated with my custom developed annotation.
Initially I used the Spring AOP which works fine. But, it is not intercepting if the method call is in the same target class.
Going through the official docs, I got to know that the Spring AOP uses proxy beans for the same.
One workaround I found was to self inject the target class. But, this seems like too much fuss. Like every time I am adding my custom annotation to a method, I need to make sure that I add the #Scope annotation, set the proxyMode & self inject target class as shown here
Later I moved on to configuring and using native AspectJ.
This is my Custom annotation:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface TrackTime {
String description() default "";
}
Here is the Aspect for TrackTime annotation:
#Configuration
#Slf4j
#Aspect
public class TrackTimeServiceImpl {
#Pointcut("execution(public * *(..))")
public void methodsToBeProfiled(){
}
#Around("methodsToBeProfiled() && #annotation(x.y.z.TrackTime)")
public Object audit(ProceedingJoinPoint joinPoint) throws Exception {
//Business logic
}
}
I would like to mention here that my application is running on Jetty server.
The configuration file:
#Configuration
#EnableLoadTimeWeaving(aspectjWeaving = EnableLoadTimeWeaving.AspectJWeaving.ENABLED)
#EnableSpringConfigured
#EnableAspectJAutoProxy(proxyTargetClass = true)
public class TrackTimeConfig implements LoadTimeWeavingConfigurer {
#Override
public LoadTimeWeaver getLoadTimeWeaver() {
return new InstrumentationLoadTimeWeaver();
}
}
The aop.xml file:
Path to file: /resources/META-INF/aop.xml
<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "https://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
<weaver>
<!-- only weave classes in our application-specific packages -->
<include within="in.xxx.*"/>
</weaver>
<aspects>
<!-- weave in just this aspect -->
<aspect name="in.xxx.yyy.zzz.TrackTimeServiceImpl"/>
</aspects>
</aspectj>
Relative dependencies added in parent pom.xml have been mentioned here:
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.21</version>
</dependency>
My service class:
#Component
public class SomeService {
public void a(){
b();
}
#TrackTime
public void b(){
//business logic
}
}
Now when the method a() is called from the controller, even though the b() has the #TrackTime annotation, it is not intercepted.
Also, I would like to mention that I have set the following program arguments while running the application
-javaagent:/Users/xxx/.m2/repository/org/springframework/spring-instrument/5.3.6/spring-instrument-5.3.6.jar
-javaagent:/Users/xxx/.m2/repository/org/aspectj/aspectjweaver/1.9.6/aspectjweaver-1.9.6.jar
I have gone through docs, articles, followed solutions on stackoverflow. But, for the above mentioned configuration/code, it is not working as I want.
Requesting help from the community.
Thanks in advance.
I did not try to run your example locally yet, but noticed two details at first glance in your code:
You are configuring both #EnableLoadTimeWeaving (for native AspectJ) and #EnableAspectJAutoProxy (for Spring AOP), which might be conflicting with each other. Try to remove the latter and see if it fixes the problem.
In aop.xml, you are using <include within="in.xxx.*"/>. Please note that this will only include classes directly in the in.xxx package, but not classes in sub-packages. In order to include them as well, please use the double-dot notation in.xxx..*.
Feel free to report back if it is still not working with the suggested changes. I can take a closer look then.
First of all, I would like to thank #hfontanez, #void void & #kriegaex for responding and helping me out in moving forward in solving the problem statement.
So if anyone is looking out on how to intercept nested & private methods, let's have this as a one stop in configuring native AspectJ.
Please check my POC here on github for a working example.
In my case, I added the aspectjweaver JAR in my project structure and passed the arguments through VM Options in IDE.
That all !!
Nested/Private methods will now be intercepted.
Thank you,
Karthik
How to apply spring aop aspect on a prototype scoped bean
Is spring aspects does not apply to prototype scoped bean ?
I have a prototype scoped bean with couple of constructor arguments. The bean gets instantiated at runtime with these arguments.
My spring configuration is like this -
#Configuration
#EnableAspectJAutoProxy
public class SpringConfiguration {
#Bean
#Scope("prototype")
public PrototypeBean prototypeBean(SomeDTO dtoArg1, OtherDTO dtoArg2) {
return new PrototypeBean(dtoArg1, dtoArg2);
}
#Bean
public TestAspect testAspect() {
return new TestAspect();
}
}
I am getting the bean - PrototypeBean in the code through applicationContext, like this -
applicationContext.getBean(PrototypeBean.class, dtoArg1, dtoArg2);
But surprisingly the aspect is not executing on the invocation of the joinpoint method of the prototype bean. I am sure that the pointcut I created is correct because in eclipse the aspectJ plugin shows the visual marker of the aspectJ reference on the joinPoint method, which shows that the pointcut is correct but not sure why it doesn't get executed at runtime when the joinpoint method of PrototypeBean gets invoked.
Am I approaching the container in incorrect manner to get the bean or the container is not getting the chance to weave the advice on this prototype bean ?
Appreciate if any help / suggestions can be provided on this.
I tried the same thing with Spring Boot 2.3.4 and it works just fine.
Here is the repo. Make sure you have the following dependencies in place for EnableAspectJAutoProxy to work.
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<scope>compile</scope>
</dependency>
I have Spring 5.0.4 application with a Dispatcher servlet config file as spring-web-servlet.xml (spring-web being Dispatcher servlet name).
To enable AOP in the application, I made the entry <aop:aspectj-autoproxy /> in spring-web-servlet.xml after including the namespace for aop.
The pom.xml has below entries:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.11</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.11</version>
</dependency>
To use AOP, I created below Aspect class:
#Aspect
#Component
public class MyAspect {
#Before("getApiPointcut()")
public void logRequest(JoinPoint joinPoint) {
//some logic here
}
#AfterReturning(value = "getApiPointcut()", returning = "returnValue")
public void logResponse(Object returnValue) {
//some logic here
}
#Pointcut("within(com.test.service.controller.*) " +
" || within(com.test.service.api.GenericService)" +
" || within(com.test.service.api.UserService)")
public void getApiPointcut() {
}
}
This class has two methods: logRequest() and logResponse().
Every API call will first go through logRequest() and before returning the response, logResponse() is called.
For the initial few requests (5-6), AOP works fine and I get the flow in MyAspect class for both the methods.
But after that, if I hit any API, flow goes through logRequest(), then the API class, and then it hangs. The API class calls the service class. But the flow never reaches the service class and hence never goes in logResponse() and the server does not return anything to the client. I checked the logs and there are no exceptions. I checked the heap memory usage too and it is not consumed fully. Even the threads are not fully occupied. (Checked these stats using jconsole).
If I disable AOP by removing <aop:aspectj-autoproxy /> from spring-web-servlet.xml file, the server returns response for all the calls without any issue.
So, the conclusion that I made is enabling the AOP is causing the server to hang but I am not able to understand why.
Any help is appreciated.
UPDATE
The APIs (controller) on which I am trying to invoke AOP are annotated with #PreAuthorize. If I comment out the #PreAuthorize annotation, the application does not hang. Uncommenting the #PreAuthorize annotation again results in 5-6 successful requests and then the application hangs.
As #PreAuthorize also uses AOP, is there any case that I need to take care of when using my own AOP with #PreAuthorize?
UPDATE 2
The #PreAuthorize method on the API is as below:
#PreAuthorize("hasAuthority('SOME_AUTH') and #myUtil.canRead(#userId) ")
If I remove the hasAuthority('SOME_AUTH') and keep the second check, the aplication works fine, but if I keep both or keep only hasAuthority() check, the application does not responds after a few successful responses.
UPDATE 3
The issue gets resolved if I annotate the API with #Transactional.
However, the code used to work without #Transactional when aop was disabled. So why does enabling AOP makes it necessary to have #Transactional on the API?
I have a problem related to Transactional boundaries and I am not able to figure out what is going wrong.
#Transactional( propagation = Propagation.REQUIRED )
Class A {
void methodA() {
try {
new B().callMethodB(obj)
} catch(Exception e) {
updateSomeProperty(obj1)
}
}
#Transactional(propagation = Propagation.REQUIRES_NEW)
void updateSomeProperty(Object obj1) {
obj1.setProperty(1);
obj1.save();
}
}
Class B {
public void callMethodB(Object obj) throws Exception {
throws new Exception();
}
}
The problem is that my object is not updating when the error is thrown . I also tried firing SQL code from within the method updateSomeProperty but that also did not work.
Basically I want to update the object's property whether despite the exception is thrown.
Any ideas ??
And it should not work. Because you call updateSomeProperty(obj1) from another method of the class and try to change default Transactional behaviour (from REQUIRED to REQUIRED_NEW). But it will not work. Thats why all your changes will be rolled back when exception occurs.
By default Spring creates proxy for interface and #Transactional annotation should be used only for public method. And this method should be called from "outside". If you will call them from another method within the class then #Transactional annotation will not work.
You can also change default settings for transactions in xml (look at properties proxy-target-class and mode). But I have never changed this and don't remember how exactly it should work.
<tx:annotation-driven transaction-manager="txManager" mode="..." proxy-target-class="..."/>
EDIT:
By the way. Here is a very good article about transaction pitfalls. It helped me very much. There are also few other very interesting articles about transactions.
EDIT 2:
Hello again. I think that I find solution for your problem. At least I tested this and it works for me well.
I proposed you to change transaction mode to "AspectJ" and to use AspectJ compile time wieving for project. This will give you a possibility to call a private transactional method from another method within one class with changing transactional behaviour (for started nested transaction). In such case you can commit some changes in nested transaction while outer transaction will be rolled back. For this you need to do such steps:
1) Change transaction mode in transactional definition:
- if you use xml configuration then:
<tx:annotation-driven transaction-manager="txManager" mode="aspectj"/>
if you use java configuration then:
#EnableTransactionManagement(mode=AdviceMode.ASPECTJ, proxyTargetClass=false)
2) Add aspectj dependencies to pom:
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${aspectj.version}</version>
</dependency>
3) Add spring-aspects dependency to pom:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>3.1.2.RELEASE</version>
<scope>compile</scope>
</dependency>
4) Add maven plugin that enables compile time wieving:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.4</version>
<configuration>
<showWeaveInfo>true</showWeaveInfo>
<source>${compiler.version}</source>
<target>${compiler.version}</target>
<Xlint>ignore</Xlint>
<complianceLevel>${compiler.version}</complianceLevel>
<encoding>UTF-8</encoding>
<verbose>false</verbose>
<aspectLibraries>
<aspectLibrary>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</aspectLibrary>
</aspectLibraries>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<!-- <goal>test-compile</goal> -->
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
</plugin>
5) Also I have maven compiler plugin in my pom thats why I think that it is beter for you to add it too:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<compilerVersion>${compiler.version}</compilerVersion>
<fork>true</fork>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
*Note: I use jdk version 1.7+. And my versions of the compiler and aspectj is sush:
<compiler.version>1.7</compiler.version>
<aspectj.version>1.6.12</aspectj.version>
Also I have such versions of other libraries (but I think this is not necessary):
<org.springframework.version>3.1.0.RELEASE</org.springframework.version>
<org.hibernate.version>4.1.0.Final</org.hibernate.version>
<org.springdata.version>1.0.2.RELEASE</org.springdata.version>
You also can try to use load time wieving in spring, but it is
more hard to configure (this is my opinion) and it is not recommended to be used in production (as I read in few posts). But if you will decide to use it you can find a lot of info in web and spring reference dicumentation.
If you want to use compile time wieving without maven then I don't know how to configure this. (I tested only with maven). You can try to find such info in web but I don't recomend this because with maven it is much easier to handle dependencies (and in case of this example - to add necessary plugin).
Here is an example that I used for tests:
Some interface:
public interface TestClassInterface {
void testMethod();
}
Some test class that implements this interface:
#Transactional(propagation = Propagation.REQUIRED, rollbackFor=Exception.class)
#Component
public class TestClass implements TestClassInterface {
#Autowired
private SpringDataFooDAO fooDao;
public void testMethod() {
try {
Foo foo = fooDao.findOne(2L);
System.out.println(TransactionSynchronizationManager.getCurrentTransactionName());
System.out.println(TransactionSynchronizationManager.isActualTransactionActive());
foo.setName("should be rolled back");
new ExceptionThrower().doSomething("default string");
} catch(Exception e) {
updateSomeProperty(1L, "Changed name");
throw new RuntimeException(e);
}
}
#Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor=Exception.class)
private void updateSomeProperty(long id, String newFooName) {
System.out.println(" --- ");
System.out.println(TransactionSynchronizationManager.getCurrentTransactionName());
System.out.println(TransactionSynchronizationManager.isActualTransactionActive());
// Update property of test object.
Foo foo = fooDao.findOne(id);
foo.setName(newFooName);
}
}
Another class with method that throws exception:
public class ExceptionThrower {
public void doSomething(Object obj) throws Exception {
throw new Exception();
}
}
Note that I rethrow exception from catch block (I do this as Runtime exception because I don't need to handle it in upper classes). This is necessary for correct outer transaction rollback.
Check out the spring reference for how to use #Transactional. #Transactional when using spring proxies comes with a lot of **conditions apply, you need to understand them before applying it in your code.
In proxy mode (which is the default), only external method calls
coming in through the proxy are intercepted. This means that
self-invocation, in effect, a method within the target object calling
another method of the target object, will not lead to an actual
transaction at runtime even if the invoked method is marked with
#Transactional.
Consider the use of AspectJ mode (see mode attribute in table below)
if you expect self-invocations to be wrapped with transactions as
well. In this case, there will not be a proxy in the first place;
instead, the target class will be weaved (that is, its byte code will
be modified) in order to turn #Transactional into runtime behavior on
any kind of method.
Can you try specifing noRollbackFor = RuntimeException.class or any other class which you want and hopefully it will let you update the database. i.e. #Transactional(noRollbackFor = RuntimeException.class)
The above arrangement is perfect to start off a new transaction and do other stuff different from the original transaction.
The one thing which was going wrong in my case was that while in the other transaction, I was throwing an exception myself resulting in the second transaction being rolled back again..
So The thing is beware off exception in the transaction because they ensure that the database state rolls back. It is for what they are meant for.
Thanks
Create a custom exception and use it to throw, Also please use #ApplicationException(rollback=false) on as class level annotation while defining custom exception.
e.g.
#ApplicationException(rollback=false)
public CustomException extends Exception{