设计模式源码git地址:design-pattern-src: 设计模式源码 (gitee.com)
日常生活中常见的代理比如说老板秘书,明星经纪人,本来是老板或明星的活,代理就都给干了,除此之外,还可能多做一些额外的,比如帮明星筹建演唱会,打官司,做公关之类的
- 为什么需要代理模式
- 代理模式种类
- 应用场景
- 静态代理
- 动态代理
- cglib代理
- 总结
为什么需要代理模式
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()生成某个对象的代理对象就可以了,在运行期决定。

解释下传入三个参数的含义
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();
|
输出结果
如果目标类没有接口呢?就不能使用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();
|
结果
查看jdk与cglib的类型
1 2 3 4
| System.out.println(jdk.getClass());
System.out.println(cglib.getClass());
|
结果

总结
1、代理模式通过代理对象来操作目标对象的方法,并在原对象的基础上增加额外的操作来满足业务要求
2、当我们不想改变原有代码,但希望增强方法,添加一些功能,可以考虑使用代理模式
比如日志记录,性能统计,事务处理,权限校验等