AOP(Aspect Oriented Programming,面向切面编程):
在程序原有的纵向执行流程中,针对某一个点或者一些方法(每一个方法都是连接点的意思)添加通知,形成横切面;
- AOP是继OOP(Object Oriented Programming,面向对象编程)之后的又一影响巨大的技术;
- AOP是一种设计思想,实际上是对OOP的升级,是对OOP的补充,而不是替代!经常和OOP一起使用;
- OOP的核心单位是类,AOP则是切面;
- 典型的例子包括日志、验证、事务管理等;
简单的来说它就是把我们程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在不修改源码的基础上,对我们已有的方法进行增强
优点:
减少重复代码,提高开发效率,方便维护
术语:
关注点:一个关注点可以是一个特定的问题
连接点:Joinpoint 连接点是类中的方法
切入点:Pointcut 切入点是被抽取了共性功能的方法(指引通知切入连接点的一个地址)
通知(方法):Advice 被抽取的共性功能的代码逻辑(方法)
引入:Introduction 通知只能抽取逻辑代码,变量是拿不出来的,把变量引入到切入点方法中
目标对象:Target Object 有切入点方法的对象(就像明星)
代理对象:AopProxy Spring 代理目标对象就叫aop代理(就像经纪人)
织入:Weaving 代理对象吧通知织入到目标对象的切入点方法中,是一个动作,一个过程
切面:Aspect 通知和切入点之间的关系
@Transactional实现事务管理
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven transaction-manager="txManager"/> //开启事务注解
AOP的原理:动态代理(通知方法就像代理对象为目标对象额外实现的方法)
Aop两种代理模式:
jdk的动态代理 和 CGLIB实现机制
jdk的动态代理:前提是,被代理的对象必须要有实现接口!(基于接口)
使用的JDK的java.lang.reflect(反射).Proxy(代理)类的newProxyInstance方法实现动态代理,对目标对象的方法进行功能追加;
newProxyInstance的三个参数
- loader:类加载器,获得目标对象的类加载器(写法://得到字节码对象//对象.getclass().getclassloader(),写法固定)
- interfaces:得到目标对象的所有实现接口的 字节码对象(每个类被加载之后,系统就会为该类生成一个对应的字节码对象,通过该字节码对象就可以访问到JVM中的对应的类)的 数组(多个对象)(写法:对象.getclass().getInterfaces(),写法固定)
- 需要一个实现了InvocationHandler接口的对象,需要自己手动完成,增强在这完成
newProxyInstance返回一个代理对象
五大通知实现的接口:
前置通知(Before advice)MethodBeforeAdvice: 在某连接点之前执行的通知
后置通知(After returning advice) AfterReturningAdvice: 在某连接点正常完成后执行的通知
异常通知(After throwing advice) ThrowsAdvice: 在方法抛出异常退出时执行的通知
最终通知(After finally advice): 当某连接点退出的时候执行的通知(不管是否有异常)
环绕通知(Around advice) MethodInterceptor: 包围一个连接点的通知,这是最强大的一种通知类型
CGLIB实现机制:
- 配置包扫描器,把目标类和切面类纳入到spring容器中管理
<context:component-scan base-package="com.cj.study.spring.aop.annotation" />
- 启动aop的注解解析器
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
- 切面类
@Aspect
- 切入点和切入点表达式
@Pointcut("execution(* 完全限定名.*.*(..))")
public void aaa(){}
- 切面里的通知方法
@Before("aaa()")
XML文件方式实现AOP
<!--目标实现类-->
<bean id="calculateImpl" class="com.xxx.aspectj.xml.CalculateImpl"></bean>
<!--切面实现类-->
<bean id="aspects" class="com.xxx.aspectj.xml.MyAspect" />
<!-- 配置切面 -->
<aop:config>
<aop:pointcut expression="" id=""/>
<aop:aspect id="aspect" ref="aspects">
<aop:pointcut expression="execution(* com.xxx.aspectj.xml.CalculateImpl.add(..)) " id="pointcut"/>
<aop:around method="five" pointcut-ref="pointcut" />
<aop:before method="one" pointcut-ref="pointcut" />
<aop:after-returning method="two" pointcut-ref="pointcut" returning="re"/>
<!--
<aop:after-throwing method="three" pointcut-ref="pointcut" throwing="ex"/>
<aop:after method="four" pointcut-ref="pointcut" /> -->
</aop:aspect>
</aop:config>
MyAspect类
public class MyAspect {
//在Java文件中选择类名,然后Copy Qualified Name;
//怎样访问当前连接点的细节
public void one(JoinPoint jp){
String name = jp.getSignature().getName();
Object[] args = jp.getArgs();
System.out.println(name+"前置通知执行"+",参数列表是"+Arrays.asList(args));
}
public void two(JoinPoint jp,Object re){
String name = jp.getSignature().getName();
System.out.println("后置通知执行的返回结果是"+re);
}
public void three(JoinPoint jp,Exception ex){
System.out.println("异常通知执行的异常信息为"+ex);
}
public void four(){
System.out.println("最终通知执行");
}
public Object five(ProceedingJoinPoint pjp){
Object[] args = pjp.getArgs();
String name = pjp.getSignature().getName();
Object result=null;
try {
System.out.println(name+"环绕准备就绪,参数列表是:"+Arrays.asList(args));
System.out.println("前置通知执行");
result = pjp.proceed();
System.out.println(name+"的返回结果是:"+result);
System.out.println("后置通知执行");
} catch (Throwable e) {
e.printStackTrace();
System.out.println("异常通知执行");
}finally{
System.out.println(name+"环绕执行完毕,参数列表是:"+Arrays.asList(args));
System.out.println("最终通知执行");
}
return result;
}
}
注解方式实现AOP
<!-- spring不自动寻找带注解的类,需要告诉哪些包中存在带注解的类 -->
<context:component-scan base-package="com.xxx.aspectj.annotation"></context:component-scan>
<aop:aspectj-autoproxy ></aop:aspectj-autoproxy>
MyAspect类
@Component
@Aspect
//通过@Order(*)标注指定切面优先级,*整数数值越小,优先级越高
public class MyAspect {
//定义切点
@Pointcut("execution(* com.xxx.aspectj.annotation.CalculateImpl.*(..))")
public void pointcut(){
}
@Before("pointcut()")
public void c(JoinPoint jp){
String name = jp.getSignature().getName();
Object[] args = jp.getArgs();
System.out.println(name+"前置通知1执行"+",参数列表是"+Arrays.asList(args));
}
@Before("execution(* com.xxx.aspectj.annotation.CalculateImpl.*(..))")
public void b(JoinPoint jp){
String name = jp.getSignature().getName();
Object[] args = jp.getArgs();
System.out.println(name+"前置通知2执行"+",参数列表是"+Arrays.asList(args));
}
/*@After("execution(public int com.xxx.aspectj.annotation.CalculateImpl.*(..))")
public void four(){
System.out.println("最终通知执行");
}*/
@AfterReturning(pointcut="execution(public int com.xxx.aspectj.annotation.CalculateImpl.*(..))",returning="re")
public void two(JoinPoint jp,Object re){
String name = jp.getSignature().getName();
System.out.println(name+"后置通知执行的返回结果是"+re);
}
@AfterThrowing(pointcut="execution(public int com.xxx.aspectj.annotation.CalculateImpl.*(..))",throwing="ex")
public void three(JoinPoint jp,Exception ex){
System.out.println("异常通知执行的异常信息为"+ex);
}
/*@Around("execution(public int com.xxx.aspectj.annotation.CalculateImpl.*(..))")
public Object five(ProceedingJoinPoint pjp){
Object[] args = pjp.getArgs();
String name = pjp.getSignature().getName();
System.out.println(name+"环绕前置就绪,参数列表是:"+Arrays.asList(args));
Object result=null;
try {
result = pjp.proceed();
System.out.println(name+"的环绕返回结果是:"+result);
} catch (Throwable e) {
e.printStackTrace();
System.out.println("异常通知执行");
}finally{
System.out.println(name+"环绕后置执行完毕,参数列表是:"+Arrays.asList(args));
}
return result;
}*/
}