Spring实战——面向切面的Spring
来源:互联网 发布:java马士兵整套视频 编辑:程序博客网 时间:2024/05/18 20:09
在我的CSDN博客的Spring分类里面,之前就已经有三篇关于面向切面编程的博客了,最近在阅读《Spring实战》这本书,不禁感叹这确实是一本好书,需要细细的阅读,今天再记述一下Spring AOP的知识,一些AOP的术语,这里就不说了,之前的博客里面都有介绍!下面开始重点:
除了添加Spring所需要的jar包之外,还需要添加aspect相关的jar包,点击下载aspect相关jar包
一、Spring对AOP的支持
Spring提供了4种类型的AOP支持:
- 基于代理的经典Spring AOP;
- 纯POJO切面
- @AspectJ注解驱动切面
- 注入式AspectJ切面(适用于Spring各版本)
前三种都是Spring AOP实现的变体,Spring AOP 构建在动态代理基础之上,因此,Spring对AOP的支持局限于方法拦截。
二、通过切点来选择连接点
切点用于准确定位应该在什么地方应用切面的通知。
1、Spring借助AspectJ的切点表达式语言来定义Spring切面:
AspectJ指示器 描述
arg() 限制连接点匹配参数为指定类型的执行方法
@arg() 限制连接点匹配参数由指定注解标注的执行方法
execution() 用于匹配是连接点的执行方法
this() 限制连接点匹配AOP代理的Bean引用为指定类型的类
target 限制连接点匹配目标对象为指定类型的类
@target() 限制连接点匹配特定的执行对象,这些对象对应的类要具有指定类型的注解
within() 限制连接点匹配指定的类型
@within() 限制连接点匹配指定注解所标注的类型(当使用Spring AOP时,方法定义在由指定的注解所标注的类里)
@annotation 限制匹配带有指定注解的连接点
注意:以上只有execution指示器是实际执行匹配的,其他指示器都是用来限制匹配的。
2、编写切点:
假设有一个业务接口,取名为Performance(这个类在com.mfc.annotation.noparameter包下),接口里有一个方法perform(),现在要写一个切点表达式,这个切点表达式能设置当前的perform()方法执行时处罚通知的调用,那么这个表达式是: execution(* com.mfc.annotation.noparameter.Performance.perform(..))
对这个表达式解释一下:表达式以“*”开始,表名不关心返回值类型,然后使用了全限定类名和方法名,对于方法参数列表,使用了两个点号(..)表明其诶单要选择任意的perform()方法,无论该方法的参数是什么。
三、使用注解创建切面:
1、通知中没有参数:
@AfterReturning 通知方法在目标方法执行成功之后调用
@AfterThrowing 通知方法在目标方法执行抛出异常后调用
@Around 环绕通知:通知方法将目标方法封装起来
@Before 通知方法在目标方法执行之前调用
package com.mfc.annotation.noparameter;/** * 2017年11月18日19:25:40 * 目标接口 * */public interface Performance {public void perform();}
package com.mfc.annotation.noparameter;import org.springframework.stereotype.Component;/** * 2017年11月18日19:26:43 * 目标方法的类,继承了目标接口 * perform()方法作为切点 * */@Component("performance")public class PerformanceImpl implements Performance {@Overridepublic void perform() {System.out.println("-------开始表演!");}}
定义切面:
package com.mfc.annotation.noparameter;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.AfterReturning;import org.aspectj.lang.annotation.AfterThrowing;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.aspectj.lang.annotation.Pointcut;/** * 2017年11月18日19:28:16 * 切面类 * */@Aspectpublic class Audience {/* * 这里的performance()方法只是一个标识,供@Pointcut注解依附,并没有其他意义 * *///定义命名的切点@Pointcut("execution(** com.mfc.annotation.noparameter.Performance.perform(..))")public void performance(){}/* * 其实这个类中可以不用performance()存在,如果performance()不存在: * 一下的通知@Before就要写成@Before("execution(** com.mfc.annotation.Performance.perform(..))") * 如此的话,@After等注解每一个注解后面都要加上("execution(** com.mfc.annotation.Performance.perform(..))") * 代码重复真不是什么光彩的事情,所以有了performance()存在,下面的@After等注解后面就可以直接加上("performance()")了。 * * Spring使用AspectJ注解来声明通知的方法 * @After 通知方法在目标方法执行成功或者抛出异常后调用 * @AfterReturning 通知方法在目标方法执行成功之后调用 * @AfterThrowing 通知方法在目标方法执行抛出异常后调用 * @Around 环绕通知:通知方法将目标方法封装起来 * @Before 通知方法在目标方法执行之前调用 * *//*@Before("performance()")public void beforeAdvice(){System.out.println("前置通知:表演要开始了,请将手机调制静音!");}@AfterReturning("performance()")public void successAfterAdvice(){System.out.println("成功后置通知:表演完美结束,现在开始鼓掌!");}@AfterThrowing("performance()")public void failAfterAdvice(){System.out.println("失败后置通知:表演出意外了,请大家谅解!");}*///环绕通知@Around("performance()")public void aroundAdvice(ProceedingJoinPoint jp){try {System.out.println("前置通知:表演要开始了,请将手机调制静音!");jp.proceed();System.out.println("成功后置通知:表演完美结束,现在开始鼓掌!");} catch (Throwable e) {System.out.println("失败后置通知:表演出意外了,请大家谅解!");e.printStackTrace();}}}
再看看java的配置类,这里使用了自动装配和java装配混合:
package com.mfc.annotation.noparameter;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.EnableAspectJAutoProxy;/** * 2017年11月18日19:52:35 * java配置类,这里采用了bean的自动装配 * @EnableAspectJAutoProxy注解是启用Spring的自动代理功能 * 也可以在xml配置文件中使用<aop:aspectj-autoproxy />代替@EnableAspectJAutoProxy * */@Configuration@EnableAspectJAutoProxy@ComponentScan("com.mfc.annotation")public class JavaConfig {@Beanpublic Audience audience(){return new Audience();}}
最后看测试类:
package com.mfc.annotation.noparameter;import org.springframework.context.ApplicationContext;import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class JavaConfigAopTest {public static void main(String[] args) {ApplicationContext applicationContext = new AnnotationConfigApplicationContext(JavaConfig.class);Performance performance = (Performance) applicationContext.getBean("performance");performance.perform();}}
测试结果:
2、通知中有参数
业务类接口及其实现:
package com.mfc.annotation.hasparameter;/** * 2017年11月18日21:15:58 * 接口 * */public interface CompactDisc {public void play();public void playTrack(String trackNumber);}
package com.mfc.annotation.hasparameter;import java.util.List;/** * 2017年11月18日21:16:51 * 实现类 * */public class BlankDisc implements CompactDisc {private String title;private String artist;private List<String> tracts;public void setTitle(String title) {this.title = title;}public void setArtist(String artist) {this.artist = artist;}public void setTracts(List<String> tracts) {this.tracts = tracts;}@Override public void play() {System.out.println("Playing "+title+" by "+artist);for (String string : tracts) {System.out.println("-Track : "+string);}}@Overridepublic void playTrack(String trackNumber){System.out.println("播放了:"+trackNumber);}}
定义切面:
package com.mfc.annotation.hasparameter;import java.util.HashMap;import java.util.Map;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.aspectj.lang.annotation.Pointcut;/** * 2017年11月18日21:35:33 * 切面类 * */@Aspectpublic class TrackCounter {Map<String, Integer> trackCounts = new HashMap<String, Integer>();//通知playTrack方法/* * 这个切点表达式应该关注的是表达式中的args(trackNumber)限定符。 * 它表明传递给playTrack()方法的String类型参数也会传递到通知中去, * 参数的名称trackNumber也与切点方法签名中的参数相匹配 * */@Pointcut("execution(* com.mfc.annotation.hasparameter.CompactDisc.playTrack(String)) && args(trackNumber)")public void trackPlayer(String trackNumber){}@Before("trackPlayer(trackNumber)")public void countTrack(String trackNumber){System.out.println(trackNumber+"触发了这个前置通知");int count = getPlayCount(trackNumber);trackCounts.put(trackNumber, count+1);}public int getPlayCount(String trackNumber){//containsKey判断map集合中是否存在制定的键名return trackCounts.containsKey(trackNumber)?trackCounts.get(trackNumber):0;}}
配置java:
package com.mfc.annotation.hasparameter;import java.util.ArrayList;import java.util.List;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.EnableAspectJAutoProxy;/** * 2017年11月18日21:36:07 * java配置类 * */@Configuration@EnableAspectJAutoProxypublic class TrackCounterConfig {@Bean(name = "compactDisc")public CompactDisc sgtPepers(){BlankDisc blankDisc = new BlankDisc();blankDisc.setTitle("测试标题");blankDisc.setArtist("测试艺术家");List<String> tracks = new ArrayList<String>();tracks.add("测试数据一");tracks.add("测试数据二");tracks.add("测试数据三");tracks.add("测试数据四");return blankDisc;}@Bean(name = "trackCounter")public TrackCounter trackCounter(){return new TrackCounter();}}
测试:
package com.mfc.annotation.hasparameter;import org.springframework.context.ApplicationContext;import org.springframework.context.annotation.AnnotationConfigApplicationContext;/** * 2017年11月18日21:40:44 * 测试类 * */public class TrackCounterTest {public static void main(String[] args) {ApplicationContext applicationContext = new AnnotationConfigApplicationContext(TrackCounterConfig.class);CompactDisc compactDisc = (CompactDisc) applicationContext.getBean("compactDisc");TrackCounter counter = (TrackCounter) applicationContext.getBean("trackCounter");compactDisc.playTrack("A");compactDisc.playTrack("A");compactDisc.playTrack("B");compactDisc.playTrack("C");compactDisc.playTrack("B");compactDisc.playTrack("B");System.out.println("A--------"+counter.getPlayCount("A"));System.out.println("B--------"+counter.getPlayCount("B"));System.out.println("C--------"+counter.getPlayCount("C"));}}
测试结果:
3、通过注解引入新功能:
一般情况,一个java类写好了就这些功能,除非修改这个java类,否则它不会有新的功能,现在Spring可以实现一个功能:写好一个java类之后,不用修改它,给它添加新的功能:
基础功能的业务类接口及其实现:
package com.mfc.annotation.newfunction;public interface BaseFunction {public void baseFunction();}
package com.mfc.annotation.newfunction;import org.springframework.stereotype.Component;@Component("baseFunction")public class BaseFunctionImpl implements BaseFunction {@Overridepublic void baseFunction() {System.out.println("这是BaseFunctionImpl的基础功能方法");}}
新添加功能的业务类及其接口:
package com.mfc.annotation.newfunction;public interface NewFunction {public void newFunction();}
package com.mfc.annotation.newfunction;import org.springframework.stereotype.Component;@Component("newFunction")public class NewFunctionImpl implements NewFunction {@Overridepublic void newFunction() {System.out.println("这是通过NewFunctionImpl给BaseFunctionImpl添加的新功能方法");}}
AOP配置:
package com.mfc.annotation.newfunction;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.DeclareParents;import org.springframework.stereotype.Component;@Component@Aspectpublic class AopConfig {/** * 2017年11月18日22:32:17 * value 指定了那种类型的Bean要引入该接口,在本例中,就是所有实现了BaseFunction的类型,加号代表BaseFunction的所有子类,而不是它本身 * defaultImpl 指定了为引入功能提供实现的类 * */@DeclareParents(value = "com.mfc.annotation.newfunction.BaseFunction+",defaultImpl = NewFunctionImpl.class)public static NewFunction function;}
java配置:
package com.mfc.annotation.newfunction;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.EnableAspectJAutoProxy;/** * 2017年11月18日22:16:07 * java配置文件 * */@Configuration@EnableAspectJAutoProxy@ComponentScan("com.mfc.annotation.newfunction")public class JavaConfig {}
测试类:
package com.mfc.annotation.newfunction;import org.springframework.context.ApplicationContext;import org.springframework.context.annotation.AnnotationConfigApplicationContext;/** * 2017年11月18日22:35:14 * 测试类 * */public class Test {public static void main(String[] args) {ApplicationContext applicationContext = new AnnotationConfigApplicationContext(JavaConfig.class);BaseFunction baseFunction = (BaseFunction) applicationContext.getBean("baseFunction");baseFunction.baseFunction();NewFunction newFunction = (NewFunction) baseFunction;newFunction.newFunction();}}
测试结果:
四、在XML中声明切面:
在《Spring实战》中有一句话:基于注解的配置要由于基于java的配置,基于java的配置要基于XML的配置。但是,如果需要声明切面,又不能为通知类添加注解,就必须使用xml配置。
1、通知中没有参数:
Spring的AOP配置元素能够以非侵入性的方式声明切面
AOP配置元素 用途
<aop:advisor> 定义AOP通知器
<aop:after> 定义AOP后置通知(不管被通知的方法是否执行成功)
<aop:after-returning> 定义AOP返回通知(被通知方法执行成功后通知)
<aop:after-throwing> 定义AOP异常通知(被通知方法执行失败后通知)
<aop:around> 定义AOP环绕通知
<aop:aspect> 定义一个切面
<aop:aspectj-autoproxy> 启用@Aspect注解(自动代理Aspect注解的通知类)
<aop:before> 定义一个AOP前置通知
<aop:config> 顶层的AOP配置元素,大多数的<aop:*>元素必须包含在<aop:config>元素内
<aop:declare-parents> 以透明的方式为被通知的对象引入额外的接口
<aop:pointcut> 定义一个切点
示例:
业务类接口与实现跟上面(三)中的(1)一样,Performance的接口以及它的实现类。切面类把(三)中的(1)的切面类(Audience.java)里面的注解全部删掉即可。
看XML配置文件:
<?xml version="1.0" encoding="UTF-8"?><beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:p="http://www.springframework.org/schema/p"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd"><bean id="performanceImpl" class="com.mfc.xml.noparameter.PerformanceImpl"></bean><bean id="audience" class="com.mfc.xml.noparameter.Audience"></bean><aop:config><aop:aspect ref="audience"><aop:pointcut expression="execution(* com.mfc.xml.noparameter.Performance.perform(..))" id="performance"/><!-- <aop:before method="beforeAdvice" pointcut-ref="performance"/><aop:after-returning method="successAfterAdvice" pointcut-ref="performance"/><aop:after-throwing method="failAfterAdvice" pointcut-ref="performance"/>--><aop:around method="aroundAdvice" pointcut-ref="performance"/></aop:aspect></aop:config></beans>
测试类:
package com.mfc.xml.noparameter;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class XmlConfigTest {public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("noparameter.xml");Performance performance = (Performance) applicationContext.getBean("performanceImpl");performance.perform();}}
测试结果:
2、通知中有参数:
业务类接口及其实现跟上面(三)中的(2)一样,CompactDisc的接口一起它的实现类。切面类把(三)中的(2)的切面类(TrackCounter.java)里面的注解全部删掉即可。
xml的配置:
<?xml version="1.0" encoding="UTF-8"?><beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:p="http://www.springframework.org/schema/p"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd"><bean id="trackCounter" class="com.mfc.xml.hasparameter.TrackCounter"></bean><bean id="blankDisc" class="com.mfc.xml.hasparameter.BlankDisc"><property name="title" value="测试标题"></property><property name="artist" value="测试内容"></property><property name="tracts"><list><value>测试数据一</value><value>测试数据二</value><value>测试数据三</value></list></property></bean><aop:config><aop:aspect ref="trackCounter"><aop:pointcut expression="execution(* com.mfc.xml.hasparameter.CompactDisc.playTrack(String)) and args(trackNumber)" id="performance"/><aop:before method="countTrack" pointcut-ref="performance"/></aop:aspect></aop:config></beans>
测试类:
package com.mfc.xml.hasparameter;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;/** * 2017年11月18日21:40:44 * 测试类 * */public class TrackCounterTest {public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("hasparameter.xml");CompactDisc compactDisc = (CompactDisc) applicationContext.getBean("blankDisc");TrackCounter counter = (TrackCounter) applicationContext.getBean("trackCounter");compactDisc.playTrack("A");compactDisc.playTrack("A");compactDisc.playTrack("B");compactDisc.playTrack("C");compactDisc.playTrack("B");compactDisc.playTrack("B");System.out.println("A--------"+counter.getPlayCount("A"));System.out.println("B--------"+counter.getPlayCount("B"));System.out.println("C--------"+counter.getPlayCount("C"));}}
测试结果:
2、通过切面引入新的功能
基础功能的业务类接口及其实现和新添加功能的业务类及其接口和(三)中(3)一样,这里可以不要java配置类JavaConfig.java了。
xml配置:
<?xml version="1.0" encoding="UTF-8"?><beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:p="http://www.springframework.org/schema/p"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd"><bean id="baseFunctionImpl" class="com.mfc.xml.newfunction.BaseFunctionImpl"></bean><bean id="newFunctionImpl" class="com.mfc.xml.newfunction.NewFunctionImpl"></bean><aop:config><aop:aspect><aop:declare-parents types-matching="com.mfc.xml.newfunction.BaseFunction+" implement-interface="com.mfc.xml.newfunction.NewFunction"default-impl="com.mfc.xml.newfunction.NewFunctionImpl"/></aop:aspect></aop:config></beans>
测试类:
package com.mfc.xml.newfunction;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;/** * 2017年11月18日22:35:14 * 测试类 * */public class Test {public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("newfunction.xml");BaseFunction baseFunction = (BaseFunction) applicationContext.getBean("baseFunctionImpl");baseFunction.baseFunction();NewFunction newFunction = (NewFunction) baseFunction;newFunction.newFunction();}}
测试结果:
源码下载
- Spring实战——面向切面的Spring
- Spring实战——面向切面的Spring
- 面向切面的 Spring —— 什么是面向切面编程?
- Spring实战笔记——面向切面编程(一)
- 面向切面的 Spring —— 如何注入 AspectJ 切面?
- Spring实战读书笔记 第四章 面向切面的Spring
- 《Spring实战》学习笔记-第四章:面向切面的Spring
- 《Spring实战》学习笔记-第四章:面向切面的Spring
- 《Spring实战》学习笔记(三)面向切面的Spring
- Spring实战-读书笔记(章节四)-面向切面的Spring
- 《Spring实战》学习笔记-第四章:面向切面的Spring
- 面向切面的Spring
- 面向切面的spring
- 面向切面的Spring
- 面向切面的Spring
- 面向切面的Spring
- (笔记)Spring实战_面向切面的Spring(1)_什么是面向切面编程
- Spring面向切面编程——什么是面向切面编程
- COW(copy on write), SSO (small string optimization)浅析
- 了解c#中的三层架构(DAL,BLL,UI)
- 为什么你会Excel快捷键可还是比别人效率低?
- 数据比赛 Pandas 相关用法
- C#个人重构之注册、退卡
- Spring实战——面向切面的Spring
- 参考java初级工程师面试题添加了一点知识点和链接
- IA-32 Intel手册学习笔记(二)保护模式下的内存管理
- MySQL insert into select
- MATLAB 之 图像小波变换函数
- win7安装node,win7卸载node,win7安装npm ,淘宝镜像,win7webpack打包错误解决
- java IText 导出word表格
- “default”标签跳过“ ”的初始化操作问题解决
- 无人便利店抢人饭碗?这些“黑科技”将创造百万就业