Java的三种代理模式(Spring动态代理对象)

   对于不使用接口的业务类,无法使用JDK动态代理,cglib采用非常底层的字节码技术,可以为一个类创建子类,解决无接口代理问题。

  动态代理和静态代理区别??

  解析:静态代理需要手工编写代理类,代理类引用被代理对象。

          动态代理是在内存中构建的,不需要手动编写代理类。

  代理的目的:是为了在原有的方法上进行增强。

  动态代理有两种实现方法:JDK方式和cglib方式。

区别

  jdk动态代理的目标对象必须有接口,且创建的代理对象也是需要转化成接口类型去执行方法,达到增强目标对象行为的目的。这个过程制作快,执行慢。

  cglib动态代理以继承来实现,主要解决没有接口类的代理实现。这个过程制作慢,执行快。

1.代理模式

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

用图表示如下:

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

1.1.静态代理(类似于装饰者模式)

  静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类。

  案例解释:

  接口:ProxyInterface.java,然后目标对象实现这个接口的方法GoalObject.java,此时如果使用静态代理方式,就需要在代理对象(StaticProxyObjects.java)中也实现ProxyInterface接口。调用的时候通过调用代理对象的方法来调用目标对象。注意:代理对象与目标对象要实现相同的接口,然后通过调用相同的方法来调用目标对象的方法。

/*** 接口类 */

public interface ProxyInterface {
    void proxyInterface();
}

/** * 目标对象 *

public class GoalObject implements ProxyInterface{
    //实现接口
    public void proxyInterface() {
        System.out.println("实现目标对象功能");
    }
}

静态代理对象:

public class StaticProxyObjects implements ProxyInterface{
    private GoalObject goalObject;
    public StaticProxyObjects(GoalObject goalObject) {
        this.goalObject = goalObject;
    }
    //实现方法
    public void proxyInterface() {
        before();
        goalObject.proxyInterface();
        after();
    }
    private void after() {  
        System.out.println("已经完成代理");  
    }
    private void before() {  
        System.out.println("准备开始代理");  
    }  
}

测试:

public class StaticProxyTest {
    /**
     * 目标对象的实现主要是由代理类对象实现
     * @param args
     */
    public static void main(String[] args) {
        //目标对象
        GoalObject goalObject=new GoalObject();
        //静态代理
        StaticProxyObjects staticProxyObjects=new StaticProxyObjects(goalObject);
        staticProxyObjects.proxyInterface();
    }
}

静态代理总结: 1.可以做到在不修改目标对象的功能前提下,对目标功能扩展; 

          2.代理对象持有目标对象引用,重写构造方法,对目标对象的方法做了增强(实现其他业务功能)。

 缺点:(1)代理对象和目标对象都要实现的公共接口;如果接口增加方法,目标对象与代理对象都要维护。

         (2)代理角色固定(即:目标对象代码侵入),一次只能代理一个对象。

动态代理

  说明:根据需要通过反射机制在程序运行期动态的为目标对象创建代理对象,代理的行为可以代理多个方法,即满足生产需要的同时又达到代码的通用目的。

动态代理有以下特点:

   1)继承了Proxy类,实现了代理的接口,由于java不能多继承,这里已经继承了Proxy类了,不能再继承其他的类,所以JDK的动态代理不支持对实现类的代理,只支持接口的代理。但是,代理对象,不需要实现接口。

  2)提供了一个使用InvocationHandler作为参数的构造方法。

  3)生成静态代码块来初始化接口中方法的Method对象,以及Object类的equals、hashCode、toString方法。

  4)重写了Object类的equals、hashCode、toString,它们都只是简单的调用了InvocationHandler的invoke方法,即可以对其进行特殊的操作,也就是说JDK的动态代理还可以代理上述三个方法。

jdk的动态代理

  本质:在内存中构建出接口的实现类 特点:被代理对象,必须有接口

  代理类所在包:java.lang.reflect.Proxy JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:

static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )

  注意该方法是在Proxy类中是静态方法,且接收的三个参数依次为:

  ClassLoader loader:指定当前目标对象使用类加载器,获取加载器的方法是固定的;

  Class<?>[] interfaces:目标对象实现的接口的类型,使用泛型方式确认类型

  InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入

  代码示例: 接口类ProxyInterface.java以及接口实现类,目标对象GoalObject是一样的,没有做修改.在这个基础上,增加一个代理工厂类 (JDKProxy.java),将代理类写在这个地方,然后在测试类(需要使用到代理的代码)中先建立目标对象和代理对象的联系,然后代用代理对象的中同名方法:

接口类:

public interface ProxyInterface {
    void proxyInterface();
}

目标对象:

public class GoalObject implements ProxyInterface{
    //实现接口
    public void proxyInterface() {
        System.out.println("实现目标对象功能");
    }
}

JDK动态代理:

   1)目标对象要有接口,且最后创建的代理对象要转换成此接口类型,来调用方

   2)动态代理类实现InvocationHandler接口,持有目标对象引用,利用构造器动态传入目标对象

  3)使用proxy.newProxyInstance()来动态地创建代理对象

  4)代理对象重写invoke方法,利用反射调用目标对象的方法,同时增加行为

public class JDKProxy implements InvocationHandler{
    //目标对象引用  
    private Object target;
    //构造器,可以动态传入目标对象  
    public JDKProxy(Object target) {
        this.target = target;  
    }
    //创建代理对象
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }
    //利用反射机制调用目标对象的方法,并增强行为
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        //使用反射机制
        Object result=method.invoke(target, args);
        after();
        return result;
    }
    private void after() {  
        System.out.println("JDK已经完成代理");  
    }
    private void before() {  
        System.out.println("JDK准备开始代理");  
    } 
}

  测试,目标对象的实现主要是由代理类对象实现:

public static void main(String[] args) {
        //目标对象
        GoalObject goalObject=new GoalObject();
        //JDK代理对象,使用构造函数传入对象参数
        JDKProxy jDKProxy=new JDKProxy(goalObject);
        //JDK代理反射机制,获取对象信息(对象是接口)
        ProxyInterface proxyInterface=(ProxyInterface) jDKProxy.getProxy();
        //通过代理获取的对象,该对象调用其增强行为
        proxyInterface.proxyInterface();
    }

  总结: 代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理。

  cglib动态代理本质:在内存中生成被代理对象的【子类】 特点:可以在没有接口的情况下代理

  上面的静态代理和动态代理模式都是要求目标对象是实现一个接口的目标对象,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用以目标对象子类的方式类实现代理,这种方法就叫做:Cglib代理

  Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.

  JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,如果想代理没有实现接口的类,就可以使用Cglib实现.

  Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP和synaop,为他们提供方法的interception(拦截)

  Cglib包的底层是通过使用一个小而块的字节码处理框架ASM来转换字节码并生成新的类.不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉.

  Cglib子类代理实现方法: 1.需要引入cglib的jar文件,但是Spring的核心包中已经包括了Cglib功能,所以直接引入pring-core-3.2.5.jar即可. 2.引入功能包后,就可以在内存中动态构建子类 3.代理的类不能为final,否则报错 4.目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.

public class CglibObject {
    public void printInfo(){
        System.out.println("打印cglib目标对象");
    }
}

cglib动态代理:

  1)目标对象;

  2)实现MethodInterceptor接口,持有目标对象引用,利用构造器动态传入目标对象;

  3)重写intercept方法,实现行为增强;

public class CglibProxy implements MethodInterceptor{
    //目标对象
    private Object target;
    //构造器
    public CglibProxy(Object target) {
        this.target = target;
    }
    //动态创建代理对象
    public Object getproxy(){
        Enhancer enhancer=new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }
    
    //利用反射技术,重新intercept使得行为增强
    public Object intercept(Object arg0, Method arg1, Object[] objects, MethodProxy methodProxy) throws Throwable {
        before();
        Object result = methodProxy.invoke(target,objects);  
        after();
        return result;  
    }
    
    private void after() {  
        System.out.println("cglib已经完成代理");  
    }  
  
    private void before() {  
        System.out.println("cglib准备开始代理");  
    } 
}

   测试,目标对象的实现主要是由代理类对象实现:

public static void main(String[] args) {
        //Cglib代理对象
        CglibProxy cglibProxy=new CglibProxy(new CglibObject());
        //Cglib代理反射机制
        CglibObject cglibObject=(CglibObject) cglibProxy.getproxy();
        
        cglibObject.printInfo();
    }

   经验:在Spring的AOP编程中: 如果加入容器的目标对象有实现接口,用JDK代理;如果目标对象没有实现接口,用Cglib代理。

posted @ 2018-03-11 19:02  小码农成长记  阅读(4640)  评论(0编辑  收藏  举报