Spring詳解AOP

一、什么是代理模式?

代理模式就是給某個(gè)對(duì)象提供一個(gè)代理對(duì)象,由代理對(duì)象控制對(duì)原對(duì)象的控制,訪問(wèn)原對(duì)象的方法。

二、Spring AOP的理解

AOP:Aspect Oriented Programing,面向切面編程,是Spring的兩大核心思想之一,是一種編程的思想,也是對(duì)面向?qū)ο笏枷氩蛔愕难a(bǔ)充和擴(kuò)展,因?yàn)槿f(wàn)物皆對(duì)象的這種思想,將現(xiàn)實(shí)世界所有的事物映射為Java世界中的一個(gè)個(gè)對(duì)象,可以看作是縱向在設(shè)計(jì)一個(gè)個(gè)類,而當(dāng)這些類都需要一些公共的實(shí)現(xiàn)代碼時(shí),比如說(shuō)日志、權(quán)限,抽象類或接口的設(shè)計(jì)已經(jīng)不太能滿足這個(gè)需求了,因?yàn)镴ava是單繼承,而且使用抽象類或接口會(huì)使整個(gè)結(jié)構(gòu)變得更復(fù)雜,所以出現(xiàn)了AOP,面向切面編程,橫向去將這些類的公共代碼(系統(tǒng)代碼)提煉出來(lái),使用代理和反射的機(jī)制交由代理對(duì)象去實(shí)現(xiàn),保證了原業(yè)務(wù)代碼的純粹性,也提高了系統(tǒng)的可維護(hù)性、可擴(kuò)展性。

三、Spring AOP中常見(jiàn)通知的類型

  • Before:前置通知
  • After:后置通知
  • AfterReturning:有返回值的后置通知,當(dāng)方法正常執(zhí)行后,執(zhí)行該通知;如果方法執(zhí)行時(shí)出現(xiàn)異常,則不執(zhí)行該通知
  • AfterThrowing:異常通知,方法出現(xiàn)異常時(shí),執(zhí)行該通知
  • Around:環(huán)繞通知

四、Spring AOP的實(shí)現(xiàn)

  • 使用Spring AOP

    ?? 第一步,定義通知類,繼承相關(guān)接口,例如:MethodBeforeAdvice,AfterAdvice,AfterReturningAdvice,ThrowsAdvice,MethodInterceptor,重寫或自定義相關(guān)方法。

    ?? 第二步:配置XML:

    <!-- 大前提 -->
    <!-- 通知類 -->
    <bean id="logAdviceBean" class="com.apesource.advice.LogAdvice"/>
    
    <!-- 目標(biāo)類 -->
    <bean id="homeControllerBean" class="com.apesource.web.controller.HomeController"/>
    

    形式1:手動(dòng)代理:使用ProxyFactoryBean手動(dòng)為目標(biāo)類創(chuàng)建代理類。

    缺點(diǎn):每個(gè)目標(biāo)類都要寫一個(gè)Bean,很繁瑣,在獲取Bean的時(shí)候需要獲取代理類Bean。

    <!-- 手動(dòng)代理 -->
    <bean id="homeControllerProxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!-- 注入目標(biāo)類 -->
        <property name="target" ref="homeControllerBean"/>
        
      <!-- 注入通知類 -->
        <property name="interceptorNames" value="logAdviceBean"/>
    </bean>
    

    形式2:自動(dòng)代理:使用BeanNameAutoProxyCreator自動(dòng)為所匹配的Bean創(chuàng)建代理類。

    優(yōu)點(diǎn):簡(jiǎn)潔,在獲取Bean的時(shí)候直接獲取目標(biāo)類的Bean,Spring會(huì)自動(dòng)地創(chuàng)建代理類對(duì)象并返回。

    <!-- 自動(dòng)代理(匿名Bean) -->
    <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
        <!-- 注入符合表達(dá)式的目標(biāo)類 -->
        <property name="beanNames" value="*ControllerBean"/>
        
        <!-- 注入通知類 -->
        <property name="interceptorNames" value="logAdviceBean"/>
    </bean>
    

    ?? 注入的通知類類型:

    1. 普通的通知類:類級(jí)別的通知,會(huì)使注入該通知的類的所有方法都執(zhí)行該通知,顆粒度太大。

      <bean id="logAdviceBean" class="com.apesource.advice.LogAdvice"/>
      
    2. 高級(jí)的通知類:Advisor切面,方法級(jí)別的通知,可以具體到某一個(gè)符合規(guī)則的方法,顆粒度小。

      實(shí)現(xiàn)方式1:

      <!-- 實(shí)現(xiàn)方式1 -->
      <!-- 1.1切入點(diǎn)(Pointcut):通過(guò)正則表達(dá)式描述指定切入點(diǎn) -->
      <bean id="createMethodPointcutBean" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
          <!-- 描述符合表達(dá)式的方法 -->
          <property name="pattern" value=".*create.*"/>
      </bean>
      
      <!-- 和1.1類似的實(shí)現(xiàn) -->
      <!-- <bean id="controllerLogMethodPointcut" class="org.springframework.aop.support.NameMatchMethodPointcut">-->
          <!-- 描述符合表達(dá)式的方法 -->
      <!--<property name="mappedName" value="*create*"/>-->
      <!--</bean>-->
      
      <!-- 1.2 -->
      <!-- Advisor = Advice(通知) + Pointcut(切入點(diǎn)) -->
      <bean id="performanceAdvisorBean" class="org.springframework.aop.support.DefaultPointcutAdvisor">
          <!--注入切入點(diǎn)-->
          <property name="pointcut" ref="createMethodPointcutBean"/>
      
          <!--注入通知-->
          <property name="advice" ref="performanceBean"/>
      </bean>
      

      實(shí)現(xiàn)方式2:一步到位

      <!-- 實(shí)現(xiàn)方式2 -->
      <bean class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
          <!-- 通過(guò)正則表達(dá)式描述切入點(diǎn),并注入 -->
          <property name="mappedName" value="*create*"/>
          
          <!--注入通知-->
          <property name="advice" ref="logAdviceBean"/>
      </bean>
      
  • 使用AspectJ框架:

    方式1:使用POJO+XML的形式

    ?? 第一步,自定義通知類,自定義通知方法。

    public class LogAdvice{
        private Logger logger = Logger.getLogger(LogAdvice.class.getName());
    
        public void beginLogging(JoinPoint jp){
            logger.info("日志開(kāi)始");
        }
    
        public void endLogging(JoinPoint jp){
            logger.info("日志結(jié)束");
        }
    }
    

    ?? 第二步,進(jìn)行XML配置

    1.1 首先加入aop的命名空間配置

    <beans xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    

    1.2 定義通知類

    <bean id="logAdvice" class="com.apesource.advice.LogAdvice"/>
    

    1.3 進(jìn)行aop配置

    <!-- 頂層aop配置 -->
    <aop:config>
        <!-- 定義切面,注入通知類 -->
        <aop:aspect ref="logAdvice">
            <!--定義切點(diǎn)-->
            <aop:pointcut id="adminControllerPointcut" expression="execution(* com.apesource.web.controller.*.handler*(..))"/>
    
            <!-- 配置前置同通知 -->
            <aop:before method="beginLogging" pointcut-ref="adminControllerPointcut"/>
    
            <!-- 配置后置通知 -->
            <aop:after method="endLogging" pointcut-ref="adminControllerPointcut"/>
        </aop:aspect>
    </aop:config>
    

    方式2:使用純注解

    ?? 第一步,自定義通知類,使用相關(guān)注解,并將通知類交給容器管理

    @Aspect // 聲明為一個(gè)切面類
    @Component
    public class LogAdvice2 {
        private Logger logger = Logger.getLogger(LogAdvice.class.getName());
    
        @Before("execution(* com.apesource.web.controller.*.handler*(..))")
        public void beginLogging(JoinPoint jp){
            logger.info("日志開(kāi)始");
    
        }
    
        @After("execution(* com.apesource.web.controller.*.handler*(..))")
        public void endLogging(JoinPoint jp){
            logger.info("日志結(jié)束");
        }
    }
    

    ?? 第二步,開(kāi)啟AspectJ自動(dòng)代理

    @ComponentScan // 默認(rèn)掃描同包之下
    @EnableAspectJAutoProxy
    public class SystemConfig {
    
    }
    

相關(guān)注解:

@Aspect

@EnableAspectJAutoProxy

@Pointcut

@Before

@After

@AfterReturning

@AfterThrowing

@Around

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容