This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.

The premise is introduced

One of the most powerful features we use when talking about the actual project development of the Spring framework is AOP. If used properly, its biggest effect is less invasive and simplify our work task (save a lot of repetitive code), the most important thing is that it allows us to under the condition of without changing the original code, woven into our logic, especially when we don’t have the source code, and when we restore the logic before, Just remove the proxy.

AOP’s dynamic proxy

The common implementations of Spring AOP are Cglib and JDK dynamic proxies. Both can be implemented, with slight performance differences that are not detailed here.

  1. The limitations of both the JDK’s dynamic proxy and Cglib’s Spring proxy mechanisms are that AOP functionality does not work when a generated proxy object calls a method internally, because after all, it is not the proxy object that calls it, but this.
  2. They only apply to public and protected member methods.

Of course, there are workarounds, such as injecting the bean into itself as a property through which all method calls are made. Or use aopContext. currentProxy to get the proxy object. But with these solutions, it’s easy for developers to slip up and cause problems during development.

So if you need a more powerful and easy-to-use implementation of AOP, it’s the bytecode weaving technology AspectJ. By modifying the bytecode, you can achieve all methods, including (final, private, static methods), powerful. And Spring supports AspectJ-style AOP.

AOP technology types

Before introducing the powerful techniques of Aspectj, let’s start by categorizing the basics of AOP technology implementation and enhancing the capabilities of target classes by weaving in facets for them. Divided by time when the slice was woven into the target class

  • Compile-time: The use of a special compiler to weave facets into the target class at compile time is rare because special compiler support is required. The AspectJ compiler, for example, is rare and I haven’t used it yet.
  • Load-time: Weaving facets into the target class during class loading by bytecode editing technology. The core idea is that before the class file of the target class is loaded by JVM, the cross-cutting logic is woven into the class file of the target class by custom classloader or classfile converter, and then the modified class file is handed to the JVM for loading. This method of weaving can be shortened to LTW, AspectJ’s LoadTimeWeaving
  • Runtime: Implementing AOP at runtime by generating dynamic proxies for target classes is runtime weaving. This is the default implementation in Spring AOP and provides two ways to create dynamic proxies: the JDK’s built-in dynamic proxies for interfaces and using CGLib to create dynamic proxies.

Here we introduce Spring’s LTW mechanism for integrating AspectJ. Belong to dynamic load weaving.

Problems that LTW can solve

  • Problems with non-Spring-managed class dependency injection and aspects not working.
  • A problem with calling an in-class method aspect that does not take effect.
  • AOP section weaving method

The principle of LTW

Class loading uses bytecode editing technology to weave the slice into the target class, which is called LTW (Load Time Weaving).

Implement AOP functionality by converting bytecode at class load using the java.lang.instrument package added to JDK5.

The JDK’s proxy functionality gives agents access to the underlying components of the JVM to register classfile converters with the JVM to convert the bytecode of the class files at classload time to the ClassFileTransformer interface. For specific directions, you can study Java Agent technology.

Implement LTW in Spring

  • Spring implements AOP functionality by default through run-time generation of dynamic proxies for aspect weaving, but Spring can also implement AOP using LTW technology and provides fine-grained control to implement classfile conversions within a single ClassLoader.

  • Spring in the org. Springframework. Instrument. The classloading. LoadTimeWeaver interface defines for the class loader added ClassFileTransfomer abstraction

  • Spring’s LTW supports aspectJ-defined facets, either directly using AspectJ syntax or via Java classes using @AspectJ annotations.

  • Spring LTW reads meta-INF /aop. XML file in classpath to get the information about the aspect class and the target class to be woven into the aspect, and uses LoadTimeWeaver to weave the aspect into the target class when ClassLoader loads the class file.

  1. You can register a spring-provided ClassFileTransformer with a ClassLoader using LoadTimeWeaver.

  2. During class loading, the registered ClassFileTransformer reads the aspect and target class information defined in the meta-INF/Aop.xml file under the classpath, weaves the aspect information into the target class file before it is actually loaded by the VM, and generates the new class file bytecode, which is then delivered to the VM for loading.

  3. Thus, the subsequent creation of an instance of the target class already implements AOP functionality.

Maven dependencies and plug-ins

Spring AOP and aspectJ

Maven configuration for Spring
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>5.2.5</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>5.2.5</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aop</artifactId>
			<version>5.2.5</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aspects</artifactId>
			<version>5.2.5</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-instrument</artifactId>
			<version>5.2.5</version>
		</dependency>
Copy the code
Springboot configuration
<dependencies>
 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
            <version>2.3.1. RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.2.7. RELEASE</version>
        </dependency>
	<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-instrument</artifactId>
            <version>5.2.7. RELEASE</version>
	</dependency>
</dependencies>
Copy the code

The Maven aspectjWeaver package will be included

 <dependency>
    <groupId>aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.5.3</version>
</dependency>
Copy the code

Spring full annotations

@Configuration
// Enable Aspectj runtime weaving
@EnableLoadTimeWeaving(aspectjWeaving = AspectJWeaving.ENABLED)
// Enable dependency injection support for non-Spring container beans
@EnableSpringConfigured
@ComponentScan(basePackages = "demo")
public class AppConfig extends WebMvcConfigurationSupport {}
Copy the code

Springboot full annotation mode

@SpringBootApplication
@EnableLoadTimeWeaving
@EnableSpringConfigured
@EnableAsync(mode = AdviceMode.ASPECTJ)
Copy the code

Cut class

@Aspect
// Enable Spring to inject dependencies into aspect objects
@Configurable
public class SampleAspectj {
	@Autowired
	private RestBean rbean;
	@Around(value = "@annotation(rpc) ")
	public void aop(ProceedingJoinPoint joinPoint,Rpc rpc) { rbean.rwar(); }}Copy the code

Object tangential class

@Configurable
public class TestOriginObject {
	
	// dependency injection
    @Autowired
    public TestService testService;

	// Spring facets
    @Async
    public void print(a) {
        System.out.println("TestOriginObject print thread " + Thread.currentThread().toString());
    }

	// Custom section
    @Rpc
    public void print1(a) {
        System.out.println("TestOriginObject print1"); }}Copy the code
@Component
public class TestService {

    @Async
    @Profile
    public void asyncPrint(a) {
        System.out.println("TestService print thread " + Thread.currentThread().toString());
    }

    public void print(a) {
        asyncPrint();
    }
	
	private static void test04(a) {
        log.info("------------test04-----------");
    }

	private void test03(a) {
        log.info("------------test03-----------");
    }

    public void test02(a) {
        log.info("------------test02-----------"); }}Copy the code

Aop. XML configuration

Put it in the/SRC /main/resources/ meta-INF directory

<! DOCTYPEaspectj PUBLIC "-//AspectJ//DTD//EN" "https://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
    <weaver options="-showWeaveInfo -XnoInline -Xset:weaveJavaxPackages=true -Xlint:ignore -verbose -XmessageHandlerClass:org.springframework.aop.aspectj.AspectJWeaverMessageHandler">
        <! Braid only classes under specified package -->
        <include within="demo.. *"/>
    </weaver>
    <aspects>
        <! Weave with the specified section class -->
        <aspect name="test.SampleAspectj"/>
    </aspects>
</aspectj>

Copy the code
@SpringBootApplication
@EnableLoadTimeWeaving
@EnableSpringConfigured
@EnableAsync(mode = AdviceMode.ASPECTJ)
public class AppApplication {
    public static void main(String[] args) {
        // Initialize the Spring context
        ApplicationContext context = SpringApplication.run(AppApplication.class, args);
        // Create a POJO into which TestService will be injected
        TestOriginObject pojo = new TestOriginObject();
        System.out.println("inject bean " + pojo.testService);
        TestService testService = context.getBean(TestService.class);
        // Call the section normally
        testService.asyncPrint();
        // Internal calls to the section
        testService.print();
        // Non-spring-managed class aspect call, spring-defined aspect
        pojo.print();
        // Non-spring-managed class facets call, custom facetspojo.print1(); testService.test02(); testService.test03(); testService.test04(); }}Copy the code
instructions
  • @enableloadtimeWeaving to start LTW, or use context:load-time-weaver/
  • The @64x works together with @enablespringconfigured (or context:spring-configured/)
  • @ Signals specifies whether to inject before or after the constructor
  • @enableAsync or @Enablecaching must use the ASPECTJ mode

@enableAspectJAutoProxy also enables runtime proxy placement.

Start VM Parameters

- javaagent: path \ spring - instrument - 5.1.6. The jar - javaagent: path \ aspectjweaver - 1.9.2. JarCopy the code

You can use Maven to package for execution

<build>
 <plugins>
 <plugin>
 <groupId>org.apache.maven.plugins</groupId>
 <artifactId>maven-surefire-plugin</artifactId>
 <configuration>
 <argLine>-javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.ja r" -javaagent:"${settings.localRepository}/org/springframework/spring-instrument/${spring.version}/spring-instrument-${spri ng.version}.jar"<! -- -Dspring.profiles.active=test-->
 </argLine>
 </configuration>
 </plugin>
 <plugin>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-maven-plugin</artifactId>
 <configuration>
 <agent>
 ${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar
 </agent>
 <agent>${settings.localRepository}/org/springframework/spring-instrument/${spring.version}/spring-instrument-${spring.version}. jar</agent>
 </configuration>
	 </plugin>
 </plugins>
</build>
Copy the code

LTW official documentation

  • AspectJ:www.eclipse.org/aspectj/doc…

  • Using AspectJ: (5.10.4) : docs. Spring. IO/Spring…

public class Run {
	public static void main(String[] args) {
		// Key code used to start agent in the program
		// Get the Instrumentation instance of the current JVM through ByteBuddy
		Instrumentation instrumentation = ByteBuddyAgent.install();
		// Activate Aspectj's proxy object
		Agent.agentmain("", instrumentation);
		// Activate the Spring proxy object
		InstrumentationSavingAgent.agentmain("", instrumentation);
		Start the Spring container
		/ /...}}Copy the code