GithubHelp home page GithubHelp logo

dsyer / spring-boot-aspectj Goto Github PK

View Code? Open in Web Editor NEW
168.0 10.0 51.0 154 KB

Sample applications showing how to use AspectJ with Spring Boot

Home Page: https://projects.spring.io/spring-boot/

Java 100.00%

spring-boot-aspectj's Introduction

Sample Apps Showing How to Use AspectJ Natively with @Aspect and Spring Boot

Build Status

AspectJ lets you write aspects using Java annotations @Aspect and friends. Conveniently, but sometimes confusingly, Spring lets you use the same programming model, and then uses the AspectJ tooling APIs at runtime, so some people can’t tell the difference between Spring AOP and AspectJ. You don’t need any special tools to use Spring AOP, but it has it’s limitations, one of which is performance (you take a hit on startup while all the beans are analysed, and you take another smaller hit when the app is running and one of your aspects is executed through a proxy).

Luckily, it is quite easy to set up AspectJ to weave your code natively (not with Spring), and it is sometimes faster - just more steps to set up. In fact, there are multiple ways of using AspectJ to weave your code, some of which happen at runtime, and one (broadly speaking) that happens at compile time. Compile time is faster (unsurprisingly), but not massively (heuristically, maybe a 20% effect).

Note
The projects are updated with AspectJ 1.9.5 (included with Spring Boot 2.2.4) and use OpenJDK 11 now.
Note
AspectJ 1.8.13 (included with Spring Boot 1.5.9) has some major performance improvements affecting Spring AOP - the optimizations were suggested by running the benchmarks in this project. If you think Spring AOP might be slowing your app down on startup, upgrade now.

Contents

  • spring = Spring AOP standalone sample. Aspects and application code in the same module.

  • ltw = Load Time Weaving standalone sample. Aspects and application code in the same module.

  • ctw = Compile Time Weaving standalone sample. Aspects and application code in the same module.

  • multi-ltw = Load Time Weaving multi-module sample. Aspects and application code in separate modules.

  • multi-ctw = Compile Time Weaving multi-module sample. Aspects and application code in separate modules. The aspect library (as well as the application) has to be processed by AspectJ at build time.

  • timing = A utility library for logging timing data on startup for some internal features of Spring and Spring Boot

  • benchmarks = benchmarks for using AspectJ with the help of JMH (Java Microbenchmark Harness) for example of how to do microbenchmarking in Java. Also this article about JMH is a good place to start.

Converting from Spring AOP to AspectJ

You need to tell AspectJ where to find your aspects, and which classes you want it to weave. You also need to tell Spring not to try and weave the same aspects again (in Spring Boot you can do that with spring.aop.auto=false). The configuration for AspectJ lives in an XML file aop.xml, which can be in META-INF or in a package called org.aspectj. Here’s a simple example:

src/main/resources/org/aspectj/aop.xml
<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
	<weaver>
		<include within="com.example.*" />
		<include within="org.springframework.boot..*" />
	</weaver>

	<aspects>
		<aspect name="com.example.Interceptor" />
	</aspects>

</aspectj>
Warning
If the AspectJ config file is in META-INF/aop.xml then the fat jar built by Spring Boot has it is in the wrong place, and there are nasty looking errors on startup. The app still works and the aspects are woven, but you can work around it by putting aop.xml in the org.aspectj package instead of in META-INF.

Load Time Weaving

One of the runtime options is called "load time weaving", and Spring even has an annotation @EnableLoadTimeWeaving to help get it off the ground. The Spring annotation is rooted in the culture and mechanics of application servers - it only makes sense if you have multiple apps running in the same JVM with class loader isolation. What happens is that it uses a special ClassLoader that manipulates the classes as they are loaded.

The other option for load time weaving is the AspectJ agent, which you add to the JVM on the command line, and it can look at all the classes and potentially weave them with aspects.

E.g. :

$ cd ltw
$ mvn clean install
$ java -javaagent:$HOME/.m2/repository/org/aspectj/aspectjweaver/[version]/aspectjweaver-[version].jar -jar target/*.jar

You can also use the copied version of aspectweaver-[version].jar in the target/aspectj directory of the Maven project, since it is copied to that directory with Maven Dependency plugin.

$ java -javaagent:target/aspectj/aspectjweaver-[version].jar -jar target/*.jar

If you don’t add the agent (and don’t provide an implementation of Interceptor.aspectOf()) you might get a confusing "cyclic dependency" error from Spring on startup. It’s not really cyclic, but because the Interceptor in this sample is an @EventListener Spring is trying to create it very early and gets confused. The real problem is just that Aspects.aspectOf() doesn’t work unless the agent is attached.

Note
@EnableLoadTimeWeaving doesn’t actually make much sense in a Spring Boot app that is going to run in its own process - you can just add the AspectJ agent. Many people confuse "load time weaving" with "runtime weaving" generally (because for a long time it was the only option for many Spring apps). So if someone asks you to implement "load time weaving" in a Spring Boot app, just add the agent.
Note
The "timing" library is a dependency of this project so it gets woven at runtime and prints out interesting (but verbose) information about timings for bean initialization. This is in addition to the aspects library.

Compile Time Weaving

To weave the aspects at compile time (and also optionally use the AspectJ language, as opposed to Java with annotations), you need an additional plugin in your build. If all you need is to weave existing byte code, the plugin configuration is very simple:

<plugin>
  <groupId>org.codehaus.mojo</groupId>                   (3)
  <artifactId>aspectj-maven-plugin</artifactId>
  <version>1.10</version>
  <configuration>
    <source>${java.version}</source>
    <target>${java.version}</target>
    <proc>none</proc>                                    (1)
    <complianceLevel>${java.version}</complianceLevel>
  </configuration>
  <executions>
    <execution>
      <goals>
        <goal>compile</goal>
      </goals>
    </execution>
  </executions>
  <dependencies>
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjtools</artifactId>
      <version>${aspectj.version}</version>               (2)
    </dependency>
  </dependencies>
</plugin>
  1. Useful if you have annotation processors (like Spring Boot configuration processor), to avoid them being processed again by the AspectJ compiler.

  2. Optionally update the AspectJ tooling to the latest version. The aspectrt jar has to be included separately.

  3. See the note below for Java 11

In this sample build the app with the weave profile to run this plugin. E.g.

$ cd ctw
$ mvn clean install
$ java -jar target/*.jar
Note
for Java 11 you cannot use org.codehaus.mojo::aspectj-maven-plugin because the plugin is not developed anymore, see mojohaus/aspectj-maven-plugin#49. Instead use https://github.com/nickwongdev/aspectj-maven-plugin with com.nickwongdev::aspectj-maven-plugin
Note
in the plugin configuration above, we haven’t asked AspectJ to weave the dependencies, and it won’t do that by default. Consequently we won’t see as much output from the app when it runs as we did with the runtime weaving (where all the classes were available for weaving as soon as they were loaded).
Note
AspectJ is smart enough not to try and weave the same class twice, so you can always add the agent at runtime even when the application classes are already woven. That would be one way to pick up additional join points that you hadn’t woven at compile time.
Note
The "timing" library is not a dependency of this project, and there wouldn’t be much point doing that because the pointcuts it defines would not match anything that was being compiled here.

Running the LTW Sample

You can run the samples from the command line and see the aspect logging to stderr:

$ cd ltw
$ mvn spring-boot:run
...
execution(InterceptorApplication..EnhancerBySpringCGLIB..8ce66f62.setBeanFactory(..))
execution(InterceptorApplication..EnhancerBySpringCGLIB..8ce66f62.setBeanFactory(..))
...

To run in the IDE you need to add the agent to your launch configuration.

-javaagent:${system_property:user.home}/.m2/repository/org/aspectj/aspectjweaver/[version]/aspectjweaver-[version].jar

or simply

-javaagent:target/aspectj/aspectjweaver-[version].jar

Note that you could add @EnableLoadTimeWeaving to the main application class, but it should probably be removed, as it’s misleading.

There’s an open issue asking for @EnableLoadTimeWeaving support in Spring Boot, and a user who says he made it work with a PropertiesLauncher (because it can set the class loader really early): spring-projects/spring-boot#739. It doesn’t work to set the classloader in the main method because too many Spring Boot classes have already been loaded by then, but there is a trick you can play with [attaching the agent at runtime](http://www.eclipse.org/aspectj/doc/released/README-187.html) (in which case all classes loaded up to that point can not be woven).

Note
You can enable logging of the weaving using -Daj.weaving.verbose=true.

Run the CTW Sample

$ cd ctw
$ mvn spring-boot:run

The compile time weaving example does not need any Java agent.

spring-boot-aspectj's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

spring-boot-aspectj's Issues

Adding an example with use of Lombok

I'm using Lombok plugin, with JDK 1.8.0_172, when I apply your configuration for Spring, the aspectj-maven-plugin shows me tons of errors with the usage of various language constructs and Lombok.val. For instance, the getter method is implemented by Lombok, but aspectj-maven-plugin sees nothing.

Also, for what it's worth, I would like to use AspectJ language to define my pointcuts instead of programmatic implementation, but it's entirely another story.

Here is a part of the Maven output:

  [ERROR] The type MessageNotification must implement the inherited abstract method Notification.getAlertTitle()
  /obelisk/src/main/java/sa/com/stcpay/blink/obelisk/rush/notification/MessageNotification.java:21
  public class MessageNotification implements Notification {
  ^^^^^^^^^^^^^^^^^^
  
  [ERROR] The type RawNotification must implement the inherited abstract method Notification.getQualityOfService()
  /obelisk/src/main/java/sa/com/stcpay/blink/obelisk/rush/notification/RawNotification.java:21
  public class RawNotification implements Notification {
  ^^^^^^^^^^^^^^
  
  [ERROR] The type RawNotification must implement the inherited abstract method Notification.getToken()
  /obelisk/src/main/java/sa/com/stcpay/blink/obelisk/rush/notification/RawNotification.java:21
  public class RawNotification implements Notification {
  ^^^^^^^^^^^^^^
  
  [ERROR] The type RawNotification must implement the inherited abstract method Notification.getUniqueId()
  /obelisk/src/main/java/sa/com/stcpay/blink/obelisk/rush/notification/RawNotification.java:21
  public class RawNotification implements Notification {
  ^^^^^^^^^^^^^^
  
  [ERROR] The type RawNotification must implement the inherited abstract method Notification.getRawContent()
  /obelisk/src/main/java/sa/com/stcpay/blink/obelisk/rush/notification/RawNotification.java:21
  public class RawNotification implements Notification {
  ^^^^^^^^^^^^^^
  
  [ERROR] The type RawNotification must implement the inherited abstract method Notification.getExpiration()
  /obelisk/src/main/java/sa/com/stcpay/blink/obelisk/rush/notification/RawNotification.java:21
  public class RawNotification implements Notification {
  ^^^^^^^^^^^^^^
  
  [ERROR] The type RawNotification must implement the inherited abstract method Notification.getCollapseId()
  /obelisk/src/main/java/sa/com/stcpay/blink/obelisk/rush/notification/RawNotification.java:21
  public class RawNotification implements Notification {
  ^^^^^^^^^^^^^^
  
  [ERROR] Type mismatch: cannot convert from String to val
  /obelisk/src/main/java/sa/com/stcpay/blink/obelisk/rush/medium/apns/ApnsMediumAdapter.java:51
  val topic = authenticationConfiguration.getTargetTopic();
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  
  [ERROR] The constructor PushyNotification(Notification, val) is undefined
  /obelisk/src/main/java/sa/com/stcpay/blink/obelisk/rush/medium/apns/ApnsMediumAdapter.java:52
  val actualNotification = new PushyNotification(notification, topic);
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

running a spring fat jar with aspectj

Thanks for the work. A question for you...

Should you be able to run a Spring fat jar using the following command?

$ java -javaagent:$HOME/.m2/repository/org/aspectj/aspectjweaver/1.8.13/aspectjweaver-1.8.13.jar -jar target/*.jar

I have tried that approach using an ajc-aop.xml file, similar to the following and it doesn't seem to be working

java -jar libs/my_spring_app.jar -javaagent:libs/aspectjweaver.jarDorg.aspectj.weaver.loadtime.configuration=file:config/ajc-aop.xml

Thanks in advance.

Example of Spring Transaction AspectJ On Spring Boot

It would be great if there is an definitive example how to configure Spring Transaction (AspectJ mode), the current documentation seems confusing. It would be great if you can provide one.

I have an application based on Spring Boot 2.0.x, its running fine but when upgrading to 2.1.x its producing many error output at startup saying error can't determine implemented interfaces which causing slow start up:

[LaunchedURLClassLoader@5fcfe4b2] error can't determine implemented interfaces of missing type net.sf.ehcache.CacheManager
when weaving type org.springframework.boot.autoconfigure.cache.EhCacheCacheConfiguration
when weaving classes
when weaving
 [Xlint:cantFindType]
[LaunchedURLClassLoader@5fcfe4b2] error can't determine implemented interfaces of missing type net.sf.ehcache.CacheManager
when weaving type org.springframework.boot.autoconfigure.cache.EhCacheCacheConfiguration
when weaving classes
when weaving

And seems like I'm not the only one:

I get it to working by having the spring transaction in aop.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
    <weaver>
        <include within="com.my.app..*" />
        <include within="org.springframework.transaction..*" />
    </weaver>
</aspectj>

But I left wondering if this is the correct way, since many other package do not get woven did i miss something that i do not aware of?

Update Java 11 OpenJDK, Spring Boot 2.2.4 and Add Travis for CI

Hi all,

My repo: https://github.com/lofidewanto/spring-boot-aspectj

I've done following things in this repo:

  • Update to Java 11 and Spring Boot 2.2.4
  • Add Travis for CI. Therefore I added Logger instead of System.out to control the output. Travis does not like to have a lot of text in its console.
  • Rename some projects / directories to comply Maven and make them clearer. ArtifactId, directories and so on (see my screenshot)

Bildschirmfoto 2020-04-20 um 11 15 42

  • Add more docs..

@dsyer If you like I could make a PR. Thanks a lot for this nice work explaining AspectJ!

Add gradle support

Since gradle becomes more and more popular, It would be nice to make the examples to support gradle.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.