SpringInAction.4th.面向切面的Spring
来源:互联网 发布:韩版西装品牌 知乎 编辑:程序博客网 时间:2024/05/21 15:04
SpringInAction.4th.面向切面的Spring
@(spring)[AOP]
面向切面编程,按我的理解就是,在执行一个动作的同时执行一些公共的动作。这些公共的动作没必要每个都写在各自的方法里,可以提取出公共的方法。Spring提供了4种类型的AOP支持:
- 基于代理的经典Spring AOP
- 纯POJO切面
- @AspectJ注解驱动的切面
- 注入式AspectJ切面(适用于Spring各版本)
前三种都是Spring AOP实现的变体,Spring AOP构建在动态代理基础之上,因此,Spring对AOP的支持局限于方法拦截。
- SpringInAction4th面向切面的Spring
- AOP术语
- 简单归纳
- AOP实现
- 编写切点
- 1定义简单的切面
- 2改进切面Pointcut
- 3在JavaConfig中启用AspectJ注解的自动代理
- 创建环绕通知切面
- 使用环绕通知重新实现Audience切面
- 处理通知中的参数
- 通过注解引入新功能
- 编写切点
- 在XML中声明切面
- 为通知传递参数
- 通过切面引入新的功能
- 总结
- AOP术语
AOP术语
spring AOP有很多伤脑筋的术语,没办法为了理解AOP,还是得稍微看下:
1. 通知(Advice):定义了切面是什么以及何时使用。通俗的说通知就是自己定义的要执行的公共的方法(例如发短信方法前后要执行流水操作方法)
2. 连接点(Join point):我们的应用可能也有数以千计的时机应用通知。这些时机被称为连接点。程序执行过程中能够应用通知的所有点
3. 切点(Poincut):切点就定义了“何处”。通俗的说,切点就是在公共方法执行后要执行的方法(或叫那个操作点例如上面的发短信方法)
4. 切面(Aspect):切面是通知和切点的结合。
5. 引入(Introduction):引入允许我们向现有的类添加新方法或属性。
6. 织入(Weaving):织入是把切面应用到目标对象并创建新的代理对象的过程。
简单归纳
1.切面 = 通知+切点:是什么东西在什么时候使用在什么地方使用(说白了切面就类似于代理类吧)
2.切面、连接点都是名词
3.引入和织入是动词,是动作
AOP实现
编写切点
切点的编写格式:
AOP简单实例:
1、定义简单的切面
@Aspectpublic class Audience { @Before("execution(* com.lemontree.spring4.day02.Performance.perform(..))") public void silence(){ System.out.println("phone silence..."); } @Before("execution(* com.lemontree.spring4.day02.Performance.perform(..))") public void taslSeats(){ System.out.println("task seats."); } @AfterReturning("execution(* com.lemontree.spring4.day02.Performance.perform(..))") public void applause(){ System.out.println("Clap..."); } @AfterThrowing("execution(* com.lemontree.spring4.day02.Performance.perform(..))") public void demendRefund(){ System.out.println("demend refund"); }}
2、改进切面@Pointcut
通过@Pointcut注解声明频繁使用的切点表达式,perform 方法本身并不重要,它只是一个标识
@Aspectpublic class Audience { // perform 本身并不重要,它只是一个标识 @Pointcut("execution(* com.lemontree.spring4.day02.Performance.perform(..))") public void perform(){} @Before("perform()") public void silence(){ System.out.println("phone silence..."); } @Before("perform()") public void taslSeats(){ System.out.println("task seats."); } @AfterReturning("perform()") public void applause(){ System.out.println("Clap..."); } @AfterThrowing("perform()") public void demendRefund(){ System.out.println("demend refund"); }}
3、在JavaConfig中启用AspectJ注解的自动代理
如果只是单单的只是作为一个@bean注入就来,那么Spring无法将audience 识别为一个切面,所以使用@EnableAspectJAutoProxy开启自动代理,让audience注册为一个切面{@see AspectJAutoProxyRegistrar.class}。
AspectJ自动代理都会为使用@Aspect注解的bean创建一个代理,这个代理会围绕着所有该切面的切点所匹配的bean。在这种情况下,将会为{basePackge}下的类创建一个代理,Audience类中的通知方法将会在perform()调用前后执行。
/** * spring 配置类 */@Configuration@ComponentScan@EnableAspectJAutoProxy // 启用AspectJ注解的自动代理,让audience注册为一个切面{@see AspectJAutoProxyRegistrar.class}public class SpringConfig { @Bean public Audience audience(){ return new Audience(); }}
注意:测试类中切点中的方法必须是接口方法(涉及到代理的知识点)
创建环绕通知切面
使用环绕通知重新实现Audience切面
需要注意的是,别忘记调用proceed()方法。如果不调这个方法的话,那么你的通知实际上会阻塞对被通知方法的调用(意思就是只会执行上下文方法,而不会去执行通知方法,有可能就是要这种效果)。
/** * 定义环绕通知 */@Aspectpublic class Audience02 { //切点 @Pointcut("execution(* com.lemontree.spring4.day02.Performance.perform(..))") public void perform(){} @Around("perform()") public void warchperform(ProceedingJoinPoint proceedingJoinPoint){ try { System.out.println("silencing cell phone"); proceedingJoinPoint.proceed(); //通知 System.out.println("Clap ..."); } catch (Throwable throwable) { System.out.println("Demanding a refund"); } }}
处理通知中的参数
在切点表达式中声明参数,这个参数传入到通知方法中
public interface Performance { void perform(); void needArg(int num);}@Aspectpublic class TrackAop { @Pointcut("execution(* com.lemontree.spring4.day02.Performance.needArg(int))" + "&&args(num)") public void needArg(int num) {}; @Before("needArg(num)") public void count(int num) { System.out.println("num -> do"+num); }}
调用者传入的参数会入到cont里面。
通过注解引入新功能
利用被称为引入的AOP概念,切面可以为Spring bean添加新方法。
引入其实就是:一个接口a,一个实现类c,一个AOP引入,引入里面定义一个接口b(+号的意思就是子类的意思)子类和前面的实现类c,意思就是说,b的子类可以使用c里面的方法(当转换为接口a类型时才可以)。
/** * @Author: YLBG-YCY-1325 * @Description: * @Date: 2017/8/21 */public interface Encoreable { void doSomething();}public class DefaultEncoreableImpl implements Encoreable{ @Override public void doSomething() { System.out.println("do something...."); }}@Aspectpublic class Encoreableroducer { @DeclareParents(value = "com.lemontree.spring4.day02.Performance+", defaultImpl = DefaultEncoreableImpl.class) public static Encoreable encoreable;}
这么说可能不好理解,看下测试类:
@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(classes = {SpringConfig.class})public class Spring4Test02 { @Autowired private Performance audiencePerform; @Test public void test(){ Encoreable en = (Encoreable) audiencePerform; en.doSomething(); }}
在XML中声明切面
有这样一种原则,那就是基于注解的配置要优于基于Java的配置,基于Java的配置要优于基于XML的配置。但是,如果你需要声明切面,但是又不能为通知类添加注解的时候,那么就必须转向XML配置了。
在Spring的aop命名空间中,提供了多个元素用来在XML中声明切面:
与java 配置类似,切点可以单独提出来进行配置,<\aop:aspectj-autoproxy/> xml配置默认开启aop,所以不需要这个标签也是可以的
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!--委托类--> <bean class="com.lemontree.spring3.Aop.AudiencePerform" id="perform"/> <!--切面1--> <bean class="com.lemontree.spring3.Aop.Audience" id="audience"/> <!--切面2--> <bean class="com.lemontree.spring3.Aop.Audience02" id="audience02"/> <!--开启aop--> <aop:aspectj-autoproxy/> <!--前置、后置--> <aop:config> <aop:aspect id="audience" ref="audience"> <!--定义切点--> <aop:pointcut id="ap" expression="execution(* com.lemontree.spring3.Aop.Performance.perform(..))"/> <aop:before method="silence" pointcut-ref="ap"/> <aop:before method="taslSeats" pointcut-ref="ap"/> <aop:after method="applause" pointcut-ref="ap"/> <aop:after-throwing method="demendRefund" pointcut-ref="ap"/> </aop:aspect> </aop:config> <!--环绕--> <aop:config> <aop:aspect id="audience2" ref="audience02"> <!--定义切点--> <aop:pointcut id="ap" expression="execution(* com.lemontree.spring3.Aop.Performance.perform(..))"/> <aop:around method="warchperform" pointcut-ref="ap"/> </aop:aspect> </aop:config></beans>
来看看切面的定义:
public class Audience { public void silence(){ System.out.println("phone silence..."); } public void taslSeats(){ System.out.println("task seats."); } public void applause(){ System.out.println("Clap..."); } public void demendRefund(){ System.out.println("demend refund"); }}
为通知传递参数
代码演示就去掉了,不过要注意一点,那就是xml中&&会被解析为实体的开始,所以要用and来代替。
通过切面引入新的功能
使用default-impl来直接标识委托和间接使用delegate-ref的区别在于后者是Spring bean,它本身可以被注入、通知或使用其他的Spring配置。(大致和java的配置一样)
<aop:config> <aop:aspect> <aop:declare-parents types-matching="com.lemontree.spring3.Aop.Performance+" implement-interface="com.lemontree.spring3.Aop.Encoreable" delegate-ref="defaultEncoreable"/> <!--default-impl="com.lemontree.spring3.Aop.DefaultEncoreableImpl"/>--> </aop:aspect> </aop:config>
测试类:
<aop:config> <aop:aspect> <aop:declare-parents types-matching="com.lemontree.spring3.Aop.Performance+" implement-interface="com.lemontree.spring3.Aop.Encoreable" default-impl="com.lemontree.spring3.Aop.DefaultEncoreableImpl"/> </aop:aspect> </aop:config>
总结
其实AOP代理本质就是,让别人代你做事情,别人可以在你要做的事情的前后做一些其他的事情。Spring里面就是说,调用者实际调用的不是实际的类(或说是方法),调用者调用的是代理类,代理类方法做完后再去处理实际类的方法。
而引入的本质就是,当你用另外一个接口来声明(或者叫强转)时,你可以用这个接口实现类(引入了的实现类)的方法。
- SpringInAction.4th.面向切面的Spring
- SpringInAction:在Spring中应用切面
- 面向切面的Spring
- 面向切面的spring
- 面向切面的Spring
- 面向切面的Spring
- 面向切面的Spring
- 第4章 面向切面的Spring
- Spring面向切面的编程
- Spring的面向切面AOP
- 面向切面的Spring<一>
- spring(4)面向切面的Spring(AOP)
- 面向切面的 Spring —— 什么是面向切面编程?
- (笔记)Spring实战_面向切面的Spring(4)_注解切面
- Spring 第四章 面向切面的Spring
- Spring入门之面向切面的Spring
- 第4章 面向切面的Spring 笔记1
- 《Spring3实战》摘要(4-1)--面向切面的Spring
- 手机版网页设计html5元素meta name=”viewport”设置需要注意的地方
- js自己封装的开发使用工具方法总结
- 语言层次
- 大数的乘法
- SQL报错型盲注教程(原理全剖析)
- SpringInAction.4th.面向切面的Spring
- caffe相关问题(持续更新。。。。)
- 栈的应用之表达式求值
- 针对二分类问题的线性判别分析模型
- 十九、mcg-helper业务系统单表业务模块自动化生成生成js文件
- 【C#学习】索引函数
- 引用参数和传值参数
- mysql ERROR 1267 (HY000): Illegal mix of collations (utf8mb4_general_ci,COERCIBLE) ....
- mybatis+oracle generator 配置和异常处理