AOP
AOP
实现切面有两种方式:通知和顾问
注册目标对象
<!-- 注册目标对象-->
<bean id="someService" class="priv.ze.spring.AOP.SomeServiceImpl"/>
<bean id="someService1" class="priv.ze.spring.AOP.SomeServiceImpl"/>
通知
通知分类:
(1)前置通知
实现MethodBeforeAdvice接口
<!-- 注册切面:通知-->
<bean id="MyAdvice" class="priv.ze.spring.AOP.MyMethodBeforeAdvice"/>
(2)后置通知
实现AfterReturningAdvice接口
(3)环绕通知
实现MethodInterceptor接口
(4)异常通知
实现ThrowsAdvice接口
顾问
顾问分类:
(1)名称匹配方法切入点顾问
匹配的是简单的方法名
<!-- 注册切面:顾问(名称匹配方法切入点顾问)-->
<bean id="MyAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<!-- 指定通知-->
<property name="advice" ref="MyAdvice"/>
<!-- 指定切入点-->
<property name="mappedNames" value="doFirst,doSecond"/> value中可以使用通配符
</bean>
(2)正则表达式方法切入点顾问
正则表达式匹配的对象是全限定方法名
<!-- 注册切面:顾问(正则表达式方法切入点顾问)-->
<bean id="MyAdvisor1" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<!-- 指定通知-->
<property name="advice" ref="MyAdvice"/>
<!-- 指定切入点-->
<property name="pattern" value=".*doFirst|.*doSecond"/>
</bean>
使用BeanFactoryBean存在的问题:
(1)若存在多个目标对象,就需要使用多次BeanFactoryBean来创建多个代理对象,这会使配置文件变得臃肿,不便于管理
(2)用户真正想调用的是目标对象,而真正可以调用的确实代理对象,这不符合正常的逻辑
生成代理对象
<!-- 生成代理对象-->
<bean id="serviceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 指定目标对象-->
<property name="target" ref="someService"/>
<!-- 另一种方式-->
<property name="targetName" value="someService"/>
<!-- 指定切面(实现切面有两种方式:通知和顾问)-->
<property name="interceptorNames" value="MyAdvisor1"/>
</bean>
无接口自动使用CGlib来进行代理对象的生成
有接口的情况下默认使用JDK代理,若想使用CGlib则需要在注册代理对象中加入属性
<property name="proxyTargetClass" value="true"/>或将proxyTargetClass改为optimize
自动代理生成器
(1)默认advisor自动代理生成器
DefaultAdvisorAutoProxyCreator,底层使用Bean后处理器完成对指定类的加强。
存在问题:
(1)不能选择目标对象
(2)不能选择切面类型,切面只能是advisor
(3)不能选择advisor,所以advisor均被作为切面织入到目标方法
(2)Bean名称自动代理生成器
<!-- 注册自动代理生成器-->
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<!-- 指定具体哪个目标对象-->
<property name="beanNames" value="someService1"/>
<!-- 指定某个顾问或通知-->
<property name="interceptorNames" value="MyAdvisor1"/>
</bean>
AspectJ
配置文件:需要加入aop约束
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation=
"http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
AspectJ通知类型
(1) 前置通知
(2) 后置通知
(3)环绕通知
(4)异常通知
(5)最终通知(这种是AspectJ特有的)
AspectJ的切入点表达式
符号 | 意义 |
---|---|
* | 0至多个任意字符 |
… | 用在方法参数中,表示任意多个参数 用在包名后,表示当前包及其子包路径 |
+ | 用在类名后,表示当前类及其子类 用在接口后,表示当前接口及其实现类 |
基于注解的Aop的实现
通知
需要在配置文件中注册切面与目标对象,并且还要注册AspectJ的自动代理
五种异常通知的实现:
(1)前置通知@Before
(2) 后置通知@AfterReturning
(3)环绕通知@Around
(4)异常通知@AfterThrowing
(5)最终通知@After:最终通知类似于finally,不论是否出现异常都会执行
配置文件(针对每一个类的):
<!-- 注册切面-->
<bean id="MyAspect" class="priv.ze.spring.AspectJ.MyAspect"/>
<!-- 注册目标对象-->
<bean id="someService" class="priv.ze.spring.AspectJ.SomeServiceImpl"/>
<!-- 注册AspectJ 自动代理-->
<!-- 使用注解时使用该标签-->
<aop:aspectj-autoproxy/>
代码:
// 此处调用AspectJ切入点表达式
// 前置通知
@Before("execution(* *..SomeService.doFirst(..))")
//参数中可加入JoinPoint类参数
public void before() {
System.out.println("执行了前置通知方法");
}
@Before("execution(* *..SomeService.doFirst(..))")
public void before1(JoinPoint jp) {
// 加入此参数可以打印出全限定方法名
System.out.println("执行了前置通知方法");
System.out.println(jp);
}
// 后置通知
@AfterReturning("execution(* *..SomeService.doSecond(..))")
public void afterReturning() {
System.out.println("执行了后置通知方法");
}
// 环绕通知
@Around("execution(* *..SomeService.doThird(..))")
// 此处必须要加参数
public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("执行了环绕通知,在目标方法之前");
Object result = pjp.proceed();
System.out.println("执行了环绕通知,在目标方法之后");
return result;
}
// 异常通知
@AfterThrowing("doThirdPoint()")
public void myAfterThrowing() {
System.out.println("执行了异常通知");
}
@AfterThrowing(value = "doThirdPoint()", throwing = "e")
// 加入参数可以打印异常信息
public void myAfterThrowing1(Exception e) {
System.out.println("执行了异常通知,异常为" + e.getMessage());
}
// 最终通知
@After("doThirdPoint()")
public void myAfter() {
System.out.println("执行了最终通知");
}
定义切入点
//定义了一个切入点,叫doThirdPoint()
@Pointcut("execution(* *..SomeService.doThird(..))")
public void doThirdPoint(){}
基于XML的实现
// 前置通知
public void myBefore() {
System.out.println("执行了前置通知方法");
}
public void myBefore1(JoinPoint jp) {
System.out.println("执行了前置通知方法");
System.out.println(jp);
}
// 后置通知
public void myAfterReturning() {
System.out.println("执行了后置通知方法");
}
public void myAfterReturning1(Object result) {
System.out.println("执行了后置通知方法"+result);
}
// 环绕通知
// 此处必须要加参数
public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("执行了环绕通知,在目标方法之前");
Object result = pjp.proceed();
System.out.println("执行了环绕通知,在目标方法之后");
return result;
}
// 异常通知
public void myAfterThrowing() {
System.out.println("执行了异常通知");
}
// 加入参数可以打印异常信息
public void myAfterThrowing1(Exception e) {
System.out.println("执行了异常通知,异常为" + e.getMessage());
}
// 最终通知
public void myAfter() {
System.out.println("执行了最终通知");
}
<!--基于XML的实现-->
<!--AOP配置-->
<aop:config>
<!--定义切入点-->
<aop:pointcut id="doFirstPointcut" expression="execution(* *..SomeService.doFirst(..))"/>
<aop:pointcut id="doSecondPointcut" expression="execution(* *..SomeService.doSecond(..))"/> <aop:pointcut id="doThirdPointcut" expression="execution(* *..SomeService.doThird(..))"/> <aop:aspect ref="MyAspect">
<!--前置通知-->
<aop:before method="myBefore" pointcut-ref="doFirstPointcut"/>
<aop:before method="myBefore1" pointcut-ref="doFirstPointcut"/>
<!--后置通知-->
<aop:after-returning method="myAfterReturning" pointcut-ref="doSecondPointcut"/> <aop:after-returning method="myAfterReturning1" pointcut-ref="doSecondPointcut" returning="result"/>
<!--环绕通知-->
<aop:around method="myAround" pointcut-ref="doFirstPointcut"/>
<!--异常通知-->
<aop:after-throwing method="myAfterThrowing" pointcut-ref="doThirdPointcut"/>
<aop:after-throwing method="myAfterThrowing1" pointcut-ref="doThirdPointcut" throwing="e"/> <!--最终通知-->
<aop:after method="myAfter" pointcut-ref="doThirdPointcut"/>
</aop:aspect>
</aop:config>