笔记7 AOP练习<有疑问>

场景描述:

  核心业务:举行一场古典音乐会。

  周边功能:观众入场,关闭手机、落座,觉得音乐好听时鼓掌,觉都不好听则退票。(切面)

1.编写切点(切点用于准确定位应该在什么地方应用切面的通 知)————即核心业务

首先定义一个Performance接口:

1 package concert;
2 
3 public interface Performance {
4     public void perform();
5 }

2.定义切面,即编写Audience.java。

Audience类使用@Aspect注解进行了标注。该注解表明Audience不仅仅是一个POJO,还是一个切面。

@Pointcut注解能够在一 个切面内定义可重用的切点。

也可以在通知注解中直接使用切点表达式。

 1 package concert;
 2 
 3 import org.aspectj.lang.annotation.AfterReturning;
 4 import org.aspectj.lang.annotation.AfterThrowing;
 5 import org.aspectj.lang.annotation.Aspect;
 6 import org.aspectj.lang.annotation.Before;
 7 import org.aspectj.lang.annotation.Pointcut;
 8 import org.springframework.stereotype.Component;
 9 
10 @Aspect
11 @Component
12 public class Audience {
13     @Pointcut("execution(** concert.Performance.perform(..))")
14     public void perform() {
15 
16     }
17 
18     // @Before("execution(** concert.Performance.perform(..))")
19     @Before("perform()")
20     public void silenceCellPhones() {
21         System.out.println("Silencing cell phone");
22     }
23 
24     // @Before("execution(** concert.Performance.perform(..))")
25     @Before("perform()")
26     public void takeSeats() {
27         System.out.println("Taking seats");
28     }
29 
30     // @AfterReturning("execution(** concert.Performance.perform(..))")
31     @AfterReturning("perform()")
32     public void applause() {
33         System.out.println("CLAP CLAP CLAP");
34     }
35 
36     // @AfterThrowing("execution(** concert.Performance.perform(..))")
37     @AfterThrowing("perform()")
38     public void demandRefund() {
39         System.out.println("Demanding a refund");
40     }
41 }

Audience有四个方法,定义了一个观众在观看演出时可能会做的事 情。在演出之前,观众要就坐(takeSeats())并将手机调至静音 状态(silenceCellPhones())。如果演出很精彩的话,观众应 该会鼓掌喝彩(applause())。不过,如果演出没有达到观众预期 的话,观众会要求退款(demandRefund())。 这些方法都使用了通知注解来表明它们应该在什么时候调用。

在这里也可以使用环绕通知,环绕通知是最为强大的通知类型。它能够让你所编写的逻辑将被通知 的目标方法完全包装起来。实际上就像在一个通知方法中同时编写前 置通知和后置通知,代码如下:

 1      @Around("performs()") // 环绕通知方法
 2      public void watchPerformance(ProceedingJoinPoint jp) {
 3      try {
 4      System.out.println("Silencing cell phone");
 5      System.out.println("Taking seats");
 6      jp.proceed();
 7      System.out.println("CLAP CLAP CLAP");
 8      } catch (Throwable e) {
 9      System.out.println("Demanding a refund");
10      }
11      }

在这里,@Around注解表明watchPerformance()方法会作 为performance()切点的环绕通知。ProceedingJoinPoint作为参数。这个对象是必须要有的,因为要在通知中通过它来调用被通知的方法。通知方法中可以做任何的 事情,当要将控制权交给被通知的方法时,它需要调 用ProceedingJoinPoint的proceed()方法。 

<可以不调用proceed()方法,从而阻塞对被通知方 法的访问,与之类似,也可以在通知中对它进行多次调用。要这样 做的一个场景就是实现重试逻辑,也就是在被通知方法失败后,进行 重复尝试。>

3.定义Java配置文件ConcertConfig.java,使用Spring自动装配。在Java配置文件中启用AspectJ注解的自动代理。

AspectJ自动代理都会为使 用@Aspect注解的bean创建一个代理,这个代理会围绕着所有该切面 的切点所匹配的bean。

 1 package concert;
 2 
 3 import org.springframework.context.annotation.ComponentScan;
 4 import org.springframework.context.annotation.Configuration;
 5 import org.springframework.context.annotation.EnableAspectJAutoProxy;
 6 
 7 @Configuration
 8 @EnableAspectJAutoProxy // 启用AspectJ自动代理
 9 @ComponentScan
10 public class ConcertConfig {
11 
12 }

也可以定义XML配置文件

在Spring中要使用XML来装配bean的话,那么需要使用Spring aop命名空间中的<aop:aspectj-autoproxy>元素启用自动代理。

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4     xmlns:aop="http://www.springframework.org/schema/aop"
 5     xmlns:tx="http://www.springframework.org/schema/tx"
 6     xmlns:context="http://www.springframework.org/schema/context"
 7     xmlns:c="http://www.springframework.org/schema/c"
 8     xsi:schemaLocation="
 9    http://www.springframework.org/schema/beans
10    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
11    http://www.springframework.org/schema/aop
12    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
13    http://www.springframework.org/schema/tx
14    http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
15    http://www.springframework.org/schema/context     
16    http://www.springframework.org/schema/context/spring-context-3.0.xsd">
17           <context:component-scan base-package="concert"></context:component-scan>
18           <aop:aspectj-autoproxy></aop:aspectj-autoproxy>   <!--启用自动代理-->
19          <!-- <bean class="concert.Audience"></bean>
20           <bean class="concert.Classcial"></bean>   -->
21    </beans>

 注:在使用XML进行装配的时候,如果在XML声明了bean后,一定要去掉两个bean原来的@Component注解,且不用使用自动代理;如果不在XML文件中声明bean,在Audience和Classcial中添加@Component注解,则可以启用自动代理。

4.编写测试文件ConcertTest.java

package concert;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = concert.ConcertConfig.class)
public class ConcertTest {
    @Autowired
    private Performance perform;

    @Test
    public void test() {
        perform.perform();
    }
}

疑问:在进行测试时,只能定义接口的对象来进行测试,定义接口的实现类对象时就会报错。

5.结果

两种配置方式结果一样

原文地址:https://www.cnblogs.com/lyj-gyq/p/8881594.html