一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

SpringBoot系列之AOP實(shí)現(xiàn)的兩種方式

 liang1234_ 2020-11-13

AOP常用的實(shí)現(xiàn)方式有兩種,一種是采用聲明的方式來實(shí)現(xiàn)(基于XML),一種是采用注解的方式來實(shí)現(xiàn)(基于AspectJ)。

首先復(fù)習(xí)下AOP中一些比較重要的概念:

Joinpoint(連接點(diǎn)):程序執(zhí)行時(shí)的某個(gè)特定的點(diǎn),在Spring中就是某一個(gè)方法的執(zhí)行 。
Pointcut(切點(diǎn)):說的通俗點(diǎn),spring中AOP的切點(diǎn)就是指一些方法的集合,而這些方法是需要被增強(qiáng)、被代理的。一般都是按照一定的約定規(guī)則來表示的,如正則表達(dá)式等。切點(diǎn)是由一類連接點(diǎn)組成。 
Advice(通知):還是說的通俗點(diǎn),就是在指定切點(diǎn)上要干些什么。 
Advisor(通知器):其實(shí)就是切點(diǎn)和通知的結(jié)合 。

一、基于XML配置的Spring AOP

采用聲明的方式實(shí)現(xiàn)(在XML文件中配置),大致步驟為:配置文件中配置pointcut, 在java中用編寫實(shí)際的aspect 類, 針對(duì)對(duì)切入點(diǎn)進(jìn)行相關(guān)的業(yè)務(wù)處理。

業(yè)務(wù)接口:

package com.springboottime.time.service;

public interface AdviceService {
    /*查找用戶*/
    public String findUser();

    /*添加用戶*/
    public void addUser();
}

業(yè)務(wù)實(shí)現(xiàn):

package com.springboottime.time.service.serviceImpl;

import com.springboottime.time.service.AdviceService;
import lombok.Data;

@Data
public class AdviceServiceImpl implements AdviceService {

    private String name;

    @Override
    public String findUser() {
        System.out.println("***************執(zhí)行業(yè)務(wù)方法findUser,查找的用戶名字為:"+name+"****************");
        return name;
    }

    @Override
    public void addUser() {
        System.out.println("***************執(zhí)行業(yè)務(wù)方法addUser****************");
        throw new RuntimeException();
    }
}

切面類:

package com.springboottime.time.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

public class AopAspect {

    /**
     * 前置通知:目標(biāo)方法調(diào)用之前執(zhí)行的代碼
     * @param jp
     */
    public void doBefore(JoinPoint jp){
        System.out.println("===========執(zhí)行前置通知============");
    }

    /**
     * 后置返回通知:目標(biāo)方法正常結(jié)束后執(zhí)行的代碼
     * 返回通知是可以訪問到目標(biāo)方法的返回值的
     * @param jp
     * @param result
     */
    public void doAfterReturning(JoinPoint jp,String result){
        System.out.println("===========執(zhí)行后置通知============");
        System.out.println("返回值result==================="+result);
    }

    /**
     * 最終通知:目標(biāo)方法調(diào)用之后執(zhí)行的代碼(無論目標(biāo)方法是否出現(xiàn)異常均執(zhí)行)
     * 因?yàn)榉椒赡軙?huì)出現(xiàn)異常,所以不能返回方法的返回值
     * @param jp
     */
    public void doAfter(JoinPoint jp){
        System.out.println("===========執(zhí)行最終通知============");
    }

    /**
     *
     * 異常通知:目標(biāo)方法拋出異常時(shí)執(zhí)行的代碼
     * 在目標(biāo)方法執(zhí)行的時(shí)候,如果拋出異常,立即進(jìn)入此方法
     * 可以訪問到異常對(duì)象
     * @param jp
     * @param ex
     */
    public void doAfterThrowing(JoinPoint jp,Exception ex){
        System.out.println("===========執(zhí)行異常通知============");
    }

    /**
     * 環(huán)繞通知:目標(biāo)方法調(diào)用前后執(zhí)行的代碼,可以在方法調(diào)用前后完成自定義的行為。
     * 包圍一個(gè)連接點(diǎn)(join point)的通知。它會(huì)在切入點(diǎn)方法執(zhí)行前執(zhí)行同時(shí)方法結(jié)束也會(huì)執(zhí)行對(duì)應(yīng)的部分。
     * 主要是調(diào)用proceed()方法來執(zhí)行切入點(diǎn)方法,來作為環(huán)繞通知前后方法的分水嶺。
     *
     * 環(huán)繞通知類似于動(dòng)態(tài)代理的全過程:ProceedingJoinPoint類型的參數(shù)可以決定是否執(zhí)行目標(biāo)方法。
     * 而且環(huán)繞通知必須有返回值,返回值即為目標(biāo)方法的返回值
     * @param pjp
     * @return
     * @throws Throwable
     */
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable{
        System.out.println("======執(zhí)行環(huán)繞通知開始=========");
        // 調(diào)用方法的參數(shù)
        Object[] args = pjp.getArgs();
        // 調(diào)用的方法名
        String method = pjp.getSignature().getName();
        // 獲取目標(biāo)對(duì)象
        Object target = pjp.getTarget();
        // 執(zhí)行完方法的返回值
        // 調(diào)用proceed()方法,就會(huì)觸發(fā)切入點(diǎn)方法執(zhí)行
        Object result=pjp.proceed();
        //如果調(diào)用pjp.proceed()執(zhí)行業(yè)務(wù)方法的時(shí)候拋出異常,那么下面的代碼將不會(huì)執(zhí)行
        System.out.println("輸出,方法名:" + method + ";目標(biāo)對(duì)象:" + target + ";返回值:" + result);
        System.out.println("======執(zhí)行環(huán)繞通知結(jié)束=========");
        return result;
    }
}

Spring的AOP配置:spring-aop.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans
        xmlns="http://www./schema/beans"
        xmlns:xsi="http://www./2001/XMLSchema-instance"
        xmlns:aop="http://www./schema/aop"
        xsi:schemaLocation="http://www./schema/beans
        http://www./schema/beans/spring-beans-3.0.xsd
        http://www./schema/aop
    http://www./schema/aop/spring-aop-3.0.xsd">

    <!-- 聲明一個(gè)業(yè)務(wù)類 -->
    <bean id="userManager" class="com.springboottime.time.service.serviceImpl.AdviceServiceImpl">
        <property name="name" value="lixiaoxi"></property>
    </bean>

    <!-- 聲明通知類 -->
    <bean id="aspectBean" class="com.springboottime.time.aop.AopAspect" />

    <aop:config>
        <aop:aspect ref="aspectBean">
            <aop:pointcut id="pointcut" expression="execution(* com.springboottime.time.service.serviceImpl.AdviceServiceImpl..*(..))"/>

            <aop:before method="doBefore" pointcut-ref="pointcut"/>
            <aop:after-returning method="doAfterReturning" pointcut-ref="pointcut" returning="result"/>
            <aop:after method="doAfter" pointcut-ref="pointcut" />
            <aop:around method="doAround" pointcut-ref="pointcut"/>
            <aop:after-throwing method="doAfterThrowing" pointcut-ref="pointcut" throwing="ex"/>
        </aop:aspect>
    </aop:config>
</beans>

springboot啟動(dòng)類設(shè)置:注意要引入xml文件

package com.springboottime.time;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportResource;

@ImportResource("classpath:spring-aop.xml")
//@ImportResource("classpath:spring-aop-aspectJ.xml")
@SpringBootApplication
public class TimeApplication {

    public static void main(String[] args) {
        SpringApplication.run(TimeApplication.class, args);
    }

}

測(cè)試類:

package com.springboottime.time;

import com.springboottime.time.service.AdviceService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class TimeApplicationTests {

    @Autowired
    private AdviceService adviceService;

    @Test
    public void contextLoads() {
    }

    @Test
    public void test(){
        String user = adviceService.findUser();
        System.out.println("<><><><><><><><><><><><><>");
        adviceService.addUser();
    }
}

測(cè)試結(jié)果:

===========執(zhí)行前置通知============
======執(zhí)行環(huán)繞通知開始=========
***************執(zhí)行業(yè)務(wù)方法findUser,查找的用戶名字為:lixiaoxi****************
輸出,方法名:findUser;目標(biāo)對(duì)象:AdviceServiceImpl(name=lixiaoxi);返回值:lixiaoxi
======執(zhí)行環(huán)繞通知結(jié)束=========
===========執(zhí)行最終通知============
===========執(zhí)行后置通知============
返回值result===================lixiaoxi
<><><><><><><><><><><><><>
===========執(zhí)行前置通知============
======執(zhí)行環(huán)繞通知開始=========
***************執(zhí)行業(yè)務(wù)方法addUser****************
===========執(zhí)行異常通知============
===========執(zhí)行最終通知============

java.lang.RuntimeException
at com.springboottime.time.service.serviceImpl.AdviceServiceImpl.addUser(AdviceServiceImpl.java:20)

二、使用注解配置AOP

采用注解來做aop, 主要是將寫在spring 配置文件中的連接點(diǎn)寫到注解里面。

業(yè)務(wù)接口和業(yè)務(wù)實(shí)現(xiàn)與上邊一樣,具體切面類如下:

package com.springboottime.time.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;

@Aspect
public class AopAspectJ {

    /**
     * 必須為final String類型的,注解里要使用的變量只能是靜態(tài)常量類型的
     */
    public static final String EDP="execution(* com.springboottime.time.service.serviceImpl.AdviceServiceImpl..*(..))";

    /**
     * 切面的前置方法 即方法執(zhí)行前攔截到的方法
     * 在目標(biāo)方法執(zhí)行之前的通知
     * @param jp
     */
    @Before(EDP)
    public void doBefore(JoinPoint jp){

        System.out.println("=========AopAspectJ執(zhí)行前置通知==========");
    }


    /**
     * 在方法正常執(zhí)行通過之后執(zhí)行的通知叫做返回通知
     * 可以返回到方法的返回值 在注解后加入returning
     * @param jp
     * @param result
     */
    @AfterReturning(value=EDP,returning="result")
    public void doAfterReturning(JoinPoint jp,String result){
        System.out.println("===========AopAspectJ執(zhí)行后置通知============");
    }

    /**
     * 最終通知:目標(biāo)方法調(diào)用之后執(zhí)行的通知(無論目標(biāo)方法是否出現(xiàn)異常均執(zhí)行)
     * @param jp
     */
    @After(value=EDP)
    public void doAfter(JoinPoint jp){
        System.out.println("===========AopAspectJ執(zhí)行最終通知============");
    }

    /**
     * 在目標(biāo)方法非正常執(zhí)行完成, 拋出異常的時(shí)候會(huì)走此方法
     * @param jp
     * @param ex
     */
    @AfterThrowing(value=EDP,throwing="ex")
    public void doAfterThrowing(JoinPoint jp,Exception ex) {
        System.out.println("===========AopAspectJ執(zhí)行異常通知============");
    }

    /**
     * 環(huán)繞通知:目標(biāo)方法調(diào)用前后執(zhí)行的通知,可以在方法調(diào)用前后完成自定義的行為。
     * @param pjp
     * @return
     * @throws Throwable
     */
    @Around(EDP)
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable{

        System.out.println("======AopAspectJ執(zhí)行環(huán)繞通知開始=========");
        // 調(diào)用方法的參數(shù)
        Object[] args = pjp.getArgs();
        // 調(diào)用的方法名
        String method = pjp.getSignature().getName();
        // 獲取目標(biāo)對(duì)象
        Object target = pjp.getTarget();
        // 執(zhí)行完方法的返回值
        // 調(diào)用proceed()方法,就會(huì)觸發(fā)切入點(diǎn)方法執(zhí)行
        Object result=pjp.proceed();
        System.out.println("輸出,方法名:" + method + ";目標(biāo)對(duì)象:" + target + ";返回值:" + result);
        System.out.println("======AopAspectJ執(zhí)行環(huán)繞通知結(jié)束=========");
        return result;
    }

}

spring的配置:spring-aop-aspectJ.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans
        xmlns="http://www./schema/beans"
        xmlns:xsi="http://www./2001/XMLSchema-instance"
        xmlns:aop="http://www./schema/aop"
        xsi:schemaLocation="http://www./schema/beans
    http://www./schema/beans/spring-beans-3.0.xsd
    http://www./schema/aop
    http://www./schema/aop/spring-aop-3.0.xsd">

    <!-- 聲明spring對(duì)@AspectJ的支持 -->
    <aop:aspectj-autoproxy/>

    <!-- 聲明一個(gè)業(yè)務(wù)類 -->
    <bean id="userManager" class="com.springboottime.time.service.serviceImpl.AdviceServiceImpl">
        <property name="name" value="lixiaoxi"></property>
    </bean>

    <!-- 聲明通知類 -->
    <bean id="aspectBean" class="com.springboottime.time.aop.AopAspectJ" />
</beans>

springboot啟動(dòng)類配置:

package com.springboottime.time;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportResource;

//@ImportResource("classpath:spring-aop.xml")
@ImportResource("classpath:spring-aop-aspectJ.xml")
@SpringBootApplication
public class TimeApplication {

    public static void main(String[] args) {
        SpringApplication.run(TimeApplication.class, args);
    }

}

測(cè)試結(jié)果:

======AopAspectJ執(zhí)行環(huán)繞通知開始=========
=========AopAspectJ執(zhí)行前置通知==========
***************執(zhí)行業(yè)務(wù)方法findUser,查找的用戶名字為:lixiaoxi****************
輸出,方法名:findUser;目標(biāo)對(duì)象:AdviceServiceImpl(name=lixiaoxi);返回值:lixiaoxi
======AopAspectJ執(zhí)行環(huán)繞通知結(jié)束=========
===========AopAspectJ執(zhí)行最終通知============
===========AopAspectJ執(zhí)行后置通知============
<><><><><><><><><><><><><>
======AopAspectJ執(zhí)行環(huán)繞通知開始=========
=========AopAspectJ執(zhí)行前置通知==========
***************執(zhí)行業(yè)務(wù)方法addUser****************
===========AopAspectJ執(zhí)行最終通知============
===========AopAspectJ執(zhí)行異常通知============

java.lang.RuntimeException
at com.springboottime.time.service.serviceImpl.AdviceServiceImpl.addUser(AdviceServiceImpl.java:20)

入門級(jí)例子。 
引用一個(gè)猴子偷桃,守護(hù)者守護(hù)果園抓住猴子的小情節(jié)。 

1、猴子偷桃類(普通類): 

package com.samter.common;  
  
/** 
 * 猴子 
 * @author Administrator 
 * 
 */  
public class Monkey {  
      
    public void stealPeaches(String name){  
        System.out.println("【猴子】"+name+"正在偷桃...");  
    }  
}

2、守護(hù)者類(聲明為Aspect): 

package com.samter.aspect;  
  
import org.aspectj.lang.annotation.AfterReturning;  
import org.aspectj.lang.annotation.Aspect;  
import org.aspectj.lang.annotation.Before;  
import org.aspectj.lang.annotation.Pointcut;  
  
/** 
 * 桃園守護(hù)者 
 * @author Administrator 
 * 
 */  
  
@Aspect  
public class Guardian {  
      
     
     /**
     * 定義一個(gè)方法,用于聲明切入點(diǎn)表達(dá)式。一般的,該方法中再不需要添加其他的代碼
     * 使用@Pointcut 來聲明切入點(diǎn)表達(dá)式
     * 后面的其他通知直接使用方法名直接引用方法名即可
     */
    @Pointcut("execution(* com.samter.common.Monkey.stealPeaches(..))") 
    public void foundMonkey(){}  
  
    @Before(value="foundMonkey()")  
    public void foundBefore(){  
        System.out.println("【守護(hù)者】發(fā)現(xiàn)猴子正在進(jìn)入果園...");  
    }  
      
    @AfterReturning("foundMonkey() && args(name,..)")  
    public void foundAfter(String name){  
        System.out.println("【守護(hù)者】抓住了猴子,守護(hù)者審問出了猴子的名字叫“"+name+"”...");  
    }  
      
}

3、XML配置文件: 

<?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www./schema/beans"  
       xmlns:xsi="http://www./2001/XMLSchema-instance"  
       xmlns:aop="http://www./schema/aop"  
       xsi:schemaLocation="  
       http://www./schema/beans  
       http://www./schema/beans/spring-beans-2.0.xsd  
       http://www./schema/aop  
       http://www./schema/aop/spring-aop-2.0.xsd"  
>  
  
    <!-- 定義Aspect -->  
    <bean id="guardian" class="com.samter.aspect.Guardian" />  
  
    <!-- 定義Common -->  
    <bean id="monkey" class="com.samter.common.Monkey" />  
  
    <!-- 啟動(dòng)AspectJ支持 -->  
    <aop:aspectj-autoproxy />  
  
</beans>

4、測(cè)試類: 

package com.samter.common;  
  
import org.springframework.context.ApplicationContext;  
import org.springframework.context.support.ClassPathXmlApplicationContext;  
  
public class Main {  
  
    public static void main(String[] args) {  
        ApplicationContext context = new ClassPathXmlApplicationContext("config.xml");  
        Monkey monkey = (Monkey) context.getBean("monkey");  
        try {  
            monkey.stealPeaches("孫大圣的大徒弟");  
        }  
        catch(Exception e) {}  
    }  
  
}

5、控制臺(tái)輸出: 

【守護(hù)者】發(fā)現(xiàn)猴子正在進(jìn)入果園...  
【猴子】孫大圣的大徒弟正在偷桃...  
【守護(hù)者】抓住了猴子,守護(hù)者審問出了猴子的名字叫“孫大圣的大徒弟”...

解說:

      1寫了一個(gè)猴子正在偷桃的方法。 
      2寫了一個(gè)標(biāo)志為@Aspect的類,它是守護(hù)者。它會(huì)在猴子偷桃之前發(fā)現(xiàn)猴子,并在猴子偷桃之后抓住猴子。 
      原理:

            A、@Aspect的聲明表示這是一個(gè)切面類。 
            B、@Pointcut使用這個(gè)方法可以將com.samter.common.Monkey.stealPeaches(..)方法聲明為poincut即切入點(diǎn)。作用,在stealPeaches方法被調(diào)用的時(shí)候執(zhí)行2的foundMonkey方法。其中execution是匹配方法執(zhí)行的切入點(diǎn),也就是spring最常用的切入點(diǎn)定義方式。 
            C、@Before(value="foundMonkey()"):@Before聲明為在切入點(diǎn)方法執(zhí)行之前執(zhí)行,而后面沒有直接聲明切入點(diǎn),而是value="foundMonkey()",是因?yàn)槿绻鸃afterReturning等都有所改動(dòng)的時(shí)候都必須全部改動(dòng),所以統(tǒng)一用Pointcut的foundMonkey代替,這樣子有改動(dòng)的時(shí)候僅需改動(dòng)一個(gè)地方。其他@AfterReturning類同。 
      3是xml配置文件,里面有具體的注釋。 

特別說明:Guardian類里面的@Pointcut("execution(* com.samter.common.Monkey.stealPeaches(..))"),如果stealPeaches有參數(shù)則..表示所有參數(shù),@AfterReturning("foundMonkey() && args(name,..)")的&& args(name,..)可以獲取切入點(diǎn)方法stealPeaches的參數(shù)。 

總結(jié):這里列舉了一個(gè)簡(jiǎn)單的例子,但是不難引申到應(yīng)用中,當(dāng)你寫一個(gè)登陸系統(tǒng)的時(shí)候,你或許要記錄誰成功登陸了系統(tǒng),誰登陸系統(tǒng)密碼錯(cuò)誤等等的信息,這樣子你用切面是再合適不過的了,總之當(dāng)你的事務(wù)邏輯都設(shè)計(jì)到日志、安全檢查、事務(wù)管理等等共同的內(nèi)容的時(shí)候,用切面是要比你沒有一個(gè)事務(wù)邏輯類都有相關(guān)代碼或者相關(guān)引用好得多。 

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    国产成人精品一区二区在线看| 亚洲高清欧美中文字幕| 成人免费视频免费观看| 国产精品免费无遮挡不卡视频| 亚洲一区二区三在线播放| 国内午夜精品视频在线观看| 欧美日韩国产二三四区| 婷婷色网视频在线播放| 97人妻精品一区二区三区男同| 亚洲专区中文字幕在线| 国产欧美高清精品一区| 麻豆国产精品一区二区三区| 成人免费高清在线一区二区| 欧美午夜视频免费观看| 天海翼高清二区三区在线| 91爽人人爽人人插人人爽| 东京热男人的天堂久久综合| 国产内射一级二级三级| 欧美午夜一级特黄大片| 五月天综合网五月天综合网| 五月激情婷婷丁香六月网| 国产又粗又猛又爽色噜噜| 六月丁香六月综合缴情| 美女被后入福利在线观看| 日本熟妇五十一区二区三区| 人妻久久这里只有精品| 蜜桃传媒视频麻豆第一区| 亚洲av日韩av高潮无打码| 99久免费精品视频在线观| 亚洲二区欧美一区二区| 少妇在线一区二区三区| 亚洲日本久久国产精品久久| 国产91色综合久久高清| 性感少妇无套内射在线视频| 国产午夜免费在线视频| 国产欧美一区二区另类精品| 免费人妻精品一区二区三区久久久| 六月丁香六月综合缴情| 久久热九九这里只有精品| 日韩人妻一区中文字幕| 国产日韩精品激情在线观看|