编程知识 cdmana.com

[principle of spring technology] implementation guide for dynamic proxy technology of AspectJ and loadtimeweaving

Premise introduction

When we talked about Spring The project of the framework is in actual development , One of the powerful functions used is ( Section oriented programming ) This door AOP technology . If used properly , Its biggest function is to be less invasive and simplify our work tasks ( Save a lot of repetitive coding ), The most important point is , It allows us to... Without changing the original code , Weave into our logic , Especially when we don't have source code , And when we restore the previous logic , Just remove the agent .

AOP Dynamic proxy for

Spring AOP The conventional implementation of is cglib and jdk A dynamic proxy . Both can be achieved , Just a slight difference in performance , I won't go into details here .

  1. Whether it's JDK Dynamic agent and Cglib Realized Spring Agent mechanisms have some limitations , That is, when the generated proxy object calls the method internally ,AOP Invalid function , Because after all, it is not the call of the proxy object, but this Call to .
  2. They can only public、protected The member method of type takes effect .

Of course , There are also flexible solutions , For example, will bean Inject into itself as an attribute , Then all method calls are called through this property . Or by AopContext.currentProxy To get the proxy object . But these solutions , In the development process, developers can easily cause problems due to negligence .

therefore , If you need a more powerful and easy-to-use aop Implementation scheme , That's bytecode weaving technology aspectj. By modifying the bytecode , You can cut through all methods , Include (final、private、static Method of type ), Powerful . also spring Support aspectj The way of aop.

AOP Type of Technology

Introducing powerful Aspectj Before the technology , Let's start with AOP Technology implementation basis for classification , By weaving slices into the target class , Achieve the target class function enhancement . By section is woven into the time in the target class

  • Compile time : Use a special compiler to weave facets into the target class at compile time , It's rare , Because special compiler support is required . for example ,AspectJ compiler , Very few people come to , I haven't used it yet .
  • When loading : Bytecode editing is used to weave the cut plane into the target class during class loading , Its core idea is : In the target class class The file is JVM Before loading , The crosscutting logic is woven into the target class by a custom class loader or class file converter class In file , Then it will be modified after class Documents to JVM load . This method of weaving can be abbreviated as LTW(LoadTimeWeaving),AspectJ Of LoadTimeWeaving (LTW)
  • Runtime : The runtime is achieved by generating a dynamic proxy for the target class AOP It's a run-time weave , This is also Spring AOP Default implementation in , It also provides two ways to create dynamic proxies :JDK Built-in dynamic proxy and usage for interfaces CGLib Dynamic proxy is created by subclassing dynamically .

Here we introduce Spring Integrate AspectJ Of LTW Mechanism . It belongs to dynamic loading weaving .

LTW Problems that can be solved

  • Not spring Managed class dependency injection and aspect failure .
  • The aspect of calling methods within classes does not take effect .
  • AOP Section weaving method

LTW Principle

During class loading, the section is woven into the target class by bytecode editing technology , This way is called LTW(Load Time Weaving).

Use JDK5 Newly added java.lang.instrument package , The bytecode is converted when the class is loaded , So as to achieve AOP function .

JDK The proxy function for the proxy to access JVM Underlying components , Take this to JVM Register the class file converter , Converts the bytecode of the class file when the class is loaded ClassFileTransformer Interface . The specific direction can be studied java agent Technology is enough .

Spring To realize LTW

  • Spring By default, the weaving of sections is implemented by generating dynamic proxy at runtime , Realization AOP function , however Spring You can also use LTW Technology to implement AOP, It also provides fine-grained control , Single ClassLoader Implement class file conversion within the scope .

  • Spring Medium org.springframework.instrument.classloading.LoadTimeWeaver The interface is defined to add to the class loader ClassFileTransfomer The abstraction of

  • Spring Of LTW Support AspectJ Section of the definition , It can be used directly AspectJ Section of the syntax definition , It can also be used @AspectJ annotation , adopt java Section of a class definition .

  • Spring LTW By reading the classpath Next META-INF/aop.xml file , Gets information about the section class and the target class to be cut into , adopt LoadTimeWeaver stay ClassLoader When the class file is loaded, the cut surface is woven into the target class .

  1. Spring Through LoadTimeWeaver take Spring Provided ClassFileTransformer Sign up to ClassLoader in .

  2. During class loading , Registered ClassFileTransformer Read under the classpath META-INF/aop.xml Section class and target class information defined in the file , In the target class class The file is actually VM Weave section information before loading , Generate a new Class File bytecode , And then to VM load .

  3. Thus an instance of the target class that is created later , It's already done AOP function .

maven Dependencies and plug-ins

spring-AOP and aspectJ

spring Of maven To configure
		<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>
springboot Configuration of
<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>

There will be maven aspectjWeaver package

 <dependency>
    <groupId>aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.5.3</version>
</dependency>

spring Full annotation

@Configuration
// Turn on Aspectj Runtime weaving 
@EnableLoadTimeWeaving(aspectjWeaving = AspectJWeaving.ENABLED)
// Open non spring Containers bean Dependency injection support 
@EnableSpringConfigured
@ComponentScan(basePackages = "demo")
public class AppConfig extends WebMvcConfigurationSupport {}

springboot Full annotation

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

Section class

@Aspect
// Give Way spring You can inject dependencies into slice objects 
@Configurable
public class SampleAspectj {
	@Autowired
	private RestBean rbean;
	@Around(value = "@annotation(rpc) ")
	public void aop(ProceedingJoinPoint joinPoint,Rpc rpc) {
		rbean.rwar();
	}
}

Object pointcut class

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

	// spring  Section of 
    @Async
    public void print() {
        System.out.println("TestOriginObject print thread " + Thread.currentThread().toString());
    }

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

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

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

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

    public void test02() {
        log.info("------------test02-----------");
    }
}

aop.xml To configure

Put it in /src/main/resources/META-INF Under the table of contents

<!DOCTYPE aspectj 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 the classes under the specified package  -->
        <include within="demo..*"/>
    </weaver>
    <aspects>
        <!--  Use the specified cut class for weaving  -->
        <aspect name="test.SampleAspectj"/>
    </aspects>
</aspectj>

@SpringBootApplication
@EnableLoadTimeWeaving
@EnableSpringConfigured
@EnableAsync(mode = AdviceMode.ASPECTJ)
public class AppApplication {
    public static void main(String[] args) {
        //  initialization  spring context
        ApplicationContext context = SpringApplication.run(AppApplication.class, args);
        //  establish  POJO, here  TestService  Will be injected into  POJO  in 
        TestOriginObject pojo = new TestOriginObject();
        System.out.println("inject bean " + pojo.testService);
        TestService testService = context.getBean(TestService.class);
        //  Normal call section 
        testService.asyncPrint();
        //  Internal call of section 
        testService.print();
        //  Not  spring  Managed class aspect calls ,spring  Section of the definition 
        pojo.print();
        //  Not  spring  Managed class aspect calls , Custom section 
        pojo.print1();
		
		testService.test02();
	    testService.test03();
	    testService.test04();
    }
}
explain
  • @EnableLoadTimeWeaving For opening LTW, Or use context:load-time-weaver/
  • @Configurable It has to be with @EnableSpringConfigured ( or context:spring-configured/) In combination with
  • @Configurable It can be specified to inject... Before or after the constructor
  • @EnableAsync or @EnableCaching You have to use ASPECTJ Pattern

@EnableAspectJAutoProxy The runtime agent seeding mode will also be started .

start-up VM Parameters

-javaagent:path\spring-instrument-5.1.6.RELEASE.jar
-javaagent:path\aspectjweaver-1.9.2.jar

May adopt Maven Package and enter the execution mode

<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}.jar"
 -javaagent:"${settings.localRepository}/org/springframework/spring-instrument/${spring.version}/spring-instrument-${spring.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>

LTW Official documents

public class Run {
	public static void main(String[] args) {
		// Key code , Used to open... In the program agent
		// adopt bytebuddy Get the present jvm Of Instrumentation example 
		Instrumentation instrumentation = ByteBuddyAgent.install();
		// Activate Aspectj Proxy object of 
		Agent.agentmain("", instrumentation);
		// Activate spring Proxy object 
		InstrumentationSavingAgent.agentmain("", instrumentation);
		// start-up spring Containers 
		//...
	}
}

版权声明
本文为[Li Haoyu Alex]所创,转载请带上原文链接,感谢
https://cdmana.com/2021/10/20211002145410445z.html

Scroll to Top