欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

AOP

程序员文章站 2022-05-07 12:08:48
...

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>
相关标签: 个人总结