Intercept nested methods using native AspectJ in Spring Boot - java

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

Related

How to use Annotations in a Spring Boot Library (without Main Class)?

I am new to spring boot and trying to figure out some of its working. Here I am getting Null Pointer Exception for the below implementation. I am not sure can we use #Autowire annotation for a Library project without a Main class. Maybe this sounds stupid,I believe we can do a #ComponentScan for the Library Project from a Service project that's created.My Question is looking at the below implementation is there any possibility to use annotation in the below library project, because Annotations are throwing NullPointerException for the below code?
Library
The below code is a library and it Doesn't have a Main Class
#Service
class Data {
public String getData(){
return "DATA";
}
}
class Access{
#Autowired
private Data data;
public String myData(){
return data.getData(); // Null pointer exception
}
}
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
Service
The below code is of a Service which is accessing the above library
Controller
#GetMapping("/")
String print(){
// Accesses the Library
Access access=new Access();
return access.myData();
}
#SpringBootApplication
#ComponentScan(basePackages = { "com.service", "com.library" }) // Hopes this Scans the library package
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
}
pom.xml
<dependency>
<groupId>com.library</groupId>
<artifactId>library</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
If I remove the Autowired from the library code, and create the object normally (using new keyword), everything works fine. So my question is, Main class with #SpringBootApplication is required inorder to use annotations, without a Main class can't we run it?
The best practice if you're not relying on any Spring AOP features (such as #Transactional) is don't do it at all. #Autowired on fields is fragile; instead, use a normal constructor. Spring needs no annotations to instantiate a bean if you have only a single constructor, and this makes your class usable in a different DI environment or in plain Java (such as for testing).
Similarly, don't put #Service on a class in a library; clients who want it can simply use #Import to pull it in.
The one exception to this is that if you are providing a Boot auto-configuration setup, that module will need to depend on spring-boot-autoconfigure to access the annotations. Note that it is customary to put your starter in a separate dependency that contains only the Boot classes and metafiles.
All you need to do is annotate your library Access class with #Service Or #Component
And in your Controller class create a field for Access class and autowire it.
Then use it inside your method
#Service
class Access{
#Autowired
private Data data;
public String myData(){
return data.getData();
}
}
#RestController
class YourController {
#Autowired
Access access
#GetMapping("/")
String print(){
return access.myData();
}
With SpringBootApplication annotation, It will create application context with all required beans when starting the application. The object will be injected whenever it needed.
But in the Normal java application, the Object will be created while calling the new keyword.
#SpringBootApplication it required when you have #Autowired annotation otherwise you will get error.

Initialising Spring AOP hangs the entire application after few successful requests

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?

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.

JUnit weaving wrong Spring AOP Bean

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.

Does Spring #Transactional attribute work on a private method?

If I have a #Transactional -annotation on a private method in a Spring bean, does the annotation have any effect?
If the #Transactional annotation is on a public method, it works and open a transaction.
public class Bean {
public void doStuff() {
doPrivateStuff();
}
#Transactional
private void doPrivateStuff() {
}
}
...
Bean bean = (Bean)appContext.getBean("bean");
bean.doStuff();
The answer your question is no - #Transactional will have no effect if used to annotate private methods. The proxy generator will ignore them.
This is documented in Spring Manual chapter 10.5.6:
Method visibility and #Transactional
When using proxies, you should apply
the #Transactional annotation only
to methods with public visibility. If
you do annotate protected, private or
package-visible methods with the
#Transactional annotation, no error
is raised, but the annotated method
does not exhibit the configured
transactional settings. Consider the
use of AspectJ (see below) if you need
to annotate non-public methods.
The Question is not private or public, the question is: How is it invoked and which AOP implementation you use!
If you use (default) Spring Proxy AOP, then all AOP functionality provided by Spring (like #Transactional) will only be taken into account if the call goes through the proxy. -- This is normally the case if the annotated method is invoked from another bean.
This has two implications:
Because private methods must not be invoked from another bean (the exception is reflection), their #Transactional Annotation is not taken into account.
If the method is public, but it is invoked from the same bean, it will not be taken into account either (this statement is only correct if (default) Spring Proxy AOP is used).
#See Spring Reference: Chapter 9.6 9.6 Proxying mechanisms
IMHO you should use the aspectJ mode, instead of the Spring Proxies, that will overcome the problem. And the AspectJ Transactional Aspects are woven even into private methods (checked for Spring 3.0).
By default the #Transactional attribute works only when calling an annotated method on a reference obtained from applicationContext.
public class Bean {
public void doStuff() {
doTransactionStuff();
}
#Transactional
public void doTransactionStuff() {
}
}
This will open a transaction:
Bean bean = (Bean)appContext.getBean("bean");
bean.doTransactionStuff();
This will not:
Bean bean = (Bean)appContext.getBean("bean");
bean.doStuff();
Spring Reference: Using #Transactional
Note: In proxy mode (which is the default), only 'external' method calls coming in through the proxy will be intercepted. This means that 'self-invocation', i.e. a method within the target object calling some other method of the target object, won't lead to an actual transaction at runtime even if the invoked method is marked with #Transactional!
Consider the use of AspectJ mode (see below) if you expect self-invocations to be wrapped with transactions as well. In this case, there won't be a proxy in the first place; instead, the target class will be 'weaved' (i.e. its byte code will be modified) in order to turn #Transactional into runtime behavior on any kind of method.
If you need to wrap a private method inside a transaction and don't want to use AspectJ, you can use TransactionTemplate.
#Service
public class MyService {
#Autowired
private TransactionTemplate transactionTemplate;
private void process() {
transactionTemplate.executeWithoutResult(status -> processInTransaction());
}
private void processInTransaction(){
//...
}
}
Yes, it is possible to use #Transactional on private methods, but as others have mentioned this won't work out of the box. You need to use AspectJ. It took me some time to figure out how to get it working. I will share my results.
I chose to use compile-time weaving instead of load-time weaving because I think it's an overall better option. Also, I'm using Java 8 so you may need to adjust some parameters.
First, add the dependency for aspectjrt.
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.8</version>
</dependency>
Then add the AspectJ plugin to do the actual bytecode weaving in Maven (this may not be a minimal example).
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.8</version>
<configuration>
<complianceLevel>1.8</complianceLevel>
<source>1.8</source>
<target>1.8</target>
<aspectLibraries>
<aspectLibrary>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</aspectLibrary>
</aspectLibraries>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
Finally add this to your config class
#EnableTransactionManagement(mode = AdviceMode.ASPECTJ)
Now you should be able to use #Transactional on private methods.
One caveat to this approach: You will need to configure your IDE to be aware of AspectJ otherwise if you run the app via Eclipse for example it may not work. Make sure you test against a direct Maven build as a sanity check.
Spring Docs explain that
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.
Another way is user BeanSelfAware
The answer is no. Please see Spring Reference: Using #Transactional
:
The #Transactional annotation may be placed before an interface definition, a method on an interface, a class definition, or a public method on a class
Same way as #loonis suggested to use TransactionTemplate one may use this helper component (Kotlin):
#Component
class TransactionalUtils {
/**
* Execute any [block] of code (even private methods)
* as if it was effectively [Transactional]
*/
#Transactional
fun <R> executeAsTransactional(block: () -> R): R {
return block()
}
}
Usage:
#Service
class SomeService(private val transactionalUtils: TransactionalUtils) {
fun foo() {
transactionalUtils.executeAsTransactional { transactionalFoo() }
}
private fun transactionalFoo() {
println("This method is executed within transaction")
}
}
Don't know whether TransactionTemplate reuse existing transaction or not but this code definitely do.

Categories

Resources