代理模式
定义:为其他对象提供一种代理机制,以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。-《设计模式》
在代理模式下,有一个是真正的你要访问的对象(目标类),一个是代理对象,真正对象与代理对象实现同一个接口,先访问代理类再访问真正要访问的对象。
代理模式分为静态代理、动态代理
静态代理
- 静态代理是由程序员创建或工具生成代理类的源码,再编译代理类。
- 所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。
//主题接口,后续类需要实现此接口
public interface Subject {
public void doSomething();
}
//实现上述接口的具体类,表示目标对象
public class RealSubject implements Subject {
public void doSomething() {
System.out.println("这是真正的对象");
}
}
//代理类,如上文所述,需要与目标类实现相同的接口
public class ProxySubject implements Subject {
//包含真正目标对象作为成员
private Subject subject;
public ProxySubject(Subject subject) {
this.subject = subject;
}
//通过访问代理类实现对目标对象的访问
public void doSomething() {
System.out.println("这是一个代理对象");
this.subject.doSomething();
}
}
public class ProxyTest {
public static void main(String[] args){
//根据真实对象创建一个代理对象
ProxySubject proxy = new ProxySubject(new RealSubject());
//通过访问代理对象实现对真实对象的访问
proxy.doSomething();
}
}
运行结果:
优点:代理类接受一个Subject接口的对象,任何实现该接口的对象,都可以通过代理类进行代理,增加了通用性。
缺点:代理对象和真实对象高度耦合,每一个代理类都必须实现一遍委托类的接口,如果接口增加方法,则代理类也必须跟着修改;其次,代理类每一个接口对象对应一个委托对象,如果委托对象非常多,则静态代理类就非常臃肿,难以胜任。
动态代理
- 顾名思义,动态代理是在实现阶段不用关心代理类,而在运行阶段才指定哪一个对象。Java的动态代理有两种方式,传统的jdk动态代理和cglib动态代理,主要区别在于前者通过实现接口,后者通过继承方式。
jdk动态代理
1.通过实现InvocationHandler接口来自定义自己的InvocationHandler;
2.通过Proxy.getProxyClass获得动态代理类
3.通过反射机制获得代理类的构造方法,方法签名为getConstructor(InvocationHandler.class)
4.通过构造函数获得代理对象并将自定义的InvocationHandler实例对象传为参数传入
5.通过代理对象调用目标方法
//实现主题接口的目标类
public class ActionImpl_01 implements ActionInf{
public void doSomething(String word) {
System.out.println(this.getClass().getName()+"::"+"Hello "+word);
}
}
//实现InvocationHandler接口
public class InvocationHandlerImpl implements InvocationHandler{
private ActionInf object;
public InvocationHandlerImpl(ActionInf object) {
this.object = object;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("======before invoke======");
System.out.println(proxy.getClass().getName());
Object res = method.invoke(this.object, args);
System.out.println("======after invoke======");
return res;
}
public Object getProxyObject(){
return Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
this.object.getClass().getInterfaces(),
this
);
}
}
public class DynamicProxyTest {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
ActionImpl_01 a1 = new ActionImpl_01();
//通过构造函数获得代理对象并将自定义的InvocationHandler实例对象传为参数传入
InvocationHandler handler1 = new InvocationHandlerImpl(a1);
//通过Proxy.getProxyClass获得动态代理类
Class proxyClass = Proxy.getProxyClass(ActionInf.class.getClassLoader(), ActionInf.class);
//通过反射机制获得代理类的构造方法,方法签名为getConstructor(InvocationHandler.class)
Constructor constructor = proxyClass.getConstructor(InvocationHandler.class);
//通过代理对象调用目标方法
ActionInf inf = (ActionInf)constructor.newInstance(handler1);
inf.doSomething("world");
}
}
当然,上述步骤中的2、3、4步也可通过调用Proxy.newProxyInstance()一步完成,读者可以自行查看此方法的实现过程。
上述过程中,我们并没有静态构造实现Subject主题接口的代理类,而是在运行过程中生成了一个名为com.sun.proxy.$Proxy0的代理类,此类运行时生成,结束后销毁,达到了动态的标准。
cglib动态代理
- CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。
//实现cglib动态代理的关键,就是要实现MethodInterceptor(方法拦截器接口)
public class CglibProxyImpl implements MethodInterceptor {
private ActionInf object;
public CglibProxyImpl(ActionInf object) {
this.object = object;
}
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("======before invoke======");
ActionInf res = (ActionInf) methodProxy.invokeSuper(o, objects);
System.out.println("======after invoke======");
return res;
}
public ActionInf getProxyObject(){
Enhancer enhancer = new Enhancer();
//设置enhancer对象的父类
enhancer.setSuperclass(this.object.getClass());
//设置enhancer的回调对象
enhancer.setCallback(this);
//创建代理对象
return (ActionInf)enhancer.create();
}
}
public class DynamicProxyTest {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
ActionImpl_01 a1 = new ActionImpl_01();
CglibProxyImpl cg = new CglibProxyImpl(a1);
ActionInf inf = cg.getProxyObject();
inf.doSomething("World");
}
}
可以看出,cglib通过继承方式基于目标类动态生成一个子类,那很明显一点,cglib不能动态代理final修饰的方法,这点与jdk动态代理有较大差别。