“ 在前面的两篇文章中,我们了解了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我们也可以实现方法的拦截器链,跟拦截器相关的还是适配器模式的相关应用。在后续我也将按照这个方向来进行相关文章的开展,今天我们就说到这里。