【归纳总结】Spring之AspectJ

AspectJ实际上是对AOP编程思想的一个实践

有二种实践方式:

  1. advisor(写自定义的通知)
  2. aspect(提供现成的通知)

两者都需要有切入点表达式

需要导包:

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

切入点表达式

使用标签 aop:pointcut(注意需要引入相关的schema)

有两种方式:execution群攻) 和 使用注解精准打击

1️⃣execution(群攻)

配置标签
<aop:pointcut id=“切入点id” expression="execution(修饰符 返回值 包名、类名、方法名(形参))/>

所谓群攻就是execution中涵盖的所有方法都会被增强

写法

①修饰符

  • 可以省略,省略不写代表任意修饰符

②返回值

  • 不能省略
  • 可以使用*来通配
  • 如果返回值为pojo,需要写全类名

③包名、类名、方法名

  • 可以部分省略 ,除了头和尾,中间的任意一部分都可以使用…来进行省略
  • 可以通配 ,*来通配一整个词或词的一部分

④形参

  • 可以省略 ,省略表示它(们)是无参方法
  • 可以通配,* 代表的单个任意参数;
    … 任意数量任意类型的参数
  • 和返回值一样,pojo类要写全类名

2️⃣annotation(精准打击)

配置标签
<aop:pointcut id=“切入点id” expression="@annotation(自定义注解的全类名)"/>

所谓精准打击就是注解写在哪个容器组件的方法上,哪个方法就会被增强

精准打击需要自己定义一个注解


Advisor(通知器)

Advisor更侧重于写一个自定义的通知,需要实现接口MethodInterceptor

1️⃣与execution结合(群攻)

例如,现在我们需要让特定的方法执行完成后输出方法执行的时间。

第一步

写一个通知器,实现MethodInterceptor接口,需要重写接口中的invoke方法,methodInvocation.proceed()会执行委托类中需要被增强的方法。

@Component
public class CountTimeAdvice implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        long start = System.currentTimeMillis();
        Object proceed = methodInvocation.proceed();
        long end = System.currentTimeMillis();
        System.out.println(end - start);
        return proceed;
    }
}

第二步

配置advisor和pointcut

<context:component-scan base-package="com.cskaoyan"/>

    <aop:config>
        <aop:pointcut id="pointcut01" expression="execution(* com.cskaoyan.sevice..*(..))"/>

        <aop:advisor advice-ref="countTimeAdvice" pointcut-ref="pointcut01"/>
    </aop:config>

execution括号里所有的方法都会被增强

测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:application.xml")
public class Mytest {

    @Autowired
    OrderService orderService;

    @Test
    public void test01(){
        orderService.method1();
        orderService.method2();
        orderService.method3();
    }
}

测试结果:

method1
0
method2
0
method3
500

2️⃣与annotation结合(精准打击)

与之前一样,我们需要让特定的方法执行完成后输出方法执行的时间。

第一步

同上,写一个通知器,实现MethodInterceptor接口,需要重写接口中的invoke方法,methodInvocation.proceed()会执行委托类中需要被增强的方法。

@Component
public class CountTimeAdvice implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        long start = System.currentTimeMillis();
        Object proceed = methodInvocation.proceed();
        long end = System.currentTimeMillis();
        System.out.println(end - start);
        return proceed;
    }
}

第二步

自定义注解,用来标记需要被增强的方法

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

第三步

配置advisor和pointcut

<context:component-scan base-package="com.cskaoyan"/>

    <aop:config>
        <aop:pointcut id="pointcut02" expression="@annotation(com.cskaoyan.anno.CountTime)"/>

        <aop:advisor advice-ref="countTimeAdvice" pointcut-ref="pointcut02"/>
    </aop:config>

第四步

在需要被增强的方法上添加上该注解

@Service
public class OrderServiceImpl implements OrderService{
    @Override
    public void method1() {
        System.out.println("method1");
    }

    @Override
    @CountTime
    public void method2() {
        System.out.println("method2");
    }

    @Override
    @CountTime
    public void method3() {
        System.out.println("method3");
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

在这里,方法1未添加注解。方法2,3添加了注解,并且方法3睡眠了500毫秒。

测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:application.xml")
public class Mytest {

    @Autowired
    OrderService orderService;
    
    @Test
    public void test01(){
        orderService.method1();
        orderService.method2();
        orderService.method3();
    }
}

测试结果:

method1
method2
1
method3
500

Aspect

不同于advisor的通知以类的方法出现,aspect的通知以方法的形式存在,是一些现成的通知

可以使用配置文件或者注解的方式

一共有五个通知,相较于委托类方法的顺序如下:
在这里插入图片描述

  1. before:在委托类方法执行之前执行
  2. after:在委托类方法执行之后执行,类似于finally,就算委托类方法有异常,也会执行
  3. around:“环绕”着委托类方法执行,需要将连接点作为参数传入,连接点可以实现ProceedingJoinPoint接口也可以实现JoinPoint接口。利用joinPoint.proceed()方法可以调用委托类的方法。(!重要JoinPoint中有很多方法,可以用来获得方法签名、方法参数、委托类对象、代理类对象等等
  4. afterReturning:需要传入一个Object类型的参数,就是方法的执行结果。可以执行结果做一些事情,如:计算出购物车商品的价格后,将该价格记录到数据库。
  5. afterThrowing:将异常作为参数传入,只有委托类方法抛出异常的时候才会执行,如果try-catch了就不会执行

1️⃣使用配置文件

第一步

新建一个类,可以不用实现任何接口,注册成组件

其中的方法名可以任意写,只需要在后续的配置中配置对即可

@Component
public class CustomAspect {

    public void before(){
        System.out.println("before");
    }

    public void after(){
        System.out.println("after");
    }

    //注意返回值为Object
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("around-before");
        Object proceed = joinPoint.proceed();
        System.out.println("around-after");
        return proceed;
    }

    //需要将执行结果传入
    public void afterReturning(Object result){
        System.out.println("afterReturning" + result);
    }

    //将异常作为参数传入
    public void afterThrowing(Exception e){
        System.out.println("afterThrowing" + e.getMessage());
    }

}

第二步

配置:

<context:component-scan base-package="com.cskaoyan"/>

    <aop:config>
        <!--service层的任意方法-->
        <aop:pointcut id="mypointcut" expression="execution(* com.cskaoyan.sevice..*(..))"/>

        <aop:aspect ref="customAspect">
            <!--pointcut和通知的结合
                method属性: 方法名,父标签中的ref属性对应的组件中的方法名 👉 切面组件中的方法名
            -->
            <aop:before method="before" pointcut-ref="mypointcut"/>
            <aop:after method="after" pointcut-ref="mypointcut"/>
            <aop:around method="around" pointcut-ref="mypointcut"/>
            <!--需要通过returning属性指定method对应方法的哪一个参数来接收委托类方法的执行结果-->
            <aop:after-returning method="afterReturning" pointcut-ref="mypointcut" returning="result"/>
            <!--需要通过throwing属性指定method对应方法的哪一个参数来接收委托类方法执行过程中抛出的异常-->
            <aop:after-throwing method="afterThrowing" pointcut-ref="mypointcut" throwing="e"/>
        </aop:aspect>
    </aop:config>

测试:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:application.xml")
public class Mytest2 {

    @Autowired
    UserService userService;

    @Test
    public void test01(){
        userService.sayHello("octavius");
    }
}

测试结果:

before
around-before
hello octavius
afterReturning hello octavius
around-after
after

2️⃣使用注解

第一步

同上

第二步

打开开关:打开使用注解的开关

在配置文件中做如下配置

<context:component-scan base-package="com.cskaoyan"/>

    <aop:aspectj-autoproxy/>

第三步

添加注解

首先,在类上添加注解@Aspect,注明这是一个Aspect类
然后需要在类中新添加一个方法
返回值为void,方法名任意写(作为id),参数不需要,方法体不需要写东西
在方法名上使用@Pointcut注解

后续的方法注解中的value值可以写范围,也可以写@Pointcut注解的id,例子中用后者

@Aspect
@Component
public class CustomAspect {

    //value为范围
    @Pointcut("execution(* com.cskaoyan.sevice..*(..))")
    public void mypointcut(){}

    @Before("mypointcut()")
    public void before(){
        System.out.println("before");
    }

    @After("mypointcut()")
    public void after(){
        System.out.println("after");
    }

    //注意返回值为Object
    @Around("mypointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("around-before");
        Object proceed = joinPoint.proceed();
        System.out.println("around-after");
        return proceed;
    }

    //需要将执行结果传入
    @AfterReturning(value = "mypointcut()",returning = "result")
    public void afterReturning(Object result){
        System.out.println("afterReturning " + result);
    }

    //将异常作为参数传入
    @AfterThrowing(value = "mypointcut()",throwing = "e")
    public void afterThrowing(Exception e){
        System.out.println("afterThrowing " + e.getMessage());
    }

}

测试

测试的办法和结果和之前一模一样

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值