AOP原理分析
AOP(面向切面编程)的本质就是Spring的动态代理开发,通过代理类为原始类增加额外功能。
AOP的开发方式
MethodInterceptor
MethodInterceptor方法拦截器
接口---->实现类–>invoke
方法(书写额外功能)
参数:MethodInvocation
: 额外功能所增加给的原始方法
//object为原始方法的返回值
public Object invoke(MethodInvocation invocation)throws Throwable{
//前置额外方法(befor)
----
Object ret = invocation.proceed(); //目标(原始)方法运行
----
//后置额外方法
return ret;
}
@Aspect
@Order(0) //优先级
@Aspect //切面类
@Component
public class DataBaseAop {
@Around("@annotation(database)") //切入点
public Object setWrite(ProceedingJoinPoint joinPoint, Database database) throws Throwable {
DbContextHolder.setDbType(database.databaseName());
return joinPoint.proceed(); //原始方法运行
}
}
AOP如何创建动态代理类(动态字节码技术)
JDK动态代理
代码实例:
public void TestProxy(){
//1.创建原始方法
ApiOaService apiOaService = new ApiOaServiceImpl();
//2.JDK创建动态代理
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("--------log-----------");
//1.原始方法运行
Object ret = method.invoke(apiOaService, args);
//2.添加额外功能
System.out.println("----------log----------");
return ret;
}
};
ApiOaService proxyApiOAService = (ApiOaService) Proxy.newProxyInstance(apiOaService.getClass().getClassLoader(), apiOaService.getClass().getInterfaces(), handler);
//调用被代理的方法
proxyApiOAService.applyOA();
}
图解分析:
代理类和原始类实现相同接口的原因:
CGLIB动态代理
如果目标类(原始类)没有实现接口,这时候JDK的方法就无法创建动态代理。CGLIB会已继承的方式创建动态代理。
CGLIB创建动态代理的原理:父子继承关系创建代理对象,原始类作为父类,代理类作为子类,这样既可以保证2者方法一致,同时在代理中提供新的实现。
代码实例:
public void CglibTestProxy(){
//1.原始对象
WechatServiceImpl wechatService = new WechatServiceImpl();
//2.通过cglib创建代理对象
Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(wechatService.getClass().getClassLoader());
enhancer.setSuperclass(wechatService.getClass());
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("-----------log------------");
Object ret = method.invoke(wechatService, args); //原始方法运行
System.out.println("-----------log------------");
return ret;
}
});
WechatServiceImpl proxy = (WechatServiceImpl) enhancer.create();
proxy.getAccessToken();
}
总结
- 1.JDK实现动态代理 Proxy.newProxyInstance(); //通过接口创建动态代理
- 2.CGLIB实现动态代理 Enhancer //通过父子继承的方式创建动态代理
底层默认是JDK的方式,如果需要切换CGlib
@EnableAspectJAutoProxy(proxyTargetClass = true)
Spring工厂如何加工创建代理对象
BeanPostProcessor
AOP的坑
同一个service中,不同方法之间会有调用的情况。
举个栗子:
-
首先创建一个切面类
/** * @ClassName: TestAop * @Description: * @author: wangz48 * @date: 2022-1-12 14:50 */ @Aspect @Component public class TestAop { @Around("@annotation(Log)") public Object setLog(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("======log======="); Object ret = joinPoint.proceed(); System.out.println("=======log======"); return ret; } }
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited public @interface Log { }
-
正常情况:
-
不同方法间调用的情况:
可以发现,a并没有调用b的代理方法,而是调用了原始方法。
如何才能获取到代理对象呢??
这里提供两种解决方案:
-
启用类加入 @EnableAspectJAutoProxy(exposeProxy = true)
-
从ApplicationContext获取当前Service对象
- 1.实现 ApplicationContextAware接口 - 2. 重写setApplicationConetext方法获取工厂 - 3. 用工厂的getBean()方法创建对象,创建的对象几位代理对象。