天天看点

2015_04_14 动态代理小结

动态代理小结

What:动态代理是什么?顾名思义,动态代理可以分为两部分学习,一个是代理设计模式,一个是动态,如何实现动态,java的反射机制很好解决了该问题,之后我们也能看到,由于java反射机制中是基于方法类(接口)实现的,所以由此会带来一系列的限制,在之后我们就能够看到了,之所谓成也风云,败也风云。

Why:为什么要使用动态代理?我们在spring等j2ee框架编码时,会编写很多代理类,编写这些代理类代码,除了方法不同,另外流程都是一样的,动态代理就在这种情况下诞生的,开发人员可以不用手工编写代理类代码就可以了

How:先理解代理设计模式;之后了解java相关接口,了解接口实现原理;再看下限制

Where:动态代理在哪里使用呢?

学习第一块:代理模式

代理模式是常用的java设计模式,特征是代理类与委托类有同样的接口,代理类负责为委托类处理消息、过滤消息、把消息转发给委托类以及事后处理消息。代理类一般与一个委托类相关联,代理类通过调用委托类对象的相关方法,提供特定服务。

按照代理的创建时期,我们分为静态代理和动态代理(程序运行时运用反射机制创建,即是本文的重点)

2015_04_14 动态代理小结

代码示例:

package com.wangshui;

/**
 * Created by wenchao.zwc on 2015/4/12.
 */
public interface BookFace {
    public void addBook();
    public void deleteBook();
}

package com.wangshui;

/**
 * Created by wenchao.zwc on 2015/4/12.
 */
public class BookFaceImp implements BookFace{

    @Override
    public void addBook() {
        System.out.println("real adding working");
    }

    @Override
    public void deleteBook() {
        System.out.println("real deteleing working");
    }
}

package com.wangshui;

/**
 * Created by wenchao.zwc on 2015/4/14.
 */
public class BookProxy implements BookFace{

    private BookFace bookFaceImp;

    public BookProxy(BookFace bookFaceImp){
        this.bookFaceImp = bookFaceImp;
    }
    @Override
    public void addBook() {
        System.out.println("static proxy adding");
        bookFaceImp.addBook();
    }

    @Override
    public void deleteBook() {
        System.out.println("static proxy deleting");
        bookFaceImp.deleteBook();
    }
}


package com.wangshui;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

/**
 * Created by wenchao.zwc on 2015/4/12.
 */
public class TestMain {
    public static void main(String[] args){
        BookFace book = new BookFaceImp();
        BookFace bookface = new BookProxy(book);
        bookface.addBook();
        bookface.deleteBook();
    }
}
           

通过上面的例子,我们不难发现,代理类之间的代码会是多么相似,除了方法,另外都是一个模板出来的。我们是否可以用模板的思想来实现呢,接下来我们的主角就要登场了,动态代理

学习第二块:动态代理 java相关代码

Java 动态代理的机制,首先需要了解以下相关的类或接口:一个接口 一个类:

  • java.lang.reflect.Proxy:这是 Java 动态代理机制的主类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象。
    清单 1. Proxy 的静态方法
    // 方法 1: 该方法用于获取指定代理对象所关联的调用处理器
    static InvocationHandler getInvocationHandler(Object proxy) 
    
    // 方法 2:该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象
    static Class getProxyClass(ClassLoader loader, Class[] interfaces) 
    
    // 方法 3:该方法用于判断指定类对象是否是一个动态代理类
    static boolean isProxyClass(Class cl) 
    
    // 方法 4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
    static Object newProxyInstance(ClassLoader loader, Class[] interfaces, 
        InvocationHandler h)      
  • java.lang.reflect.InvocationHandler:这是调用处理器接口,它自定义了一个 invoke 方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问。
    清单 2. InvocationHandler 的核心方法
    // 该方法负责集中处理动态代理类上的所有方法调用。第一个参数既是代理类实例,第二个参数是被调用的方法对象
    // 第三个方法是调用参数。调用处理器根据这三个参数进行预处理或分派到委托类实例上发射执行
    Object invoke(Object proxy, Method method, Object[] args)       
    每次生成动态代理类对象时都需要指定一个实现了该接口的调用处理器对象(参见 Proxy 静态方法 4 的第三个参数)。
  • java.lang.ClassLoader:这是类装载器类,负责将类的字节码装载到 Java 虚拟机(JVM)中并为其定义类对象,然后该类才能被使用。Proxy 静态方法生成动态代理类同样需要通过类装载器来进行装载才能使用,它与普通类的唯一区别就是其字节码是由 JVM 在运行时动态生成的而非预存在于任何一个 .class 文件中。

    每次生成动态代理类对象时都需要指定一个类装载器对象(参见 Proxy 静态方法 4 的第一个参数)

我们来看下面的一个例子

package com.wangshui;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * Created by wenchao.zwc on 2015/4/12.
 */
public class BookFacePorxy implements InvocationHandler {
    private Object target;


    public BookFacePorxy(Object target) {
        this.target = target;
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = null;
        System.out.println("事物开始");
        //执行方法
        result = method.invoke(target, args);
        System.out.println("事物结束");
        return result;
    }
}

package com.wangshui;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

/**
 * Created by wenchao.zwc on 2015/4/12.
 */
public class TestMain {
    public static void main(String[] args){
        BookFace bookFaceImp = new BookFaceImp();
        BookFacePorxy ds = new BookFacePorxy(bookFaceImp);

        Class<?> cls = bookFaceImp.getClass();

        BookFace bookFace = (BookFace) Proxy.newProxyInstance(cls.getClassLoader(),
                cls.getInterfaces(), ds);
        bookFace.addBook();
        bookFace.deleteBook();
    }
}
           
从Proxy.newProxyInstance(cls.getClassLoader(),
                cls.getInterfaces(), ds)
           

由上述我们不难发现,java动态代理机制有一个很严重的限制,需要提供接口,如果此类的接口在某些情况下无法提供,则无法用该方案实现(Spring AOP机制便是受到此类实现限制,故只能提供弱AOP功能支持,其他需求需要用AspectJ实现)

但在这里,我们可以怎样跳过此类限制呢?cglib动态代理很好地解决了该局限。

学习第三块:Cglib动态代理

cglib原理是采用子类继承重写方法,达到方法增强。故当类用final修饰时无法实现 

代码详见博文 http://www.cnblogs.com/jqyp/archive/2010/08/20/1805041.html

更多参考: http://www.cnblogs.com/jqyp/archive/2010/08/20/1805041.html

http://www.ibm.com/developerworks/cn/java/j-lo-proxy1/  这篇要求相对较高,建议先编译jdk代码,然后查看

继续阅读