SpringBoot中使用AOP面向切面编程

转自:https://blog.csdn.net/Luck_ZZ/article/details/79504900

一、概述
AOP是Spring框架的重要特性。

通知类型有:前置通知、后置最终通知、后置返回通知、后置异常通知、环绕通知

二、添加maven依赖
<dependency>  
    <groupId>org.springframework.boot</groupId>  
    <artifactId>spring-boot-starter-aop</artifactId>  
</dependency>  
三、创建Aspect切面类
在com.example.aop包下创建切面类WebAspect

package com.example.aop;
 
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
 
import java.util.Arrays;
 
@Component
@Aspect
public class WebAspect {
    /**
     * 切入点
     * 匹配com.example.controller1包及其子包下的所有类的所有方法
     */
    @Pointcut("execution(* com.example.controller1..*.*(..))")
    public void executePackage(){
    }
 
    /**
     * 前置通知,目标方法调用前被调用
     * @param joinPoint
     */
    @Before("executePackage()")
    public void beforeAdvice(JoinPoint joinPoint){
        System.out.println("- - - - - 前置通知 - - - - -");
        Signature signature = joinPoint.getSignature();
        System.out.println("返回目标方法的签名:"+signature);
        System.out.println("代理的是哪一个方法:"+signature.getName());
        Object[] obj = joinPoint.getArgs();
        System.out.println("获取目标方法的参数信息:"+Arrays.asList(obj));
    }
 
    /**
     * 后置最终通知,目标方法执行完执行
     */
    @After("executePackage()")
    public void afterAdvice(){
        System.out.println("- - - - - 后置最终通知- - - - -");
    }
    /**
     * 后置返回通知
     * 如果参数中的第一个参数为JoinPoint,则第二个参数为返回值的信息
     * 如果参数中的第一个参数不为JoinPoint,则第一个参数为returning中对应的参数
     * returning 只有目标方法返回值与通知方法相应参数类型时才能执行后置返回通知,否则不执行
     * @param joinPoint
     * @param keys
     */
    @AfterReturning(value = "execution(* com.example.controller1..*.*(..))",returning = "keys")
    public void afterReturningAdvice(JoinPoint joinPoint,String keys){
        System.out.println("- - - - - 后置返回通知- - - - -");
        System.out.println("后置返回通知 返回值:"+keys);
    }
 
 
    /**
     * 后置异常通知
     *  定义一个名字,该名字用于匹配通知实现方法的一个参数名,当目标方法抛出异常返回后,将把目标方法抛出的异常传给通知方法;
     *  throwing 只有目标方法抛出的异常与通知方法相应参数异常类型时才能执行后置异常通知,否则不执行,
     * @param joinPoint
     * @param exception
     */
    @AfterThrowing(value = "executePackage()",throwing = "exception")
    public void afterThrowingAdvice(JoinPoint joinPoint,NullPointerException exception){
        System.out.println("- - - - - 后置异常通知 - - - - -");
    }
 
    /**
     * 环绕通知:
     * 环绕通知非常强大,可以决定目标方法是否执行,什么时候执行,执行时是否需要替换方法参数,执行完毕是否需要替换返回值。
     * 环绕通知第一个参数必须是org.aspectj.lang.ProceedingJoinPoint类型
     */
    @Around("execution(* com.example.controller1.AopController1.testAround(..))")
    public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint){
        System.out.println("- - - - - 环绕通知 - - - -");
        System.out.println("环绕通知的目标方法名:"+proceedingJoinPoint.getSignature().getName());
        try {//obj之前可以写目标方法执行前的逻辑
            Object obj = proceedingJoinPoint.proceed();//调用执行目标方法
            return obj;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }finally {
            System.out.println("- - - - - 环绕通知 end - - - -");
        }
        return null;
    }
}


1.@Pointcut是创建切入点
切入点方法不用写代码,返回类型为void

 execution:用于匹配表达式

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)throws-pattern?) 
修饰符匹配(modifier-pattern?)
返回值匹配(ret-type-pattern)可以为*表示任何返回值,全路径的类名等
类路径匹配(declaring-type-pattern?)
方法名匹配(name-pattern)可以指定方法名 或者 *代表所有, set* 代表以set开头的所有方法
参数匹配((param-pattern))可以指定具体的参数类型,多个参数间用“,”隔开,各个参数也可以用“*”来表示匹配任意类型的参数,如(String)表示匹配一个String参数的方法;(*,String) 表示匹配有两个参数的方法,第一个参数可以是任意类型,而第二个参数是String类型;可以用(..)表示零个或多个任意参数
异常类型匹配(throws-pattern?) 其中后面跟  着“?”的是可选项
1)execution(* *(..)) 
//表示匹配所有方法  
2)execution(public * com. example.controller.*(..))  
//表示匹配com. example.controller中所有的public方法  
3)execution(* com. example.controller..*.*(..))  
//表示匹配com. example.controller包及其子包下的所有方法 
2.JoinPoint
除@Around外,每个方法里都可以加或者不加参数JoinPoint。JoinPoint包含了类名、被切面的方法名、参数等属性。@Around参数必须为ProceedingJoinPoint。

四、创建Controller类
在com.example.controller1包下创建Controller类AopController1。

package com.example.controller1;
 
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 
@RestController
@RequestMapping("/aop1")
public class AopController1 {
 
    @RequestMapping("/test")
    public String testAop(String key){
        return "key="+key;
    }
 
    @RequestMapping("/testAfterThrowing")
    public String testAfterThrowing(String key){
        throw new NullPointerException();
    }
 
    @RequestMapping("/testAround")
    public String testAround(String key){
        return "key="+key;
    }
 
}
五、创建测试类
当然,也可以启动项目,直接在浏览器访问路径看效果,不用创建测试类。

在src/test/java下建测试类

在com.example.controller1下建AopController1Test测试类

package com.example.controller1;
 
import com.example.SpringWebThyApplication;
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.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
 
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = SpringWebThyApplication.class, webEnvironment = SpringBootTest.WebEnvironment.MOCK)
@WebAppConfiguration
public class AopController1Test {
    @Autowired
    private AopController1 aopController1;
    @Test
    public void testAop() {
        this.aopController1.testAop("mykey");
    }
    @Test
    public void testAfterThrowing() {
        this.aopController1.testAfterThrowing("mykey");
    }
    @Test
    public void testAround() {
        this.aopController1.testAround("mykey");
    }
}
六、测试结果
1.执行第一个测试方法testAop()

2.执行第二个测试方法testAfterThrowing()

3.执行第三个测试方法testAround()

七、自定义注解
对于特定的方法执行时需要经过通知,可以在方法上加注解切面。

1.创建WebDesc注解
package com.example.aop;
 
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface WebDesc {
    String describe() default "mydesc";
}
2.创建Aspect切面类
在com.example.aop包下创建切面类WebAspectAnno

package com.example.aop;
 
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
 
@Component
@Aspect
public class WebAspectAnno {
    /**
     * 切入点
     * 用注解
     */
    @Pointcut("@annotation(com.example.aop.WebDesc)")
    public void executeAnnotation(){
    }
    @Before("executeAnnotation()")
    public void beforeAdviceAnnotation(){
        System.out.println("- - - - - 前置通知 annotation - - - - -");
    }
    @Around("@annotation(webDesc)")
    public Object aroundAnnotation(ProceedingJoinPoint proceedingJoinPoint, WebDesc webDesc){
        System.out.println("- - - - - 环绕通知 annotation - - - -");
        //获取注解里的值
        System.out.println("注解的值:" + webDesc.describe());
        try {//obj之前可以写目标方法执行前的逻辑
            Object obj = proceedingJoinPoint.proceed();//调用执行目标方法
            System.out.println("- - - - - 环绕通知 annotation end - - - -");
            return obj;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return null;
    }
}
3.创建Controller
在com.example.controller2包下创建Controller类AopController1。

package com.example.controller2;
 
import com.example.aop.WebDesc;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 
@RestController
@RequestMapping("/aop2")
public class AopController2 {
    @RequestMapping("/testAnnotation")
    @WebDesc(describe = "This is testAnnotation Controller")
    public String testAnnotation(String key){
        return "key="+key;
    }
}
4.创建测试类
package com.example.controller2;
 
import com.example.SpringWebThyApplication;
import com.example.controller1.AopController1;
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.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
 
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = SpringWebThyApplication.class, webEnvironment = SpringBootTest.WebEnvironment.MOCK)
@WebAppConfiguration
public class AopController2Test {
    @Autowired
    private AopController2 aopController2;
    @Test
    public void testAnnotation() {
        this.aopController2.testAnnotation("mykey");
    }
}
5.测试结果

Spring Boot实现AOP面向切面编程的具体方法如下: 1. 首先,你需要在项目的pom.xml文件添加spring-boot-starter-aop依赖。可以参考以下代码: ``` <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> ``` 2. 然后,你需要编写一个用于拦截的bean。这个bean将包含你想要在目标方法执行前后执行的逻辑。你可以使用注解或者编程方式来定义切面。例如,你可以使用@Aspect注解来定义一个切面,然后在切面的方法上使用@Before、@After等注解来定义具体的拦截行为。 3. 接下来,你需要将切面应用到目标对象上,创建代理对象。这个过程称为织入(Weaving)。在Spring Boot,你可以使用@EnableAspectJAutoProxy注解来启用自动代理,它会根据切面定义自动创建代理对象。 总而言之,Spring Boot实现AOP面向切面编程的具体方法包括:添加依赖、编写用于拦截的bean,以及启用自动代理。这样就能实现在目标方法执行前后执行特定逻辑的效果了。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [SpringBoot整合aop面向切面编程过程解析](https://download.csdn.net/download/weixin_38689551/12743012)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [SpringBoot实现AOP面向切面编程](https://blog.csdn.net/weixin_52536274/article/details/130375560)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值