欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Springboot項(xiàng)目快速實(shí)現(xiàn)Aop功能

 更新時(shí)間:2023年03月27日 17:19:57   作者:凡夫販夫  
這篇文章主要介紹了Springboot項(xiàng)目如何快速實(shí)現(xiàn)Aop功能,對(duì)此方面感興趣的小伙伴可以詳細(xì)參考閱讀本文,本文有一定的參考價(jià)值

前言

這篇文章的有幾個(gè)關(guān)鍵點(diǎn),第一,關(guān)于AOP的一些基礎(chǔ)理論知識(shí),在正式使用AOP前需要了解;第二,Springboot項(xiàng)目中怎么快速集成Aop功能的;第三,AOP的一些使用小技巧和注意事項(xiàng)。

依賴引入

Springboot引入AOP依賴包后,一般來(lái)說(shuō)是不需要再做其他配置了,在比較低的版本或者有其他配置影響了AOP的相關(guān)功能,導(dǎo)致aop功能不生效,可以試試在啟動(dòng)類上增加@EnableAspectJAutoProxy來(lái)啟用;

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

代碼實(shí)現(xiàn)

1、自定義注解@TestAop

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAop {
}

2、ExampleAop .java

@Component
@Aspect
@Slf4j
public class ExampleAop {
 
    //切入點(diǎn):增強(qiáng)標(biāo)有@TestAop注解的方法
    @Pointcut(value = "@annotation(TestAop)")
    //切入點(diǎn)簽名
    public void pointcut() {
        System.out.println("pointCut簽名。。。");
    }
 
    //前置通知
    @Before("pointcut()")
    public void deBefore(JoinPoint joinPoint) throws Throwable {
        log.info("前置通知被執(zhí)行");
        //可以joinpoint中得到命中方法的相關(guān)信息,利用這些信息可以做一些額外的業(yè)務(wù)操作;
    }
 
    //返回通知
    @AfterReturning(returning = "ret", pointcut = "pointcut()")
    public void doAfterReturning(Object ret) throws Throwable {
        log.info("返回通知被執(zhí)行");
        //可以joinpoint中得到命中方法的相關(guān)信息,利用這些信息可以做一些額外的業(yè)務(wù)操作;
    }
 
    //異常通知
    @AfterThrowing(throwing = "ex", pointcut = "pointcut()")
    public void throwss(JoinPoint jp, Exception ex) {
        log.info("異常通知被執(zhí)行");
        //可以joinpoint中得到命中方法的相關(guān)信息,利用這些信息可以做一些額外的業(yè)務(wù)操作;
        //可以從ex中獲取到具體的異常信息
    }
 
    //后置通知
    @After("pointcut()")
    public void after(JoinPoint jp) {
        log.info("后置通知被執(zhí)行");
        //可以joinpoint中得到命中方法的相關(guān)信息,利用這些信息可以做一些額外的業(yè)務(wù)操作;
    }
 
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        log.info("------環(huán)繞通知 start");
        String methodName = proceedingJoinPoint.getSignature().getName();
        String className = proceedingJoinPoint.getTarget().getClass().getName();
        Object[] args = proceedingJoinPoint.getArgs();
        String argsName = null;
        StringBuilder sb = new StringBuilder();
        if (args != null && args.length > 0) {
            for (int i = 0; i < args.length; i++) {
                if (args[i] != null) {
                    sb.append(";").append(args[i].getClass().getName());
                }
            }
            if (sb.toString().length() > 0) {
                argsName = sb.toString().substring(1);
            }
        }
        log.info("命中類:{},方法{},參數(shù){};", className, methodName, argsName);
        Object proceed = proceedingJoinPoint.proceed();
        log.info("------環(huán)繞通知 end");
        return proceed;
    }
 
}

核心注解和類

1、Aspect,表示當(dāng)前類是一個(gè)切面類,簡(jiǎn)單理解就是切入點(diǎn)和通知的抽象封裝,表述的是切入點(diǎn)和通知方法之間的對(duì)應(yīng)關(guān)系;

2、@Before,前置通知,用于方法上,被@Before注解標(biāo)記的方法會(huì)在被切入方法執(zhí)行之前執(zhí)行;

3、@After,后置通知,用于方法上,被@After注解標(biāo)記的方法會(huì)在被切入方法執(zhí)行之后執(zhí)行;

4、@AfterReturning,返回通知,用于方法上,被@AfterReturning注解標(biāo)記的方法會(huì)在被切入方法返回結(jié)果之后執(zhí)行;

6、@AfterThrowing:異常通知,用于方法上,被@AfterThrowing注解標(biāo)記的方法會(huì)在被切入方法拋出異常之后執(zhí)行,一般用于有目的的獲取異常信息;

7、@Aroud:環(huán)繞通知,用于方法上,被@Around注解標(biāo)記的方法會(huì)在被切入方法執(zhí)行前后執(zhí)行;

8、@Pointcut,切入點(diǎn),標(biāo)記在方法上,用于定義切入點(diǎn),所謂的切入點(diǎn)是指對(duì)哪些連接點(diǎn)處進(jìn)行切入增強(qiáng)的定義,在Spring中具體就是指對(duì)哪些方法進(jìn)行切入增強(qiáng)的定義;被@Pointcut注解表示切入點(diǎn)的表達(dá)式有多種,最常用的是兩種,execution表達(dá)式和注解;

9、Jointpoint,連接點(diǎn),所謂的連接點(diǎn)是指被aop切面切入的位置點(diǎn),在Spring中具體就是指被切入的方法;

10、PointCut,

11、Advice,通知,所謂的通知是指對(duì)定義好的切入點(diǎn)進(jìn)行攔截后,要具體做哪些操作的定義;在Spring中就是指被@Before、@After、@AfterReturning、@AfterThrowing、@Around注解標(biāo)記的方法;

標(biāo)記切入點(diǎn)的常用方式

1、execution表達(dá)式

表達(dá)式請(qǐng)法:訪問(wèn)修飾符 返回值 包名.包名...類名.方法(參數(shù)列表)

示例1:表示匹配所有com.fanfu包以及子包下的所有類中以add開(kāi)頭的方法,返回值、參數(shù)不限;

@Pointcut("execution(* com.fanfu..*.*.add*(..))")

示例2:表示匹配所有com.fanfu包以及子包下的所有類中以add開(kāi)頭,參數(shù)類型是String的方法,返回值不限;

@Pointcut("execution(* com.fanfu..*.*.add*(String))")

示例3:表示匹配com.fanfu包下任意類、任意方法、任意參數(shù);

@Pointcut("execution(* com.fanfu..*.*.*(String))")

execution()為表達(dá)式的主體;
第一個(gè)*表示返回值類型為任意,即不限制返回值類型;
包后的*表示當(dāng)前包,包后面連續(xù)兩個(gè)..表示當(dāng)前包以及子包;
(..)表示任意參數(shù);
最后的*.*(..)表示匹配任意類、任意方法、任意參數(shù);

2、注解

注解語(yǔ)法:@annotation(自定義的注解)

示例:表示匹配所有標(biāo)記@TestAop注解的方法;

@Pointcut("@annotation(com.fanfu.config.TestAop)")

Spring Aop的小技巧

每一個(gè)@Pointcut可以使用execution或注解來(lái)定義切入點(diǎn),多個(gè)切點(diǎn)之間還可以使用邏輯運(yùn)算符,如&&、||、!運(yùn)算;

1、point1()&&point2()表示命中point1和point2的所有切入點(diǎn)的交集;如示例:com.fanfu包以及下屬所有子包的所有類中,方法名是以add開(kāi)頭,參數(shù)類型是String的所有方法,與com.fanfu.service包以及下屬所有子包的所有類中,不限方法名和參數(shù)類型的所有方法取交集,即com.fanfu.service包以及下屬所有子包的所有類中,方法或是add1或add2的方法,在調(diào)用前后都會(huì)執(zhí)行環(huán)繞通知around()方法內(nèi)的邏輯;

@Component
@Aspect
@Slf4j
public class ExampleAop {
    @Pointcut("execution(* com.fanfu..*.*.add*(String))")
    public void point1() {
    }
    @Pointcut("execution(* com.fanfu.service..*.*(..))")
    public void point2() {
    }
    @Pointcut("point1()&&point2()")
    public void point() {
    }
    @Around("point()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        log.info("------around start");
        Object proceed = proceedingJoinPoint.proceed();
        log.info("------around end");
        return proceed;
    }
}

2、point1()&&point2()表示命中point1和point2的所有切入點(diǎn)的并集;如示例:com.fanfu.service包以及下屬所有子包的所有類中,方法名是add1,參數(shù)類型是String的所有方法,與com.fanfu.controller包以及下屬所有子包的所有類中,方法名是add2,參數(shù)類型是String的所有方法取并集,即com.fanfu.service或com.fanfu.controller的包以及下屬所有子包的所有類中,方法或是add1或add2的方法,在調(diào)用前后都會(huì)執(zhí)行環(huán)繞通知around()方法內(nèi)的邏輯;

@Component
@Aspect
@Slf4j
public class ExampleAop {
    @Pointcut("execution(* com.fanfu.service..*.add*(String))")
    public void point1() {
    }
    @Pointcut("execution(* com.fanfu.controller..*.*.add*(String))")
    public void point2() {
    }
    @Pointcut("point1()||point2()")
    public void point() {
    }
    @Around("point()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        log.info("------around start");
        Object proceed = proceedingJoinPoint.proceed();
        log.info("------around end");
        return proceed;
    }
}

3、!point()表示命中point()的所有切入點(diǎn)取反,如示例:com.fanfu.service包及下屬所有子包的所有類中,不是以add開(kāi)頭的方法,在調(diào)用前后都會(huì)執(zhí)行環(huán)繞通知around()方法內(nèi)的邏

@Component
@Aspect
@Slf4j
public class ExampleAop {
    @Pointcut("execution(* com.fanfu.service..*.add*(String))")
    public void point() {
    }
    @Around("!point()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        log.info("------around start");
        Object proceed = proceedingJoinPoint.proceed();
        log.info("------around end");
        return proceed;
    }
}

Spring Aop注意事項(xiàng)

1、與定義的切點(diǎn)匹配方法,如果在當(dāng)前調(diào)用鏈中,方法在當(dāng)前類是首次匹配則會(huì)命中,即執(zhí)行相關(guān)的通知,如果當(dāng)前的調(diào)用鏈沒(méi)有結(jié)束,又在當(dāng)前方法里調(diào)用了當(dāng)前類的與其他切入點(diǎn)匹配方法,則不會(huì)再命中,即其他與切入點(diǎn)匹配的方法執(zhí)行的時(shí)候不會(huì)再觸發(fā)相關(guān)的通知;

如下示例:

當(dāng)請(qǐng)求http://localhost:8080/example時(shí),ExampleController中的example方法被觸發(fā),ExampleController#example()又調(diào)用了ExampleService#test(),在ExampleService#test()內(nèi)部,又順序調(diào)用了ExampleService#test1()和ExampleService#test2();在ExampleAop中,按照execution中的配置,是可以匹配到test()、test1()、test2(),實(shí)際是命中的方法只有test();

@Component
@Aspect
@Slf4j
public class ExampleAop {
    @Pointcut("execution(* com.fanfu.service.impl.ExampleServiceImpl.test*(..))")
    public void point2() {
        log.info("切入點(diǎn)匹配");
    }
    @Around("point()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        log.info("------around start");
        String methodName = proceedingJoinPoint.getSignature().getName();
        String className = proceedingJoinPoint.getTarget().getClass().getName();
        Object[] args = proceedingJoinPoint.getArgs();
        String argsName=null;
        StringBuilder sb=new StringBuilder();
        if (args!=null&&args.length>0) {
            for (int i = 0; i < args.length; i++) {
                if (args[i] != null) {
                    sb.append(";").append(args[i].getClass().getName());
                }
            }
            if (sb.toString().length()>0) {
                argsName=sb.toString().substring(1);
            }
        }
        log.info("命中類:{},方法{},參數(shù){};",className,methodName,argsName);
        Object proceed = proceedingJoinPoint.proceed();
        log.info("------around end");
        return proceed;
    }
}
@Service
@Slf4j
public class ExampleServiceImpl implements IExampleService {
 
    @Override
    public String test(String msg) {
        log.info("test方法被執(zhí)行");
        this.test1(msg);
        this.test2(msg);
        return msg;
    }
 
    public String test1(String msg) {
        log.info("test1方法被執(zhí)行");
        return "msg1";
    }
 
    public String test2(String msg) {
        log.info("test2方法被執(zhí)行");
        return "msg2";
    }
}
public interface IExampleService {
    public String test(String msg);
    public String test1(String msg);
    public String test2(String msg);
}
@RestController
@Slf4j
public class ExampleController {
    @Autowired
    private IExampleService exampleService;
    @GetMapping("/example")
    public String example() {
        log.info("example start");
        exampleService.test(null);
        log.info("example end");
        return String.valueOf(System.currentTimeMillis());
    }
}

2、對(duì)于上面的問(wèn)題,如果把execution表達(dá)換成注解,會(huì)不會(huì)結(jié)果不一樣?再把ExampleAop中的@Pointcut改成注解形式,再在ExampleService#test1()、ExampleService#test2()、ExampleService#test()添加注解@TestAop,驗(yàn)證結(jié)果依然是一樣的,只有test()會(huì)命中,其他不會(huì)!所以要注意呀。

@Component
@Aspect
@Slf4j
public class ExampleAop {
    @Pointcut("@annotation(TestAop)")
    public void point() {
    }
    @Around("point()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        log.info("------around start");
        String methodName = proceedingJoinPoint.getSignature().getName();
        String className = proceedingJoinPoint.getTarget().getClass().getName();
        Object[] args = proceedingJoinPoint.getArgs();
        String argsName = null;
        StringBuilder sb = new StringBuilder();
        if (args != null && args.length > 0) {
            for (int i = 0; i < args.length; i++) {
                if (args[i] != null) {
                    sb.append(";").append(args[i].getClass().getName());
                }
            }
            if (sb.toString().length() > 0) {
                argsName = sb.toString().substring(1);
            }
        }
        log.info("命中類:{},方法{},參數(shù){};", className, methodName, argsName);
        Object proceed = proceedingJoinPoint.proceed();
        log.info("------around end");
        return proceed;
    }
}
@Service
@Slf4j
public class ExampleServiceImpl implements IExampleService {
 
    @Override
    @TestAop
    public String test(String msg) {
        log.info("test方法被執(zhí)行");
        this.test1(msg);
        this.test2(msg);
        return msg;
    }
    @Override
    @TestAop
    public String test1(String msg) {
        log.info("test1方法被執(zhí)行");
        return "msg1";
    }
    @Override
    @TestAop
    public String test2(String msg) {
        log.info("test2方法被執(zhí)行");
        return "msg2";
    }
   
}

3、那什么情況下,ExampleService#test1()、ExampleService#test2()、ExampleService#test()會(huì)同時(shí)命中呢?讓從ExampleController#example()到ExampleService#test1()、ExampleService#test2()、ExampleService#test()分別在不同的調(diào)用鏈上,那么就可以同時(shí)命中了;

@RestController
@Slf4j
public class ExampleController {
    @Autowired
    private IExampleService exampleService;
    @GetMapping("/example")
    public String example() {
        log.info("example start");
        exampleService.test(null);
        exampleService.test1(null);
        exampleService.test2(null);
        log.info("example end");
        return String.valueOf(System.currentTimeMillis());
    }
}

總結(jié)

Springboot集成Aop并不難,難的是如何在實(shí)際業(yè)務(wù)中用好。如果想要用好AOP,那么理解Spring AOP的工作原理就十分有必要了,比如明明按照@Pointcut中定義已經(jīng)匹配到了具體的切入點(diǎn),但實(shí)際上并不一定會(huì)命中,即觸發(fā)相關(guān)的通知。Spring AOP的工作原理,這個(gè)坑后面肯定會(huì)補(bǔ)上的。

到此這篇關(guān)于Springboot項(xiàng)目快速實(shí)現(xiàn)Aop功能的文章就介紹到這了,更多相關(guān)Springboot實(shí)現(xiàn)Aop功能內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot項(xiàng)目多層級(jí)多環(huán)境yml設(shè)計(jì)詳解

    SpringBoot項(xiàng)目多層級(jí)多環(huán)境yml設(shè)計(jì)詳解

    這篇文章主要為大家介紹了SpringBoot項(xiàng)目多層級(jí)多環(huán)境yml設(shè)計(jì)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-03-03
  • 使用Pinyin4j進(jìn)行拼音分詞的方法

    使用Pinyin4j進(jìn)行拼音分詞的方法

    下面小編就為大家分享一篇使用Pinyin4j進(jìn)行拼音分詞的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-01-01
  • spring boot微服務(wù)自定義starter原理詳解

    spring boot微服務(wù)自定義starter原理詳解

    這篇文章主要介紹了spring boot微服務(wù)自定義starter原理詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-12-12
  • 淺析Java?ReentrantLock鎖的原理與使用

    淺析Java?ReentrantLock鎖的原理與使用

    這篇文章主要為大家詳細(xì)介紹了Java中ReentrantLock鎖的原理與使用,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,感興趣的小伙伴可以了解下
    2023-08-08
  • java實(shí)現(xiàn)的n*n矩陣求值及求逆矩陣算法示例

    java實(shí)現(xiàn)的n*n矩陣求值及求逆矩陣算法示例

    這篇文章主要介紹了java實(shí)現(xiàn)的n*n矩陣求值及求逆矩陣算法,結(jié)合具體實(shí)例形式分析了java基于數(shù)組的矩陣定義、遍歷、運(yùn)算等相關(guān)操作技巧,需要的朋友可以參考下
    2017-09-09
  • 基于spring boot 的配置參考大全(推薦)

    基于spring boot 的配置參考大全(推薦)

    下面小編就為大家?guī)?lái)一篇基于spring boot 的配置參考大全(推薦)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-09-09
  • 從0開(kāi)始學(xué)習(xí)大數(shù)據(jù)之java spark編程入門與項(xiàng)目實(shí)踐

    從0開(kāi)始學(xué)習(xí)大數(shù)據(jù)之java spark編程入門與項(xiàng)目實(shí)踐

    這篇文章主要介紹了從0開(kāi)始學(xué)習(xí)大數(shù)據(jù)之java spark編程入門與項(xiàng)目實(shí)踐,結(jié)合具體入門項(xiàng)目分析了大數(shù)據(jù)java spark編程項(xiàng)目建立、調(diào)試、輸出等相關(guān)步驟及操作技巧,需要的朋友可以參考下
    2019-11-11
  • IDEA自定義常用代碼塊及自定義快捷摸板

    IDEA自定義常用代碼塊及自定義快捷摸板

    這篇文章主要介紹了IDEA自定義常用代碼塊及自定義快捷摸板的相關(guān)知識(shí),本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2020-09-09
  • springboot實(shí)現(xiàn)以代碼的方式配置sharding-jdbc水平分表

    springboot實(shí)現(xiàn)以代碼的方式配置sharding-jdbc水平分表

    這篇文章主要介紹了springboot實(shí)現(xiàn)以代碼的方式配置sharding-jdbc水平分表,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • java實(shí)現(xiàn)文件下載的兩種方式

    java實(shí)現(xiàn)文件下載的兩種方式

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)文件下載的兩種方式,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-11-11

最新評(píng)論