使用Java原生代理实现AOP

本文由博主柒。原创,转载请注明出处

完整源码下载地址 https://github.com/MatrixSeven/JavaAOP

一说到AOP,大家一定会想到Spring,因为这东西实在是太强大了.但是大家一定要清楚,AOP是一只编程思想,而Spring仅仅是AOP的一种实现罢了.

首先百度下:

在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

如果你对AOP还没有了解请左移百度百科:http://baike.baidu.com/search/word?word=AOP查看.

今天呢,咱们就一起用Java原生代理实现简单的AOP功能.

首先,你得需要了解基本的反射知识,否则可能会感到困惑.

不罗嗦了,直接开始撸码

首先,咱们先写一个简单的接口.名字叫AnimalInterface,用来声明规范动物的一些基本方法.

这些方法包括 设置名字,获取名字,叫声,属性(原谅我没文化,其实就是获得是陆栖还是水栖或者水陆两栖)

package proxy.imp;
public interface AnimalInterface {
    //设置名字
    void setName(String name);
    //获取名字
    String getName();
    //叫声
    void say();
    //获取栖性
    void getProperty();
}

然后咱们实现这个接口,创建一个名叫小黑的Dog

package proxy;
 
import proxy.imp.AnimalInterface;
public class DogImp implements AnimalInterface {
    private String name = "小黑";
    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
    public void getProperty() {
        System.out.println("小狗是陆地动物,但是会游泳哦");
    }
}

大家一定迫不及待了,怎么实现类似AOP的功能呢….

咱们先创建一个名为AOPHandle的类,让其实现InvocationHandler接口,

不能使用invoke时使用proxy作为反射参数时,因为代理对象的接口,不同于对象,

这种代理机制是面向接口,而不是面向类的,如果使用proxy,会造成无限递归.然后就是栈溢出,但是依旧能反射成功一次,

这说明代理对象和对象的代理是不一样的,但是咱们可以通过proxy参数的proxy.getClass()获得class对象,然后获得被代理

类的方法和参数,这也为注解注入,特定方法注入,属性注入提供了一种实现途径吧,关于这个,咱们后面再说..

package proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class AOPHandle implements InvocationHandler{
    //保存对象
    private Object o;
    public AOPHandle(Object o) {
        this.o=o;
    }
    /**
     * 这个方法会自动调用,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;
        //打印方法名称
        System.err.println("执行方法:"+method.getName()+"n参数类型为:");
        //打印参数
        for(Class type:method.getParameterTypes())
            System.err.println(type.getName());
        //打印返回类型
        System.err.println("返回数据类型:"+method.getReturnType().getName());
        //反射调用方法
        ret=method.invoke(o, args);
        //声明结束
        System.err.println("方法执行结束");
        //返回反射调用方法的返回值
        return ret;
    }
}

动态代理已经搞定..然后就是咱们的AnimalFactory了..咱们继续

package proxy;
import java.lang.reflect.Proxy;
public class AnimalFactory {
    /***
     * 获取对象方法
     * @param obj
     * @return
     */
    private static Object getAnimalBase(Object obj){
        //获取代理对象
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),
                obj.getClass().getInterfaces(), new AOPHandle(obj));
    }
    /***
     * 获取对象方法
     * @param obj
     * @return
     */
    @SuppressWarnings("unchecked")
    public static  T getAnimal(Object obj){
        return (T) getAnimalBase(obj);
    }
    /***
     * 获取对象方法
     * @param className
     * @return
     */
    @SuppressWarnings("unchecked")
    public static   T getAnimal(String className){
        Object obj=null;
        try {
            obj= getAnimalBase(Class.forName(className).newInstance());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return (T)obj;
    }
    /***
     * 获取对象方法
     * @param clz
     * @return
     */
    @SuppressWarnings("unchecked")
    public static   T  getAnimal(Class clz){
        Object obj=null;
        try {
            obj= getAnimalBase(clz.newInstance());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return (T)obj;
    }
}

终于到最后了…还差什么呢,大家来这里看看效果吧…

哈哈…小二,上个菜..哦~不对,是个测试类..哈哈////

package proxy;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.BlockJUnit4ClassRunner;
import proxy.AnimalFactory;
import proxy.imp.AnimalInterface;
 
@RunWith(BlockJUnit4ClassRunner.class)
public class AOPTest {
 
    @Test
    public void Test1() {
        AnimalInterface dog=AnimalFactory.getAnimal(DogImp.class);
        dog.say();
        System.out.println("我的名字是"+dog.getName());
        dog.setName("二狗子");
        System.out.println("我的名字是"+dog.getName());
    }
}

对,咱们的效果已经看到了..
,咦,你会说没图没真相???
好,那就上图…

图片在sae上已经丢失,请大家脑补

啥?什么,,到了最后说,,这又卵用,这不是坑爹么?就捕获一个这个玩意,什么用啊…
什么AOP,我怎么一点AOP的影子都没有看到,怎么切入自定义方法,就一个syso输入,往这忽悠观众来了?…..
好吧,那咱们继续…看看如何实现注入自定义方法…

首先增加一个接口,咱们就称为AOP注入接口吧.取名AOPMethod哈
创建after和before方法,接收Object proxy, Method method, Object[] args参数
这样就能做更多的事情叻…比如执行方法前,记录类状态,写入log.监控xx变量,,,
开启你的脑洞吧.

package proxy.imp;
import java.lang.reflect.Method;
 
public interface AOPMethod{
    //实例方法执行前执行的方法
    void after(Object proxy, Method method, Object[] args);
    //实例方法执行后执行的方法
    void before(Object proxy, Method method, Object[] args);
}

然后修改AOPHandle类,增加AOPMethod属性.

在修改构造方法,让在类初始化时获得AOPMethod实例.

最后修改invoke方法….直接上代码哈

package proxy;
 
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
 
import proxy.imp.AOPMethod;
 
public class AOPHandle implements InvocationHandler{
    //保存对象
    private AOPMethod method;
    private Object o;
    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;
        //修改的地方在这里哦
        this.method.before(proxy, method, args);
        ret=method.invoke(o, args);
        //修改的地方在这里哦
        this.method.after(proxy, method, args);
        return ret;
    }
}

呼呼,大功告成,,看起来一切都么问题,萌萌哒..

赶紧更新下测试类…

package proxy;
 
import java.lang.reflect.Method;
 
import org.junit.runner.RunWith;
import org.junit.runners.BlockJUnit4ClassRunner;
import proxy.imp.AOPMethod;
import proxy.imp.AnimalInterface;
 
@RunWith(BlockJUnit4ClassRunner.class)
public class AOPTest {
 
    public static void main(String[] args) {
 
        AnimalInterface dog = AnimalFactory.getAnimal(DogImp.class, new AOPMethod() {
            // 这里写方法执行前的AOP切入方法
            public void before(Object proxy, Method method, Object[] args) {
                System.err.println("我在" + method.getName() + "方法执行前执行");
            }
 
            // 这里系方法执行后的AOP切入方法
            public void after(Object proxy, Method method, Object[] args) {
                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);
    }
}

来个效果图:

图片在sae上已经丢失,请大家脑补

呼呼,亲们,是不是有注入的感觉了?是不是感觉把自己的方法切进去了???哈哈….

看起来一切都已经完美了,但是总觉得差了点什么?哦,对,缺少了类似于Spring那么样的配置文件..

其实那些已经很简单了,交给你们去做吧,设计好XML格式化就妥了,等等,你说什么,还不能拦截自定义方法?

不能像Spring那样拦截自定义方法?oh~~NO,其实已经可以了在
before(Object proxy, Method method, Object[] args)
中利用method和的给methodName就能做判断了.
当然,本例的并没有什么实用意义,更不能个各种完善的AOP框架相比,本文仅仅为您提供一种思路,但是一定要记住,再牛的东西也是一点点积累出来的
学习了解原理,是为了更好的驾驭所掌握的知识和技能.咱再会回嘿嘿.

原文地址:https://www.cnblogs.com/seven007/p/6259960.html