Design Patterns(十一) Proxy

The Proxy pattern allows us to create an intermediary that acts as an interface to another resource, while also hiding the underlying complexity of the component.

前言

    代理模式是为某个对象提供一个代理对象,并且由代理对象控制对原对象的访问。代理模式通俗来讲就是我们生活中常见的中介。

代理模式

基本介绍:

1) 代理模式:为一个对象 提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象.这样做的 好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能;
2) 被代理的对象可以是 远程对象、 创建开销大的对象或 需要安全控制 的 对象;
3) 代理模式有不同的形式, 主要有三种 静态代理、理 动态代理 (JDK 代理、接口代理)和 Cglib代 代理 理 ( 可以在内存动态的创建对象,而不需要实现接口, 他是属于动态代理的范畴) 。

代理模式-UML图:


代理模式-类图:



静态代理

静态代理模式的基本介绍:

静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类。

静态代理优缺点:

1) 优点:在不修改目标对象的功能前提下, 能通过代理对象对目标功能扩展;
2) 缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类;
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
45
46
47
48
49
50
/**
* @Auther: Arsenal
* @Date: 2020-03-17 20:03
* @Description: 静态代理
*/
public class StaticProxy {
public static void main(String[] args) {
TeachDaoProxy teachDaoProxy = new TeachDaoProxy(new TeachDao());
teachDaoProxy.teach();
}
}

/**
* 接口
*/
interface ITeacherDao {
void teach();
}

/**
* 代理目标
*/
class TeachDao implements ITeacherDao {
@Override
public void teach() {
System.out.println("老师正在授课");
}
}

/**
* 代理类
*/
class TeachDaoProxy implements ITeacherDao {

ITeacherDao teacherDao;

public TeachDaoProxy(ITeacherDao teacherDao) {
this.teacherDao = teacherDao;
}

/**
* 方法增强
*/
@Override
public void teach() {
System.out.println("备课");
teacherDao.teach();
System.out.println("下课");
}
}

JDK 动态代理

动态代理模式的基本介绍:

1) 代理对象,不需要实现接口,但是目标对象要实现接口,否则不能用动态代理;
2) 代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象;
3) 动态代理也叫做:JDK代理、接口代理;

JDK 中生成代理对象的API:

1) 代理类所在包:java.lang.reflect.Proxy;
2) JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:
static Object newProxyInstance(ClassLoader loader, Class interfaces,InvocationHandler h )。

代码:

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
62
63
64
65
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;


/**
* @Auther: Arsenal
* @Date: 2020-03-17 20:03
* @Description: JDK 动态代理
*/
public class JDKDynamicProxy {
public static void main(String[] args) {
StudentProxyFactory factory = new StudentProxyFactory(new StudentDao());

IStudentDao proxyInstance = (IStudentDao) factory.getProxyInstance();

System.out.println("proxyInstance:" + proxyInstance.getClass());
proxyInstance.study();

}
}

/**
* 接口
*/
interface IStudentDao {
void study();
}

/**
* 被代理类
*/
class StudentDao implements IStudentDao {
@Override
public void study() {
System.out.println("学生在学习");
}
}

/**
* 代理工厂
*/
class StudentProxyFactory {

IStudentDao target;

public StudentProxyFactory(IStudentDao studentDao) {
this.target = studentDao;
}

public Object getProxyInstance() {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("学生预习");
Object invoke = method.invoke(target, args);
System.out.println("下课放学");
return invoke;
}
});
}
}

Cglib 动态代理

Cglib 代理模式的基本介绍:

1) 静态代理和JDK代理模式都要求目标对象是实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候可使用目标对象子类来实现代理-这就是Cglib代理;
2) Cglib代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展, 有些书也将Cglib 代理归属到动态代理;
3) Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP,实现方法拦截;
4) 在AOP编程中如何选择代理模式:
  1. 目标对象需要实现接口,用JDK代理
  2. 目标对象不需要实现接口,用Cglib代理
5) 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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
* @Auther: Arsenal
* @Date: 2020-03-17 20:03
* @Description: Cglib 动态代理
*/
public class CglibDynamicProxy {
public static void main(String[] args) {
Staff staff = new Staff();
StaffProxyFactory factory = new StaffProxyFactory(staff);
Staff proxyInstance = (Staff) factory.getProxyInstance();
proxyInstance.work();
}
}

/**
* 被代理对象
*/
class Staff {
void work() {
System.out.println("======员工工作======");
}
}

/**
* 代理工厂
*/
class StaffProxyFactory implements MethodInterceptor {

private Object target;

public StaffProxyFactory(Object target) {
this.target = target;
}

public Object getProxyInstance() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}

@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)
throws Throwable {
System.out.println("Cglib代理,打卡上班。");
Object invoke = method.invoke(target, objects);
System.out.println("Cglib代理,打卡下班。");
return invoke;
}
}

总结

代理模式的注意事项和细节:

优点: 1、职责清晰。 2、高扩展性。 3、智能化。

缺点: 1、由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。 2、实现代理模式需要额外的工作,有些代理模式的实现非常复杂。

使用场景:按职责来划分,通常有以下使用场景: 1、远程代理。 2、虚拟代理。 3、Copy-on-Write 代理。 4、保护(Protect or Access)代理。 5、Cache代理。 6、防火墙(Firewall)代理。 7、同步化(Synchronization)代理。 8、智能引用(Smart Reference)代理。

注意事项: 1、和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。 2、和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制。

延伸

    代理模式(Proxy)
    代理模式-菜鸟教程
    设计模式之——代理模式
    The Proxy Pattern in Java
    尚硅谷Java设计模式,韩顺平图解java设计模式

Content
  1. 1. 前言
  2. 2. 代理模式
  3. 3. 静态代理
  4. 4. JDK 动态代理
  5. 5. Cglib 动态代理
  6. 6. 总结
  7. 7. 延伸