Spring 知识点整理 之 AOP

AOP


AOP概述


术语

  • AOP那些学术概念—通知、增强处理连接点(JoinPoint)切面(Aspect)
  • JoinPoint (拦截点)所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点.
  • PointCut (切入点)所谓切入点是指我们要对哪些Joinpoint进行拦截的定义.
  • Advice (通知)所谓通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
  • Introduction (引介)引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field.
  • Target (目标对象)代理的目标对象
  • Weaving (织入)是指把增强应用到目标对象来创建新的代理对象的过程,spring采用动态代理织入,而AspectJ采用编译期织入和类装在期织入
  • Proxy(代理)一个类被AOP织入增强后,就产生一个结果代理类
  • Aspect(切面)是切入点和通知(引介)的结合

AOP底层实现

  • 代理机制
    • JDK动态代理
      • 对实现了接口的类生成代理
    • CGLIB动态代理
      • 对没有实现接口的类产生代理.产生这个类的子类对象。

Spring的传统AOP的开发(了解即可)

  • 核心
    • ProxyFactoryBean 对象
      • target : 代理的目标对象
      • proxyInterfaces : 代理要实现的接口
      • 如果多个接口可以使用以下格式赋值

        <list>
        <value></value>
        ....
        </list>
      • proxyTargetClass : 是否对类代理而不是接口,设置为true时,使用CGLib代理
      • interceptorNames : 需要织入目标的Advice
      • singleton : 返回代理是否为单实例,默认为单例
      • optimize : 当设置为true时,强制使用CGLib
  • Advice 通知的类型
    • 前置通知 MethodBeforeAdvice
    • 后置通知 AfterReturningAdvice
    • 环绕通知 MethodInterceptor
    • 异常抛出通知 ThrowsAdvice
    • 引介通知 IntroductionInterceptor
  • 切面类型
    • Advisor 代表一般切面,Advice本身就是一个切面,对目标类所有方法进行拦截.(不带切入点切面:增强所有方法)
    • PointcutAdvisor 代表具有切点的切面,可以指定拦截目标类哪些方法.(带有切入点切面:增强某些方法)
    • IntroductionAdvisor代表引介切面,针对引介通知而使用切面(不要求掌握)
  • 不带切入点的例子
<!-- 配置目标类: -->
<bean id="productDao" class="com.itheima.spring.demo3.ProductDaoImpl"/>
<!-- 配置通知:(前置通知) -->
<bean id="beforeAdvice" class="com.itheima.spring.demo3.MyBeforeAdvice"></bean>
<!-- 配置生成代理 -->
<bean id="productDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!-- 配置目标类 -->
        <property name="target" ref="productDao"/>
        <!-- 配置类的实现的接口 -->
        <property name="proxyInterfaces" value="com.itheima.spring.demo3.ProductDao"/>
        <!-- 配置切面要拦截的名称 -->
        <property name="interceptorNames" value="beforeAdvice"/>
    </bean>
  • 带切入点的切面
<!-- 配置通知:(环绕通知) -->
    <bean id="myAroundAdvice" class="com.itheima.spring.demo4.MyAroundAdvice"/>
    <!-- 配置带有切入点的切面 -->
<bean id="myAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
    <!-- 表达式: 正则表达式 : .:任意字符  *:任意次数-->
    <property name="pattern" value=".*"/>
    <!-- 配置增强 -->
    <property name="advice" ref="myAroundAdvice"/>
</bean>
<!-- 配置生成代理 -->
    <bean id="customerDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!-- 配置目标 -->
        <property name="target" ref="customerDao"/>
        <!-- 配置代理目标类 -->
        <property name="proxyTargetClass" value="true"/>
        <!-- 配置拦截的名称 -->
        <property name="interceptorNames" value="myAdvisor"/>
    </bean>

Spring的传统AOP的开发:自动代理

  • 自动代理的方式

    • BeanNameAutoProxyCreator :根据Bean名称创建代理
    • DefaultAdvisorAutoProxyCreator :根据Advisor本身包含信息创建代理
    • AnnotationAwareAspectJAutoProxyCreator :基于Bean中的AspectJ 注解进行自动代理
  • 实现机制

    • BeanPostProcessor后处理器
    • 在类的生成过程中就产生了代理.
  • 基于BeanName的自动代理

<!-- 配置目标类: -->
    <bean id="productDao" class="com.itheima.spring.demo3.ProductDaoImpl"/>
    <bean id="customerDao" class="com.itheima.spring.demo4.CustomerDao"/>

    <!-- 配置通知:(前置通知) -->
    <bean id="beforeAdvice" class="com.itheima.spring.demo3.MyBeforeAdvice"/>
    <!-- 配置通知:(环绕通知) -->
    <bean id="myAroundAdvice" class="com.itheima.spring.demo4.MyAroundAdvice"/>
    <!-- 配置基于Bean名称的自动代理 (自动加载,id可以省略)-->
    <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
        <!-- 配置Bean名称 -->
        <property name="beanNames" value="*Dao"/>
        <!-- 配置拦截的名称 -->
        <property name="interceptorNames" value="beforeAdvice"/>
    </bean>
  • 基于切面的自动代理
    <!-- 配置目标类: -->
    <bean id="productDao" class="com.itheima.spring.demo3.ProductDaoImpl"/>
    <bean id="customerDao" class="com.itheima.spring.demo4.CustomerDao"/>

    <!-- 配置通知:(前置通知) -->
    <bean id="beforeAdvice" class="com.itheima.spring.demo3.MyBeforeAdvice"/>
    <!-- 配置通知:(环绕通知) -->
    <bean id="myAroundAdvice" class="com.itheima.spring.demo4.MyAroundAdvice"/>
    <!-- 配置切面 -->
    <bean id="myAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <!-- 表达式 -->
        <property name="pattern" value="com\.itheima\.spring\.demo4\.CustomerDao\.save"/>
        <!-- 配置增强 -->
        <property name="advice" ref="myAroundAdvice"/>
    </bean>
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>

Spring基于AspectJ注解方式的AOP的开发:(当前流行的开发方式)

  • 基于AspectJ(面向切面的框架)
  • 只需要配置一个Bean即可
  • AspectJ通知类注解(使用在方法上,将目标方法定义为一个通知)

    • @Before 前置 Joinpoint:获得切入点信息.
        @Before("execution(* com.itheima.spring.demo1.OrderDao.save(..))")
        public void before(JoinPoint joinPoint){
            System.out.println("前置通知================");
        }
    }
    
    • @AfterReturning 后置 在return之前 获取返回结果 Object result

          // 后置通知:
          @AfterReturning(value="execution(* com.itheima.spring.demo1.OrderDao.update(..))",returning="result")
          public void afterReturing(Object result){
              System.out.println("后置通知================"+result);
          }
      
    • @Around 环绕 ProceedingJoinPoint joinPoint 切入点信息

      // 环绕通知: 
      @Around("execution(* com.itheima.spring.demo1.OrderDao.delete(..))")
      public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
          System.out.println("环绕前通知===============");
          // 执行目标方法:
          Object obj = joinPoint.proceed();
          System.out.println("环绕后通知===============");
          return obj;
      }
      
    • @AfterThrowing 异常抛出 Throwable 获取异常信息

      // 异常抛出通知:
      @AfterThrowing(value="execution(* com.itheima.spring.demo1.OrderDao.find(..))",throwing="e")
      public void afterThrowing(Throwable e){
          System.out.println("异常抛出通知=============="+e.getMessage());
      }
      
    • @After final通知 相当于添加了Final块

      // 最终通知
      @After(value="execution(* com.itheima.spring.demo1.OrderDao.find(..))")
      public void after(){
          System.out.println("最终通知================");
      }
      
    • @DeclareParents 引介通知,相当于IntroductionInterceptor

  • 切入点表达式
    • execution
    • 可以理解为 方法的签名 抽象
    • 语法 [访问修饰符] 方法返回值 方法名(参数)
    • 例子
      • execution(public * com.itheim.spring.demo1.OrderDao.save(..))
      • execution(* *.*(..))
      • execution(public * com.itheim.spring.demo1.*.*(..))
      • execution(public * com.itheim.spring.demo1..*.*(..))
      • execution(public * com.itheim.spring.demo1.OrderDao+.*(..))
  • 切面
@Aspect
public class MyAspectAnno {

    // 定义通知:
    @Before("execution(* com.itheima.spring.demo1.OrderDao.save(..))")
    public void before(){
        System.out.println("前置通知================");
    }
}
  • 开启AspectJ 注解
@Aspect
public class MyAspectAnno {

    // 定义通知:
    @Before("execution(* com.itheima.spring.demo1.OrderDao.save(..))")
    public void before(){
        System.out.println("前置通知================");
    }
}
  • 配置切面类
<!-- 配置切面类 -->
    <bean class="com.itheima.spring.demo1.MyAspectAnno"/>

基于AspectJ(xml)配置方式的AOP

  • 引入约束
<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">
<!-- 配置目标类 -->
    <bean id="customerDao" class="com.itheima.spring.demo2.CustomerDaoImpl"/>
public class MyAspectXml {

    public void before(){
        System.out.println("前置通知==============");
    }
}
    <!-- 配置切面类 -->
    <bean id="myAspectXml" class="com.itheima.spring.demo2.MyAspectXml"/>
    <!-- 配置AOP -->
    <aop:config>
        <aop:pointcut expression="execution(* com.itheima.spring.demo2.CustomerDao+.save(..))" id="pointcut1"/>
        <aop:aspect ref="myAspectXml">
            <aop:before method="before" pointcut-ref="pointcut1"/>
        </aop:aspect>
    </aop:config>
  • 其他类型的通知配置方式
    <!-- 配置AOP -->
    <aop:config>
        <aop:pointcut expression="execution(* com.itheima.spring.demo2.CustomerDao+.save(..))" id="pointcut1"/>
        <aop:pointcut expression="execution(* com.itheima.spring.demo2.CustomerDao+.update(..))" id="pointcut2"/>
        <aop:pointcut expression="execution(* com.itheima.spring.demo2.CustomerDao+.delete(..))" id="pointcut3"/>
        <aop:pointcut expression="execution(* com.itheima.spring.demo2.CustomerDao+.find(..))" id="pointcut4"/>
        <aop:aspect ref="myAspectXml">
            <aop:before method="before" pointcut-ref="pointcut1"/>
            <aop:after-returning method="afterReturing" pointcut-ref="pointcut2" returning="result"/>
            <aop:around method="around" pointcut-ref="pointcut3"/>
            <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut4" throwing="e"/>
            <aop:after method="after" pointcut-ref="pointcut4"/>
        </aop:aspect>
    </aop:config>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值