定义
为其他对象提供一种代理以控制这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理模式可以在客户端与目标对象之间起到中介的作用。
分类
1.静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
2.动态代理:在程序运行时,运用反射机制动态创建而程。
UML
静态代理实现
1.被代理类
2.代理类:实现被代理接口,持有被代理对象
3.test
动态代理
动态代理是指在运行时,动态生成代理类。即,代理类的字节码将在运行时生成并载入当前的ClassLoader。与静态代理类想比,动态类有诸多好处。首先,不需要为真是主题写一个形式上完全一样的封装类,假如主题接口中的方法很多,为每一个接口写一个代理方法也是非常烦人的事,如果接口有变动,则真实主题和代理类都要修改,不利于系统维护;其次,使用一些动态代理的生成方法甚至可以在运行时指定代理类的执行逻辑,从而大大提升系统的灵活性。
Jdk动态代理
Jdk的动态代理是基于接口的。现在想要为RealSubject这个类创建一个动态代理对象,Jdk主要会做一下工作:
- 获取RealSubject上的所有接口列表
- 确定要生成的代理类的类名,默认为:com.sun.proxy.$ProxyXXXX;
- 根据需要实现的接口信息,在代码中动态创建该Proxy类的字节码;
- 将对应的字节码转换为对于的class对象;
- 创建InvocationHandler实例handler,用来处理Proxy所有方法的调用;
- Proxy的class对象以创建的handler对象为参数,实例化一个proxy对象;
1 .Subject
public interface Subject
{
String operate1();
String operate2();
}
2. RealSubject
public class RealSubject1 implements Subject
{
@Override
public String operate1()
{
return "RealSubject-operate1()";
}
@Override
public String operate2()
{
return "RealSubject-operate2()";
}
}
public class RealSubject2 implements Subject
{
@Override
public String operate1()
{
return "RealSubject2-operate1()";
}
@Override
public String operate2()
{
return "RealSubject2-operate2()";
}
}
3. Proxy
在实际的编程中,需要优先定义一个实现InvocationHandler接口的调用处理器对象,然后将它作为创建代理类实例的参数。(抑或在调用newProxyInstance方法时使用匿名内部类。)这样就得到了代理对象。
真实对象本身的实例化在调用处理器对象内部完成,实例化时需要的参数也应该及时传入调用处理器对象中。这样一来就完成了代理对象对真实对象的包装,而代理对象需要执行的额外操作也在invoke方法中处理。
其后,在客户端中,如果需要使用真实对象时,就可以用代理对象来替代它了(有时需要类型强制转化)。
动态代理模式通过使用反射,可以在运行期决定加载哪个类,避免了一个类对应一个代理的问题;同时,通过统一的invoke方法,统一了代理类对原函数的处理过程,使用动态代理很大程度上减少了重复的代码,降低了维护的复杂性和成本。
public class ProxyHandler implements InvocationHandler
{
Object obj = null;
public Object newProxyInstance(Object realObj){
this.obj = realObj;
Class<?> classType = this.obj.getClass();
/** Jdk通过java.lang.reflect.Proxy包来支持动态代理,在Java中要创建一个代理对象,必须调用Proxy类的静态方法newProxyInstance
* 1.loader,表示类加载器,可以使用null来使用默认的加载器
* 2.interfaces,表示接口或对象的数组,它就是前述代理对象和真实对象都必须共有的父类或者接口;
* 3.handler,表示调用处理器,它必须是实现了InvocationHandler接口的对象,其作用是定义代理对象中需要执行的具体操作。
* */
return Proxy.newProxyInstance(classType.getClassLoader(), classType.getInterfaces(), this);
}
/**
* InvocationHandler中只有一个方法invoke,它的作用就跟Runnable中的run方法类似,定义了代理对象在执行真实对象的方法时所希望执行的动作。
1.proxy,表示执行这个方法的代理对象.
2.method,表示真实对象实际需要执行的方法(关于Method类参见Java的反射机制).
3.args,表示真实对象实际执行方法时所需的参数.
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
System.out.print("I'm Proxy, I'm invoking...");
Object object = method.invoke(obj, args);
System.out.println(object);
return object;
}
}
4 测试代码
Subject object = new RealSubject();
Subject subject = (Subject) new ProxyHandler().newProxyInstance(new RealSubject());
Subject subject2 = (Subject) new ProxyHandler().newProxyInstance(new RealSubject2());
subject.operate1();
subject2.operate3();
I'm Proxy, I'm invoking...RealSubject-operate1()
I'm Proxy, I'm invoking...RealSubject2-operate2()
CGLIB动态代理
生成动态代理的方法很多,不止jdk自带的动态代理这一种,还有CGLIB,Javassist或者ASM。Jdk的动态代理依靠接口实现,如果有些类并没有实现接口,则不能使用jdk代理,这就要用到CGLIB代理了。CGLIB是针对类来实现的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
CGLIB创建某个类A的动态代理类的模式是:- 查找A上的所有非final的public类型的方法定义
- 将这些方法的定义转换成字节码
- 将组成的字节码转换成相应的代理的class对象
- 实现MethodInterceptor接口,用来处理对代理类上所有方法的请求(这个接口和Jdk动态代理InvocationHandler的功能和角色是一样的)
(需要用到cglib-nodep-2.2.jar和asm.jar两个jar包):
1. 真实代理角色RealSubject
public class RealSubjectCglib
{
public String operate(){
return "RealSubjectCglib";
}
}
2. 代理类
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class ProxyCglib implements MethodInterceptor
{
private Object target;
public Object getInstance(Object target)
{
this.target = target;
//Cglib中的加强器,用来创建动态代理
Enhancer enhancer = new Enhancer();
//设置要创建动态代理的类
enhancer.setSuperclass(this.target.getClass());
//设置回调,这里相当于是对于代理类上所有方法的调用,都会调用Callback,而Callback则需要实现intercept()方法进行拦截
enhancer.setCallback(this);
Object obj = enhancer.create();
return obj;
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable
{
System.out.print("I'm Proxy, I'm invoking...");
Object object = proxy.invokeSuper(obj, args);
System.out.println(object);
return object;
}
}
测试代码:
ProxyCglib proxy = new ProxyCglib();
RealSubjectCglib cglib = (RealSubjectCglib)proxy.getInstance(new RealSubjectCglib());
cglib.operate();
输出结果:I’m Proxy, I’m invoking…RealSubjectCglib