java代理模式

来源:互联网 发布:阿甘 java 编辑:程序博客网 时间:2024/06/06 19:05

代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能.
这里使用到编程中的一个思想:不要随意去修改别人已经写好的代码或者方法,如果需改修改,可以通过代理的方式来扩展该方法

举个例子来说明代理的作用:假设我们想邀请一位明星,那么并不是直接连接明星,而是联系明星的经纪人,来达到同样的目的.明星就是一个目标对象,他只要负责活动中的节目,而其他琐碎的事情就交给他的代理人(经纪人)来解决.这就是代理思想在现实中的一个例子

用图表示如下:

代理模式的关键点是:代理对象与目标对象.代理对象是对目标对象的扩展,并会调用目标对象。

代理实现可以分为静态代理和动态代理(JDK动态代理(基于接口实现)和Cglib动态代理(基于继承实现))。

Spring-AOP对两种动态代理的选择:
1.如果目标对象实现了接口,默认采用JDK动态代理
2.如果目标对象没有实现接口,则采用Cglib进行动态代理
3.如果目标对象实现了接口,且强制使用cglib代理,则使用cglib代理,比如springboot注解@EnableAspectJautoProxy(ProxyTargetClass = true)
多个AOP作用于同一个目标对象如何叠加?
spring-AOP采用了责任链模式(filterChain也是采用了该模式)
由于实现方式的原因,无法拦截static、final、private方法;也无法拦截内部方法调用(例如通过this调用类内部的aop目标方法),该问题可以通过ApplicationContext获取bean实例,然后通过实例调用解决。

静态代理

静态代理模式其实很常见,比如买火车票这件小事:黄牛相当于是火车站的代理,我们可以通过黄牛买票,但只能去火车站进行改签和退票。在代码实现中相当于为一个委托对象realSubject提供一个代理对象proxy,通过proxy可以调用realSubject的部分功能,并添加一些额外的业务处理,同时可以屏蔽realSubject中未开放的接口。

1、RealSubject 是委托类,Proxy 是代理类;

2、Subject 是委托类和代理类的接口;
3、request() 是委托类和代理类的共同方法;

具体代码实现如下:

interface Subject {    void request();}class RealSubject implements Subject {    public void request(){        System.out.println("RealSubject");    }}class Proxy implements Subject {    private Subject subject;    public Proxy(Subject subject){        this.subject = subject;    }    public void request(){        System.out.println("begin");        subject.request();        System.out.println("end");    }}public class ProxyTest {    public static void main(String args[]) {        RealSubject subject = new RealSubject();        Proxy p = new Proxy(subject);        p.request();    }}

静态代理实现中,一个委托类对应一个代理类,代理类在编译期间就已经确定。

JDK动态代理

动态代理中,代理类并不是在Java代码中实现,而是在运行时期生成,相比静态代理,动态代理可以很方便的对委托类的方法进行统一处理,如添加方法调用次数、添加日志功能等等,动态代理分为jdk动态代理和cglib动态代理,下面通过一个例子看看如何实现jdk动态代理。

1、定义业务逻辑

public interface Service {      //目标方法     public abstract void add();  } public class UserServiceImpl implements Service {      public void add() {          System.out.println("This is add service");      }  }

2、利用java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口定义代理类的实现。

class MyInvocatioHandler implements InvocationHandler {    private Object target;    public MyInvocatioHandler(Object target) {        this.target = target;    }    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        System.out.println("-----before-----");        Object result = method.invoke(target, args);        System.out.println("-----end-----");        return result;    }    // 生成代理对象    public Object getProxy() {        ClassLoader loader = Thread.currentThread().getContextClassLoader();        Class<?>[] interfaces = target.getClass().getInterfaces();        return Proxy.newProxyInstance(loader, interfaces, this);    }}

3、使用动态代理

public class ProxyTest {    public static void main(String[] args) {        Service service = new UserServiceImpl();        MyInvocatioHandler handler = new MyInvocatioHandler(service);        Service serviceProxy = (Service)handler.getProxy();        serviceProxy.add();    }}

执行结果:

-----before-----This is add service-----end-----

代理对象的生成过程由Proxy类的newProxyInstance方法实现,分为3个步骤:
1、ProxyGenerator.generateProxyClass方法负责生成代理类的字节码,生成逻辑比较复杂,有兴趣的同学可以继续分析源码 sun.misc.ProxyGenerator;

// proxyName:格式如 "com.sun.proxy.$Proxy.1";// interfaces:代理类需要实现的接口数组;// accessFlags:代理类的访问标识;byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);

2、native方法Proxy.defineClass0负责字节码加载的实现,并返回对应的Class对象。

Class clazz = defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);

3、利用clazz.newInstance反射机制生成代理类的对象;

反编译代理类
为了更清楚的理解动态代理,通过以下方式把代理类字节码生成class文件。

byte[] classFile = ProxyGenerator.generateProxyClass("com.sun.proxy.$Proxy.1", service.getClass().getInterfaces());FileOutputStream out = new FileOutputStream("com.sun.proxy.$Proxy.1.class");out.write(classFile);out.flush();

使用 反编译工具 jad jad com.sun.proxy.$Proxy.1 看看代理类如何实现,反编译出来的java代码如下:

public final class $proxy1 extends Proxy implements Service {    public $proxy1(InvocationHandler invocationhandler) {        super(invocationhandler);    }    public final boolean equals(Object obj) {        try {            return ((Boolean)super.h.invoke(this, m1, new Object[] {                obj            })).booleanValue();        }        catch(Error _ex) { }        catch(Throwable throwable) {            throw new UndeclaredThrowableException(throwable);        }    }    public final String toString() {        try {            return (String)super.h.invoke(this, m2, null);        }        catch(Error _ex) { }        catch(Throwable throwable) {            throw new UndeclaredThrowableException(throwable);        }    }    public final void add() {        try {            super.h.invoke(this, m3, null);            return;        }        catch(Error _ex) { }        catch(Throwable throwable) {            throw new UndeclaredThrowableException(throwable);        }    }    public final int hashCode() {        try {            return ((Integer)super.h.invoke(this, m0, null)).intValue();        }        catch(Error _ex) { }        catch(Throwable throwable) {            throw new UndeclaredThrowableException(throwable);        }    }    private static Method m1;    private static Method m2;    private static Method m3;    private static Method m0;    static {        try {            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {                Class.forName("java.lang.Object")            });            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);            m3 = Class.forName("zzzzzz.Service").getMethod("add", new Class[0]);            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);        }        catch(NoSuchMethodException nosuchmethodexception) {            throw new NoSuchMethodError(nosuchmethodexception.getMessage());        }        catch(ClassNotFoundException classnotfoundexception) {            throw new NoClassDefFoundError(classnotfoundexception.getMessage());        }    }}

从上述代码可以发现:
1、生成的$proxy1继承自Proxy类,并实现了Service接口。
2、执行代理对象的方法,其实就是执行InvocationHandle对象的invoke方法,传入的参数分别是当前代理对象,当前执行的方法和参数。

super.h.invoke(this, m3, null);

jdk动态代理使用的局限性
通过反射类ProxyInvocationHandler回调接口实现的jdk动态代理,要求委托类必须实现一个接口,但事实上并不是所有类都有接口,对于没有实现接口的类,便无法使用该方方式实现动态代理。







原创粉丝点击