Javassist的动态代理实现。

一提到jdk中的java.lang.reflect.Proxy,用过spring,hibernate等框架的人应该都有所了解,对!就是动态代理。AOP - 面向切面编程 - 就是基于动态代理实现的。
为什么要提代理模式。因为AOP的广泛实现都是通过动态代理,而动态代理又不得不说代理模式。
代理模式,顾名思义,就是对一个类的访问,变为访问这个类的代理人。经由代理再访问这个类。(代理与被代理的类实现了相同的接口,因此客户感觉不到通过代理访问这个类和直接访问这个类的区别)
为什么需要代理呢,因为一个良好的设计不应该轻易的修改。这正是开闭原则的体现:一个良好的设计应该对修改关闭,对扩展开放。而代理正是为了扩展类而存在的。他可以控制对现有类(就是需要被代理的类)服务的访问,通俗的解释就是 可以拦截对于现有类方法的调用并做些处理。
而动态代理,是指在运行期动态的为指定的类生成其代理类。(需要相关的运行时编译技术,)
AOP如Spring的AOP实现就是以这种方式实现的。他使用动态生成的代理类拦截了现有类的“切点”。并进行控制,使得这些切面的逻辑完全与该类脱离,实现了关注点分离。
下面附上我用Javassist实现的简单动态代理。(Javassist是一个运行时编译库,他能动态的生成或修改类的字节码,类似的有ASM和CGLIB,大多数框架就是基于后者实现的)
在项目中加入所需要的jar包

我使用的maven管理jar包

<dependency>
    <groupId>javassistgroupId>
    <artifactId>javassistartifactId>
    <version>3.12.1.GAversion>
dependency>

要代理的类
A

package com.cglib.demo02;

public  class A {

    public void save(){
        System.out.println("保存商品");
    }
    public void del(){
        System.out.println("删除商品");
    }
}

第一种方式
JavassistProxyFactory.java

package com.javassist.demo;

import java.lang.reflect.Method;

import javassist.util.proxy.MethodHandler;
import javassist.util.proxy.ProxyFactory;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class JavassistProxyFactory {
    private T target;

    public void setTarget(T target) {
        this.target = target;
    }

    @SuppressWarnings( "deprecation")
    public T getProxy() throws InstantiationException, IllegalAccessException {
        // 代理工厂
        ProxyFactory proxyFactory = new ProxyFactory();
        // 设置需要创建子类的父类
        proxyFactory.setSuperclass(target.getClass());
        /*
         * 定义一个拦截器。在调用目标方法时,Javassist会回调MethodHandler接口方法拦截,
         * 来实现你自己的代理逻辑,
         * 类似于JDK中的InvocationHandler接口。
         */

        proxyFactory.setHandler(new MethodHandler() {
            /*
             * self为由Javassist动态生成的代理类实例,
             *  thismethod为 当前要调用的方法
             *  proceed 为生成的代理类对方法的代理引用。
             *  Object[]为参数值列表,
             * 返回:从代理实例的方法调用返回的值。
             * 
             * 其中,proceed.invoke(self, args);
             * 
             * 调用代理类实例上的代理方法的父类方法(即实体类ConcreteClassNoInterface中对应的方法)
             */
            public Object invoke(Object self, Method thismethod, Method proceed, Object[] args) throws Throwable {
                System.out.println("--------------------------------");
                System.out.println(self.getClass());
                //class com.javassist.demo.A_$$_javassist_0
                System.out.println("要调用的方法名:"+thismethod.getName());
                System.out.println(proceed.getName());
                System.out.println("开启事务-------");

                Object result = proceed.invoke(self, args);
                //下面的代码效果与上面的相同
                //不过需要传入一个目标对象
                //Object result = thismethod.invoke(target,args);

                System.out.println("提交事务-------");
                return result;
            }
        });




        // 通过字节码技术动态创建子类实例
        return (T) proxyFactory.createClass().newInstance();
    }

}

第二种方式
JavassistProxyFactory02.java
这种方式不需要事先创建要代理的对象

package com.javassist.demo;

import java.lang.reflect.Method;

import javassist.util.proxy.MethodHandler;
import javassist.util.proxy.ProxyFactory;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

/*
 * 这种方式不需要事先创建
 * 要代理的对象
 * 
 * */
public class JavassistProxyFactory02 {

    /*
     * 要代理的对象的class
     * */
    @SuppressWarnings("deprecation")
    public Object getProxy(Class clazz) throws InstantiationException, IllegalAccessException {
        // 代理工厂
        ProxyFactory proxyFactory = new ProxyFactory();
        // 设置需要创建子类的父类
        proxyFactory.setSuperclass(clazz);
        /*
         * 定义一个拦截器。在调用目标方法时,Javassist会回调MethodHandler接口方法拦截,
         * 来实现你自己的代理逻辑,
         * 类似于JDK中的InvocationHandler接口。
         */

        proxyFactory.setHandler(new MethodHandler() {
            /*
             * self为由Javassist动态生成的代理类实例,
             *  thismethod为 当前要调用的方法
             *  proceed 为生成的代理类对方法的代理引用。
             *  Object[]为参数值列表,
             * 返回:从代理实例的方法调用返回的值。
             * 
             * 其中,proceed.invoke(self, args);
             * 
             * 调用代理类实例上的代理方法的父类方法(即实体类ConcreteClassNoInterface中对应的方法)
             */
            public Object invoke(Object self, Method thismethod, Method proceed, Object[] args) throws Throwable {
                System.out.println("--------------------------------");
                System.out.println(self.getClass());
                //class com.javassist.demo.A_$$_javassist_0
                System.out.println("代理类对方法的代理引用:"+thismethod.getName());
                System.out.println("开启事务 -------");

                Object result = proceed.invoke(self, args);

                System.out.println("提交事务 -------");
                return result;
            }
        });




        // 通过字节码技术动态创建子类实例
        return  proxyFactory.createClass().newInstance();
    }

}

测试

package com.javassist.demo;

public class JavassistTest {

    public static void main(String[] args) throws InstantiationException, IllegalAccessException {
        System.out.println("*******************方式一*******************");
        JavassistProxyFactory jpf = new JavassistProxyFactory();
        A a = new A();
        jpf.setTarget(a);
        A proxy = jpf.getProxy();
        proxy.del();

        System.out.println("*******************方式二*******************");
        JavassistProxyFactory02 jpf02 = new JavassistProxyFactory02();
        A a2 = (A) jpf02.getProxy(A.class);
        a2.del();
        a2.save();



    }

}

结果

*******************方式一*******************
--------------------------------
class com.javassist.demo.A_$$_javassist_0
要调用的方法名:del
_d1del
开启事务-------
删除商品
提交事务-------
*******************方式二*******************
--------------------------------
class com.javassist.demo.A_$$_javassist_1
代理类对方法的代理引用:del
开启事务 -------
删除商品
提交事务 -------
--------------------------------
class com.javassist.demo.A_$$_javassist_1
代理类对方法的代理引用:save
开启事务 -------
保存商品
提交事务 -------

你可能感兴趣的:(java学习笔记)