@Aspect 一个简单的注解式 spring Aop demo

前言

使用spring aop,我们可在具体的执行方法前后,再执行点别的东西;比如spring的事务管理,就使用了这种机制。
接下来,先使用springboot创建个简单的web项目;

创建项目

在这里插入图片描述
新建一个service,和实现类,
在这里插入图片描述

public interface HelloService {
    void printHello();
}

@Service
public class HelloServiceImpl implements HelloService {
    @Override
    public void printHello() {
        System.out.println("hello,我是被aop的方法,printHello");
    }
}

然后新建一个测试类,

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

    @Resource
    private HelloService helloService;
    @Test
    public void printHello() {
        helloService.printHello();
    }
}

先跑一下测试类,输出:
hello,我是被aop的方法,printHello

AOP

1.编写切面


@Aspect
public class HelloAspect {
    @Before("execution(* com.example.demo.services.impl.HelloServiceImpl.printHello(..))")
    public void before(){
        System.out.println("before------");
    }
    @After("execution(* com.example.demo.services.impl.HelloServiceImpl.printHello(..))")
    public void after(){
        System.out.println("after------");
    }
    @AfterReturning("execution(* com.example.demo.services.impl.HelloServiceImpl.printHello(..))")
    public void afterReturning(){
        System.out.println("afterReturning------");
    }
    @AfterThrowing("execution(* com.example.demo.services.impl.HelloServiceImpl.printHello(..))")
    public void afterThrowing(){
        System.out.println("afterThrowing------");
    }
}

或者另外一种写法也行,使用@Pointcut注解,

@Aspect
public class HelloAspect {
    @Pointcut("execution(* com.example.demo.services.HelloService.printHello(..))")
    public void print(){

    }
    @Before("print()")
    public void before(){
        System.out.println("before------");
    }
    @After("print()")
    public void after(){
        System.out.println("after------");
    }
    @AfterReturning("print()")
    public void afterReturning(){
        System.out.println("afterReturning------");
    }
    @AfterThrowing("print()")
    public void afterThrowing(){
        System.out.println("afterThrowing------");
    }
}

使用@Aspect注解修饰的类,就是切面(当然也可以使用xml的方式,但这种方式已经不流行了,也就不演示了);

注意,使用@Aspect注解,得额外引用一个jar包(http://www.eclipse.org/aspectj/),竟然不是spring的,

<dependency>
     <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.2</version>
  </dependency>

注解解释:
这里出现了4四个注解,@Before,前置通知,在被代理对象方法前调用;
@After,后置通知,在被代理对象方法后调用;
@AfterReturning,返回通知,在被代理方法正常返回后调用;
@AfterThrowing,异常通知,在被代理对象方法抛出异常后调用;
还有一种别叫特别的,后边单独弄个例子,
@Around,环绕通知,覆盖原来的方法;

注解参数是一个正则表达式,
execution(* com.example.demo.services.impl.HelloServiceImpl.printHello(…)),解释下,
execution:表示执行方法的时候会触发;
" * " :星号表示任意返回类型的方法;
printHello :被代理的方法,这里也可以不用写实现类的方法,之际写接口方法 com.example.demo.services.HelloService.printHello(…);
(…):任意的参数;

当然,这里只是写了最简单的表达式用法,更多用法,还需要慢慢探索。

2.切面配置类

要想这个切面生效,还需要一个配置类,

@Configuration
@EnableAspectJAutoProxy
@ComponentScan("com.example.demo.aop")
public class AopConfig {
    @Bean
    public HelloAspect getHelloAspct(){
        return new HelloAspect();
    }
}

@EnableAspectJAutoProxy注解的作用是使spring识别@Aspect注解;
这样,spring才能识别到切面;
或者,直接在切面上使用注解@Component,也能行,

@Component
@Aspect
public class HelloAspect {

到此,就完成了一个简单的切面,重新跑一下测试方法,输出:

在这里插入图片描述
我们发现hello方法前后输出了别的切面中定义的内容;

3.Around通知

接下来说下Around通知,
在HelloAspect切面中加上以下内容,

@Around("print()")
    public void around(ProceedingJoinPoint proceedingJoinPoint){
        System.out.println("around before");
        try {
            proceedingJoinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println("around after");
    }

输出,
在这里插入图片描述
通过输出可以发现,环绕通知的执行顺序,挺奇怪的,记住就好;

4.给通知传递参数

有时候我们会有这种需求,就是需要把连接点,也就是被代理对象方法的参数,传递给通知,
我们先修改下service方法,加上个入参String str,

@Override
    public void printHello(String str) {
        System.out.println("hello,我是被aop的方法,printHello"+str);
    }

在修改下切面,

@Pointcut("execution(* com.example.demo.services.HelloService.printHello(..))&&args(str)")
    public void print(String str){

    }
    @Before(value = "print(str)", argNames = "str")
    public void before(String str){
        System.out.println("before------"+str);
    }

我们在切点的正则表达式中添加了:&&args(str)
为了简化,咱们就只验证下前置通知好了,看看这个str参数能不能传递到这里,
运行测试方法,发现参数传递过来了:
在这里插入图片描述

5.多个aspect

可以写多个切面,针对同一个连接点;
我们新建一个service

public interface MultiService {
    void testMulti();
}
@Service
public class MultiServiceImpl implements MultiService {
    @Override
    public void testMulti() {
        System.out.println("test multiAspect");
    }
}

新建3个切面Aspect1,Aspect2,Aspect3

@Aspect
public class Aspect1 {
    @Pointcut("execution(* com.example.demo.services.MultiService.testMulti(..))")
    public void print(){

    }
    @Before("print()")
    public void before(){
        System.out.println("before1------");
    }
    @After("print()")
    public void after(){
        System.out.println("after1------");
    }
    @AfterReturning("print()")
    public void afterReturning(){
        System.out.println("afterReturning1------");
    }
    @AfterThrowing("print()")
    public void afterThrowing(){
        System.out.println("afterThrowing1------");
    }
}

其他两个切面类似,不贴代码了;
新建切面配置类:

@Configuration
@EnableAspectJAutoProxy
@ComponentScan("com.example.demo.aop")
public class MultiAopConfig {
    @Bean
    public Aspect1 getAspect1(){
        return new Aspect1();
    }
    @Bean
    public Aspect2 getAspect2(){
        return new Aspect2();
    }
    @Bean
    public Aspect3 getAspect3(){
        return new Aspect3();
    }
}

新建测试类,运行,看输出:
在这里插入图片描述
注意下3三个切面的执行顺序;以及before和after的执行顺序;
因为我们没有明确指定3个切面顺序,所以默认按照配置类中声明的顺序(123),比如我们改变下顺序为213:

@Configuration
@EnableAspectJAutoProxy
@ComponentScan("com.example.demo.aop")
public class MultiAopConfig {
    @Bean
    public Aspect2 getAspect2(){
        return new Aspect2();
    }
    @Bean
    public Aspect1 getAspect1(){
        return new Aspect1();
    }
    @Bean
    public Aspect3 getAspect3(){
        return new Aspect3();
    }
}

运行输出:
在这里插入图片描述
我们也可以使用@Order注解来指定顺序,比如我们分别指定Aspect1,Aspect2,Aspect3的顺序为 312,

@Aspect
@Order(3)//值越小,优先级越高
public class Aspect1 {

@Aspect
@Order(1)
public class Aspect2 {

@Aspect
@Order(2)
public class Aspect3 {

运行测试输出:
在这里插入图片描述

以上是spring aop最简单的一个demo,后续会慢慢丰富内容。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java Spring AOP (Aspect Oriented Programming) 是一种编程范,它通过将应用的业务逻辑和系统关注点(如日志、事务管理等)分离,提高了代码的可维护性和复用性。下面是一个简单Spring AOP切面(Aspect)的Demo示例: 首先,你需要在Spring配置文件中启用AOP支持,并定义一个切面(Aspect): ```xml <beans xmlns="http://www.springframework.org/schema/beans" xmlns:aspectj="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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"> <!-- 启用AOP --> <aop:aspectj-autoproxy/> <!-- 定义切面 --> <bean id="myAspect" class="com.example.MyAspect"> <!-- 配置通知(advice) --> <property name="beforeAdvice" ref="beforeAdvice"/> </bean> <!-- 定义通知 --> <bean id="beforeAdvice" class="com.example.BeforeAdvice"/> </beans> ``` 然后,创建一个`MyAspect`切面类,通常包含通知(advice),例如前置通知(BeforeAdvice): ```java import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; @Aspect public class MyAspect { @Before("execution(* com.example.service.*.*(..))") public void beforeAdvice(JoinPoint joinPoint) { // 在方法执行前添加的操作,如日志记录 System.out.println("Method " + joinPoint.getSignature() + " is about to execute."); } } ``` 在这个例子中,`@Before`注解定义了一个前置通知,它将在`com.example.service`包下的所有方法执行前执行。 接下来,创建`BeforeAdvice`类,这是一个具体的通知实现: ```java public class BeforeAdvice { // 可能包含一些自定义逻辑,比如参数检查或资源获取 } ``` 相关问题--: 1. Spring AOP中的通知有哪些类型? 2. `@Aspect`注解Spring AOP中的作用是什么? 3. 如何在Spring中配置切点(execution表达)?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值