动手实现AOP

2020-06-02 10:07:18 浏览数 (1)

在前面的两篇文章中,我们了解了Spring AOP的应用以及两种动态代理的实现,但是如何实现AOP我想小伙伴应该会很疑惑,所以今天我们自己来动手撸代码简单的实现一下AOP。

先说一说我对Spring AOP的理解:在Spring应用启动的时候,Spring框架会将配置文件或者注解下的对象注入到工厂类中,当相应的对象被引用的时候,我们通过方法拦截器链进行拦截判断,如果该对象存在AOP的相关配置(拦截范围内),那么我们就返回其增强的代理对象,否则返回其本身的实例。有过Spring项目的小伙伴应该都知道Autowired注解,它帮助我们完成自动装配的工作,而在我写完前面两篇文章的时候,我就在想如何实现自动装配并且返回一个代理对象。下面我们具体来看看如何实现。

相关文章:

解读Java 注解 (Annotation)

动态代理技术的运用

Spring AOP的简单应用

01

首先我们定义一个对象。

代码语言:javascript复制
package com.example.demo.aop.test;

/**
 * @Auther: chenlong
 * @Date: 2019/3/15 14:37
 * @Description:
 */
public class TestAPIImp  {

    public void test() {
        System.out.println("测试数据");
    }
}

定义一个注解,我们规定被这个注解修饰的对象使用自动装配,而不用自己去new对象。对于注解不是很熟悉的小伙伴可以先看相关文章:解读Java 注解

代码语言:javascript复制
package com.example.demo.aop.test;

import java.lang.annotation.*;

/**
 * @Auther: chenlong
 * @Date: 2019/3/15 14:55
 * @Description:
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.TYPE, ElementType.METHOD,ElementType.FIELD})
@Documented
@Inherited
public @interface Demo {
}

我们在定义一个类似与Controller的类,这里大家可以看一下,我们并没有去new它,直接调用其方法,核心在于init()方法将对象进行了装配,其中存在Demo的注解我们使用动态代理增加一些日志,而testAPIImp2我们返回其实例。

代码语言:javascript复制
public class MyProxy {

    @Demo
    public static TestAPIImp testAPIImp;

    public static TestAPIImp testAPIImp2;

    public static void main(String[] args) throws IllegalAccessException, InstantiationException, ClassNotFoundException {
        init();
        //被代理
        testAPIImp.test();
        //不被代理
        testAPIImp2.test();
    }
}

在具体分析之前我们看一下效果:这里的执行前和执行后就是使用CGLib动态代理的效果。

具体实现我们来一步一步看,首先我们看一下CGLib,其核心就是为了在方法执行前后打印日志

代码语言:javascript复制
package com.example.demo.aop.cglib;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * @Auther: chenlong
 * @Date: 2019/3/9 21:42
 * @Description:
 */
public class CGLIBProxy implements MethodInterceptor {


    @Override
    public Object intercept(Object sub, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("执行前...");
        Object object = methodProxy.invokeSuper(sub, objects);
        System.out.println("执行后...");
        return object;
    }

}

然后我们再看init()方法通过getClasses方法(该方法在小程序的代码库中有详细代码,可自行去复制)获取到com.example.demo.aop.test目录下的class对象,如何对象名称为MyProxy我们就开始对字段进行遍历和赋值(实际上这一步也可以用自定义注解实现,如果遍历的class有我们自定义的注解,我们再遍历其字段,而不是定死为MyProxy),如果Field对象通过set方法可以进行赋值(详细的这里就不描述了),我们可以看到如果field.getAnnotation(Demo.class)!=null也就是说该字段被注解修饰我们就是就将代理对象set进去,否则set自己的实例对象。

代码语言:javascript复制
    public static void init() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        Set<Class<?>> set = new MyProxy().getClasses("com.example.demo.aop.test");
        for (Class<?> class1 : set) {
            if (class1.getSimpleName().equals("MyProxy")) {
                Field[] fields = class1.getFields();
                for (final Field field : fields) {
                        try {
                            for (final Class<?> class2 : set) {
                                if (class2.getSimpleName().equals(field.getType().getSimpleName())) {
                                    if (field.getAnnotation(Demo.class)!=null) {
                                    Enhancer enhancer = new Enhancer();
                                    enhancer.setSuperclass(class2);
                                    enhancer.setCallback(new CGLIBProxy());
                                    Object o = enhancer.create();
                                    field.set(class1.newInstance(), o);
                                    }else {
                                    field.set(class1.newInstance(), field.getType().newInstance());
                                    }
                                }
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }


                }
            }
        }
    }

到这里就简单的实现了自己理解下的AOP,实际上我们还可以继续优化,比如说自己写工厂类,将对象都注入到该工厂中,同时通过CGLib我们也可以实现方法的拦截器链,跟拦截器相关的还是适配器模式的相关应用。在后续我也将按照这个方向来进行相关文章的开展,今天我们就说到这里。

0 人点赞