4.1 什么是面向切面编程
横切关注点可以被模块化为特殊的类,这些类被称为切面。
好处:1关注点集中在一处,不是分散在多处代码中;2服务(主要的业务)模块更简洁,他们只包含主要关注点(或核心功能)的代码,次要关注点的代码被转移到切面中了;
4.1.1 定义AOP术语
术语:通知(advice),切点(pointcut)和连接点(join point);
通知(advice)定义了切面是什么以及何时使用(定义切面是“什么”及“何时”)。
- Before——在方法被调用之前调用通知
- After——在方法完成之后
- after-returning——在方法成功执行之调用通知
- after-throwing——在方法抛出异常后调用通知
- Around——通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。
连接点(Joinpoint):是在应用执行过程中能够插入切面的一个点。这个点可以是调用的方法时,抛出异常时,修改一个字段时(是一个时机),切面代码可以利用这些点插入到应用的正常流程之中,添加新的行为。
切点(Pointcut):匹配通知所要织入的一个或多个连接点(定义切面在“何处”)
切面(Aspect):是通知和切点的结合,通知和切点共同定义了关于切面的全部内容——是什么,何时、何处完成其功能;
织入(Weaving):是将切面引用到目标对象来创建新的dialing对象的过程,切面在指定的连接点被织入到目标对象中。在目标对象的生命周期中有多个点可以进行织入。
- 编译期——切面在目标类编译时被织入。这种方式需要特殊的编译器。AspectJ的织入编译期就是以这种方式织入切面的。
- 类加载期——切面子啊目标被加载到JVM时被织入。这种方式需要特殊的类加载器(ClassLoader),它可以在目标类被引入引用之前增强该目标类的字节码。AspectJ5的LTW(load-time weaving)就支持以这种方式织入。
- 运行期——切面在应用运行的某个时刻被织入。一般情况下,在织入切面时,AOP容器会为目标对象动态地创建一个代理对象。SpringAOP就是以这种方式织入切面的
4.1.2 Spring对AOP的支持
spring提供了4中AOP支持:
- 基于代理的经典AOP(这本书中不介绍);
- @AspectJ注解驱动的切面
- 纯POJO切面
- 注入式AspectJ切面(适合spring个版本)
前三种都是Spring基于代理的AOP变体,因此Spring对AOP的支持局限于方法拦截。若是AOP需求超过了简单方法的拦截的范凑,那么考虑在AspectJ里实现切面,利用Spring的DI把Spring Bean注入到AspectJ切面中。
4.2 使用切点选择连接点
略过。。。
4.3 在xml中声明切面
4.3.1 声明前置通知和后置通知
声明切面,我们先创建一个观众的Audience切面类:
1 package com.springinaction.springidol; 2 3 /** 4 * 5 * @ClassName: Audience 6 * @Description: 观众的切面类 7 * @author mao 8 * @date 2017年3月23日 下午4:07:01 9 * 10 */ 11 public class Audience { 12 13 public void takeSeats(){ 14 //表演之前 15 System.out.println("观众开始入座!"); 16 } 17 18 public void turnOffCellPhones(){ 19 //表演之前 20 System.out.println("观众正在坐着"); 21 } 22 23 public void applaud(){ 24 //表演之后 25 System.out.println("表演完后,鼓掌!鼓掌!鼓掌!"); 26 } 27 28 public void demandRefund(){ 29 //表演失败之后 30 System.out.println("表演失败,我们要退钱!"); 31 } 32 33 34 35 }
首先,pom.xml文件中引入spring.aspects.jar文件
1 <!-- spring aspects依赖,AOP用到 --> 2 <dependency> 3 <groupId>org.springframework</groupId> 4 <artifactId>spring-aspects</artifactId> 5 <version>4.2.9.RELEASE</version> 6 </dependency>
其次,springbean.xml文件中<beans>标签中需要引入aop的命名空间 :xmlns:aop="http://www.springframework.org/schema/aop" 和xsi:schemaLocation="
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd"
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:context="http://www.springframework.org/schema/context" 5 xmlns:aop="http://www.springframework.org/schema/aop" 6 xmlns:p="http://www.springframework.org/schema/p" 7 xsi:schemaLocation="http://www.springframework.org/schema/beans 8 http://www.springframework.org/schema/beans/spring-beans-4.0.xsd 9 http://www.springframework.org/schema/context 10 http://www.springframework.org/schema/context/spring-context-4.0.xsd 11 http://www.springframework.org/schema/aop 12 http://www.springframework.org/schema/aop/spring-aop-4.0.xsd"> 13 14 15 <!--观众的bean --> 16 <bean id="audience" 17 class="com.springinaction.springidol.Audience"></bean> 18 <context:component-scan 19 base-package="com.springinaction.springidol"></context:component-scan> 20 21 <!-- 声明aop --> 22 <!-- 23 <aop:config>顶层的AOP配置元素,大多数<aop:*>都包在这个元素内 24 --> 25 <aop:config> 26 <!-- 引用audience Bean 27 <aop:aspect>定义切面的元素 28 --> 29 <aop:aspect ref="audience"> 30 31 <!-- 表演之前 --> 32 <!-- <aop:before>定义aop前置通知 33 pointcut=""是切点表达式,和method=""一起用,告诉通知切点在哪 34 整个标签表达出 在哪个类的那个方法执行什么通知 35 --> 36 <aop:before 37 pointcut="execution(* com.springinaction.springidol.Performer.perform(..))" method="takeSeats"/> 38 39 <!-- 表演之后 --> 40 <aop:before 41 pointcut="execution(* com.springinaction.springidol.Performer.perform(..))" 42 method="turnOffCellPhones"/> 43 44 <!-- 表演之后 --> 45 <!-- <aop:after-returning>定义aop返回通知 --> 46 <aop:after-returning 47 pointcut="execution(* com.springinaction.springidol.Performer.perform(..))" 48 method="applaud"/> 49 50 <!-- 表演失败之后 --> 51 <!-- <aop:after-throwing>定义aop异常通知 --> 52 <aop:after-throwing 53 pointcut="execution(* com.springinaction.springidol.Performer.perform(..))" 54 method="demandRefund"/> 55 56 </aop:aspect> 57 </aop:config> 58 59 60 61 62 63 </beans>
1 package com.springinaction.springidol; 2 3 import javax.inject.Inject; 4 5 import javax.inject.Named; 6 7 import org.springframework.beans.factory.annotation.Autowired; 8 import org.springframework.beans.factory.annotation.Qualifier; 9 import org.springframework.beans.factory.annotation.Value; 10 import org.springframework.stereotype.Component; 11 12 13 14 /** 15 * 16 * @ClassName: Instrumentalist 17 * @Description: 一个有天赋的音乐家 18 * @author mao 19 * @date 2017年3月19日 下午7:15:17 20 * 21 */ 22 //将该类注册到Spring容器中,显示的为其命名为kenny。即ID为kenny 23 @Component("kenny") 24 public class Instrumentalist implements Performer { 25 26 @Value("演员---薛之谦") 27 private String song; 28 29 @Autowired 30 @Qualifier("saxophone") 31 private Instrument instrument; 32 33 //注入乐器 34 public void setInstrument(Instrument instrument) { 35 this.instrument = instrument; 36 } 37 public Instrument getInstrument() { 38 return instrument; 39 } 40 //注入歌曲 41 public void setSong(String song) { 42 this.song = song; 43 } 44 public String getSong() { 45 return song; 46 } 47 48 public Instrumentalist(){ 49 50 } 51 52 public void perform() throws Exception { 53 // int i = 3/0; 54 System.out.println("Playing "+song+": "); 55 } 56 57 }
测试:
1 package com.springinaction.test; 2 3 import org.junit.Test; 4 5 6 import org.springframework.context.ApplicationContext; 7 import org.springframework.context.support.ClassPathXmlApplicationContext; 8 9 import com.springinaction.springidol.Auditorium; 10 import com.springinaction.springidol.Instrumentalist; 11 import com.springinaction.springidol.OneManBand; 12 import com.springinaction.springidol.Performer; 13 import com.springinaction.springidol.PoeticJuggler; 14 import com.springinaction.springidol.Stage; 15 import com.springinaction.springidol.Ticket; 16 17 18 public class TestCase { 19 20 //这个ClassPathXmlApplicationContext是springframe-context中 21 //org.springframework.context.support包下的类 22 ApplicationContext ac = new ClassPathXmlApplicationContext("spring/springbean.xml");//注意路径 23 24 //测试声明式的切面、通知 25 @Test 26 public void testAspect() throws Exception { 27 28 Performer kenny = (Performer) ac.getBean("kenny"); 29 // System.out.println(kenny); 30 31 kenny.perform();//输出 Playing 演员——薛之谦: 32 33 34 35 } 36 37 38 }
结果:
测试异常通知在Instrumentalist 的perform()方法中的int i=3/0注释解封,结果:
这里要注意的是,这里的aop使用的是JDK的动态代理,因为Instrumentalist是实现了Perfromer的类,这个是JDK动态代理的条件吧,spring AOP还有个cglib代理,这个是通过创建一个类的子类来获得一个代理(这个子类是底层完成的),两者有区别,这里不详细说了(默认是使用JDK动态代理,若是满足不了JDK动态代理的条件,那就启用cglib代理)。
上述的springbean.xml的aop配置还可以如下配置,简化一下代码:
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:context="http://www.springframework.org/schema/context" 5 xmlns:aop="http://www.springframework.org/schema/aop" 6 xmlns:p="http://www.springframework.org/schema/p" 7 xsi:schemaLocation="http://www.springframework.org/schema/beans 8 http://www.springframework.org/schema/beans/spring-beans-4.0.xsd 9 http://www.springframework.org/schema/context 10 http://www.springframework.org/schema/context/spring-context-4.0.xsd 11 http://www.springframework.org/schema/aop 12 http://www.springframework.org/schema/aop/spring-aop-4.0.xsd"> 13 14 15 <!--观众的bean --> 16 <bean id="audience" 17 class="com.springinaction.springidol.Audience"></bean> 18 <!--注解形式的 组件扫描 --> 19 <context:component-scan 20 base-package="com.springinaction.springidol"></context:component-scan> 21 22 <!-- 声明aop --> 23 <!-- 24 <aop:config>顶层的AOP配置元素,大多数<aop:*>都包在这个元素内 25 --> 26 <!-- <aop:config> 27 引用audience Bean 28 <aop:aspect>定义切面的元素 29 30 <aop:aspect ref="audience"> 31 32 表演之前 33 <aop:before>定义aop前置通知 34 pointcut=""是切点表达式,和method=""一起用,告诉通知切点在哪 35 整个标签表达出 在哪个类的那个方法执行什么通知 36 37 <aop:before 38 pointcut="execution(* com.springinaction.springidol.Performer.perform(..))" method="takeSeats"/> 39 40 表演之后 41 <aop:before 42 pointcut="execution(* com.springinaction.springidol.Performer.perform(..))" 43 method="turnOffCellPhones"/> 44 45 表演之后 46 <aop:after-returning>定义aop返回通知 47 <aop:after-returning 48 pointcut="execution(* com.springinaction.springidol.Performer.perform(..))" 49 method="applaud"/> 50 51 表演失败之后 52 <aop:after-throwing>定义aop异常通知 53 <aop:after-throwing 54 pointcut="execution(* com.springinaction.springidol.Performer.perform(..))" 55 method="demandRefund"/> 56 57 </aop:aspect> 58 </aop:config> --> 59 60 <!-- 上面配置aop的切点表达式是一样的,所以可以写成如下配置 --> 61 <aop:config> 62 <!-- 引用audience Bean --> 63 <aop:aspect ref="audience"> 64 <!-- 定义切点表达式 --> 65 <aop:pointcut id="performance" expression="execution(* com.springinaction.springidol.Performer.perform(..))" /> 66 <!-- 表演之前--> 67 <aop:before 68 pointcut-ref="performance" 69 method="takeSeats"/> 70 <!-- 表演之前--> 71 <aop:before 72 pointcut-ref="performance" 73 method="turnOffCellPhones"/> 74 <!-- 75 表演之后 76 <aop:after-returning>定义aop返回通知 --> 77 <aop:after-returning 78 pointcut-ref="performance" 79 method="applaud"/> 80 81 <!-- 表演失败之后 82 <aop:after-throwing>定义aop异常通知 --> 83 <aop:after-throwing 84 pointcut-ref="performance" 85 method="demandRefund"/> 86 87 </aop:aspect> 88 </aop:config> 89 90 91 </beans>
4.3.2 声明环绕通知
在Audience类中增加如下的方法:
1 //环绕通知 2 public void watchPerformance(ProceedingJoinPoint joinpoint){ 3 try { 4 System.out.println("观众正在坐在自己的位置上---表演之前"); 5 System.out.println("观众关闭他们的手机---表演之前"); 6 long start = System.currentTimeMillis(); 7 8 joinpoint.proceed(); 9 10 long end=System.currentTimeMillis(); 11 12 System.out.println("表演很精彩,大家鼓掌...."); 13 System.out.println("表演耗时:"+(end-start)+"毫秒"); 14 15 } catch (Throwable e) { 16 //表演失败, 17 System.out.println("太糟糕了,我们要退钱!"); 18 } 19 20 21 }
1 <!-- 上面配置aop的切点表达式是一样的,所以可以写成如下配置 --> 2 <aop:config> 3 <!-- 引用audience Bean --> 4 <aop:aspect ref="audience"> 5 <!-- 定义切点表达式 --> 6 <aop:pointcut id="performance" expression="execution(* com.springinaction.springidol.Performer.perform(..))" /> 7 <!-- 表演之前--> 8 <!-- <aop:before 9 pointcut-ref="performance" 10 method="takeSeats"/> --> 11 <!-- 表演之前--> 12 <!-- <aop:before 13 pointcut-ref="performance" 14 method="turnOffCellPhones"/> --> 15 <!-- 16 表演之后 17 <aop:after-returning>定义aop返回通知 --> 18 <!-- <aop:after-returning 19 pointcut-ref="performance" 20 method="applaud"/> --> 21 22 <!-- 表演失败之后 23 <aop:after-throwing>定义aop异常通知 --> 24 <!-- <aop:after-throwing 25 pointcut-ref="performance" 26 method="demandRefund"/> --> 27 28 <!-- 环绕通知 29 将其他通知都注释 30 --> 31 <aop:around 32 pointcut-ref="performance" 33 method="watchPerformance"/> 34 </aop:aspect> 35 </aop:config>
为了看出表演耗时的时间,在perform()方法中加Thread.sleep(1000)(加不加无所谓的)睡1秒;
结果:
4.3.3 为通知传递参数
直接上一个读心者的例子,先创建几个类:
1 package com.springinaction.springidol; 2 3 /** 4 * 读心者类 5 * @ClassName: MindReader 6 * @Description: 可以读取别人的思想 7 * @author mao 8 * @date 2017年3月23日 下午5:23:49 9 * 10 */ 11 public interface MindReader { 12 13 //拦截别人的想法(感应别人的内心想法) 14 void interceptThoughts(String thoughts); 15 16 //获得想法 17 String getThoughts(); 18 }
1 package com.springinaction.springidol; 2 3 /** 4 * 读心者的实现类 5 * @ClassName: Magician 6 * @Description: 可以读取别人想法的具体实例 7 * @author mao 8 * @date 2017年3月23日 下午5:29:12 9 * 10 */ 11 public class Magician implements MindReader { 12 13 14 private String thoughts; 15 16 public void interceptThoughts(String thoughts) { 17 18 System.out.println("感应志愿者的想法!"); 19 this.thoughts = thoughts; 20 21 } 22 23 public String getThoughts() { 24 25 return thoughts; 26 } 27 28 }
1 package com.springinaction.springidol; 2 3 /** 4 * 思考者的接口 5 * @ClassName: Thinker 6 * @Description: 沉思者 7 * @author mao 8 * @date 2017年3月23日 下午5:30:07 9 * 10 */ 11 public interface Thinker { 12 //瞎想 13 void thinkOfSomething(String thoughts); 14 }
1 package com.springinaction.springidol; 2 3 /** 4 * 沉思者 5 * @ClassName: Volunteer 6 * @Description: 一个时代的思想家 7 * @author mao 8 * @date 2017年3月23日 下午5:31:58 9 * 10 */ 11 public class Volunteer implements Thinker { 12 13 private String thoughts; 14 15 public void thinkOfSomething(String thoughts) { 16 17 this.thoughts = thoughts; 18 19 } 20 21 public String getThoughts() { 22 return thoughts; 23 } 24 25 }
xml的配置(晕倒,在切点表达式中把thoughts写错了,找了老半天的错误) arg()限制连接点匹配参数为指定类型的执行方法:
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:context="http://www.springframework.org/schema/context" 5 xmlns:aop="http://www.springframework.org/schema/aop" 6 xmlns:p="http://www.springframework.org/schema/p" 7 xsi:schemaLocation="http://www.springframework.org/schema/beans 8 http://www.springframework.org/schema/beans/spring-beans-4.0.xsd 9 http://www.springframework.org/schema/context 10 http://www.springframework.org/schema/context/spring-context-4.0.xsd 11 http://www.springframework.org/schema/aop 12 http://www.springframework.org/schema/aop/spring-aop-4.0.xsd"> 13 14 15 <!--观众的bean --> 16 <bean id="audience" 17 class="com.springinaction.springidol.Audience"></bean> 18 <!--注解形式的 组件扫描 --> 19 <context:component-scan 20 base-package="com.springinaction.springidol"></context:component-scan> 21 22 <!-- 声明aop --> 23 <!-- 24 <aop:config>顶层的AOP配置元素,大多数<aop:*>都包在这个元素内 25 --> 26 <!-- <aop:config> 27 引用audience Bean 28 <aop:aspect>定义切面的元素 29 30 <aop:aspect ref="audience"> 31 32 表演之前 33 <aop:before>定义aop前置通知 34 pointcut=""是切点表达式,和method=""一起用,告诉通知切点在哪 35 整个标签表达出 在哪个类的那个方法执行什么通知 36 37 <aop:before 38 pointcut="execution(* com.springinaction.springidol.Performer.perform(..))" method="takeSeats"/> 39 40 表演之后 41 <aop:before 42 pointcut="execution(* com.springinaction.springidol.Performer.perform(..))" 43 method="turnOffCellPhones"/> 44 45 表演之后 46 <aop:after-returning>定义aop返回通知 47 <aop:after-returning 48 pointcut="execution(* com.springinaction.springidol.Performer.perform(..))" 49 method="applaud"/> 50 51 表演失败之后 52 <aop:after-throwing>定义aop异常通知 53 <aop:after-throwing 54 pointcut="execution(* com.springinaction.springidol.Performer.perform(..))" 55 method="demandRefund"/> 56 57 </aop:aspect> 58 </aop:config> --> 59 60 61 <!-- 上面配置aop的切点表达式是一样的,所以可以写成如下配置 --> 62 <aop:config> 63 <!-- 引用audience Bean --> 64 <aop:aspect ref="audience"> 65 <!-- 定义切点表达式 --> 66 <aop:pointcut id="performance" expression="execution(* com.springinaction.springidol.Performer.perform(..))" /> 67 <!-- 表演之前--> 68 <!-- <aop:before 69 pointcut-ref="performance" 70 method="takeSeats"/> --> 71 <!-- 表演之前--> 72 <!-- <aop:before 73 pointcut-ref="performance" 74 method="turnOffCellPhones"/> --> 75 <!-- 76 表演之后 77 <aop:after-returning>定义aop返回通知 --> 78 <!-- <aop:after-returning 79 pointcut-ref="performance" 80 method="applaud"/> --> 81 82 <!-- 表演失败之后 83 <aop:after-throwing>定义aop异常通知 --> 84 <!-- <aop:after-throwing 85 pointcut-ref="performance" 86 method="demandRefund"/> --> 87 88 <!-- 环绕通知 89 将其他通知都注释 90 --> 91 <aop:around 92 pointcut-ref="performance" 93 method="watchPerformance"/> 94 </aop:aspect> 95 96 <aop:aspect ref="magician"> 97 <aop:pointcut 98 id="thinking" 99 expression="execution(* com.springinaction.springidol.Thinker.thinkOfSomething(String)) and args(thoughts)" /> 100 101 <aop:before 102 pointcut-ref="thinking" 103 method="interceptThoughts" 104 arg-names="thoughts"/> 105 106 </aop:aspect> 107 </aop:config> 108 109 <bean id="volunteer" class="com.springinaction.springidol.Volunteer"></bean> 110 <bean id="magician" class="com.springinaction.springidol.Magician"></bean> 111 112 </beans>
测试代码:
1 //测试传入参数的通知 2 @Test 3 public void testArgsAdvisor() throws Exception { 4 5 //思考者,先思考 6 Thinker volunteer = (Thinker) ac.getBean("volunteer"); 7 volunteer.thinkOfSomething("你猜我想啥?"); 8 9 //读心者 对他想的啥 10 MindReader magician = (MindReader) ac.getBean("magician"); 11 System.out.println(magician.getThoughts()); 12 13 }
结果:
4.4 注解切面
注解使用:
1 package com.springinaction.springidol; 2 3 import org.aspectj.lang.ProceedingJoinPoint; 4 import org.aspectj.lang.annotation.After; 5 import org.aspectj.lang.annotation.AfterReturning; 6 import org.aspectj.lang.annotation.AfterThrowing; 7 import org.aspectj.lang.annotation.Around; 8 import org.aspectj.lang.annotation.Aspect; 9 import org.aspectj.lang.annotation.Before; 10 import org.aspectj.lang.annotation.Pointcut; 11 import org.springframework.stereotype.Component; 12 13 /** 14 * 15 * @ClassName: Audience 16 * @Description: 观众的切面类 17 * @author mao 18 * @date 2017年3月23日 下午4:07:01 19 * 20 */ 21 @Component//这个不要忘了,注解的自动扫描 会将其加入到IOC容器中 22 @Aspect 23 public class Audience { 24 25 //定义一个可以在@AspectJ切面内可重用的切点 26 @Pointcut("execution(* com.springinaction.springidol.Performer.perform(..))") 27 public void performance(){ 28 29 } 30 31 32 @Before("performance()") 33 public void takeSeats(){ 34 //表演之前 35 System.out.println("观众开始入座!"); 36 } 37 38 @Before("performance()") 39 public void turnOffCellPhones(){ 40 //表演之前 41 System.out.println("观众正在坐着"); 42 } 43 44 @AfterReturning("performance()") 45 public void applaud(){ 46 //表演之后 47 System.out.println("表演完后,鼓掌!鼓掌!鼓掌!"); 48 } 49 50 @AfterThrowing("performance()") 51 public void demandRefund(){ 52 //表演失败之后 53 System.out.println("表演失败,我们要退钱!"); 54 } 55 56 //环绕通知 57 @Around("performance()") 58 public void watchPerformance(ProceedingJoinPoint joinpoint){ 59 try { 60 System.out.println("观众正在坐在自己的位置上---表演之前"); 61 System.out.println("观众关闭他们的手机---表演之前"); 62 long start = System.currentTimeMillis(); 63 64 joinpoint.proceed(); 65 66 long end=System.currentTimeMillis(); 67 68 System.out.println("表演很精彩,大家鼓掌...."); 69 System.out.println("表演耗时:"+(end-start)+"毫秒"); 70 71 } catch (Throwable e) { 72 //表演失败, 73 System.out.println("太糟糕了,我们要退钱!"); 74 } 75 76 77 } 78 79 80 }
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:context="http://www.springframework.org/schema/context" 5 xmlns:aop="http://www.springframework.org/schema/aop" 6 xmlns:p="http://www.springframework.org/schema/p" 7 xsi:schemaLocation="http://www.springframework.org/schema/beans 8 http://www.springframework.org/schema/beans/spring-beans-4.0.xsd 9 http://www.springframework.org/schema/context 10 http://www.springframework.org/schema/context/spring-context-4.0.xsd 11 http://www.springframework.org/schema/aop 12 http://www.springframework.org/schema/aop/spring-aop-4.0.xsd"> 13 14 15 <!--注解形式的 组件扫描 --> 16 <context:component-scan 17 base-package="com.springinaction.springidol"></context:component-scan> 18 <!--使用@AspectJ注解作为指引来创建给予代理的切面,就是把他编程切面,这个@AspectJ类中的有且点表达式和通知 --> 19 <aop:aspectj-autoproxy></aop:aspectj-autoproxy> 20 21 22 23 </beans>
测试结果(测试用例之前写过):
这本书中错误还不少,不过不妨碍阅读:<aop:aspectj-autoproxy> 红色写成了aespectj,还有前面有一个写环绕通知的时候,method="watchPerformance()";括弧是不需要的。
4.5 注入AspectJ切面
表示看不懂。。。