AOP的概述:
AOP Aspect Oriented Programing 面向切面编程 ------- Aspect 切面
Spring AOP:在运行期间为目标对象创建代理对象,并织入增强代码!
AspectJ : Spring本身AOP 编写非常麻烦,整合AspectJ 简化 AOP开发 (重点掌握)
AOP相关术语:
JoinPoint(连接点):所谓连接点就是spring中被拦截到的点(方法),这些点指的就是方法,因为在spring中只支持方法类型的拦截;
PointCut(切入点):意思是要对哪些JointPoint进行拦截;
Advice(通知/增强):拦截到方法后需要织如的增强代码,其中包括 前置增强,后置增强,环绕增强,异常增强,最终增强;
Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field;.
Target(目标对象):需要代理的目标对象;
Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程. spring采用动态代理织入,而AspectJ采用编译期织入和类装在期织入
Proxy(代理):一个类被aop增强后就产生了一个结果代理类
Aspect(切面):= PointCut(切入点) + Advice(通知) ;
代理模式:
为真是的业务对象创建代理对象,通过访问代理对象的方法,间接访目标对象,代理对象控制其目标对象的访问;
代理分为两种 : 静态代理 和 动态代理 ------------ 在实际开发中 静态代理使用很少 ,主要动态代理
1) ,JDK提供的动态代理
* 前提: 必须代理对象要实现接口,返回代理对象,一定转换接口类型
// 不想拦截
return method.invoke(userService, args);
// 想拦截
if(method.getName().equals(方法名)){
// 拦截代码
}
********* 必须针对接口代理 ,没接口不能用jdk代理
2),CGLIB动态代理
* 优点:可以对具体类生成代理 ,不需要接口
* 如果之前版本Spring 需要手动导入 cglib
* Spring3.2 在 core包 整合cglib 开发包
第一步: Enhancer 创建
第二步: enhancer.setSuperClass(Class) ; 根据类型生成代理 (可以是具体类型)
第三步: enhancer.setCallback(new MethodInterceptor(){})
**** MethodInterceptor 相当于 InvocationHandler。0么**** 实现intercept 相当于 invoke
不想拦截
return method.invoke(真实业务对象, args);
想拦截
if(method.getName().equals("xxxx")){
拦截代码
}
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Spring 传统AOP编程及概念
Spring 提供5种 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 在目标类中添加一些新的方法和属性 (不重要)
Spring 提供Advisor (传统Spring 提供切面)
* Aspect 和 Advisor 区别 Aspect 指可以有多个PointCut和多个Advice Advisor 指只能有一个PointCut 和 一个Advice
1) Advisor : Advice 本身就是一个切面,拦截目标类所有方法
2) PointcutAdvisor : 拦截PointCut 指定的方法
使用Spring AOP 必须导入 AOP相关jar (传统AOP)
spring-aop-3.2.0.RELEASE.jar
com.springsource.org.aopalliance-1.0.0.jar (aop联盟jar)
Spring的applicationContext配置AOP内容:
1,通过 ProxyFactoryBean 为目标对象生成代理,织入 定义Advice
ProxyFactoryBean 常见属性 :
1) proxyInterfaces 根据目标接口生成代理
2) interceptorNames 织入Advice
3) target 目标bean
4) optimize 设置true 强制使用CGlib 生成代理
5) proxyTargetClass 设置true 为目标类生成代理,而不是接口
案例一:通过一个Advice 作为一个切面 ,对目标类的所有方法进行拦截
<!-- 为目标对象生成代理对象 -->
<!-- 并没有配置 Advisor, 一个Advice 就是 Advisor , 对目标类 所有方法进行拦截 -->
<bean id="userServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 代理接口 -->
<!-- <property name="proxyInterfaces" value="cn.itcast.service.UserService"></property> -->
<!-- 增强Advice -->
<property name="interceptorNames" value="myMethodBeforeAdvice"></property>
<!-- 配置拦截目标 -->
<property name="target" ref="userService"></property>
<!-- 如果为目标类生成代理 -->
<!-- <property name="proxyTargetClass" value="true"></property> -->
</bean>
案例二:定义切点,只拦截需要控制方法
RegexpMethodPointcutAdvisor 正则表达式 切点切面
<!-- 配置环绕增强 -->
<bean id="myMethodInterceptor" class="cn.itcast.advice.MyMethodInterceptor"></bean>
<!-- 定义带有切点的切面 -->
<bean id="regexAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<!-- 将Advice 配置 Advisor中 -->
<property name="advice" ref="myMethodInterceptor"></property>
<!-- 向patterns参数 传入正则表达式 -->
<property name="patterns" value=".*search.*,.*save.*"></property>
</bean>
<!-- 生成代理对象 -->
<bean id="userServiceProxy3" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces" value="cn.itcast.service.UserService"></property>
<property name="interceptorNames" value="regexAdvisor"></property>
<property name="target" ref="userService"></property>
</bean>
自动代理
BeanNameAutoProxyCreator 根据Bean名称创建代理 (针对Bean所有方法) ---- 例如 100Service,只需要配置一次,为所有Bean方法生成代理
DefaultAdvisorAutoProxyCreator 根据Advisor本身包含信息创建代理 (针对特定的方法) --------- 非常重要
**********************
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
二,AspectJ实现AOP(注解配置)
1,导入jar包 Spring核心4个 日志2个 AOP2个 Aspecj2个
2,配置applicationContext.xml导入aop名称空间
<aop:aspectj-autoproxy />
3,@AspectJ注解提供Advice类型
@Before 前置通知,相当于BeforeAdvice
@AfterReturning 后置通知,相当于AfterReturningAdvice
@Around 环绕通知,相当于MethodInterceptor
@AfterThrowing抛出通知,相当于ThrowAdvice
@After 最终final通知,不管是否异常,该通知都会执行
@DeclareParents 引介通知,相当于IntroductionInterceptor (不要求掌握)
4、切点定义
通过execution 函数定义切入点
语法 : execution(<访问修饰符>?<返回类型><方法名>(<参数>)<异常>)
execution(* *(..)) ---- 匹配所有方法
execution(* cn.itcast.service..*(..)) 不包含子包
execution(* cn.itcast.service.UserService.*(..)) 匹配UserService类所有方法
5,例子,定义一个前置通知:
@Aspect
public class ProductAspect {
@Before(value="execution(* cn.itcast.service.ProductService.*(..))")
public void before(){
System.out.println("前置通知....");
}
}
applicationContext.xml配置:
<aop:aspectj-autoproxy />
<!-- 配置目标bean -->
<bean id="productService" class="cn.itcast.service.ProductServiceImpl"></bean>
<bean id="userService" class="cn.itcast.service.UserServiceImpl"></bean>
<!-- 配置切面 -->
<bean id="productAspect" class="cn.itcast.aspect.ProductAspect"/>
在通知方法传入参数 JoinPoint , 通过JoinPoint 获得当前拦截目标信息
@Before前置通知 : @Before(value="execution(* cn.itcast.service.*Service.*(..))")
@AfterReturning 后置通知 : @AfterReturning(value="execution(* cn.itcast.service.*Service.*(..))",returning="returnValue")
public void afterReturning(JoinPoint joinPoint, Object returnValue){ }
@Around 环绕通知: 传入参数 ProccedingJoinPoint 控制目标方法执行 proceedingJoinPoint.proceed();
@AfterThrowing 异常通知: 发生异常后,通知方法执行
@After 最终通知 : 无论是否发生异常,目标方法都会执行
struts2 提供大量 interceptors 使用AOP 思想