使用Java动态代理技术实现AOP

实现动态代理

根据网络上很多资料,实现一个业务接口的动态代理只需要三步:
- 定义业务接口
- 定义实现业务接口的业务类
- 根据Proxy类创建任何接口的代理类

第一:定义业务接口

AnimalInterface.java

package proxy.imp;

/**
 * 动态代理的业务接口定义
 * 
 * @ClassName: AnimalInterface
 * @Description: TODO(这里用一句话描述这个类的作用)
 * @author PengRong
 * @date 2017年3月5日 下午3:52:15
 *
 */
public interface AnimalInterface {
    // 设置名字
    void setName(String name);

    // 获取名字
    String getName();

    // 叫声
    void say();

    // 获取栖性
    void getProperty();

    // 设置栖性
    void setProperty(String Property);
}

第二:定义一个实现业务接口的具体类,也叫委托类,等下被代理的类

DogImp.java

package proxy;

import proxy.annon.Seven;
import proxy.imp.AnimalInterface;

/**
 * 实现接口的具体业务类
 * 
 * @ClassName: DogImp
 * @Description: TODO(这里用一句话描述这个类的作用)
 * @author PengRong
 * @date 2017年3月5日 下午3:53:12
 *
 */
public class DogImp implements AnimalInterface {

    @Seven(value = "Lumia")
    private String name;

    private String Property;

    public DogImp() {
    }

    @Override
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public void say() {
        System.out.println("小狗:汪汪汪汪.....");
    }

    @Override
    @Seven(Property = "水陆两栖战士")
    public void setProperty(String Property) {
        this.Property = Property;
    }

    @Override
    public void getProperty() {
        System.out.println(this.name + "= " + this.Property);
    }
}

可以看到我这里使用了注解给具体业务类属性赋值的技术,所以就引入了注解的定义和解析;详情可看下面

第三步:先要实现一个调用处理器,然后Proxy类动态生成代理类。

实现调用处理器

AOPHandle.java

package proxy;

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

import proxy.imp.AOPMethod;

/*
 *   调用处理器,每一个代理类都必须有一个关联的调用处理器,当代理类上的一个方法被调用都会被分发到这个调用处理器上
 *   的invoke方法上面;  InvocationHandler接口被实现可以作为代理类的调用处理器功能
    * @ClassName: AOPHandle 
    * @Description: TODO(这里用一句话描述这个类的作用) 
    * @author PengRong
    * @date 2017年3月5日 下午3:55:29 
    *
 */
public class AOPHandle implements InvocationHandler {
    // 保存AOP切入的接口引用
    private AOPMethod method;
    /**
     * 被代理的对象
     */
    private Object o;

    /**
     * 
     * 创建一个新的实例 AOPHandle.
     * 
     * @param o
     *            委托类实例引用
     * @param method
     */
    public AOPHandle(Object o, AOPMethod method) {
        this.o = o;
        this.method = method;
    }

    /**
     * 这个方法会自动调用,Java动态代理机制 会传入下面是个参数
     * 
     * @param Object
     *            proxy 代理对象的接口,不同于对象
     * @param Method
     *            method 被调用方法业务接口
     * @param Object[]
     *            args 方法参数 不能使用invoke时使用proxy作为反射参数时,因为代理对象的接口,不同于对象
     *            这种代理机制是面向接口,而不是面向类的
     **/
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object ret = null;
        if (this.method != null) {
            // 修改的地方在这里哦
            this.method.before(proxy, method, args);
            // 反射调用方法
            ret = method.invoke(o, args);
            // 修改的地方在这里哦
            this.method.after(proxy, method, args);
        } else {//无AOP的路径
            System.out.println("invocation handler before");
            ret = method.invoke(o, args);
            System.out.println("invocation hander after");
        }
        return ret;
    }
}

代理类动态生成:这个是生成动态代理类的核心

AnimalFactory.java

package proxy;

import java.lang.reflect.Proxy;

import proxy.annon.AnnoInjection;
import proxy.imp.AOPMethod;

/**
 * 根据 传进来的委托实例引用创建并返回代理类引用
 * 
 * @ClassName: AnimalFactory
 * @Description: TODO(这里用一句话描述这个类的作用)
 * @author PengRong
 * @date 2017年3月5日 下午4:13:37
 *
 */
public class AnimalFactory {

    /***
     * 获取对象方法
     * 
     * @param obj
     * @return
     */
    private static Object getAnimalBase(Object obj, AOPMethod method) {
        // 获取代理对象
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),
                new AOPHandle(AnnoInjection.getBean(obj), method));
    }

    /***
     * 获取对象方法
     * 
     * @param obj
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T getAnimal(Object obj, AOPMethod aopMethod) {
        return (T) getAnimalBase(obj, aopMethod);
    }

    /***
     * 获取对象方法
     * 
     * @param className
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T getAnimal(String className, AOPMethod method) {
        Object obj = null;
        try {
            obj = getAnimalBase(Class.forName(className).newInstance(), method);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return (T) obj;
    }

    /***
     * 获取对象方法
     * 
     * @param clz
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T getAnimal(Class<?> clz, AOPMethod method) {
        Object obj = null;
        try {
            obj = getAnimalBase(clz.newInstance(), method);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return (T) obj;
    }
}

然后就可以进行测试了
先进行注解的测试,看我在代码中注解的值到底有没有赋值到类实例中;

注解测试

package proxy;

import proxy.annon.AnnoInjection;

public class TestInjection {
    /***
     * 创建一个实例然后,通过注入逻辑自动将注解的内容赋值给实例属性
     */
    public static void main(String[] args) throws InterruptedException {
        DogImp dogImp = (DogImp) AnnoInjection.getBean(new DogImp());//等下还会介绍其中的处理逻辑
        Thread.sleep(100);
        System.out.println(dogImp.getName());
        dogImp.getProperty();
    }
}

输出结果:可以看到输出结果我没有对实例属性进行任何的赋值操作,但是最后两句输出已经可以看出属性已经有值了。前两句是注入逻辑中输出的log,可以看到进行了一次属性注入,一次方法注入。

注入 name 属性      Lumia
注入 setProperty 方法注解 水陆两栖战士

Lumia
Lumia= 水陆两栖战士
代理类测试

上面我们是建立了一个简单的基于接口的动态代理技术框架,动态代理技术主要有委托接口,以及委托类,调用处理器,代理类动态生成这些技术组成;下面给出代理类的测试案例。


package proxy;

import java.lang.reflect.Method;

import proxy.imp.AOPMethod;
import proxy.imp.AnimalInterface;

public class AOPTest {

    public static void main(String[] args) {


        /**
         * 返回的dog为代理实例
         */
        AnimalInterface dog = AnimalFactory.getAnimal(DogImp.class,  null);//通过这个函数调用返回DogImp类的代理类;
        //注意下这里为什么第二个参数为null,这个就是AOP切入的位置
        dog.say();// 实体的一个行为
        String name1 = "我的名字是= " + dog.getName();// 通过这个可以看到可以将注解注入到属性中
        System.out.println(name1);
        dog.setName("二狗子");
        String name2 = "我的名字是" + dog.getName();
        System.out.println(name2);
        dog.getProperty();
    }
}

输出结果:看到注解注入成功,然后在注解处理器中走得是无AOP的路径。

注入 name 属性      Lumia
注入 setProperty 方法注解 水陆两栖战士
invocation handler before
小狗:汪汪汪汪.....
invocation hander after
invocation handler before
invocation hander after
我的名字是= Lumia
invocation handler before
invocation hander after
invocation handler before
invocation hander after
我的名字是二狗子
invocation handler before
二狗子= 水陆两栖战士
invocation hander after

自定义注解并用于给属性赋值

定义注解

Seven.java文件

package proxy.annon;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 自定义的注解
 * 
 * @ClassName: Seven
 * @Description: TODO(这里用一句话描述这个类的作用)
 * @author PengRong
 * @date 2017年3月5日 下午4:20:44
 *
 */
@Retention(RetentionPolicy.RUNTIME) // 表示这个注解可以生存到运行期
@Target({ ElementType.FIELD, ElementType.METHOD }) // 指定注解的使用范围
public @interface Seven {
    // 设定注解的方法,注解方法没有方法体,可以设置默认值
    public String value() default "小黑";

    public String Property() default "无属性";

}

注解注入

注解自动注入是在程序定义一个空实例后,根据注解进行自动注入。根据刚才自定义的注解可以这个注解只能用于字段,函数中,所有自动注入逻辑主要是遍历一个变量的所有字段和函数,然后查看是否具有指定的Seven注解,然后读取我们在源码中设置的值并通过反射机制属性对应的setXXX方法完成初始化。

package proxy.annon;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

/*
 * 注解注入,将一个业务类实例的注解自动赋值给类属性
    * @ClassName: AnnoInjection 
    * @Description: TODO(这里用一句话描述这个类的作用) 
    * @author PengRong
    * @date 2017年3月5日 下午4:22:52 
    *
 */
public class AnnoInjection {

    public static Object getBean(Object obj) {
        try {
            // 获得类属性
            Field f[] = obj.getClass().getDeclaredFields();
            // 遍历属性,查找所有的属性注解
            for (Field ff : f) {
                // 获得属性上的注解
                Seven s = ff.getAnnotation(Seven.class);// 返回ff属性的Seven类型的注解
                if (s != null) {
                    System.err.println("注入 " + ff.getName() + " 属性" + "\t\t" + s.value());
                    // 反射调用public set方法,如果访问级别为private,那么可以直接使用属性的set(obj,
                    // value);
                    obj.getClass()
                            .getMethod("set" + ff.getName().substring(0, 1).toUpperCase() + ff.getName().substring(1), // 组配函数名称出来
                                    new Class<?>[] { String.class })
                            .invoke(obj, s.value());// 通过反射调用属性对应的setXXX函数将注解的值赋值给类属性
                }
            }
            // 获得所有方法,查找方法注解
            Method m[] = obj.getClass().getDeclaredMethods();
            for (Method mm : m) {
                // 获得方法注解
                Seven s = mm.getAnnotation(Seven.class);
                if (s != null) {
                    System.err.println("注入 " + mm.getName() + " 方法注解" + "\t" + s.Property());
                    mm.invoke(obj, s.Property());// 通过方法注入注解的值
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return obj;
    }

}

定义一个AOP接口用于AOP切面编程

上面只是使用了动态代理技术和注解技术,下面通过定义一个AOP接口,将AOP接口的能力添加到代理类中

AOP接口

package proxy.imp;

import java.lang.reflect.Method;

/**
 * 这是一个AOP的一个切面;在每个接口方法中可以做一些类似于日志处理的功能
 * 
 * @ClassName: AOPMethod
 * @Description: TODO(这里用一句话描述这个类的作用)
 * @author PengRong
 * @date 2017年3月5日 下午4:18:20
 *
 */
public interface AOPMethod {
    // 实例方法执行前执行的方法,比如执行方法前,记录类状态,写入log.监控xx变量,,,
    void after(Object proxy, Method method, Object[] args);

    // 实例方法执行后执行的方法
    void before(Object proxy, Method method, Object[] args);
}

通过在生产代理类的代码中将实现AOP接口的实例传递进去。

package proxy;

import java.lang.reflect.Method;

import proxy.imp.AOPMethod;
import proxy.imp.AnimalInterface;

public class AOPTest {

    public static void main(String[] args) {

        /**
         * new AOPMethod() { // 这里写方法执行前的AOP切入方法
         * 
         * @Override public void before(Object proxy, Method method, Object[]
         *           args) { if (method.getName().equals("getProperty")) {
         *           System.err.println("成功拦截" + method.getName() + "方法,启动"); }
         *           }
         * 
         *           // 这里系方法执行后的AOP切入方法
         * @Override public void after(Object proxy, Method method, Object[]
         *           args) { if (method.getName().equals("getProperty"))
         *           System.err.println("成功拦截" + method.getName() + "方法,结束");
         * 
         *           } }
         */
        /**
         * 返回的dog为代理实例
         */
        AnimalInterface dog = AnimalFactory.getAnimal(DogImp.class, new AOPMethod() { // 这里写方法执行前的AOP切入方法

            @Override
            public void before(Object proxy, Method method, Object[] args) {
                if (method.getName().equals("getProperty")) {
                    System.err.println("成功拦截" + method.getName() + "方法,启动");
                }
            }

            // 这里系方法执行后的AOP切入方法
            @Override
            public void after(Object proxy, Method method, Object[] args) {
                if (method.getName().equals("getProperty"))
                    System.err.println("成功拦截" + method.getName() + "方法,结束");

            }
        });// 返回一个代理类
        dog.say();// 实体的一个行为
        String name1 = "我的名字是= " + dog.getName();// 通过这个可以看到可以将注解注入到属性中
        System.out.println(name1);
        dog.setName("二狗子");
        String name2 = "我的名字是" + dog.getName();
        System.out.println(name2);
        dog.getProperty();
    }
}

执行结果:通过实现AOP接口实例传递进去,那么将可以在实际业务方法执行前进行很多其他操作,比如统计,监控,日志功能。

注入 name 属性      Lumia
注入 setProperty 方法注解 水陆两栖战士
成功拦截getProperty方法,启动
成功拦截getProperty方法,结束
小狗:汪汪汪汪.....
我的名字是= Lumia
我的名字是二狗子
二狗子= 水陆两栖战士

综上:动态代理类技术是实现AOP的技术基础。
源代码
使用Java原生代理并实现注解自动注入

使用Java原生代理实现AOP

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值