一、 AOP 概述
1、AOP AspectOriented Programing 面向切面编程
AOP是OOP升华,SOA service-oriented architecture 面向服务架构(web service是SOA实现技术, 分布系统整合)
特点:AOP采取横向抽取机制,取代了传统纵向继承体系 重复性代码
spring的底层采用两种方式进行增强
第一:Spring传统AOP 纯java实现,在运行期,对目标对象进行代理,织入增强代码
第二:AspectJ第三方开源技术,Spring已经整合AspectJ,提供对AspectJ注解的支持,开发AOP程序 更加容易(企业主流)
2、了解AOP 开发中术语
Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方 法类型的连接点.
Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义.
Advice (通知/增强):所谓通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知, 异常通知,最终通知,环绕通知(切面要完成的功能)
Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地 添加一些方法或Field.
Target (目标对象):代理的目标对象
Weaving (织入):是指把增强应用到目标对象来创建新的代理对象的过程.
注:spring采用动态代理织入,而AspectJ采用编译期织入和类装在期织入
Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类
Aspect(切面): 是切入点和通知(引介)的结合
二、 Spring AOP 代理底层实现
第一种 JDK 自带的动态代理技术
JDK动态代理必须基于接口 进行代理
作用:使用代理可以对目标对象进行性能监控(计算运行时间)、安全检查(是否具有权限)、 记录日 志等。
注意:必须要有接口才能进行代理,代理后对象必须转换为接口类型
例:
第二种 CGLIB(CodeGenerationLibrary)是一个开源项目
Spring使用CGlib 进行AOP代理, hibernate 也是支持CGlib(默认使用 javassist )需要下载cglib 的jar包(Spring 最新版本3.2 内部已经集成了cglib ,无需下载cglib的jar )
作用:可以为目标类,动态创建子类,对目标类方法进行代理(无需接口)
原理:Spring AOP 底层,会判断用户是根据接口代理还是目标类代理,如果针对接口代理就使用JDK代理,如果针对目标类代理就使用Cglib代理。
三、 Spring 传统AOP 编程(配置复杂,企业基本不用了)
1、AOP 规范由 AOP 联盟组织(aopalliance)来定义,最初目的采用代理方式,对目标方法进行增强
定义了五种增强方式(Advice)
1) 前置通知 org.springframework.aop.MethodBeforeAdvice
2) 后置通知org.springframework.aop.AfterReturningAdvice
3) 环绕通知org.aopalliance.intercept.MethodInterceptor
4) 异常抛出通知 org.springframework.aop.ThrowsAdvice
5) 引介通知 org.springframework.aop.IntroductionInterceptor
2、 理解Advisor 概念
Advisor是Spring 框架内部提出切面的概念,通常指一个PointCut和一个Advice组合
Advisor和Aspect的区别
Advisor代表一般切面,Advice本身就是一个切面,对目标类所有方法进行拦截
Aspect代表很多切面和切点。Advisor只代表一个切面一个切点。
不带有Pointcut(切点)的切面Advisor:使用一个Advice本身作为一个切面,拦截目标类中所有方法
PointcutAdvisor(有切点的切面):通过定义切点,来指定拦截目标类中哪些方法。
3、案例: 以前置通知为例,制作一个不带有切点的Advisor(拦截所有方法)
在昨天原有6个jar包 基础上 ,导入 2个jar包
spring-aop-3.2.0.RELEASE.jar 、 com.springsource.org.aopalliance-1.0.0.jar(来自AOP联盟)
第一步 : 编写被代理类
ProductService接口 ---- ProductServiceImpl 实现类
第二步 : 编写增强代码
第三步:通过配置,使MyMethodBeforeAdvice作为一个不带切点的切面,应用到ProductServiceImpl 上
通过ProxyFactoryBean 为目标对象,添加Advice增强,生成代理对象
ProxyFactoryBean 可以配置的属性
target: 代理的目标对象
proxyInterfaces : 代理要实现的接口
如果多个接口可以使用以下格式赋值
<list>
<value></value>
....
</list>
proxyTargetClass: 是否对类代理而不是接口,设置为true时,使用CGLib代理
interceptorNames: 需要织入目标的Advice
singleton: 返回代理是否为单实例,默认为单例
optimize: 当设置为true时,强制使用CGLib
小结:先配置目标Bean,再配置Advice增强Bean,通过ProxyFactoryBean为目标Bean生成代理对象, 织入Advice。
4、案例:以环绕通知为例, 定义带有切点的Advisor
JdkRegexpMethodPointcut构造正则表达式切点(指定拦截目标类中某些方法)
其方法切面为RegexpMethodPointcutAdvisor类
第一步: 编写被代理对象 CustomerService
第二步 :编写增强代码 Advice
第三步:配置 applicationContext.xml
注意: 正则表达式写法“ .* ” 代表拦截目标类中所有方法
例:cn\.itcast\.springaop\.service\.CustomerService\.save 只拦截save
cn\.itcast\.springaop\.service\.CustomerService\.s.* 拦截所有s开头的方法
5、自动代理
前面的案例中,每个代理都是通过ProxyFactoryBean织入切面代理,在实际开发中,非常多的Bean每个都配置ProxyFactoryBean开发维护量巨大
自动创建代理应用到的类
BeanNameAutoProxyCreator:根据Bean名称创建代理(针对Bean所有方法)
DefaultAdvisorAutoProxyCreator:根据Advisor本身包含信息创建代理(针对特定的方法)
以上两个案例用自动代理改
1) BeanNameAutoProxyCreator根据Bean名称创建代理,拦截符合名称的Bean中所有方法(类级别)
与原理的配置区别
原来的是有一个被代理的bean和一个增强的bean,BeanNameAutoProxyCreator的配置是采用了后处 理bean的方式,在生成bean的时候进行增强。只有一个bean。
2) DefaultAdvisorAutoProxyCreator根据Advisor本身包含信息创建代理
根据切面配置进行代理 (切面中包含切点信息 )
四、Spring框架使用AspecJ进行AOP编程
AspectJ是一个基于Java语言的AOP框架(第三方)
工程 在基本6个jar包的基础上还需导入
spring-aop-3.2.0.RELEASE.jar
com.springsource.org.aopalliance-1.0.0.jar
spring-aspects-3.2.0.RELEASE.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
AspectJ的开发有两种方式:注解与xml文件
1、使用@AspectJ注解开发AOP程序
使用AspectJ 进行开发
Spring 配置文件applicationContext.xml需要引入 aop用的Schema
<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">
<!-- beandefinitions here -->
</beans>
开启自动代理 <aop:aspectj-autoproxy />
@AspectJ 注解提供六种通知Advice类型
* @Before:前置通知
* @AfterReturning:后置通知
* @Around:环绕通知
* @AfterThrowing:异常抛出通知
* @After:最终通知,相当于代码 finally(之前没有的)
* @DeclareParents:引介通知 (不要求)
注:以上Advice对应注解,都提供value属性,通过属性可以用来定义切点表达式
切点表达式语法:execution(<访问修饰符>? <返回类型><方法名>(<参数>)<异常>)
举例说明:
execution(public* *(..)) ----- 所有public 的方法都进行增强
execution(*cn.itcast.dao.*(..)) ------cn.itcast.dao包中所有方法
execution(*cn.itcast.dao..*(..)) ------ cn.itcast.dao包和其子包中所有方法
execution(*cn.itcast.service.UserService.*(..)) ----- cn.itcast.service.UserService类中所有方法
execution(*cn.itcast.dao.GenericDAO+.*(..)) ------ cn.itcast.dao.GenericDAO 接口所有实现类的方法
execution(*save*(..)) ------- 匹配所有以save开头的方法
注:AspecJ对原有代码进行增强,不需要实现接口,任何JavaBean都可以
例:第一步 : 编写被代理类 EmployeeService
第二步 : 编写增强代码 MyAspect (自定义JavaBean ----作为一个切面)
在切面类内部 ,提供五种Advice 增强方法, 并使用对应注解修饰
第三步:在配置文件中进行配置
注意事项:
1) @Before (前置通知):方法中传入如果传入JoinPoint参数就可以获得拦截的切点信息
2) @AfterReturing (后置通知):方法可以传入JoinPoint用来获得切点信息,也可以传入Object类 型参数用于获得目标业务方法的返回值
3) @Around (环绕通知)方法中传入ProceedingJoinPoint用于控制目标方法的执行
应用: 权限控制 、方法运行时间统计
4) @AfterThrowing (抛出通知):在目标方法出现异常的时候才调用advice,方法中传入 Throwable 对象,代表被捕获的异常
注:异常抛出后目标代码不会继续执行。
5) @After(最终通知):无论目标方法是否出现异常,仍然执行通知 ,效果类似finally
注:在目标方法产生异常后,@AfterReturing不会得到执行
@Pointcut为切点命名
如果每个Advice的注解都定义切点表达式,开发或者维护非常不方便
*通过@Pointcut 注解将切点单独定义出来,只需要在 advice注解中引用定义的切点就可以了
* 定义切点方法规则:private void无参数方法,方法名为切点名
小结:需要掌握的注解:@Aspect@Before @AfterReturning @After @Around @AfterThrowing @Ponitcut
2、 Spring通过xml方式配置 AspectJ
第一步:编写被代理类EmployeeService
第二步:编写切面JavaBean ---- MyAspect2
第三步: 编写增强方法进行配置
注:无需配置自动代理了