基于AspectJ实现AOP(注解方式)

基于AspectJ实现AOP(注解方式)

这里我们采用的是非完全注解方式

1. 创建类, 在类里面定义方法(连接点)
package com.ffyc.spring.aop;

//被增强的类(也就是被代理的类)
public class User {
    //被增强的方法(也就是被代理的方法)
    public void add(){
        System.out.println("add...");
    }
}
2. 创建增强类, 编写增强逻辑
  • 注意: 此时我们并没有给增强类添加注解来生成代理对象, 也没有配置通知(增强的逻辑)
package com.ffyc.spring.aop;

//增强类
public class UserProxy {
    //前置通知
    public void before(){
        System.out.println("before");
    }
    //这里还有后置通知, 异常通知, 环绕通知, 最终通知等, 现在逻辑先省略
}
3. 进行通知的配置
①: 在spring配置文件中, 开启注解扫描与开启注解自动生成代理对象
  • 注解扫描大家都知道: 就是componentScan
  • 注解自动生成代理对象是什么?
    • 就是开启了注解自动生成代理对象之后当SpringIOC容器扫描basePackages的时候如果扫描到某个类上面有@Aspect注解, 则会生成该类的代理对象放到SpringIOC容器中由SpringIOC容器管理
      • 所以@Aspect注解就是在增强(通知)类上添加的
<!--    开启组件扫描, 开启了组件扫描之后我们的spring容器就会在对应的base-package位置进行一个扫描-->
    <!--    注意: 其实我们的组件扫描也是可以通过使用注解的方式进行配置-->
        <context:component-scan base-package="com.ffyc.spring"></context:component-scan>

<!--    开启Aspectj自动生成代理对象-->
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
②: 使用注解创建User和UserProxy类的对象
  • 也就是让SpringIOC容器帮我们创建
  • 在User类和UserProxy类上加上@Component注解即可
package com.ffyc.spring.aop;

import org.springframework.stereotype.Component;

//被增强的类(也就是被代理的类)
@Component
public class User {
    //被增强的方法(也就是被代理的方法)
    public void add(){
        System.out.println("add...");
    }
}
package com.ffyc.spring.aop;

import org.springframework.stereotype.Component;

//增强类
@Component
public class UserProxy {
    //前置通知
    public void before(){
        System.out.println("before");
    }
}
③: 在增强类上面添加注解@Aspect
  • 这里注意: @Aspect注解要和@Component注解一起使用, 原因在最后
package com.ffyc.spring.aop;

import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

//增强类
@Component
@Aspect
public class UserProxy {
    //前置通知
    public void before(){
        System.out.println("before");
    }
}
3. 配置不同类型的通知
  • 在增强类里面, 在作为通知的方法上面添加对应的通知类型的注解, 并使用切入点表达式将对应的通知配置到某个切入点上
    • 这样Spring底层就会帮我们根据我们添加的注解来创建代理对象
package com.ffyc.spring.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

//增强类
@Component
@Aspect
public class UserProxy {
    @Pointcut(value = "execution(* com.ffyc.spring.aop.User.add(..))")
    private void method(){
    }

    //前置通知
    @Before(value = "execution (* com.ffyc.spring.aop.User.add(..))")
    public void before(){
        System.out.println("before...");
    }

    @AfterReturning(value = "method()")
    public void afterReturning(){
        System.out.println("afterReturning...");
    }

    @After(value = "execution(* com.ffyc.spring.aop.User.add(..))")
    public void after(){
        System.out.println("after...");
    }

    @AfterThrowing(value = "method()")
    public void afterThrowing(){
        System.out.println("afterThrowing...");
    }

    @Around(value = "method()")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("环绕前...");

        proceedingJoinPoint.proceed();

        System.out.println("环绕之后...");
    }
}
  • @PointCut注解用来创建一个切入点, 该注解的value属性(String类型)为切入点表达式
  • @Around, @Before, @After, @AfterReturning, @AfterThrowing注解的value属性(String类型)都为切入点表达式
  • @AfterThrowing针对的是被代理方法抛出异常, 如果异常在内部被解决, 那么并不会执行@AfterThrowing通知
  • 我们通过JVM的学习可以知道, 如果方法是通过异常结束(指抛出异常), 那么肯定就不是正常退出(return), 那么也就意味着@AfterReturning(后置通知), 和@AfterThrowing(异常通知)只能同时触发一个
  • @Around注解使用的时候要在参数位置预留一个ProceedingJoinPoint类型的参数, 最终会通过这个通知类构建一个被代理类的代理对象, 这个代理对象是由Spring帮我们创建的, 所以这个通知类也是Spring容器帮我们去扫描的, 对应的@Around注解标注的方法肯定也是由Spring帮我们调用的, 而Spring调用的方法我们可以在参数位置写一个形参, 这个时候Spring默认会对该参数类型进行一个自动装配(基于类型的自动装配), 这里就会由Spring帮我们装配一个ProceedingJoinPoint对象, 我们可以调用其中的proceed()方法, 调用proceed()方法就会执行切入点方法(被增强的方法)
    • ProceedingJoinPoint : 正在进行的连接点
4. 测试:
package com.ffyc.spring.aop;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestAop {
    @Test
    public void testAop(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        User user = applicationContext.getBean("user", User.class);
        System.out.println(user);
        user.add();
    }
}
  • 当我们调用user对象的add()方法的时候其实会执行代理对象的add()方法
各个通知的执行顺序:

环绕前[@Around(前)] --> 前置通知[@Before] --> 被增强方法(切入点) --> 环绕后(@Around(后)) --> 最终通知(@After) --> 后置通知[@AfterReturning] (或者异常通知[@AfterThrowing])

补充:

@Aspect注解为什么要和@Component注解一起使用, 按理将使用一个@Aspect注解不是就已经是由SpringIOC荣IQ创建代理对象并交由SpringIOC容器管理了?

**官方文档解释如下: **

You may register aspect classes as regular beans in your Spring XML configuration, or autodetect them through classpath scanning - just like any other Spring-managed bean. However, note that the @Aspect annotation is not sufficient for autodetection in the classpath: For that purpose, you need to add a separate @Component annotation (or alternatively a custom stereotype annotation that qualifies, as per the rules of Spring’s component scanner).

翻译:
您可以在Spring XML配置中注册aspect类,或者通过类路径扫描自动检测它们,就像任何其他Spring管理bean一样。但是,请注意,@aspect注释对于在类路径中自动检测是不够的:为了达到这个目的,您需要添加一个单独的@component注解(或者根据Spring的组件扫描器的规则来定义一个定制的原型注解)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值