Aopalliance-02-通过 aspect 对 private 和 static 方法进行增强
2019年2月26日大约 3 分钟
背景
spring aop 平时的应用范围非常广泛,但是对于 private 和 static 的支持并不友好。
因为本质原理是通过 CGLIB 实现一个继承子类。
Spring AOP vs AspectJ
Spring AOP是基于Spring IoC实现的,它解决大部分常见的需求,但它并不是一个完整的AOP解决方案。
对于非Spring容器管理的对象,它更没有办法了。而AspectJ旨在提供完整的AOP方案,因此也会更复杂。
2.1 织入方式
两者织入方式有极大的不同,这也是它们的本质区别,它们实现代理的方式不同。
AspectJ是在运行前织入的,分为三类:
编译时织入
编译后织入
加载时织入
因此需要AspectJ编译器(ajc)的支持。
而Spring AOP是运行时织入的,主要使用了两种技术:JDK动态代理和CGLIB代理。对于接口使用JDK Proxy,而继承的使用CGLIB。
2.2 Joinpoints
因为织入方式的区别,两者所支持的Joinpoint也是不同的。
像final的方法和静态方法,无法通过动态代理来改变,所以Spring AOP无法支持。但AspectJ是直接在运行前织入实际的代码,所以功能会强大很多。
2.3 性能
编译织入会比较运行时织入快很多,Spring AOP是使用代理模式在运行时才创建对应的代理类,效率没有AspectJ高。
Aspectj 入门例子
maven 引入
4.0.0
org.example
springboot-aspectj-demo
1.0-SNAPSHOT
8
8
UTF-8
1.3.5.RELEASE
1.9.7
org.springframework.boot
spring-boot-starter-web
${spring-boot.version}
org.springframework.boot
spring-boot-starter-test
${spring-boot.version}
org.aspectj
aspectjweaver
${aspectj.version}
org.aspectj
aspectjrt
${aspectj.version}
org.springframework.boot
spring-boot-maven-plugin
${spring-boot.version}
org.codehaus.mojo
aspectj-maven-plugin
1.14.0
1.8
1.8
1.8
none
true
true
${project.build.directory}/classes
compile
测试 controller
package org.example.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* localhost:8080/test/hello
*/
@RestController
@RequestMapping("/test")
public class TestController {
@RequestMapping("/hello")
public String hello() {
System.out.println("---hello start---");
privateRest();
staticTest();
System.out.println("---hello end---");
return "Hello";
}
private void privateRest() {
System.out.println("---privateRest start---");
System.out.println("---privateRest start---");
}
public static void staticTest() {
System.out.println("---staticTest start---");
System.out.println("---staticTest start---");
}
}
切面 aspect
package org.example.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
@Aspect
@Component
//@EnableAspectJAutoProxy
public class ControllerAspect {
@Pointcut("execution(* org.example.controller..*.*(..))")
private void testControllerPointcut() {
}
@Around("testControllerPointcut()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("------------------------- around start ------------------------ ");
long start = System.nanoTime();
Object obj = joinPoint.proceed();
long end = System.nanoTime();
System.out.println("------------------------- cost " + (end-start) + "ns");
System.out.println("------------------------- around end ------------------------ ");
return obj;
}
}
测试
1) 首先进行编译
mvn clean package
此时,生成的 class 文件会进行增强。
查看 class 文件,效果如下:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.example.controller;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.runtime.reflect.Factory;
import org.example.aspect.ControllerAspect;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping({"/test"})
public class TestController {
public TestController() {
}
@RequestMapping({"/hello"})
public String hello() {
JoinPoint var1 = Factory.makeJP(ajc$tjp_0, this, this);
return (String)hello_aroundBody1$advice(this, var1, ControllerAspect.aspectOf(), (ProceedingJoinPoint)var1);
}
private void privateRest() {
JoinPoint var1 = Factory.makeJP(ajc$tjp_1, this, this);
privateRest_aroundBody3$advice(this, var1, ControllerAspect.aspectOf(), (ProceedingJoinPoint)var1);
}
public static void staticTest() {
JoinPoint var0 = Factory.makeJP(ajc$tjp_2, (Object)null, (Object)null);
staticTest_aroundBody5$advice(var0, ControllerAspect.aspectOf(), (ProceedingJoinPoint)var0);
}
static {
ajc$preClinit();
}
}
2)页面访问
http://localhost:8080/test/hello
对应的日志效果:
------------------------- around start ------------------------
---hello start---
------------------------- around start ------------------------
---privateRest start---
---privateRest start---
------------------------- cost 49000ns
------------------------- around end ------------------------
------------------------- around start ------------------------
---staticTest start---
---staticTest start---
------------------------- cost 34000ns
------------------------- around end ------------------------
---hello end---
------------------------- cost 334600ns
------------------------- around end ------------------------
参考资料
贡献者
binbin.hou