AOP概念
Aspect Oriented Programming
被称为面向方面编程。OOP是面向对象编程,AOP是以OOP为基础。OOP主要关注的是对象,如何抽象和封装对象。AOP主要关注的是方面,方面组件可以以低耦合方式切入到其他某一批目标对象方法。AOP主要解决共通处理和目标组件之间解耦的问题。
AOP相关概念
方面(Aspect)
指的是封装了共通处理的功能组件,该组件可以作用到某一批目标组件的方法上
public class LoggerBean {
//采用环绕通知
public Object logger(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("--记录用户操作--");
//前置逻辑
//获取要执行的目标组件类名
String clazz = proceedingJoinPoint.getTarget().getClass().getName();
//获取要执行的方法名
String mth = proceedingJoinPoint.getSignature().getName();
/*System.out.println(clazz);
System.out.println(mth);*/
//根据类名和方法名,给用户提示具体操作信息
String key = clazz+"."+mth;
//解析obj.properties,根据key获取信息
Object obj = proceedingJoinPoint.proceed();//执行目标方法
//后置逻辑
return obj;
}
}
切入点(Pointcut)
指的是用于指定目标组件的表达式。方面组件和哪一批目标组件方法有关系。
<!-- 定义切入点,用于指定目标组件和方法 -->
<aop:pointcut id = "actionPointcut" expression = "within(com.shagou.CostAction..*)"/>
连接点(JoinPoint)
切入点是连接点的集合。方面组件和具体哪一个目标的方法有关系。
通知(Advice)
用于指定方面组件和目标组件方法之间的作用时机。例如,先执行方面组件,再执行目标方法;或者,先执行目标方法,在执行方面组件。
<!-- 定义通知,用于指定先执行方面组件的logger方法,在执行切入点指定的目标方法,环绕通知 -->
<aop:around pointcut-ref ="actionPointcut" method = "logger"/>
目标(Target)
利用切入点指定的组件和方法。
动态代理(AutoProxy)
Spring采用了动态代理技术实现了AOP机制。当使用了AOP之后,从容器getBean获取的目标组件,返回的是一个动态生成的代理类。然后通过代理类执行业务方法,代理类负责去调用方面组件功能和原目标组件功能。
Spring提供了下面两张动态代理技术实现:
采用CGLIB技术实现(目标组件没有接口采用此方法)
public class 代理类 extends 原目标类型{}
CostAction CostAction = new 代理类();
采用JDK Proxy API实现(目标组件有接口采用此方法)
public class 代理类 implements 原目标接口{}
CostDAO costDao = new 代理类();
<!-- AOP记录操作日志案例 -->
<bean id = "loggerBean" class = "com.shagou.aop.LoggerBean"></bean>
<aop:config>
<!-- 定义切入点,用于指定目标组件和方法 -->
<aop:pointcut id = "actionPointcut" expression = "within(com.shagou.CostAction..*)"/>
<!-- 定义方面,引入上面的bean,将loggerBean对象指定为方面组件 -->
<aop:aspect id = "loggerAspect" ref = "loggerBean">
<!-- 定义通知,用于指定先执行方面组件的logger方法,在执行切入点指定的目标方法,环绕通知 -->
<aop:around pointcut-ref ="actionPointcut" method = "logger"/>
</aop:aspect>
</aop:config>
- 通知类型
通知类型方面组件和目标组件作用的关系,主要有以下几种类型通知 - 前置通知:
方面组件在目标方法之前执行 - 后置通知:
方面组件在目标方法之后执行,目标方法没有抛出异常才执行方面组件。 - 最终通知:
方面组件在目标方法之后执行,目标方法有没有异常都会执行方面组件。 - 异常通知:
方面组件在目标方法抛出异常后执行。 - 环绕通知:
方面组件在目标方法之前和之后执行。环绕通知等价于前置+后置 - 切入点:
切入点用于指定目标组件和方法。Spring提供了多种表达式的写法 - 方法限定表达式
execution(修饰符? 返回类型 方法名(参数列表)throws 异常?)
a.execution(public * * (..)) 匹配容器中所有public修饰的方法
b.execution( * set*(..)) 匹配容器中方法以set开头的
c.execution( * com.shagou.CostService.*(..)) 匹配CostService中所有方法
d.execution(* com.shagou.dao.*.*(..)) 匹配dao包下所有类所有方法
e.execution(* com.shagou.dao..*.*(..)) 匹配dao包及其子包中的所有类的所有方法
- 类型限定表达式
within(类型)
a.within(com.shagou.Service.*) 匹配Service包下的所有类所有方法
b.withim(com.shagou.Service..*) 匹配Service包及其子包中所有类所有方法
c.within(com.shagou.dao.CostDao) 匹配CostDao所有方法
- Bean名称限定
bean(id值)
a.bean(costDao) 匹配id = costDao的bean对象
b.bean(*Dao) 匹配所有id值以Dao结尾的bean对象
- args参数限定表达式
a.args(java.io.Serializable) 匹配方法只有一个参数并且类型符合Serializable的方法 序列化
public void f1(String s)
public void f1(int s)
public void f1(int s,int j) 不符合
session.load(class,Serializable);