手写一个Spring AOP
1、声明AspectJ风格的AOP的支持—配置类上添加@EnableAspectJAutoProxy注解。
2、声明一个切面—定义一个切面类,并添加@Aspect注解,并在该切面类下声明切点和通知。
参照: https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#aop.
一个简单的AOP就完成了,下面定义一个测试类测试一下效果:
可以看到,在执行service真实逻辑之前,先执行了before通知的方法。
但是,值得一提的是通过AnnotationConfigApplicationContext容器获得的IndexService bean不是原来的bean了,而是aop的代理对象。
Spring AOP代理的底层解析
那么aop是在什么时候完成了代理呢?
在我的另一篇博客(Spring Bean初始化的底层原理)的最后讲到spring是在doCreateBean()方法里完成创建bean的实例、属性注入的:
initializeBean()方法里会执行生命周期初始化回调方法并完成aop代理:
(注意:spring支持多种生命周期回调机制,执行顺序如下图所示,对应底层源码1、2、3)
—画重点—
因此,aop代理是在执行生命周期初始化回调方法之后的,具体顺序为:
创建bean的实例—>属性注入—>init-method生命周期初始化回调方法—>aop代理—>bean放入二级缓存
注意!!!
代理的主要作用是对原有的功能的增强。
spring aop有二种代理模式,一种是基于JDK动态代理,一种是基于cglib代理:
1、当类实现了接口时(此处UserDaoImpl实现了接口UserDao),默认使用JDK动态代理:
public static void Test1(){
final UserDao userDao = new UserDaoImpl();
// newProxyInstance的三个参数解释:
// 参数1:代理类的类加载器,同目标类的类加载器
// 参数2:代理类要实现的接口列表,同目标类实现的接口列表
// 参数3:回调,是一个InvocationHandler接口的实现对象,当调用代理对象的方法时,执行的是回调中的invoke方法
//proxy为代理对象
UserDao proxy = (UserDao) Proxy.newProxyInstance(userDao.getClass().getClassLoader(),
userDao.getClass().getInterfaces(), new InvocationHandler() {
@Override
// 参数proxy:被代理的对象
// 参数method:执行的方法,代理对象执行哪个方法,method就是哪个方法
// 参数args:执行方法的参数
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("proxy---new fuction");
Object result = method.invoke(userDao, args);
return result;
}
});
//代理对象执行方法
proxy.saveDao();
}
代码执行结果如下;
2、当类没有实现接口时,此时是cglib代理(UserDaoImpl类没有实现接口),实际上是生成了目标类的子类来增强:
public static void Test2() {
final UserDaoImpl userDao=new UserDaoImpl();
// 创建cglib核心对象
Enhancer enhancer = new Enhancer();
// 设置父类
enhancer.setSuperclass(userDao.getClass());
// 设置回调
enhancer.setCallback(new MethodInterceptor() {
/**
* 当你调用目标方法时,实质上是调用该方法
* intercept四个参数:
* proxy:代理对象
* method:目标方法
* args:目标方法的形参
* methodProxy:代理方法
*/
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)
throws Throwable {
System.out.println("cglib proxy---new function");
Object result = method.invoke(userDao, args);
return result;
}
});
// 创建代理对象
UserDaoImpl proxy = (UserDaoImpl) enhancer.create();
proxy.saveDao();
}
代码执行结果如下: