设计模式源码git地址:design-pattern-src: 设计模式源码 (gitee.com)

日常生活中常见的代理比如说老板秘书,明星经纪人,本来是老板或明星的活,代理就都给干了,除此之外,还可能多做一些额外的,比如帮明星筹建演唱会,打官司,做公关之类的

  1. 为什么需要代理模式
  2. 代理模式种类
  3. 应用场景
  4. 静态代理
  5. 动态代理
  6. cglib代理
  7. 总结

为什么需要代理模式

1、客户端可能无法直接操作对象(在另一台机器),那么可以在客户端建立一个代理对象,然后客户端直接调用目标对象,代理对象在与目标对象建立联系

2、需要加强目标对象的功能。有人表示为什么不在当初的类中加好,要使用代理来补充。单一职责要求一个类的功能尽可能单一,而且增强的功能大多都是与之前业务不同,比如增加日志输出,权限判断等,本来就不应该由之前的类去承担

种类

静态代理,动态代理(jdk代理/接口代理),cglib代理

在《精通Spring4.x 企业应用开发实战》给出建议

单例使用cglib,多例使用jdk

原因:jdk创建对象性能高于cglib,而生成对象的运行性能却比cglib低

应用场景

最常见的就是spring的AOP了,让关注点代码与业务代码分离

静态代理

在编译期确定,以通用的CRUD举例

1
2
3
public interface UserDao {
void save();//表示核心业务
}

现在要在业务代码前后加入事务,通常的做法是实现该接口,前后加上事务

1
2
3
4
5
6
7
8
public class UserDao implements IUserDao {
@Override
public void save() {
System.out.println("开启事务");
//核心业务
System.out.println("关闭事务");
}
}

问题:现在只是一个方法使用,方法多起来之后重复代码会很多

这时候通常的解决方案是抽取公共部分,封装方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class UserDao implements IUserDao {
@Override
public void save() {
begin();
//核心业务
System.out.println("保存成功");
close();
}
public void begin() {
System.out.println("开始事务");
}
public void close() {
System.out.println("关闭事务");
}
}

重复代码解决了,但在每个方法中还是继续调用,不够优雅,于是就用到了代理

写一个代理类实现目标类接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class UserProxy implements IUserDao {
private IUserDao userDao;

public UserProxy(IUserDao userDao) {
this.userDao = userDao;
}

@Override
public void save() {
System.out.println("开始事务");
userDao.save();
System.out.println("关闭事务");
}
}

这样就可以专注于业务逻辑了,但一个类只能代理一个接口,如果有1w个类需要代理,那还不得哭了,于是就出现了动态代理

动态代理

由JDK提供,只需要调用Proxy的newProxyInstance()生成某个对象的代理对象就可以了,在运行期决定。

img

解释下传入三个参数的含义

1、目标对象的类装载器

2、目标对象实现的接口

3、用这个对象干什么事,相当于之前的增强方法save(实现InvocationHandler接口,采取匿名内部类的方式重写该方法)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class UserJDKProxy {
private IUserDao userDao;

public UserJDKProxy(IUserDao userDao) {
this.userDao = userDao;
}

public Object getProxyInstance(){
return Proxy.newProxyInstance(
userDao.getClass().getClassLoader(),
userDao.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("使用jdk代理");
Object invoke = method.invoke(userDao, args);
return invoke;
}
}
);
}

}

测试

1
2
3
4
IUserDao userDao = new UserDao();
UserJDKProxy userJDKProxy = new UserJDKProxy(userDao);
IUserDao proxyInstance = (IUserDao)userJDKProxy.getProxyInstance();
proxyInstance.save();

输出结果

1
2
使用jdk代理
保存成功

如果目标类没有接口呢?就不能使用jdk代理了,这时候出现了cglib代理

cglib代理

需要引入cglib的jar包,但我引入了spring-core包,包含了cglib

简单的介绍下cglib

CGLib(Code Generation Library)是一个强大、高性能的Code生成类库,它可以在程序运行期间动态扩展类或接口,它的底层使用字节码处理框架ASM,来转换字节码并生成新的类。

需要实现MethodInterceptor接口,重写intercept方法,需要注意目标类不能是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
public class UserCglibProxy implements MethodInterceptor {
private IUserDao userDao;

public UserCglibProxy(IUserDao userDao) {
this.userDao = userDao;
}

@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("cglib代理");
Object invoke = method.invoke(userDao, objects);
return invoke;
}

public Object getProxyInstance() {
//创建一个工具类
Enhancer enhancer=new Enhancer();
//设置父类
enhancer.setSuperclass(userDao.getClass());
//设置回调函数
enhancer.setCallback(this);
//返回代理对象
return enhancer.create();
}
}

测试

1
2
3
UserCglibProxy userCglibProxy = new UserCglibProxy(userDao);
IUserDao proxyInstance1 = (IUserDao)userCglibProxy.getProxyInstance();
proxyInstance1.save();

结果

1
2
cglib代理
保存成功

查看jdk与cglib的类型

1
2
3
4
//jdk
System.out.println(jdk.getClass());
//cglib
System.out.println(cglib.getClass());

结果

img

总结

1、代理模式通过代理对象来操作目标对象的方法,并在原对象的基础上增加额外的操作来满足业务要求

2、当我们不想改变原有代码,但希望增强方法,添加一些功能,可以考虑使用代理模式

比如日志记录,性能统计,事务处理,权限校验等