Spring中AOP实现.docx
《Spring中AOP实现.docx》由会员分享,可在线阅读,更多相关《Spring中AOP实现.docx(45页珍藏版)》请在冰豆网上搜索。
Spring中AOP实现
1.什么是SpringAOP
什么是aop:
AspectOrientedProgramming的缩写,面向切面编程,通过预编译和动态代理实现程序功能的
统一维护的一种技术
主要功能:
日志记录,性能统计,安全控制,事务处理,异常处理等
2.SpringAOP框架的用途
提供了声明的企业服务,特别是EJB的替代服务的声明
允许用户控制自己的方面,以完成OOP和AOP的互补使用
OOP:
模拟真实的世界,一切皆是对象
3.AOP的实现方式
下边这两种Spring都是支持的
3.1预编译
-AspectJ 完整的面向切面编程解决方案--》spring不是完整的解决方案,不过spring提供比较好的实现方式,当然spring是同时也是支持这种方式的,这也是一种常用的方式
3.2运行期间动态代理(JDK动态代理,CGLib动态代理)
-SpringAop,JbossAop
Spring的AOP使用纯java实现,无需特殊的编译过程,不需要控制类的加载器层次,目前只支持方法的执行的连接点(通知SpringBean某个方法执行)
不是为了提供完整AOP实现;而是侧重于一种AOP于IOC容器之间的整合,SpringAOP不会AspectJ(完整的AOP解决方案)竞争
Spring没有使用AspectJ的时候,也可以通过如下方式实现AOP
SpringAOP默认使用标准的JavaSE动态代理作为AOP代理,这使得任何接口(或者集合)都可以被代理
SpringAOP中也可以使用CGLIB代理(如果一个业务对象没有实现一个接口)
有接口的:
使用JDK的动态里
无接口的:
使用CGLIB代理
4.SpringAOP中的一些概念
在实际使用SpringAOP之前,了解他的概念是必不可少的一个过程,
SpringAOP主要围绕以下概念展开:
切面(Aspect)一个关注点的模块化,这个关注点可能会横切多个对象
连接点(Joinpoint)程序执行过程中某个特定的连接点
通知(Advice)在切面的某个特的连接点上执行的动作
切入点(Pointcut)匹配连接的断言,在Aop中通知和一个切入点表达式关联
引入(Intruduction)在不修改类代码的前提下,为类添加新的方法和属性
目标对象(TargetObject)被一个或者多个切面所通知的对象
Aop代理(AOPProxy)AOP框架创建的对象,用来实现切面契约(aspectcontract)(包括方法执行等)
织入(Weaving)把切面连接到其他的应用程序类型或者对象上,并创建一个被通知的对象,氛围:
编译时织入,类加载时织入,执行时织入
通知类型Advice:
前置通知(beforeadvice)在某个连接点(jionpoint)之前执行的通知,但不能阻止连接点前的执行(除非抛出一个异常)
返回后通知(afterreturningadvice)在某个连接点(jionpoint)正常执行完后执行通知
抛出异常通知(afterthrowingadvice)在方法异常退出时执行的通知
后通知(after(finally)advice)在方法抛出异常退出时候的执行通知(不管正常返回还是异常退出)
环绕通知(aroundadvice)包围一个连接点(jionpoint)的通知
切入点Pointcut:
SpringAOP占时仅仅支持方法的连接点
execution(public**(..))切入点为执行所有的public方式时
execution(*set*(..))切入点执行所有的set开始的方法时
execution(*com.xyz.service.Account.*(..))切入点执行Account类的所有方法时
execution(*com.xyz.service..(..))切入点执行com.xyz.service包下的所有方法时
execution(*com.xyz.service...(..))切入点执行com.xyz.service包以及其子包的所有的方法时
上边这种方式aspectj和springaop通用的,其他方式可以自己查找资料
5.SpringAOP实现
上边的一些概念,看过后可能还是不懂,可以自行查阅资料,下边我也会说明
我们在Java项目开发中,AOP常见的配置有如下3种:
a.基于SpringAOP容器的实现,使用AspectJ(Spring封装了AspectJ的使用),这是一种比较常用的方式,可以很容易看清代码结构
b.运行期间的动态代理实现,这是一种比较老的实现方式,比较繁琐
c.注解实现,这种方式开发起来简单,但是不利于后期维护,比如说很难找出你所有使用了SpringAOP注解的地方
这里我主要介绍第一种,和第二种的使用
5.1SpringAOP中的容器实现
5.1.1前置通知 before
某个需要切入的方法之前执行切面中的方法 Spring所有的切面通知都必须放在一个config>内(可以配置多个config>元素),每一个config>可以包含pointcut,adviso
和aspect元素(注意这些元素的出现是由顺序的)
配置文件:
spring-aop-schema-advice.xml
声明了切面类,当切入点中匹配到了类名包含BIZ字符串的类时,选取面类中的一个方法,在选取某种Adivice通知,切入该方法的执行过程其中:
aop:
aspect中id的值任意(表意性强) ref的值为切面类的id值 aop:
pointcut中expression写切入点表达式,说明那个方法可能需要做切入点 aop:
before中method的值为切面中的方法名说明切入那个方法,pointcut-ref的值为切入点的id值
xmlversion="1.0"encoding="UTF-8"?
>
//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/beanshttp:
//www.springframework.org/schema/beans/spring-beans-3.2.xsd
http:
//www.springframework.org/schema/aophttp:
//www.springframework.org/schema/aop/spring-aop-3.2.xsd">
--切面类-->
--业务类-->
--aop配置可以配置多个-->
config>
--切面类-->
aspectid="aspectTest"ref="myAspect">
--切入点标识切入点aop包下类名包含Biz的类的所有方法-->
pointcutexpression="execution(*com.briup.spring.aop.bean.annotation.aop.*Biz.*(..))"id="myPointcut"/>
--通知,通过切入点切入切入切面类中的before方法-->
beforemethod="before"pointcut-ref="myPointcut"/>
aspect>
config>
切面类:
切面类中的某个方法,一般是用于切入业务类中的某个方法在某种状态时切入
/*
*声明一个切面类
**/
publicclassMyAspect{
publicvoidbefore(){
System.out.println("aspectbefore");
}
}
业务类:
//业务类
publicclassAspectBiz{
publicvoidbiz(){
System.out.println("Aspectbiz");
}
}
测试:
@Test
publicvoidaspectBefore(){
ClassPathXmlApplicationContextac=newClassPathXmlApplicationContext("com/briup/spring/chap4/spring-aop-schema-advice.xml");
AspectBizaspectBiz=(AspectBiz)ac.getBean("aspectBiz");
aspectBiz.biz();
}
结果:
aspectbefore
Aspectbiz
通过结果可以看出,前置通知在业务类AspectBiz的方法biz执行之前执行了before方法
5.1.2后置通知after-returning
某个需要切入的方法执行完成之后执行切面中指定的方法 配置文件:
spring-aop-schema-advice.xml
config>
--切面类-->
aspectid="aspectTest"ref="myAspect">
--切入点标识切入点aop包下类名以Biz结尾的类的所有方法-->
pointcutexpression="execution(*com.briup.spring.aop.bean.annotation.aop.*Biz.*(..))"id="myPointcut"/>
--通知,通过切入点切入切入切面类中的before方法在执行切入点指定的方法之前执行-->
beforemethod="before"pointcut-ref="myPointcut"/>
--返回之后的通知-->
after-returningmethod="afterReturning"pointcut-ref="myPointcut"/>
aspect>
config>
切面类:
添加afterReturning方法
/*
*声明一个切面类
**/
publicclassMyAspect{
publicvoidbefore(){
System.out.println("aspectbefore");
}
publicvoidafterReturning(){
System.out.println("aspectafterReturning");
}
}
结果:
aspectbefore
Aspectbiz
aspectafterReturning
结果可以看出,后置通知会在业务方法的执行之后
5.1.3异常通知after-throwing
在方法抛出异常后的通知配置文件:
spring-aop-schema-advice.xml
config>
--切面类-->
aspectid="aspectTest"ref="myAspect">
--切入点标识切入点aop包下类名以Biz结尾的类的所有方法-->
pointcutexpression="execution(*com.briup.spring.aop.bean.annotation.aop.*Biz.*(..))"id="myPointcut"/>
--通知,通过切入点切入切入切面类中的before方法在执行切入点指定的方法之前执行-->
beforemethod="before"pointcut-ref="myPointcut"/>
--返回之后的通知-->
after-returningmethod="afterReturning"pointcut-ref="myPointcut"/>
---->
after-throwingmethod="afteThrowing"pointcut-ref="myPointcut"/>
aspect>
config>
切面类:
/*
*声明一个切面类
**/
publicclassMyAspect{
publicvoidbefore(){
System.out.println("aspectbefore");
}
publicvoidafterReturning(){
System.out.println("aspectafterReturning");
}
publicvoidafteThrowing(){
System.out.println("aspectafteThrowing");
}
}
业务类:
修改为如下:
publicvoidbiz(){
System.out.println("Aspectbiz");
thrownewRuntimeException();//出现异常的时候afteThrowing才会执行
}
测试和结果:
aspectbefore
Aspectbiz
aspectafteThrowing
上边的结果由于抛出异常后,不会正常的返回所有没有aspectafterReturning输出
5.1.4最终通知after(finally)advice
不管方法是否会抛出异常都会通知,就像try...catch..finallly配置文件:
spring-aop-schema-advice.xml
config>
--切面类-->
aspectid="aspectTest"ref="myAspect">
--切入点标识切入点aop包下类名以Biz结尾的类的所有方法-->
pointcutexpression="execution(*com.briup.spring.aop.bean.annotation.aop.*Biz.*(..))"id="myPointcut"/>
--通知,通过切入点切入切入切面类中的before方法在执行切入点指定的方法之前执行-->
beforemethod="before"pointcut-ref="myPointcut"/>
--返回之后的通知-->
after-returningmethod="afterReturning"pointcut-ref="myPointcut"/>
---->
after-throwingmethod="afteThrowing"pointcut-ref="myPointcut"/>
--after(finally)advice不管是否抛出异常,最后都会执行的方法-->
aftermethod="after"pointcut-ref="myPointcut"/>
aspect>
config>
切面类:
/*
*声明一个切面类
**/
publicclassMyAspect{
publicvoidbefore(){
System.out.println("aspectbefore");
}
publicvoidafterReturning(){
System.out.println("aspectafterReturning");
}
publicvoidafteThrowing(){
System.out.println("aspectafteThrowing");
}
publicvoidafter(){
System.out.println("aspectafter(finally)");
}
}
业务类:
//业务类
publicclassAspectBiz{
publicvoidbiz(){
System.out.println("Aspectbiz");
thrownewRuntimeException();
}
}
测试结果:
aspectbefore
Aspectbiz
aspectafteThrowing
aspectafter(finally)
从结果中可以看出,抛出异常后,切面类中的after方法还是可以执行
如果业务类如下:
没有异常,
publicclassAspectBiz{
publicvoidbiz(){
System.out.println("Aspectbiz");
}
}
结果:
aspectbefore
Aspectbiz
aspectafter(finally)
由以上结果可以看到,不管怎么样after方法还是会执行,很像try...catch..finally
5.1.5环绕通知around
在方法的执行前后执行通知配置文件:
spring-aop-schema-advice.xml
config>
--切面类-->
aspectid="aspectTest"ref="myAspect">
--切入点标识切入点aop包下类名以Biz结尾的类的所有方法-->
pointcutexpression="execution(*com.briup.spring.aop.bean.annotation.aop.*Biz.*(..))"id="myPointcut"/>
--通知,通过切入点切入切入切面类中的before方法在执行切入点指定的方法之前执行-->
beforemethod="before"pointcut-ref="myPointcut"/>
--返回之后的通知-->
after-returningmethod="afterReturning"pointcut-ref="myPointcut"/>
---->
after-throwingmethod="afteThrowing"pointcut-ref="myPointcut"/>
--after(finally)advice不管是否抛出异常,最后都会执行的方法-->
aftermethod="after"pointcut-ref="myPointcut"/>
--adrounadvice环绕通知,方法的第一参数必须是ProceedingJoinPoint类型-->
aroundmethod="around"pointcut-ref="myPointcut"/>
aspect>
config>
切面类:
可以看出,环绕方法的第一参数必须四ProceedingJoinPoint类型
/*
*声明一个切面类
**/
publicclassMyAspect{
publicvoidbefore(){
System.out.println("aspectbefore");
}
publicvoidafterReturning(){
System.out.println("aspectafterReturning");
}
publicvoidafteThrowing(){
System.out.println("aspectafteThrowing");
}
publicvoidafter(){
System.out.println("aspectafter(finally)");
}
publicvoidaround(ProceedingJoinPointjoinPoint){
Objectobject=null;
try{
System.out.println("aspectaround1");//方法执行前
object=joinPoint.proceed();//代表业务方法的执行
System.out.println("aspectaround1");//方法执行后
}catch(Throwablee){
e.printStackTrace();
}
}
}
测试结果:
aspectaround1
Aspectbiz
aspectaround1
从结果可以看出,joinPoint.proceed其实就是执行业务方法,我们可以在其之前做和之后做一些操作
5.1.6通知中的参数传递这里我们列举环绕通知中的参数参数传递,其他通知也是同样的方式。
配置文件:
spring-aop-schema-advice.xml
xmlversion="1.0"encoding="UTF-8"?
>
//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.sprin