简介
* 定义:
* 是结构型设计模式;
* 为目标对象提供代理:
1.控制目标对象的访问;
2.增强目标对象已有功能;
* 举例:
* spring AopProxy采用代理创建bean;
* windows快捷方式;
* Controller是Service的代理, Service是DAO的代理;
* 票贩子
* 分类:
* 静态代理:
* 由程序员创建代理对象;
* 动态代理:
* 系统在运行中动态生成代理对象;
* 调用时调用动态生成的代理对象;
静态代理
代码
思路
* 代理持有目标的引用;
* 代理和目标实现相同接口;
* 使用接口接收代理对象;
* 以windows 打开快捷方式举例;
CODE
public interface IApp {
public void run();
}
public class App implements IApp {
public String name ;
public App(String name) {
// TODO Auto-generated constructor stub
this.name = name;
}
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println(name+"find source url");
}
}
public class ProxyForSimple {
IApp app;
public ProxyForSimple(IApp app) {
// TODO Auto-generated constructor stub
this.app = app;
}
public void run(){
before();
app.run();
}
public void before(){
System.out.println("find source url");
}
}
public static void main(String[] args) {
ProxyForSimple proxy = new ProxyForSimple(new App("Eclipse"));
proxy.run();
}
JDK动态代理
介绍
* Jdk Proxy特点:
1. 采用字节重组方式生产代理类, 实现动态代理;
2. 目标类必须实现接口;
* jdk Proxy生产代理类过程:
1. 生产代理对象的方法: Proxy.newInstance(Class<?> [] classes,InovcationHandler handler);
* classes: target实现的所有接口;
* handler:
* 持有增强方法invoke(Object proxy, Method method, Object[] args);
* 持有被代理对象;
2. 校验是否需要动态生成proxy的class
* 根据classes和handler;
* 如果不需要动态生成, 则不执行3,4,5步;
3. 动态编写java代码;
* 新类持有handler的引用, 且构造器赋值;
* 新类实现所有cleasses;
* 所有实现的方法体, 都是handler的增强方法invoke(Object proxy, Method method, Object[] args);
4. 动态生成字节码文件;
5. 动态加载字节码文件;
6. 创建proxy对象:
* 注入handler;
* 代理方法调用目标对象方法:
* Object obj = method.invoke(this.obj, args);
* 采用反射技术执行目标对象方法;
代码
思路
* 使用jdk动态代理, 模拟windows打开快捷方式;
* IAPP 和 APP类 , 继续使用静态代理中的代码;
code
public class ProxyForJdk implements InvocationHandler{
Object obj;
public Object getInstance(Object obj){
this.obj = obj;
Class clazz = obj.getClass();
ClassLoader classLoader = clazz.getClassLoader();
Class [] interfaces = clazz.getInterfaces();
Object objr = Proxy.newProxyInstance(classLoader, interfaces, this);
return objr;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object obj = method.invoke(this.obj, args);
return obj;
}
public void before(){
System.out.println("find source url");
}
}
public static void main(String[] args) {
IApp app = (IApp) new ProxyForJdk().getInstance(new App("Eclipse"));
app.run();
}
CGLIB动态代理
介绍
* CGLIB Proy特点:
* 通过动态继承目标对象的方式实现动态代理;
* 被代理类不需要实现任何接口;
* CGLIB Proxy生产代理类过程:
1. 生成代理对象方法: enhancer.create(clazz, args);
* clazzes: target的类;
* args : 构造器参数;
2. 判断是否需要动态生成proxy的字节码文件;
* enhancer.setCallback(Enhancer proxyEnhancer); //设置提供增强方法的enhancer;
* cglib 通过proxyEnhancer和clazz判断是否需要生成字节码文件;
3. 生成字节码文件:
* 使用ASM框架生成2个字节码文件:
* targetClass, proxyClass
* 会为新targetClass和proxyClass中的方法编号;(为FASTClass做准备)
* proxyClass重写target中的所有方法:
* 所有重写的方法都执行 2步骤proxyEnhancer中 的intercept(Object obj, Method method, Object[] args, MethodProxy proxy)方法
* 因为1步骤中enhancer.create(clazz, args)时注入了target构造参数, 所以proxyClass可以持有target对象;
* 生成字节码文件较为复杂, 相对于JDK效率较低;
4. 动态生成字节码文件;
5. 动态加载字节码文件;
6. 创建proxy和target对象;
* FASTCLASS技术:
* 当CGLIB第一次调用代理方法时, 会产生一个FASTCLASS的字节码文件;
* FASTCLASS文件中存储 proxy和target方法的对应关系;
* 根据步骤3中, targetClass和proxyClass中方法的编号
* CGLIB中proxy调用target方法:
* Object objr = proxy.invokeSuper(obj, args);
* 使用FASTCLASS技术, 快速找到对应的target中的方法;
* 执行方法效率高于JDK PROXY的反射技术;
代码
思路
* 使用CGLIB动态代理, 模拟windows打开快捷方式;
* IAPP 和 APP类 , 继续使用静态代理中的代码;
CODE
public class ProxyForCglib implements MethodInterceptor{
public Object getInstance(App obj){
Enhancer enhancer = new Enhancer();
enhancer.setCallback(this);
enhancer.setSuperclass(obj.getClass());
Class [] clazzes = {String.class};
String [] args = {obj.name};
return enhancer.create(clazzes, args);
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// TODO Auto-generated method stub
before();
System.out.println(obj.getClass()); //obj是新生成的代理对象;
Object objr = proxy.invokeSuper(obj, args);
return objr;
}
public void before(){
System.out.println("find source url");
}
}
public static void main(String[] args) {
IApp app = (IApp) new ProxyForCglib().getInstance(new App("Eclipse"));
app.run();
}
CGLIB Proxy和JDK Proxy对比
* 实现方式不同:
* JDK Proxy 采用实现接口的方式;
* CGLIB Rpoxy 采用继承的方式;
* 生产字节码方式不同:
* JDK 直接写java代码;
* CGLIB采用ASM技术, 直接生成target和proxy的class, 并采用FASTCLASS技术关联target和proxy中的方法;
* 调用被代理对象的方法不同:
* JDK 采用反射技术;
* CGLIB 采用FASTClass技术;
* 实际应用场景:
* 在实际应用场景中, proxy.class只生成一次,更多是被调用;
* 因为CGLIB调用target效率高,所以CGLIB优势;
* 比如 spring只在容器初始化时动态生成bean的代理class, 其他时候只是调用方法;
代理模式在spring中的应用
* Spring通过动态代理实现AOP:
* AopProxy采用策略模式, 决定创建Bean的方式;
* 当 Bean 有实现接口时,Spring 就会用 JDK 的动态代理
* 当 Bean 没有实现接口时,Spring 选择 CGLib
* Spring 可以通过配置强制使用 CGLib
* BeanFactory与ApplicationContext
* ApplicationContext是BeanFactory的子接口,也被称为应用上下文。
* ApplicationContext和容器一起初始化;
* BeanFactory getBean():
* 此时ApplicationContext已经初始化完成, 代理字节码文件已经生成;
* 获取代理bean的方式有两种: 一种是单例, 一种是克隆;
静态代理和动态代理的本质区别
* 静态代理:
* 只能手动完成代理操作;
* 如果被目标类增加新方法, 代理类需要同步新增;
* 违背开闭原则;
* 动态代理:
* 采用动态生成字节码完成代理操作;
* 不存在目标类增加方法时扩展限制;
* 如果增加代理方法的逻辑扩展, 可结合策略模式, 只需要新增策略类即可完成, 无需修改代码;
代理模式优缺点
* 优点:
* 代理对象控制目标对象的访问;
* 代理对象一定成都是降低了耦合度;
* 代理对象可以对目标对象增强;
* 缺点:
* 代理模式会造成系统设计中类的数量增加。
* 在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢。
* 增加了系统的复杂度。