Spring Boot AOP系列之动态代理创建

Spring Boot AOP系列之动态代理创建


序言:在本文中,我会讲到Spring中如何创建动态代理,如何利用jdk或者cglib来创建动态代理,但是同时,我也会提及如何手动创建代理,这将有助于我们了解动态代理的原理。我们先来讲讲如何手动创建吧。

一、 手动创建之jdk动态代理

1、 jdk动态代理是基于接口的,所以必须要写一个接口,以及实现类,然后还需要写一个类来实现InvocationHandler类,

接口如下

1
2
3
public interface Helloworld {
void sayHello();
}

实现类如下:

1
2
3
4
5
public class HelloworldImpl implements HelloWorld {
public void sayHello() {
System.out.print("hello world");
}
}

拦截类如下:

1
2
3
4
5
6
7
8
9
10
public class MyInvocationHandler implements InvocationHandler{
private Object target;
public MyInvocationHandler(Object target) {
this.target=target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("method :"+ method.getName()+" is invoked!");
return method.invoke(target,args);
}
}

测试如下:

1
2
3
4
5
6
7
8
9
10
11
public class JDKProxyTest {
public static void main(String[]args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//重要的是这里的newProxyInstance方法,后面博文讲原理的时候会讲到为什么会植入切面的
HelloWorld helloWorld=(HelloWorld)Proxy.
newProxyInstance(JDKProxyTest.class.getClassLoader(),
new Class<?>[]{HelloWorld.class},
new MyInvocationHandler(new HelloworldImpl()));
helloWorld.sayHello();
}
}
2、这里先教大家怎么做,以后教大家原理,现在依葫芦画瓢就行

二、手动创建之CGLIB代理

1、cglib代理和jdk代理目的差不多,但是cglib代理不仅可以代理接口,还可以代理类,比jdk代理作用 范围更加广泛一些,cglib代理借助了ASM这个非常强大的Java字节码生成框架
2、demo如下

接口:

1
2
3
public interface Subject {
public void request();
}

实现:

1
2
3
4
5
public class RealSubject implements Subject{
public void request(){
System.out.println("From real subject.");
}
}

处理类,也叫回调方法:

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
//实现了InvocationHandler,很重要
public class DynamicSubjectHandler implements InvocationHandler
{
private Object obj;//这是动态代理的好处,被封装的对象是Object类型,接受任意类型的对象
public DynamicSubject()
{
}
public DynamicSubject(Object obj)
{
this.obj = obj;
}
//这个方法不是我们显示的去调用
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
System.out.println("before calling " + method);
method.invoke(obj, args);
System.out.println("after calling " + method);
return null;
}
}

Test方法:

1
2
3
4
5
6
7
8
9
10
public class Client{
public static void main(String[] args){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(RealSubject.class);//继承被代理的那个类
enhancer.setCallback(new DynamicSubjectHandler())//设置回调类
RealSubject realSubject = (RealSubject)enhancer.create();//创建代理对象
realSubject.request();
}
}
//PS: 这段代码是我手敲的,大家粘贴的时候注意一下哈

#####2、执行main方法之后,DynamicSubjectHandler 中的invoke方法会被显式调用,打印出信息,原理后面我会介绍,详情关注下篇博文。

三、Spring boot创建之JDK动态代理

事实上,spring对于代理的类的创建选用何种方法是有规律可寻的,详细可看一下源代码,如下:

spring如何选择代理类型

spring在创建代理的时候,会调用DefaultAopProxyFactory里面的createAopProxy方法,这个方法会取判断是使用jdk动态代理还是cglib代理。第一个参数,config.isOptimize(),这个方法是一个配置的,用于判断创建代理是否乐观(是否会经常改动),默认配置为false,第二个参数是不是目标代理类。第三个参数,hasNoUserSuppliedProxyInterfaces()这个方法,判断代理类的AdviseSupport处理类是不是只会指定SpringProxy这个接口,一般会返回true。当这些条件都不满足的时候,spring 默认走jdk动态代理。当满足这些条件中某一个的时候,去判断是不是接口或者已经是代理类,如果是接口或者已经是代理类的时候,就去走jdk动态代理,否者走cglib代理

OK,讲清楚了spring选择动态代理的原因之后,我们来看看怎么指定jdk动态代理。

Spring boot配置jdk动态代理很简单,只需要在Spring boot配置文件中配置

1
spring.aop.proxy-target-class=false

然后自己写代理类的时候注意是接口就完毕啦。

四、Spring boot创建之Cglib代理

同理,spring boot创建Cglib代理也很简单,直接配置

1
spring.aop.proxy-target-class=true

就可以强制使用cglib代理

下篇博文介绍原理哈

Compartir