设计模式-代理模式

代理模式就是不直接对外提供其实现类,而是提供其代理类,隐藏实现类的细节,通常还用于在真实的实现的前后添加一部分逻辑

静态代理

目标接口

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

委托类

1
2
3
4
5
6
7
public class RealSubject implements Subject {

@Override
public void hello() {
System.out.println("hello");
}
}

代理类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//代理类为了和其实现类功能一样,所以也要实现目标接口类
public class StaticProxy implements Subject {

//代理对象持有委托类的引用
private Subject realSubject;

public StaticProxy(Subject realSubject) {
this.realSubject = realSubject;
}

@Override
public void hello() {
System.out.println("静态代理before");
realSubject.hello();
System.out.println("静态代理after");
}


public static void main(String[] args) {
Subject subject = new StaticProxy(new RealSubject());
//调用代理类对象方法
subject.hello();
}
}

上面这种方式成为静态代理,静态代理的局限在于运行前必须编写好代理类,如果需要被代理多个方法就会有很多冗余代码

动态代理

代理类在程序运行时创建的代理方式被成为 动态代理。 也就是说,这种情况下,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数

  1. jdk自带的动态代理

在使用动态代理时,需要定义一个位于代理类与委托类之间的中介类,这个中介类被要求实现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
public class JDKDynamicProxy implements InvocationHandler {

//中间类持有真正目标类的引用
private Object subject;

public DynamicProxy(Object subject) {
this.subject = subject;
}


/**
* 拦截代理类对象的所有方法
* @param proxy 代理类对象
* @param method 具体调用的是代理类的哪个方法
* @param args 方法参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

System.out.println(proxy.getClass().getName());
System.out.println("动态代理before");
method.invoke(subject, args);//执行目标类方法
System.out.println("动态代理before");
return null;
}

public static void main(String[] args) {
//让中间类持有真正目标类的引用
InvocationHandler handler = new JDKDynamicProxy(new RealSubject());

//创建动态代理类实例
Subject subject = (Subject) Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),//定义了代理类的ClassLoder
new Class[] {Subject.class},//代理类实现的接口列表
handler);//调用处理器

System.out.println(subject.getClass().getName());
//调用代理类实例方法
subject.hello();

}
1
2
3
4
5
com.sun.proxy.$Proxy0
com.sun.proxy.$Proxy0
动态代理before
hello
动态代理before

特点:

  • 类名:格式是“$ProxyN”,其中 N 是一个逐一递增的阿拉伯数字,代表 Proxy 类第 N 次生成的动态代理类,并不是每次调用 Proxy 的静态方法创建动态代理类都会使得 N 值增加,原因是如果对同一组接口(包括接口排列的顺序相同)试图重复创建动态代理类,它会很聪明地返回先前已经创建好的代理类的类对象,而不会再尝试去创建一个全新的代理类,这样可以节省不必要的代码重复生成,提高了代理类的创建效率
  • 类继承关系:Proxy 类是它的父类,这个规则适用于所有由 Proxy 创建的动态代理类。而且该类还实现了其所代理的一组接口

缺点:
Proxy只能对interface进行代理,无法实现对class的动态代理。观察动态生成的代理继承关系图可知原因,他们已经有一个固定的父类叫做Proxy,Java语法限定其不能再继承其他的父类

  1. cglib实现的动态代理
    CGLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充
  • 原理:动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。它比使用java反射的JDK动态代理要快。
  • 底层:使用字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。
  • 缺点:对于final方法,无法进行代理
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 class CglibDynamicProxy {

public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
////设置增强类的父类
enhancer.setSuperclass(RealSubject.class);
//设置拦截器,拦截对父类所有非final方法的调用
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println(o.getClass().getName());
System.out.println("cglib动态代理before");
//调用父类方法
methodProxy.invokeSuper(o, objects);
System.out.println("cglib动态代理after");
return null;
}
});

//创建动态代理对象
Subject subject = (Subject) enhancer.create();
System.out.println(subject.getClass().getName());

subject.hello();
}
}
1
2
3
4
5
me.changjie.proxy.RealSubject$$EnhancerByCGLIB$$636b402a
me.changjie.proxy.RealSubject$$EnhancerByCGLIB$$636b402a
cglib动态代理before
hello
cglib动态代理after

配合过滤器使用可选择性的对方法进行拦截

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
public class CglibDynamicProxy2 {

public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(RealSubject.class);

Callback interceptor = new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println(o.getClass().getName());
System.out.println("cglib动态代理2before");
//调用父类方法
methodProxy.invokeSuper(o, objects);
System.out.println("cglib动态代理2after");
return null;
}
};

//NoOp表示no operator,即什么操作也不做,代理类直接调用被代理的方法不进行拦截
enhancer.setCallbacks(new Callback[]{interceptor, NoOp.INSTANCE});

CallbackFilter filter = new CallbackFilter() {
@Override
public int accept(Method method) {
if (method.getName().equals("hello2")) {
System.out.println("我将此方法过滤掉了,不对该方法进行拦截");
return 1;//对应拦截器数组索引,NoOp.INSTANCE
}
return 0;//对应拦截器数组索引,interceptor
}
};


enhancer.setCallbackFilter(filter);

RealSubject realSubject = (RealSubject) enhancer.create();
realSubject.hello();
realSubject.hello2();


}
}

1
2
3
4
5
6
我将此方法过滤掉了,不对该方法进行拦截
me.changjie.proxy.RealSubject$$EnhancerByCGLIB$$afdc3840
cglib动态代理2before
hello
cglib动态代理2after
hello2
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×