Spring4笔记7--AspectJ 对 AOP 的实现
AspectJ 对 AOP 的实现:
对于 AOP 这种编程思想,很多框架都进行了实现。Spring 就是其中之一,可以完成面向切面编程。然而,AspectJ 也实现了 AOP 的功能,且其实现方式更为简捷,使用更为方便,而且还支持注解式开发。所以,Spring 又将 AspectJ 的对于 AOP 的实现也引入到了自己的框架中。
在 Spring 中使用 AOP 开发时,一般使用 AspectJ 的实现方式。
AspectJ 中常用的通知有五种类型:
(1)前置通知
(2)后置通知
(3)环绕通知
(4)异常通知
(5)最终通知
其中最终通知是指,无论程序执行是否正常,该通知都会执行。类似于 try..catch 中的finally 代码块。
AspectJ 的切入点表达式 execution():
AspectJ 除了提供了六种通知(还有一种引入通知???)外,还定义了专门的表达式用于指定切入点。表达式的原型是:
execution ( [modifiers-pattern] 访问权限类型
ret-type-pattern 返回值类型
[declaring-type-pattern] 全限定性类名
name-pattern(param-pattern) 方法名(参数名)
[throws-pattern] 抛出异常类型
)
切入点表达式要匹配的对象就是目标方法的方法名。所以,execution 表达式中明显就是方法的签名。注意,表达式中加[ ]的部分表示可省略部分,各部分间用空格分开。在其中可以使用以下符号:
* 0至多个任意字符
.. 用在方法参数中,表示任意多个参数;用在包名后,表示当前包及其子包路径
+ 用在类名后,表示当前类及其子类;用在接口后,表示当前接口及其实现类
AspectJ 对于 AOP 的实现有两种方式:
(1)注解方式
(2)XML 方式
AspectJ 基于注解的 AOP 实现:
Step1:定义业务接口与实现类:
package com.tongji.service; //主业务接口
public interface ISomeService {
public void doSome();
public String doSecond();
public String doThird();
}
package com.tongji.service; //目标类
public class SomeServiceImpl implements ISomeService { @Override
public void doSome() {
System.out.println("执行doSome()方法");
//System.out.println("执行doSome()方法" + 3/0);
} @Override
public String doSecond() {
System.out.println("执行doSecond()方法");
return "China";
} @Override
public String doThird() {
System.out.println("执行doThird()方法");
return "abcde";
} }
Step2:定义切面 POJO 类
该类为一个 POJO 类,将作为切面出现。其中定义了若干普通方法,将作为不同的通知方法。
Step3:在切面类上添加@Aspect 注解
在定义的 POJO 类上添加@Aspect 注解,指定当前 POJO 类将作为切面。
Step4:在 POJO 类的普通方法上添加通知注解
切面类是用于定义增强代码的,即用于定义增强目标类中目标方法的增强方法。这些增强方法使用不同的“通知”注解,会在不同的时间点完成织入。当然,对于增强代码,还要通过 execution 表达式指定具体应用的目标类与目标方法,即切入点。
package com.tongji.aspects; import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
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; @Aspect //表示当前POJO类为切面
public class MyAspect { //定义前置通知方法
@Before("execution(* *..service.*.doSome(..))")
public void myBefore() {
System.out.println("执行前置通知方法myBefore()");
} @Before("execution(* *..ISomeService.doSecond(..))")
public void myBefore(JoinPoint jp) {
System.out.println("执行前置通知方法myBefore() jp = " + jp);
} //定义后置通知方法
@AfterReturning("execution(* *..ISomeService.doSome(..))")
public void myAfterReturning() {
System.out.println("执行后置通知方法myAfterReturning()");
} @AfterReturning(value="execution(* *..ISomeService.doSecond(..))", returning="result")
public void myAfterReturning(Object result) {
System.out.println("执行后置通知方法myAfterReturning() result = " + result);
} //定义环绕通知方法
@Around("execution(* *..ISomeService.doThird(..))")
public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("执行环绕通知方法myAfter() 目标方法执行之前");
//执行目标方法
Object result = pjp.proceed();
System.out.println("执行环绕通知方法myAfter() 目标方法执行之后"); if (result != null) {
result = ((String)result).toUpperCase();
}
return result;
} //定义异常通知方法
@AfterThrowing("execution(* *..service.*.doSome(..))")
public void myAfterThrowing() {
System.out.println("执行异常通知方法myAfterThrowing()");
} @AfterThrowing(value="execution(* *..service.*.doSome(..))", throwing="ex")
public void myAfterThrowing(Exception ex) {
System.out.println("执行异常通知方法myAfterThrowing() ex = " + ex.getMessage());
} //定义最终通知方法
@After("execution(* *..service.*.doSome(..))")
public void myAfter() {
System.out.println("执行最终通知方法myAfter()");
} @After("mySomePointCut()")
public void myAfter2() {
System.out.println("执行最终通知方法myAfter2()");
} //定义切入点方法
@Pointcut("execution(* *..service.*.doSome(..))")
public void mySomePointCut() {
}
}
Step5:注册目标对象与 POJO 切面类
Step6:注册 AspectJ 的自动代理
在定义好切面 Aspect 后,需要通知 Spring 容器,让容器生成“目标类 + 切面”的代理对象。这个代理是由容器自动生成的。只需要在 Spring 配置文件中注册一个基于 aspectj的自动代理生成器,其就会自动扫描到@Aspect 注解,并按通知类型与切入点,将其织入,并生成代理。
<?xml version="1.0" encoding="UTF-8"?>
<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 id="someService" class="com.tongji.service.SomeServiceImpl"/>
<!-- 切面 -->
<bean id="myAspect" class="com.tongji.aspects.MyAspect"/> <!-- AspectJ的自动代理 -->
<aop:aspectj-autoproxy/> </beans>
<aop:aspectj-autoproxy/>的底层是由 AnnotationAwareAspectJAutoProxyCreator 实现的。从其类名就可看出,是基于 AspectJ 的注解适配自动代理生成器。其工作原理是,<aop:aspectj-autoproxy/>通过扫描找到@Aspect 定义的切面类,再由切面类根据切入点找到目标类的目标方法,再由通知类型找到切入的时间点。
Step7:测试类中使用目标对象的 id
package com.tongji.test; import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; import com.tongji.service.ISomeService; public class MyTest { @Test
public void test01() {
String resource = "applicationContext.xml";
@SuppressWarnings("resource")
ApplicationContext ac = new ClassPathXmlApplicationContext(resource);
ISomeService service = (ISomeService) ac.getBean("someService");
service.doSome();
System.out.println("---------------------");
service.doSecond();
System.out.println("---------------------");
String result = service.doThird();
System.out.println(result);
} }
关于切面类的补充:
(1)@Before 前置通知-增强方法有 JoinPoint 参数
在目标方法执行之前执行。被注解为前置通知的方法,可以包含一个 JoinPoint 类型参数。该类型的对象本身就是切入点表达式。通过该参数,可获取切入点表达式、方法签名、目标对象等。
(2)@AfterReturning 后置通知-注解有 returning 属性
在目标方法执行之后执行。由于是目标方法之后执行,所以可以获取到目标方法的返回值。该注解的 returning 属性就是用于指定接收方法返回值的变量名的。所以,被注解为后置通知的方法,除了可以包含 JoinPoint 参数外,还可以包含用于接收返回值的变量。该变量最好为 Object 类型,因为目标方法的返回值可能是任何类型。
(3)@Around 环绕通知-增强方法有 ProceedingJoinPoint 参数
在目标方法执行之前之后执行。被注解为环绕增强的方法要有返回值,Object 类型。并且方法可以包含一个 ProceedingJoinPoint 类型的参数。接口 ProceedingJoinPoint 其有一个proceed()方法,用于执行目标方法。若目标方法有返回值,则该方法的返回值就是目标方法的返回值。最后,环绕增强方法将其返回值返回。该增强方法实际是拦截了目标方法的执行。
(4)@AfterThrowing 异常通知-注解中有 throwing 属性
在目标方法抛出异常后执行。该注解的 throwing 属性用于指定所发生的异常类对象。当然,被注解为异常通知的方法可以包含一个参数 Throwable,参数名称为 throwing 指定的名称,表示发生的异常对象。
(5)@After 最终通知
无论目标方法是否抛出异常,该增强均会被执行。
(6)@Pointcut 定义切入点
当较多的通知增强方法使用相同的 execution 切入点表达式时,编写、维护均较为麻烦。AspectJ 提供了@Pointcut 注解,用于定义 execution 切入点表达式。
其用法是,将@Pointcut 注解在一个方法之上,以后所有的 execution 的 value 属性值均可使用该方法名作为切入点。代表的就是@Pointcut 定义的切入点。这个使用@Pointcute注解的方法一般使用 private 的标识方法,即没有实际作用的方法。
AspectJ基于XML的AOP实现(这个方式是Spring实现AOP的最常用的方式):
AspectJ 除了提供了基于注解的 AOP 的实现外,还提供了以 XML 方式的实现。切面就是一个 POJO 类,而用于增强的方法就是普通的方法。通过配置文件,将切面中的功能增强织入到了目标类的目标方法中。
Step1:定义业务接口与实现类(同上)
Step2:定义切面 POJO 类(没有注解)
Step3:注册目标对象与 POJO 切面类
Step4:在容器中定义 AOP 配置
<?xml version="1.0" encoding="UTF-8"?>
<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 id="someService" class="com.tongji.service.SomeServiceImpl"/>
<!-- 切面 -->
<bean id="myAspect" class="com.tongji.aspects.MyAspect"/> <!-- Aop配置 -->
<aop:config>
<aop:pointcut expression="execution(* *..service.*.doSome(..))" id="doSomePointCut"/>
<aop:pointcut expression="execution(* *..service.*.doSecond(..))" id="doSecondPointCut"/>
<aop:pointcut expression="execution(* *..service.*.doThird(..))" id="doThirdPointCut"/>
<aop:aspect ref="myAspect">
<aop:before method="myBefore" pointcut-ref="doSomePointCut"/>
<aop:before method="myBefore(org.aspectj.lang.JoinPoint)" pointcut-ref="doSomePointCut"/>
<aop:after-returning method="myAfterReturning" pointcut-ref="doSecondPointCut"/>
<aop:after-returning method="myAfterReturning(java.lang.Object)" pointcut-ref="doSecondPointCut" returning="result"/>
<aop:around method="myAround" pointcut-ref="doSecondPointCut"/>
<aop:after-throwing method="myAfterThrowing" pointcut-ref="doSomePointCut"/>
<aop:after-throwing method="myAfterThrowing(java.lang.Exception)" pointcut-ref="doSomePointCut" throwing="ex"/>
<aop:after method="myAfter" pointcut-ref="doSomePointCut"/>
</aop:aspect>
</aop:config>
</beans>
配置文件中,除了要定义目标类与切面的 Bean 外,最主要的是在<aop:config/>中进行aop 的配置。而该标签的底层,会根据其子标签的配置,生成自动代理。
通过其子标签<aop:pointcut/>定义切入点,该标签有两个属性,id 与 expression。分别用于指定该切入点的名称及切入点的值。expression 的值为 execution 表达式。
通过子标签<aop:aspect/>定义具体的织入规则:根据不同的通知类型,确定不同的织入时间;将 method 指定的增强方法,按照指定织入时间,织入到切入点指定的目标方法中。
<aop:aspect/>的 ref 属性用于指定使用哪个切面类。
<aop:aspect/>的子标签是各种不同的通知类型。不同的通知所包含的属性是不同的,但也有共同的属性。
method:指定该通知使用的切面中的增强方法。
pointcut-ref:指定该通知要应用的切入点。
AspectJ 的 6 种通知的 XML 标签如下:
<aop:before/>:前置通知,方法的参数类型必须是全限定类名。
<aop:after-returning/>: 后置通知,有一个属性 returning,指定用于接收目标方法的返回值所使用的变量名
<aop:around/>:环绕通知
<aop:after-throwing/>:异常通知,有一个属性 throwing,指定用于接收目标方法所抛出异常的变量名。
<aop:after/>:最终通知
<aop:declare-parents/>:引入通知???
Step5:测试类中使用目标对象的 id(同上)
Spring4笔记7--AspectJ 对 AOP 的实现的更多相关文章
- Spring4笔记9--Spring的事务管理(AOP应用的例子)
Spring的事务管理: 事务原本是数据库中的概念,在 Dao 层.但一般情况下,需要将事务提升到业务层,即 Service 层.这样做是为了能够使用事务的特性来管理具体的业务. 在 Spring ...
- 吴裕雄--天生自然JAVA SPRING框架开发学习笔记:Spring使用AspectJ开发AOP基于XML和基于Annotation
AspectJ 是一个基于 Java 语言的 AOP 框架,它扩展了 Java 语言.Spring 2.0 以后,新增了对 AspectJ 方式的支持,新版本的 Spring 框架,建议使用 Aspe ...
- Spring @AspectJ 实现AOP 入门例子(转)
AOP的作用这里就不再作说明了,下面开始讲解一个很简单的入门级例子. 引用一个猴子偷桃,守护者守护果园抓住猴子的小情节. 1.猴子偷桃类(普通类): package com.samter.common ...
- AspectJ对AOP的实现
一:你应该明白的知识 1.对于AOP这种编程思想,很多框架都进行了实现.Spring就是其中之一,可以完成面向切面编程.然而,AspectJ也实现了AOP的功能,且实现方式更为简捷,使用更加方便,而且 ...
- Spring框架(6)---AspectJ实现AOP
AspectJ实现AOP 上一篇文章Spring框架(4)---AOP讲解铺垫,讲了一些基础AOP理解性的东西,那么这篇文章真正开始讲解AOP 通过AspectJ实现AOP要比普通的实现Aop要方便的 ...
- 架构探险笔记4-使框架具备AOP特性(上)
对方法进行性能监控,在方法调用时统计出方法执行时间. 原始做法:在内个方法的开头获取系统时间,然后在方法的结尾获取时间,最后把前后台两次分别获取的系统时间做一个减法,即可获取方法执行所消耗的总时间. ...
- 使用java5的注解和Sping/AspectJ的AOP 来实现Memcached的缓存
使用java5的注解和Sping/AspectJ的AOP 来实现Memcached的缓存 今天要介绍的是Simple-Spring-Memcached,它封装了对MemCached的调用,使MemCa ...
- 8 -- 深入使用Spring -- 4...2 使用AspectJ实现AOP
8.4.2 使用AspectJ实现AOP AspectJ是一个基于Java语言的AOP框架.Spring 4.0 的AOP对AspectJ很好的集成. AspectJ是Java 语言的一个AOP实现, ...
- spring3: schema的aop与Aspectj的aop的区别
schema的aop如下: 接口: package chapter6.service; public interface IHelloAroundService { public void sayAr ...
随机推荐
- java异常处理-finally中使用return和throw语句
java异常语句中的finally块通常用来做资源释放操作,如关闭文件.关闭网络连接.关闭数据库连接等.正常情况下finally语句中不应该使用return语句也不应该抛出异常,以下讨论仅限于java ...
- CF235C_Cyclical Quest
很好的一个自动机的题目. 给原串,和若干个询问串.求原串里有多少个不同子串可以通过询问串循环移动得到. 有点类似求两个串的lcs,但是灵活一点. 首先我们把询问串长度扩大一倍,去掉最后一个字符.因为最 ...
- BZOJ5308 ZJOI2018胖
贝尔福特曼(?)的方式相当于每次将所有与源点直接相连的点的影响区域向两边各扩展一格.显然每个点在过程中最多更新其他点一次且这些点构成一段连续区间.这个东西二分st表查一下就可以了.注意某一轮中两点都更 ...
- Divisibility by 25 CodeForces - 988E(模拟)
遇见模拟题 有两种做法 例如这题: 1.直接去算次数(统计哪个数在第几位,然后去运算) 2.模拟操作 贴一个别人的代码...https://blog.csdn.net/weixin_39453270/ ...
- aliyun阿里云alibabaMaven仓库地址——加速你的maven构建
在maven的settings.xml 文件里 搜索 mirrors ,把下面内容添加在其子节点内 <mirror> <id>nexus-aliyun</id> ...
- 从商用到开源:15个维度,全面剖析DB2与MySQL数据库的差异
随着MySQL数据库的应用越来越广泛,DB2向MySQL数据库的迁移需求也越来越多.进行数据库之间迁移的时候,首先遇到的并且也是最基本最重要的就是两种数据库数据类型之间的转换. 相关阅读: 从商用到开 ...
- Java线程Dump分析工具--jstack
jstack用于打印出给定的java进程ID或core file或远程调试服务的Java堆栈信息,如果是在64位机器上,需要指定选项"-J-d64",Windows的jstack使 ...
- 【转】关于在vim中的查找和替换
1,查找 在normal模式下按下/即可进入查找模式,输入要查找的字符串并按下回车. Vim会跳转到第一个匹配.按下n查找下一个,按下N查找上一个. Vim查找支持正则表达式,例如/vim$匹配行尾的 ...
- 编写shell脚本需要特别关注的注意点
shell脚本中的条件判断句式 1. if [ condition ];then statement fi 2. If [ condition ];then statement elif [ cond ...
- GDOI2018记录
说实话,直到初三暑假升高一的时候,我才开始形成竞赛观.那时才顿觉一年的各个比赛是多么重要. 紧接着我的NOIP就直接爆炸了.这意味着我一年也完蛋了.各种人去WC,然后我留在家里. 那GDO ...