介绍
代理模式:由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。代理模式分为三种:静态代理、动态代理、Cglib代理
- 优点
- 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
- 代理对象可以扩展目标对象的功能;
- 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度,增加了程序的可扩展性
- 缺点
- 代理模式会造成系统设计中类的数量增加
- 在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
- 增加了系统的复杂度
场景
出去旅游,拍照是必不可少的,尤其是自拍,单身狗自拍怎办?自拍杆,这里自拍杆可以看作是拍照的代理,摆好pos一按按钮完成拍照,以这个场景编写代理代码
静态代理
UML类图
实现方式
- 代理对象与被代理对象实现一个共同的接口
- 代理类聚合一个被代理对象
- 被代理对象交给代理对象,进行使用
代码
- 接口
public interface IPhone {
public void button();
}
- 被代理类
public class Phone implements IPhone{
@Override
public void button() {
System.out.println("单身狗开始自拍。。。");
}
}
- 代理类
public class SelfPhoneProxy implements IPhone{
private IPhone iPhone;
public SelfPhoneProxy(IPhone iPhone){
this.iPhone = iPhone;
}
@Override
public void button() {
System.out.println("已经摆好pos...");
iPhone.button();
System.out.println("文明拍照,禁止虐狗...");
}
//测试类
public static void main(String[] args) {
SelfPhoneProxy proxy = new SelfPhoneProxy(new Phone());
proxy.button();
}
}
在代理对象(目标对象)执行前后,可以植入一些想要实现功能:记录日志,安全校验,拦截等
动态代理
JDK代理
使用java 的反射机制,实现的代理模式
UML类图
实现方式
- 代理对象不需要继承接口,区别与静态代理,但是目标对象需要实现接口
- 代理对象使用反射机制生成,动态在内存中生成代理对象(也叫JDK、接口代理)
代码
- 接口
public interface IPhone {
public void button();
}
- 被代理类
public class Phone implements IPhone{
@Override
public void button() {
System.out.println("单身狗开始自拍。。。");
}
}
- 代理对象
public class SelfPhoneProxy {
private Object target;
public SelfPhoneProxy(Object target) {
this.target = target;
}
/**
* newProxyInstance
* 第一个参数:目标对象类加载器
* 第二个参数:目标对象实现的接口对象
* 第三个参数:事件处理,执行目标对象时会触发的事件处理器方法,
*/
public Object getProxyInstance() {
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), (proxy,method,args) ->{
System.out.println("代理开始执行,请摆好pos...");
Object returnValue = method.invoke(target, args);
System.out.println("文明拍照,禁止虐狗...");
return returnValue;
});
}
}
Cglib代理
- 目标对象不需要实现接口,可以只是一个单纯的对象,可以使用目标对象的子类或者在内存中构建一个目标对象的子类实现动态代理,这就是Cglib代理,底层使用字节码处理框架ASM来转字节码并生成新类
- Cglib需要依赖
cglib
的包
UML类图
实现方式
- 代理类实现
MethodInterceptor
接口,通过intecpet
方法实现对目标对象调用 - 目标对象不需要任何继承与实现,目标对象不能是 final/static 类型
代码
- 被代理对象(目标对象)
- 代理类
public class SelfPhoneCglibProxy implements MethodInterceptor {
private Object target;
public SelfPhoneCglibProxy(Object target){
this.target = target;
}
public Object getProxInstance(){
//创建Enhancer对象,类似于JDK动态代理的Proxy类
Enhancer enhancer = new Enhancer();
//设置父类
enhancer.setSuperclass(target.getClass());
//设置回调函数
enhancer.setCallback(this);
//这里的creat方法就是正式创建代理类(子类)
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("cglib动态代理。摆好pos...");
Object invoke = method.invoke(target, args);
System.out.println("拍照完,么么哒...");
return invoke;
}
}
- 测试类
public static void main(String[] args) {
//创建代理对象并将目标对象传递给代理独享
SelfPhoneCglibProxy proxy = new SelfPhoneCglibProxy(new Phone());
//通过代理获取目标对象
Phone proxyObj = (Phone) proxy.getProxInstance();
//调用目标对象方法,触发 intercept调用
proxyObj.button();
}
总结
代理模式分为静态和动态代理,动态代理基于内存实现,动态区别在于目标类需不需要实现接口。代理运用非常广泛,在Spring AOP使用了代理,应用非常广泛。