0%

动态代理

动态代理

动态代理有很多种方式,如jdk代理,cglib,ASM等

在说动态代理之前先说一下静态代理

静态代理

静态代理在使用时,需要定义接口或者父类,被代理对象和代理对象一起实现相同的接口或者继承相同的父类

静态代理使用的是组合模式,在代理类中包含有被代理类的对象

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 TestStaticProxy {
public static void main(String[] args) {
NikeClothFactory nikeClothFactory = new NikeClothFactory();
ProxyFactory proxyFactory = new ProxyFactory(nikeClothFactory);
proxyFactory.productCloth();
}
}

// 接口
interface ClothFactory{
void productCloth();
}

// 被代理类
class NikeClothFactory implements ClothFactory{

@Override
public void productCloth() {
System.out.println("Nike工厂生产了一件Nike");
}
}

// 代理类
class ProxyFactory implements ClothFactory{

private ClothFactory clothFactory;

public ProxyFactory(ClothFactory clothFactory){
this.clothFactory = clothFactory;
}
@Override
public void productCloth() {
System.out.println("代理类开始执行,准备调用被代理类");
clothFactory.productCloth();
}
}

静态代理虽然可以在不修改目标对象功能的前提下对目标功能进行扩展,但是一旦接口增加方法,目标对象和代理类都要同时修改,而且代理对象和被代理对象要实现一样的接口,导致有很多的代理类,不便于维护

jdk动态代理

jdk动态代理的底层是用的是java的反射,但是jdk代理的前提是目标类必须实现接口

Proxy

Proxy类是所有代理类的父类,用来生成代理类和代理对象,包含有两个重要方法

getProxyClass方法用来生成代理类的Class

1
2
public static Class<?> getProxyClass(ClassLoader loader,
Class<?>... interfaces)

newProxyInstance方法是用来生成代理类的对象

1
2
3
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)

使用步骤

  • 首先实现一个InvocationHandler,方法调用会被转发到该类的invoke()方法
  • 调用时通过动态代理获取代理对象

核心方法为Proxy.newProxyInstance(ClassLoader,Class[],InvocationHandler)

三个参数分别表示

  • ClassLoader 当前目标对象使用的类加载器
  • Class[] 目标对象实现的接口类型
  • 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
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 TestDynamicProxy {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
MyInvationHandler myInvationHandler = new MyInvationHandler();
Object obj = myInvationHandler.blind(realSubject);
Subject sub = (Subject) obj;
sub.action();
}
}

// 接口
interface Subject{
void action();
}

// 真正执行的方法,被代理类
class RealSubject implements Subject{

@Override
public void action() {
System.out.println("被代理类开始执行");
}
}

class MyInvationHandler implements InvocationHandler{

// 实现了接口的被代理类的对象的声明
Object obj;
// 被代理类的实例
// 返回一个代理类的对象
public Object blind(Object obj){
this.obj = obj;
// ①使用被代理类的类加载器②被代理类的接口③代理类的实例
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),this);
}

/**
* 当通过代理类的对象被重写的方法调用时,都会转换为对invoke方法的调用
* @param proxy 正在返回的代理对象,一般情况下,在invoke方法中不使用该对象
* @param method 正在被调用的方法
* @param args 调用方法时,传入的参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

return method.invoke(obj,args);
}
}

可以在单测方法或者main方法中添加System.getProperties().put(“sun.misc.ProxyGenerator.saveGeneratedFiles”,”true”);来看到代理类的class文件,文件会被生成到com/sun/proxy目录下

cglib代理

静态代理和jdk动态代理都妖求目标对象一定要实现接口,但是有时候目标对象只是一个单独的对象,并没有实现任何接口,这个时候采用以目标对象子类的方式实现代理,该方法称为cglib代理

Cglib包的底层是通过使用一个字节码处理框架ASM来转换字节码并生成新的类,由于要生成子类,所以要被代理的类不可以被final修饰

需要引入cglib的包

1
2
3
4
5
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>

使用步骤

  • 首先实现MethodIntercepor,方法调用会被转发到intercept()方法
  • 使用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
public class CglibProxy implements MethodInterceptor { 

public Object getProxy(Class clazz){
Enhancer enhancer = new Enhancer();
// 指定代理类的父类,其实CGLIB是为该实际类生成一个子类
enhancer.setSuperclass(clazz);
// 设置Callback对象
enhancer.setCallback(this);
// 通过字节码技术动态创建子类实例
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("前置处理");
// 调用MethodProxy.invokeSuper方法将调用转发给原始对象
Object result = methodProxy.invokeSuper(o,objects);
System.out.println("后置处理");
return result;
}
}


public class Test {

public void print(){
System.out.println("方法执行");
}

public static void main(String[] args) {
CglibProxy proxy = new CglibProxy();
Test proxyImp = (Test) proxy.getProxy(Test.class);
proxyImp.print();

}
}