ajc编译器

使用aspectj编译器插件来改动class类文件来实现增强(在编译阶段)

编译器增强能突破代理仅能通过方法重写增强的限制:可以对构造方法、静态方法等实现增强

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.14.0</version>
<configuration>
<complianceLevel>1.8</complianceLevel>
<source>8</source>
<target>8</target>
<showWeaveInfo>true</showWeaveInfo>
<verbose>true</verbose>
<Xlint>ignore</Xlint>
<encoding>UTF-8</encoding>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>

因为idea 不会调用 ajc 编译器,所以一定要用 maven 的 compile 来编译
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@SpringBootApplication
public class A09 {
private static final Logger log = LoggerFactory.getLogger(A09.class);

public static void main(String[] args) {
ConfigurableApplicationContext context =
SpringApplication.run(A09.class, args);

MyService service = context.getBean(MyService.class);
log.debug("service class: {}", service.getClass());
service.foo();
context.close();
}
}

agent 类加载

运行时需要在 VM options 里加入-javaagent:(maven仓库地址)/org/aspectj/aspectjweaver/1.9.7/aspectjweaver-1.9.7.jar

类加载时修改 .class 文件实现增强


Proxy增强(JDK)

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class JdkProxyDemo {

interface Foo {
void foo();
}

static final class Target implements Foo {
public void foo() {
System.out.println("target foo");
}
}

// jdk 只能针对接口代理
public static void main(String[] param) throws IOException {
// 目标对象
Target target = new Target();
//类加载器
ClassLoader loader = JdkProxyDemo.class.getClassLoader();

//三个参数分别为
// loader:类加载器,用来加载在运行期间动态生成的字节码
// Class[]{Foo.class}:代理类将要实现的接口
// 代理类将来要调用代理类方法时要执行的行为
Foo proxy = (Foo) Proxy.newProxyInstance(loader, new Class[]{Foo.class}, new InvocationHandler() {
//三个参数分别是代理对象本身,正在执行的方法对象,方法传递的参数
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before...");
//目标.方法(参数);
//方法.invoke(目标,参数);
Object res = method.invoke(target, args);
System.out.println("after...");
//让代理也返回目标方法执行的结果
return res;
}
});
proxy.foo();
}
}

总结

代理对象和目标对象都实现了Foo接口,是兄弟关系。代理对象类型不能强转成目标对象类型

目标类定义的时候可以加final修饰


原理

1、先用代码简单模拟一下JDK动态代理。先定义一个Foo接口,里面有一个foo方法,再定义一个Target类来实现这个接口,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public interface Foo {
void foo();
}

@Slf4j
public final class Target implements Foo {
public void foo() {
log.debug("target foo");
}
}

public class $Proxy0 implements Foo {
@Override
public void foo() {
// 1. 功能增强
System.out.println("before...");
// 2. 调用目标
new Target().foo();
}
}

public class Main {
public static void main(String[] args) {
Foo proxy = new $Proxy0();
proxy.foo();
}
}

接下来对Target类中的foo()方法进行增强

首先想再定义一个类实现Foo接口,然后在foo()中编写增强代码,接着再new一个Target对象,调用它的foo()方法,代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class $Proxy0 implements Foo {
@Override
public void foo() {
// 1. 功能增强
System.out.println("before...");
// 2. 调用目标
new Target().foo();
}
}

// 测试运行
public class Main {
public static void main(String[] args) {
Foo proxy = new $Proxy0();
proxy.foo();
}
}

2、上面的代码把功能增强的代码和调用目标的代码都固定在了代理类的内部,不太灵活。

因此可以通过定义一个InvocationHandler接口的方式来将这部分代码解耦出来,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public interface InvocationHandler {
void invoke();
}
public interface Foo {
void foo();
}
@Slf4j
public final class Target implements Foo {
public void foo() {
System.out.println("target foo");
}
}
public class Main {
public static void main(String[] args) {
Foo proxy = new $Proxy0(new InvocationHandler() {
@Override
public void invoke() {
// 1. 功能增强
System.out.println("before...");
// 2. 调用目标
new Target().foo();
}
});
proxy.foo();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
public class $Proxy0 implements Foo {
private InvocationHandler h;

public $Proxy0(InvocationHandler h) {
this.h = h;
}

@Override
public void foo() {
h.invoke();
}
}

3、虽然将功能增强的代码和调用目标的代码通过接口的方式独立出来了,但是如果此时接口中新增了一个方法bar(),Target类和$Proxy0类中都要实现bar()方法,那么调用proxy的foo()bar()方法都将间接调用目标对象的foo()方法,因为在InvocationHandlerinvoke()方法中调用的是target.foo()方法


4、改进为代理类中调用方法的时候,通过反射把接口中对应的方法Method对象作为参数传给InvocationHandler,这样就可以通过保证调用proxy调用方法时一定能在invoke中调用对应的方法

5、同时,修改Foo接口的中的bar()方法,使其具有int类型的返回值,因此InvocationHandlerinvoke()方法也得有返回值,同时将代理对象本身作为第一个参数,具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class A12 {

interface Foo {
void foo();
int bar();
}

static class Target implements Foo {
public void foo() {
System.out.println("target foo");
}

@Override
public int bar() {
System.out.println("target bar");
return 100;
}
}

// interface InvocationHandler {
// Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
// }

public static void main(String[] param) throws NoSuchMethodException {
Foo proxy = new $Proxy0(new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws InvocationTargetException, IllegalAccessException {
//1、实现功能增强
System.out.println("before");
//2、调用目标
return method.invoke(new Target(),args);
}
});
proxy.foo();
proxy.bar();
}
}

当每次调用foo或者bar方法时,多次加载。此处使用静态代码块对foo和bar进行初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
//代理类
//与目标实现一个共同的接口
public class $Proxy0 extends Proxy implements Foo {

//通过构造方法传递InvocationHandler
public $Proxy0(InvocationHandler h){
super(h);
}

@Override
public void foo() {
try {
//将foo方法和方法参数进行传递(此处没有参数,使用Object[0]代替)
h.invoke(this,foo,new Object[0]);
//运行异常直接抛
} catch (RuntimeException | Error e) {
throw e;
//检查异常,转化后抛
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}

@Override
public int bar() {
try {
//将bar方法和方法参数进行传递(此处没有参数,使用Object[0]代替)
Object res = h.invoke(this,bar, new Object[0]);
return (int) res;
//运行异常直接抛出
} catch (RuntimeException | Error e) {
throw e;
//检查异常,转化为运行时异常后
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
//对foo和bar初始化
static Method foo;
static Method bar;
static {
try {
//获取 foo方法对象
foo = Foo.class.getMethod("foo");
//获取 bar方法对象
bar = Foo.class.getMethod("bar");
} catch (NoSuchMethodException e) {
throw new NoSuchMethodError(e.getMessage());
}
}
}

反射优化

前 16 次是调用了java本地的MethodAccessor 的实现类,反射性能较低

第 17 次调用会生成代理类,优化为非反射调用(用 arthas 的 jad 工具反编译第 17 次调用生成的代理类)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class TestMethodInvoke {
public static void main(String[] args) throws Exception {
Method foo = TestMethodInvoke.class.getMethod("foo", int.class);
for (int i = 1; i <= 17; i++) {
show(i, foo);
foo.invoke(null, i);
}
System.in.read();
}

// 方法反射调用时, 底层 MethodAccessor 的实现类
private static void show(int i, Method foo) throws Exception {
Method getMethodAccessor = Method.class.getDeclaredMethod("getMethodAccessor");
getMethodAccessor.setAccessible(true);
Object invoke = getMethodAccessor.invoke(foo);
if (invoke == null) {
System.out.println(i + ":" + null);
return;
}
Field delegate = Class.forName("jdk.internal.reflect.DelegatingMethodAccessorImpl").getDeclaredField("delegate");
delegate.setAccessible(true);
System.out.println(i + ":" + delegate.get(invoke));
}
public static void foo(int i) {
System.out.println(i + ":" + "foo");
}
}

Proxy增强(cglib)

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class CglibProxyDemo {

static class Target {`
public void foo() {
System.out.println("target foo");
}
}

// 代理是子类型, 目标是父类型
// 如果目标对象为final,或者代理类执行的方法为final,则无法进行代理
// 而jdk代理都实现了接口,代理类和目标类是平级的关系,所以可以进行代理
public static void main(String[] param) {
Target target = new Target();
//MethodInterceptor()决定了代理类中方法执行的行为
Target proxy = (Target) Enhancer.create(Target.class, new MethodInterceptor() {
@Override
//四个参数分别是:
//1、代理对象本身
//2、当前代理类中执行的方法
//3、方法执行时的参数
//4、方法对象(可以避免反射调用方法)
public Object intercept
(Object p, Method method, Object[] args, MethodProxy methodProxy)throws Throwable {
System.out.println("before...");

// Object res = method.invoke(target, args);
// Object res = methodProxy.invoke(target, args);//内部没有用到反射,需要目标
Object res = methodProxy.invokeSuper(p, args);//内部没有用到反射,需要代理

System.out.println("after...");
return res;
}
});
proxy.foo();
}
}

总结

1
public Object intercept (Object p, Method method, Object[] args, MethodProxy methodProxy)

MethodInterceptorintercept()方法的第2个参数是method,可以通过反射对目标方法进行调用

1
Object result = method.invoke(target, args); // 用方法反射调用目标

第4个参数proxy,可以不用反射就能对目标方法进行调用

1
2
Object result = proxy.invoke(target, args); // 需要传目标类 (spring用的是这种)
Object result = proxy.invokeSuper(obj, args); // 不需要目标类,需要代理对象本身

代理类不需要实现接口

代理对象和目标对象是父子关系,代理类继承于目标类;

目标类定义的时候不能加final修饰,否则代理类就无法继承目标类了,会报IllegalArgumentException

目标类方法定义的时候不能加final修饰,否则代理类继承目标类以后就不能重写目标类的方法了


原理

代码模拟cglib动态代理

先创建一个目标类,其中有三个方法,再创建一个代理类继承目标类,并且通过构造方法传入MethodInterceptor对象用于回调。重写目标类后,通过调用intercept方法实现功能增强(静态代码块作用与jdk动态代理中类似)

1
2
3
4
5
public class Target {
public void save() {System.out.println("save()");}
public void save(int i) {System.out.println("save(int)");}
public void save(long j) {System.out.println("save(long)");}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public class Proxy extends Target {
private MethodInterceptor methodInterceptor;

public void setMethodInterceptor(MethodInterceptor methodInterceptor) {
this.methodInterceptor = methodInterceptor;
}

@Override
public void save(){
//第一个参数:代理类对象
//第二个参数:当前正在执行的方法
//第三个参数:实参数组
//第四个参数:方法代理(可以避免反射调用)
try {
methodInterceptor.intercept(this,save0,new Object[0], null);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}

@Override
public void save(int i){
try {
methodInterceptor.intercept(this,save1,new Object[]{i}, null);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}

@Override
public void save(long j){
try {
methodInterceptor.intercept(this,save2,new Object[]{j}, null);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}

static Method save0;
static Method save1;
static Method save2;
static {
try {
save0 = Target.class.getMethod("save");
save1 = Target.class.getMethod("save",int.class);
save2 = Target.class.getMethod("save",long.class);
} catch (NoSuchMethodException e) {
throw new NoSuchMethodError(e.getMessage());
}

}
}

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static void main(String[] args) {
Proxy proxy = new Proxy();
Target target = new Target();
proxy.setMethodInterceptor(new MethodInterceptor() {
@Override
public Object intercept
(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before...");
Object res = method.invoke(target, objects);
return res;
}
});
proxy.save();
proxy.save(2);
proxy.save(5L);
}

创建MethodProxy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
static MethodProxy save0Proxy;
static MethodProxy save1Proxy;
static MethodProxy save2Proxy;
static {
//第一个参数:目标类型
//第二个参数:代理的类型
//第三个参数:用于描述参数和返回值类型
// ()V表示无参无返回值,(I)V表示参数为整型无返回值,(J)V表示参数为长整型无返回值
//第四个参数:带增强功能的方法名
//第五个参数:带原始功能的方法名
save0Proxy = MethodProxy.create(Target.class,Proxy.class,"()V","save","saveSuper");
save1Proxy = MethodProxy.create(Target.class,Proxy.class,"(I)V","save","saveSuper");
save2Proxy = MethodProxy.create(Target.class,Proxy.class,"(J)V","save","saveSuper");
}

//带原始功能的方法
public void saveSuper() {super.save();}
public void saveSuper(int i) {super.save(i);}
public void saveSuper(long j) {super.save(j);}

//带增强功能的方法
@Override
public void save(){
//第一个参数:代理类对象
//第二个参数:当前正在执行的方法
//第三个参数:实参数组
//第四个参数:代理类对象
try {
methodInterceptor.intercept(this,save0,new Object[0], save0Proxy);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}

@Override
public void save(int i){
try {
methodInterceptor.intercept(this,save1,new Object[]{i}, save1Proxy);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}

@Override
public void save(long j){
try {
methodInterceptor.intercept(this,save2,new Object[]{j}, save2Proxy);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}

cglib避免反射调用

MethodProxy在调用invoke和invokeSuper时不会走反射途经,而是通过FastClass来避免反射,cglib在调用方法时,生成两个代理类(为FastClass的子类),代理类中的方法能避免反射


调用invoke

在调用MethodProxy的create静态方法时,底层会创建一个FastClass子类(以下称代理类)来避免反射的调用(因为该代理类实现了fastClas太多功能,所以不直接继承FastClass类,而是选择其中两个功能进行实现)

1、当我们创建MethodProxy时,会传入signature(包括方法名字和参数返回值)

2、代理类中会根据传入的signature编写目标方法的编号(将传入的signature与自定义的signature进行比较)

3、代理类中根据获取方法的编号去调用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class TargetFastClass {

static Signature s0 = new Signature("save","()V");
static Signature s1 = new Signature("save","(I)V");
static Signature s2 = new Signature("save","(J)V");

//获取目标方法的编号
/**
* Target
* save() 0
* save(int) 1
* save(long) 2
*/

//signature:包括方法名字、参数返回值
public int getIndex(Signature signature){
//根据方法名字和返回值类型编写编号
if (s0.equals(signature)) {
return 0;
} else if (s1.equals(signature)) {
return 1;
} else if (s2.equals(signature)) {
return 2;
} else {
return -1;
}
}

//根据获取方法的编号,去正常调用目标对象的方法,不通过反射
public Object invoke(int index,Object target,Object[] args) {
if (index == 0) {
((Target)target).save();
return null;
} else if (index == 1) {
((Target)target).save((int) args[0]);
return null;
} else if (index == 2) {
((Target) target).save((long) args[0]);
return null;
} else {
throw new RuntimeException("无此方法");
}
}
}

调用invokeSuper

与上面调用invoke时步骤基本相同,唯一不同的是:

获取代理类中的方法编号时,应该获取的是带原始功能的方法了,而且在调用目标对象的方法时,也应该调用带原始功能的方法

原因:MethodProxy调用invokeSuper是在MethodInterceptor中执行的,增强功能已经在外面实现了,当获取到的是增强功能时,里面又间接调用了MethodInterceptor,进入循环调用


代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public class ProxyFastClass {
static Signature s0 = new Signature("saveSuper","()V");
static Signature s1 = new Signature("saveSuper","(I)V");
static Signature s2 = new Signature("saveSuper","(J)V");
//获取代理类中方法的编号
/**
* Proxy
* saveSuper() 0
* saveSuper(int) 1
* saveSuper(long) 2
*/

//signature:包括方法名字、参数返回值
public int getIndex(Signature signature){
//根据方法名字和返回值类型编写编号
if (s0.equals(signature)) {
return 0;
} else if (s1.equals(signature)) {
return 1;
} else if (s2.equals(signature)) {
return 2;
} else {
return -1;
}
}

//根据获取方法的编号,去正常调用目标对象的方法,不通过反射
public Object invoke(int index,Object proxy,Object[] args) {
if (index == 0) {
((Proxy)proxy).saveSuper();
return null;
} else if (index == 1) {
((Proxy)proxy).saveSuper((int) args[0]);
return null;
} else if (index == 2) {
((Proxy) proxy).saveSuper((long) args[0]);
return null;
} else {
throw new RuntimeException("无此方法");
}
}

public static void main(String[] args) {
//创建对象
ProxyFastClass fastClass = new ProxyFastClass();
int index = fastClass.getIndex(new Signature("saveSuper", "()V"));
System.out.println(index);
fastClass.invoke(0, new Proxy(), new Object[0]);
}
}

对比

与jdk对比,jdk是调用17次时,针对一个方法产生一个代理类

cglib是MethodProxy调用的时候就产生代理,一个proxy代理类对应两个fastClass的子类

(一个是MethodProxy调用invoke时结合目标使用,一个是MethodProxy调用invokeSuper时结合代理使用)


Spring选择代理

切点:增强的匹配规则

通知:增强的逻辑

切面:切点和通知组合

两个切面的概念

aspect =

​ 通知1(advice)+ 切点1(pointcut)

​ 通知2(advice)+ 切点2(pointcut)

​ 通知3(advice)+ 切点3(pointcut)

asvisor = 更细粒度的切面,只包含一个通知和切点

在aspect生效之前,会被拆解成多个advisor

接口及目标类实现

先创建一个包含又foo和bar方法的接口,再创建一个Target1目标类实现该接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
interface I1 {
void foo();
void bar();
}

static class Target1 implements I1 {
public void foo() {
System.out.println("target1 foo");
}
public void bar() {
System.out.println("target1 bar");
}
}

static class Target2 {
public void foo() {
System.out.println("target2 foo");
}
public void bar() {
System.out.println("target2 bar");
}
}

准备切点

创建切点对象,调用setExpression设置切点表达式

1
2
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* foo())");

准备通知

1
2
3
4
5
6
7
8
9
MethodInterceptor advice = new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("before...");
Object res = invocation.proceed();//调用目标
System.out.println("after...");
return res;
}
};

准备切面

创建DefaultPointcutAdvisor对象,传入切点和通知

然后再创建ProxyFactory对象,添加目标对象和切面,最后通过代理对象调用方法完成测试

ProxyFactory内部会根据不同的情况选择cglib或者jdk的代理)

1
2
3
4
5
6
7
8
9
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut,advice);

ProxyFactory factory = new ProxyFactory();
factory.setTarget(new Target1());//添加目标
factory.addAdvisor(advisor);//添加切面

I1 proxy = (I1) factory.getProxy();
proxy.foo();
proxy.bar();

选择代理的方式

**ProxyFactory内部存在一个成员变量proxyTargetClass决定spring的代理方式**

情况1

proxyTargetClass = false,而且目标实现了接口, 使用 JdkDynamicAopProxy 实现(默认proxyTargetClass为alse)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut,advice);

Target1 target1 = new Target1();
ProxyFactory factory = new ProxyFactory();
factory.setTarget(target1);//添加目标
factory.addAdvisor(advisor);//添加切面

factory.setInterfaces(target1.getClass().getInterfaces());//添加目标类上实现的接口
//factory.setProxyTargetClass(false);

I1 proxy = (I1) factory.getProxy();

proxy.foo();
proxy.bar();

情况2

proxyTargetClass = false,而且目标没有接口, 使用 ObjenesisCglibAopProxy 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut,advice);

Target1 target1 = new Target1();
ProxyFactory factory = new ProxyFactory();
factory.setTarget(target1);//添加目标
factory.addAdvisor(advisor);//添加切面

//factory.setProxyTargetClass(false);

I1 proxy = (I1) factory.getProxy();

proxy.foo();
proxy.bar();

情况3

proxyTargetClass = true总是使用 ObjenesisCglibAopProxy 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut,advice);

Target1 target1 = new Target1();
ProxyFactory factory = new ProxyFactory();
factory.setTarget(target1);//添加目标
factory.addAdvisor(advisor);//添加切面

factory.setProxyTargetClass(true);

I1 proxy = (I1) factory.getProxy();

proxy.foo();
proxy.bar();

切点匹配

先定义一个静态类T1,其中有用@Transactional注解的foo方法和一个普通的bar方法

再定义一个使用@Transactional注解标注的静态类T2

然后再定义一个用@Transactional注解标注的接口,内有方法foo,最后创建一个类实现该接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
   static class T1 {
@Transactional
public void foo() { }
public void bar() { }
}

@Transactional
static class T2 {
public void foo() { }
}

@Transactional
interface I3 { void foo(); }
static class T3 implements I3 {
public void foo() {
}
}
>}

1、创建AspectJExpressionPointcut对象,然后调用setExpression方法,在其中填写切点表达式(点我)

2、调用matches方法判断是否与切点表达式匹配,第一个参数为对应类的方法名,第二个参数为对应类的class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class A16 {
public static void main(String[] args) throws NoSuchMethodException {
//判断方法是否与切点表达式匹配
AspectJExpressionPointcut pointcut1 = new AspectJExpressionPointcut();
pointcut1.setExpression("execution(* bar())");
System.out.println(pointcut1.matches(T1.class.getMethod("foo"), T1.class));
System.out.println(pointcut1.matches(T1.class.getMethod("bar"), T1.class));

//判断方法上的注解类型是否与切点表达式定义的类型匹配
AspectJExpressionPointcut pointcut2 = new AspectJExpressionPointcut();
pointcut2.setExpression("@annotation(org.springframework.transaction.annotation.Transactional)");
System.out.println(pointcut2.matches(T1.class.getMethod("foo"), T1.class));
System.out.println(pointcut2.matches(T1.class.getMethod("bar"), T1.class));
}

3、为了保证当出现T3类的情况时(实现了被注解标注的接口)@Transactional注解能够被匹配到,在spring内部并不是采用以上方法匹配@Transactional注解,而是创建StaticMethodMatcherPointcut对象并重写matches方法,在方法内部添加匹配注解的逻辑

spring探秘之组合注解的处理(点我)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//匹配@Transactional注解
StaticMethodMatcherPointcut pointcut3 = new StaticMethodMatcherPointcut() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
//检查方法上是否添加@Transactional注解
MergedAnnotations annotations = MergedAnnotations.from(method);
if (annotations.isPresent(Transactional.class)) {
return true;
}
//检查类上是否添加@Transactional注解,并添加检查策略
annotations = MergedAnnotations.from(targetClass, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY);
if (annotations.isPresent(Transactional.class)){
return true;
}
return false;
}
};
//测试
System.out.println(pointcut3.matches(T1.class.getMethod("foo"), T1.class)); //true
System.out.println(pointcut3.matches(T1.class.getMethod("bar"), T1.class)); //false
System.out.println(pointcut3.matches(T2.class.getMethod("foo"), T2.class)); //true
System.out.println(pointcut3.matches(T3.class.getMethod("foo"), T3.class)); //true

@Aspect与Advisor

先准备一个高级切面类Aspect1和一个低级切面类Config,并把两个切面类注册进bean,添加处理@Bean的后处理器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
>public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("aspect1",Aspect1.class);
context.registerBean("config",Config.class);
context.registerBean(ConfigurationClassPostProcessor.class);
context.refresh();
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
>}

>static class Target1 {
public void foo() {
System.out.println("target1 foo");
}
>}
>static class Target2 {
public void bar() {
System.out.println("target2 bar");
}
>}

>@Aspect //高级切面类
>static class Aspect1 {
@Before("execution(* foo())")
public void before() {
System.out.println("aspect1 before");
}

@After("execution(* foo())")
public void after() {
System.out.println("aspect1 after");
}
>}

>@Configuration
>static class Config {
//低级切面:由一个切点和一个通知组成
@Bean
public Advisor advisor3(MethodInterceptor advice3){
//定义切点
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* foo())");
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice3);
return advisor;
}
//定义通知
@Bean
public MethodInterceptor advice3() {
return new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("advice3 before");
Object res = invocation.proceed();
System.out.println("advice3 after");
return res;
}
};
}
>}

AnnotationAwareAspectJAutoProxyCreator 作用

自动代理后处理器 AnnotationAwareAspectJAutoProxyCreator 会帮我们创建代理

通常代理创建的活在原始对象初始化后执行, 但碰到循环依赖会提前至依赖注入之前执行

高级的 @Aspect 切面会转换为低级的 Advisor 切面


findEligibleAdvisors方法

解析某个类的切面,一部分切面是低级的,如准备代码中的advisor3;另一部分是高级的, 由解析 @Aspect 后获得。解析时会将高级切面解析成多个低级切面

1
2
AnnotationAwareAspectJAutoProxyCreator creator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
List<Advisor> advisors = creator.findEligibleAdvisors(Target1.class, "target1");

wrapIfNecessary方法

判断是否有必要对目标进行代理,内部调用 findEligibleAdvisors, 只要返回集合不空, 则表示需要创建代理

如下面代码,因为在准备时Target2中的bar方法,没有与切点进行匹配,所以Target2在调用findEligibleAdvisors方法后获取的切面集合为空,所以没必要进行代理

1
2
3
4
Object o1 = creator.wrapIfNecessary(new Target1(), "target1", "target1");
System.out.println(o1.getClass());
Object o2 = creator.wrapIfNecessary(new Target2(), "target2", "target2");
System.out.println(o2.getClass());

代理创建时机

Bean创建的三个重要阶段分别为:创建 -> (?) 依赖注入 -> 初始化 (?)

代理创建的时机有两个位置,一个是创建Bean和依赖注入之间,另一个是初始化之后

初始化之后

下面代码中因为Bean1中有foo方法,所以Bean1将会被代理,而且在Bean2中还注入了Bean1。

在这种单向的依赖关系下(Bean2依赖Bean1),代理在Bean1的初始化之后创建,在代理创建之后,才继续执行Bean2的构造方法,在Bean2中注入Bean1的代理,最后完成Bean2的初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//切面
@Bean
public Advisor advisor(MethodInterceptor advice) {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* foo())");
return new DefaultPointcutAdvisor(pointcut, advice);
}
//通知
@Bean
public MethodInterceptor advice() {
return (MethodInvocation invocation) -> {
System.out.println("before...");
return invocation.proceed();
};
}

static class Bean1 {
public void foo() { }

public Bean1() { System.out.println("Bean1()"); }

@PostConstruct
public void init() { System.out.println("Bean1 init()"); }
}

static class Bean2 {
public Bean2() { System.out.println("Bean2()"); }

@Autowired
public void setBean1(Bean1 bean1) {
System.out.println("Bean2 setBean1(bean1) class is: " + bean1.getClass());
}

@PostConstruct
public void init() { System.out.println("Bean2 init()"); }
}

创建Bean和依赖注入之间

下面代码中因为Bean1中有foo方法,所以Bean1将会被代理,而且在Bean1中注入了Bean2,在Bean2中注入了Bean1。在这种循环依赖的关系下,代理在Bean1的构造和Bean1的依赖注入之间创建的。

原因:因为在Bean1中需要注入Bean2,所以执行完Bean1的构造后,就执行Bean2的构造,因为Bean2中需要注入Bean1的代理,所以再创建Bean1的代理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
	@Bean
public Advisor advisor(MethodInterceptor advice) {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* foo())");
return new DefaultPointcutAdvisor(pointcut, advice);
}

@Bean
public MethodInterceptor advice() {
return (MethodInvocation invocation) -> {
System.out.println("before...");
return invocation.proceed();
};
}

static class Bean1 {
public void foo() { }

public Bean1() { System.out.println("Bean1()"); }

@Autowired
public void setBean2(Bean2 bean2) {
System.out.println("Bean1 setBean2(bean2) class is: " + bean2.getClass());
}

@PostConstruct
public void init() { System.out.println("Bean1 init()"); }
}

static class Bean2 {
public Bean2() { System.out.println("Bean2()"); }

@Autowired
public void setBean1(Bean1 bean1) {
System.out.println("Bean2 setBean1(bean1) class is: " + bean1.getClass());
}

@PostConstruct
public void init() { System.out.println("Bean2 init()"); }
}

切面顺序控制

使用@Order注解进行控制,默认是最低优先级,值越小优先级越高

注意:可以加在高级切面类上,但对于低级切面类,需要在DefaultPointcutAdvisor类中setOrder

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Configuration
static class Config {
//低级切面
@Bean
public Advisor advisor3(MethodInterceptor advice3){
//定义切点
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* foo())");
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice3);
advisor.setOrder(2);
return advisor;
}
//定义通知
@Bean
public MethodInterceptor advice3() {
return new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("advice3 before");
Object res = invocation.proceed();
System.out.println("advice3 after");
return res;
}
};
}
}

高级切面转为低级切面(@Before为例)

类似的有

  • AspectJAroundAdvice (环绕通知)
  • AspectJAfterReturningAdvice
  • AspectJAfterThrowingAdvice (环绕通知)
  • AspectJAfterAdvice (环绕通知)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
	static class Aspect {
@Before("execution(* foo())")
public void before1() {
System.out.println("before1");
}
@Before("execution(* foo())")
public void before2() {
System.out.println("before2");
}
}

static class Target {
public void foo() { System.out.println("target foo"); }
}

public static void main(String[] args) throws Throwable {
AspectInstanceFactory factory = new SingletonAspectInstanceFactory(new Aspect());
// 高级切面转低级切面类
List<Advisor> list = new ArrayList<>();
for (Method method : Aspect.class.getDeclaredMethods()) {
//判断方法上是否有加@Before注解
if (method.isAnnotationPresent(Before.class)) {
// 根据@Before注解的value值来生成一个切点对象
String expression = method.getAnnotation(Before.class).value();//切点表达式
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();//解析后生成的切点对象
pointcut.setExpression(expression);
// 通知类
AspectJMethodBeforeAdvice advice = new AspectJMethodBeforeAdvice(method, pointcut, factory);
// 低级切面
Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
list.add(advisor);
}
}
for (Advisor advisor : list) { System.out.println(advisor); }
}

静态通知调用

不同的通知统一转换成环绕通知

ProxyFactory 在创建代理时,最后调用 advice 的是一个 MethodInvocation 对象(调用链对象)
1、因为 advisor 有多个, 且一个套一个调用, 因此需要一个调用链对象
2、MethodInvocation调用次序如下

3、为了实现上图所示的调用次序,环绕通知最适合,因此其他 before、afterReturning 都会被转换成环绕通知
4、统一转换为环绕通知, 体现的是设计模式中的适配器模式(点击查看设计模式)

统一转换为 MethodInterceptor 环绕通知, 这体现在方法中的 Interceptors 上

  • MethodBeforeAdviceAdapter 将 @Before AspectJMethodBeforeAdvice 适配为 MethodBeforeAdviceInterceptor
  • AfterReturningAdviceAdapter 将 @AfterReturning AspectJAfterReturningAdvice 适配为 AfterReturningAdviceInterceptor
  • 对外是为了方便使用要区分 before、afterReturning
  • 对内统一都是环绕通知, 统一用 MethodInterceptor 表示

定义了一个高级切面,里面含有多个通知类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
static class Aspect {
@Before("execution(* foo())")
public void before1() {System.out.println("before1");}

@Before("execution(* foo())")
public void before2() {System.out.println("before2");}

public void after() {System.out.println("after");}

@AfterReturning("execution(* foo())")
public void afterReturning() {System.out.println("afterReturning");}

@AfterThrowing("execution(* foo())")
public void afterThrowing(Exception e) {
System.out.println("afterThrowing " + e.getMessage());
}

@Around("execution(* foo())")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
try {
System.out.println("around...before");
return pjp.proceed();
} finally {
System.out.println("around...after");
}
}
}

static class Target {
public void foo() {System.out.println("target foo"); }
}

将不同的通知类型统一转换成环绕通知

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
@SuppressWarnings("all")
public static void main(String[] args) throws Throwable {
AspectInstanceFactory factory = new SingletonAspectInstanceFactory(new Aspect());
//高级切面转低级切面类
List<Advisor> list = new ArrayList<>();
for (Method method : Aspect.class.getDeclaredMethods()) {
if (method.isAnnotationPresent(Before.class)) {
// 解析切点
String expression = method.getAnnotation(Before.class).value();
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(expression);
// 通知类
AspectJMethodBeforeAdvice advice = new AspectJMethodBeforeAdvice(method, pointcut, factory);
// 切面
Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
list.add(advisor);
} else if (method.isAnnotationPresent(AfterReturning.class)) {
// 解析切点
String expression = method.getAnnotation(AfterReturning.class).value();
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(expression);
// 通知类
AspectJAfterReturningAdvice advice = new AspectJAfterReturningAdvice(method, pointcut, factory);
// 切面
Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
list.add(advisor);
} else if (method.isAnnotationPresent(Around.class)) {
// 解析切点
String expression = method.getAnnotation(Around.class).value();
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(expression);
// 通知类
AspectJAroundAdvice advice = new AspectJAroundAdvice(method, pointcut, factory);
// 切面
Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
list.add(advisor);
}
}

for (Advisor advisor : list) { System.out.println(advisor); }

Target target = new Target();
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(target); //设置目标
proxyFactory.addAdvice(ExposeInvocationInterceptor.INSTANCE); // 准备把 MethodInvocation 放入当前线程
proxyFactory.addAdvisors(list); //关联低级切面

/*
* 将除了环绕通知以外的通知,统一转为环绕通知
* 参数一:目标方法对象
* 参数二:目标的class
*/
List<Object> methodInterceptorList = proxyFactory.getInterceptorsAndDynamicInterceptionAdvice(Target.class.getMethod("foo"), Target.class);
//省略了对低级切面进行排序
for (Object o : methodInterceptorList) { System.out.println(o); }
}

调用链执行过程

注意:在调用链执行的过程中,某些通知内部有可能会使用到调用链对象。所以必须使用一个最外层的环绕通知,将 MethodInvocation 放入当前线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//创建并执行调用链 (多个环绕通知 + 目标)
MethodInvocation methodInvocation = new ReflectiveMethodInvocation(
/*
* 参数一:代理对象
* 参数二:目标对象
* 参数三:目标中对应的方法
* 参数四:目标方法的实参数组
* 参数五:目标类型
* 参数六:环绕通知集合
*/
null, target, Target.class.getMethod("foo"), new Object[0], Target.class, methodInterceptorList
);
//将环绕通知一层层逐一进行调用
methodInvocation.proceed();

模拟实现调用链

在proceed方法内部首先需要将可调用次数count(初始化为1)与通知的list集合比较,如果可调用次数大,说明集合为空,没有通知,则直接调用目标。如果集合不为空,那么就递归调用下一个通知

注意:此处递归调用并不是直接在proceed方法内部调用proceed而是通过interceptor调用invoke,从而间接地在切面中调用proceed方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
static class Target {
public void foo() {System.out.println("Target.foo()");}
}

static class Advice1 implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("Advice1 before...");
Object res = invocation.proceed(); //调用下一个通知或目标
System.out.println("Advice1 after...");
return res;
}
}

static class Advice2 implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("Advice2.before...");
Object result = invocation.proceed(); // 调用下一个通知或目标
System.out.println("Advice2.after...");
return result;
}
}

static class MyInvocation implements MethodInvocation {
private Object target;
private Method method;
private Object[] args;
List<MethodInterceptor> methodInterceptorList; // 环绕通知集合
private int count = 1; // 调用次数

public MyInvocation(Object target, Method method, Object[] args, List<MethodInterceptor> methodInterceptorList) {
this.target = target;
this.method = method;
this.args = args;
this.methodInterceptorList = methodInterceptorList;
}

// 调用每一个环绕通知, 调用目标
@Override
public Object proceed() throws Throwable {
if (count > methodInterceptorList.size()){
//调用目标,返回并结束递归
return method.invoke(target, args);
}
//调用通知,count+1
MethodInterceptor interceptor = methodInterceptorList.get(count++ - 1);
return interceptor.invoke(this);
}

@Override
public Method getMethod() {return method;}

@Override
public Object[] getArguments() {return args;}

@Override
public Object getThis() {return target;}

@Override
public AccessibleObject getStaticPart() {return method;}
}

测试

1
2
3
4
5
6
7
8
9
public static void main(String[] args) throws Throwable {
Target target = new Target();
List<MethodInterceptor> list = List.of(
new Advice1(),
new Advice2()
);
MyInvocation invocation = new MyInvocation(target, Target.class.getMethod("foo"), new Object[0], list);
invocation.proceed();
}

动态通知调用