java 动态代理实例 (jdk ,cglib)

java 动态代理范例 (jdk ,cglib)
JAVA的动态代理 

一、讲动态代理之前,我们先说下常用的java设计模式——代理模式

       1.1代理模式概念

        1)他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。

      2)关于代理模式的概念网上有一大堆,你可以好好研磨之。如果你想仔细学习23种设计模式,我推荐你阅读《研磨设计模式》,陈臣,王斌著

        简单比喻:如果律师是代理类,当事人是委托类。

           就好比当事人自己需要准备一些核心材料,律师不但通过这些材料代理了当事人的主要诉求,还要帮当事人处理一些边边脚脚的事

       1.2代理模式有何用途?

         例如spring中,你要为方法加一些日志记录,权限控制,事务处理,异常处理,我们不需要在实现主要业务逻辑的这个类增加这些处理,我们可以完全交由代理类帮我们“补充”上这些操作。
 

二、按照代理的创建时期,代理类可以分为两种。

       1)静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。 
       2)动态代理:在程序运行时,运用反射机制动态创建而成。


      2.1.  静态代理

        以下这个例子,我展示了一种最普通的静态代理模式。

        简单一点来说,就是主类RealSubject实现既定接口Subject的方法,实现的方法完成主要业务逻辑。代理类Proxy也实现既定接口Subject,同时代理类Proxy也持有Subject这么一个成员。通过构造方法,代理类Proxy构建代理对象时,同时传人一个RealSubject。代理类Proxy的实现方法中除了调用了realSubject.opration() 完成了主要业务逻辑,还在方法中加了一些事务处理。


public interface Subject {

void opration();

}

public class RealSubject implements Subject {

@Override
public void opration() {
    System.out.println("I am a RealSubject");

}

}

public class StaticProxy implements Subject {

    private Subject subject;

    public StaticProxy(Subject subject) {

        this.subject = subject;
    }

    @Override
    public void opration() {

        System.out.println("事务处理之前");
        subject.opration();
        System.out.println("事务处理之后");

    }

    public Subject getSubject() {
        return subject;
    }

    public void setSubject(Subject subject) {
        this.subject = subject;
    }

    public static void main(String[] args) {

        StaticProxy staticProxy = new StaticProxy(new RealSubject());
        staticProxy.opration();

    }

}

      2.2动态代理

       观察上述代码可以发现每一个代理类只能为一个接口服务,如果我们需要给实现不同接口的几个类同时加上相同的日志记录处理,我们必然要写好几个代理类。而且所有的代理操作除了调用的方法不一样之外,其他的操作都一样,则此时肯定是重复代码。

       解决这一问题最好的做法,就是通过一个代理类完成全部委托类的代理功能,那么此时就必须使用动态代理完成

     动态代理 :
          与静态代理类对照的是动态代理类,动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java 反射机制可以生成任意类型的动态代理类。

java 动态代理实例 (jdk ,cglib)

java 动态代理实例 (jdk ,cglib)

     jdk 动态代理示例: 

public interface Subject {

void opration();

}

public class RealSubject implements Subject {

@Override
public void opration() {
    System.out.println("I am a RealSubject");

}

}

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

public class DanamicProxyInvocationHandler implements InvocationHandler {

    private Object beProxy;

    public DanamicProxyInvocationHandler(Object beProxy) {
        super();
        this.beProxy = beProxy;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {

        System.out.println("proxy class:" + proxy.getClass() + "proxy method:"
                + method.getName());
        System.out.println("这里还可以加权限验证和日志记录!");

       return method.invoke(beProxy, args);
    }

    public static void main(String[] args) {
        Subject proxyClass = (Subject) Proxy.newProxyInstance(
                Subject.class.getClassLoader(), new Class[] { Subject.class },
                new DanamicProxyInvocationHandler(new RealSubject()));
        proxyClass.opration();

    }

}
JDK动态代理中包含一个类和一个接口: 

1) InvocationHandler接口:

public interface InvocationHandler { 
               public Object invoke(Object proxy,Method method,Object[] args) throws Throwable; 

参数说明: 
Object proxy:指被代理的对象。 
Method method:要调用的方法 
Object[] args:方法调用时所需要的参数 

可以将InvocationHandler接口的子类想象成一个代理的最终操作类,替换掉ProxySubject。 

2) Proxy类: 
Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类,此类提供了如下的操作方法:

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

参数说明: 
ClassLoader loader:类加载器 
Class<?>[] interfaces:得到全部的接口 
InvocationHandler h:得到InvocationHandler接口的子类实例 

Ps:类加载器 
在Proxy类中的newProxyInstance()方法中需要一个ClassLoader类的实例,ClassLoader实际上对应的是类加载器,在Java中主要有一下三种类加载器; 
Booststrap ClassLoader:此加载器采用C++编写,一般开发中是看不到的; 
Extendsion ClassLoader:用来进行扩展类的加载,一般对应的是jre\lib\ext目录中的类; 
AppClassLoader:(默认)加载classpath指定的类,是最常使用的是一种加载器。 



2.3  cglib动态代理(基于字节码)

       JDK 的动态代理依靠接口实现,如果有些类并没有实现接口,则不能使用JDK代理,这就要使用cglib动态代理了。  

      概念介绍

      CGLIB(Code Generation Library)是一个开源项目!是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。Hibernate用它来实现PO(Persistent Object 持久化对象)字节码的动态生成。
       CGLIB 是一个强大的高性能的代码生成包。它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的interception(拦截)。最流行的OR Mapping工具hibernate也使用CGLIB来代理单端single-ended(多对一和一对一)关联(对集合的延迟抓取,是采用其他机制实现的)。EasyMock和jMock是通过使用模仿(mock)对象来测试java代码的包。它们都通过使用CGLIB来为那些没有接口的类创建模仿(mock)对象。
     CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。除了CGLIB包,脚本语言例如Groovy和BeanShell,也是使用ASM来生成java的字节码。当然不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。

CGlib动态代理示例

public interface Subject {

void opration();

}

public class RealSubject implements Subject {

@Override
public void opration() {
    System.out.println("I am a RealSubject");

}

}

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 CglibProxy implements MethodInterceptor {

    private Object target;

    /**
     * 创建代理对象
     * 
     * @param target
     * @return
     */
    public Object getInstance(Object target) {
        this.target = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.target.getClass());
        // 回调方法
        enhancer.setCallback(this);
        // 创建代理对象
        return enhancer.create();
    }

    @Override
    // 回调方法
    public Object intercept(Object obj, Method method, Object[] args,
            MethodProxy methodProxy) throws Throwable {
        System.out.println("这里还可以加权限验证和日志记录!");
        Object result = methodProxy.invokeSuper(obj, args);
        return result;

    }

    public static void main(String[] args) {
        CglibProxy cglib = new CglibProxy();
        RealSubject subjectCglib = (RealSubject) cglib
                .getInstance(new RealSubject());
        subjectCglib.opration();
    }

}



注意 : 除了添加CGlib的类库,你还要添加关于ASM的jar包。

2.3.1)在这里给各位推荐一个CGlib.jar包下载源(maven资源库)

http://search.maven.org/#search|ga|1|cglib

假如你选择了cglib-3.1.jar

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.1</version>
</dependency>

不要忘记下载它的ASM依赖,版本号要一致,否则会报莫名其妙的错误。
http://search.maven.org/#search|ga|1|org.ow2.asm

 <dependency>
      <groupId>org.ow2.asm</groupId>
      <artifactId>asm</artifactId>
      <version>4.2</version>
    </dependency>
    <dependency>
      <groupId>org.ow2.asm</groupId>
      <artifactId>asm-util</artifactId>
      <version>4.2</version>
      <optional>true</optional>
 </dependency>

2.3.2)另外CGlib是一个开源项目,它托管在github上。

你可以下载CGlib的最新源码阅读。https://github.com/cglib/cglib