Spring AOP的理解
周一不知道干啥,更新一点复习知识点。理论有部分摘抄简化。
AOP面向切面编程,可以说是对OOP的补充和完善,在原OOP面向对象编程中,引用封装继承和多态等概念来建立一种对象的层次结构。也就是说 OOP允许你定义从上到下的关系,但不适合从左到右的关系,例如日志,事务,权限认证,会导致代码大量重复,不利于各个模块的重用。AOP则称之为“横切” ,刨开封装的内部对象,将软件分为 核心关注 点和 横切关注点。业务的主要流程就是核心关注点,而与之关系不大的就是横切关注点。横切关注点的特点就是:他们经常发生在核心关注点的多出,而且各个都基本相似。比如刚才提到的日志,事务,权限认证。将应用程序中的商业逻辑同对其提供支持的通用服务进行分离
实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码。
具体可以在下面的场景中使用:
- Authentication 权限
- Caching 缓存
- Context passing 内容传递
- Error handling错误处理
- Lazy loading 懒加载
- Debugging 调试
- logging, tracing, profiling andmonitoring 记录跟踪 优化 校准
- Performance optimization 性能优化
- Persistence 持久化
- Resource pooling 资源池
- Synchronization 同步
- Transactions 事务
expression切面规则表达式:
5个切入点:
前置 aop:before 在原业务发生之前触发 业务出现异常不影响 @Before
后置 aop:after 在原业务发生之后触发 业务出现异常不影响 @After
返回 aop:after-returning 在原业务不抛出异常时触发 获取原业务返回值 甚至可以修改结果 @AfterReturning
异常 aop:after-throwing 在原业务抛出异常时触发 如果业务层内部处理异常 对外而言 意味着该业务正常执行 @AfterThrowing
*环绕 aop:around 控制原业务回调 最符合代理模式编程模式 其原理是动态代理
其关键是 org.aspectj.lang.ProceedingJoinPoint @Around
配置式例子:
被切入点:
public class MathCaculator {
/**
* 希望在业务h执行之前 之后 错误 打印相关信息
* @param i
* @param j
* @return
*/
public int dev(int i, int j) {
return i / j;
}
}
java配置:LogAspects 切面类
@Aspect
public class LogAspects {
@Pointcut("execution(public int com.thl.aop.apotest.MathCaculator.dev(int,int ))")
public void pointCut() {
}
@Before("pointCut()")
public void logStart(JoinPoint joinPoint) {
System.out.println("运行前:" + joinPoint.getSignature().getName() + " " + Arrays.asList(joinPoint.getArgs()));
}
@After("pointCut()")
public void logEnd() {
System.out.println("运行结束:结果");
}
@AfterReturning(value = "pointCut()", returning = "obj")
public void logRun(JoinPoint joinPoint,Object obj) {
System.out.println("运行时:" +joinPoint+ obj );
}
@AfterThrowing(value = "pointCut()", throwing = "e")
public void logException(Exception e) {
System.out.println("运行异常:"+e);
}
}
添加bean: MainConfigAOP
@EnableAspectJAutoProxy
@Configuration
public class MainConfigAOP {
@Bean
public MathCaculator caculator() {
return new MathCaculator();
}
@Bean
public LogAspects logAspects() {
return new LogAspects();
}
}
test:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext((MainConfigAOP.class));
MathCaculator mathCaculator = (MathCaculator) context.getBean("caculator");
int dev = mathCaculator.dev(1, 1);
System.out.println(dev);
原理:
围绕:@EnableAspectJAutoProxy
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
boolean proxyTargetClass() default false;
boolean exposeProxy() default false;
}
获取当前bean的所有增强器(通知方法)
获取当前的bean所有的增强器,找到那些方法时需要切入bean的方法。
获取到能在bean使用的增强器。
给增强器排序
保存当前的bean在advisedBeans中;
如果当前的bean需要增强,创建当前bean的代理对象;
获取到所有增强器(通知方法)
保存在proxyFactory中
创建代理对象:自动举例,是jdk动态代理(实现接口),还是cglib代理(可以强制)
JdkDynamicAopProxy(config)
objenesisCglibAopProxy(config)
如果是cglib,给容器返回当前组件的使用的cglib增强代理对象,执行目标方法时,代理对象就会执行通知方法的流程。
链式获取每一个拦截器,执行invoke方法,每个拦截器等待下一个拦截器执行返回。保证通知方法与目标方法的执行顺序