AOP理论概述
Aspect Oriented Programming 面向切面编程
业界 AOP 实际上 OOP (面向对象编程 ) 延伸 —- OOP编程语言、 AOP设计思想
AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码(性能监视、事务管理、安全检查、缓存)
横向抽取代码复用,基于代理技术,在不修改原有对象代码情况下,对原有对象方法功能进行增强! ———- AOP 思想
Spring框架如何实现AOP
Spring1.2 版本开始 开始支持AOP技术 (传统Spring AOP )
Spring2.0之后,为了简化AOP开发,开始支持 AspectJ (第三方框架)AOP 框架
学习重点: AspectJ AOP开发
1.3.AOP相关术语
1.Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点.
2.Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义.
3.Advice(通知/增强):所谓通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
4.Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field.
5.Target(目标对象):代理的目标对象
6.Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程.
7.spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入
8.Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类
9.Aspect(切面): 是切入点和通知(引介)的结合
AOP底层实现
Spring AOP 代理实现有两种: JDK动态代理 和 Cglib框架动态代理
JDK动态代理
JDK API 内置 —- 通过 Proxy类,为目标对象创建代理 (必须面向接口代理 )
public class JdkProxyFactory implements InvocationHandler {
// 被代理对象
private Object target;
// 在构造方法对象时,传入被代理对象
public JdkProxyFactory(Object target) {
this.target = target;
}
// 创建代理
public Object createProxy() {
// 三个参数: 类加载器、 实现接口、 invocationhandler
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("记录日志!!!!!!");
// 调用目标真实方法
// target 被代理对象, args 方法参数 , method 被调用的方法
return method.invoke(target, args);
}
}
缺点: 使用Jdk动态代理,必须要求target目标对象,实现接口 ,如果没有接口,不能使用Jdk动态代理 。
Cglib 动态代理
CGLIB(Code Generation Library)是一个开源项目!是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。
Cglib 不但可以对接口进行代理,也可以对目标类对象,实现代理 (解决了 Jdk 只能对接口代理问题 )
下载网址: http://sourceforge.net/projects/cglib/files/
——- 在spring3.2版本 core包中内置cglib 类
public class CglibProxyFactory implements MethodInterceptor {
// 被代理目标对象
private Object target;
// 在构造工厂时传入被代理对象
public CglibProxyFactory(Object target) {
this.target = target;
}
// 创建代理对象方法
public Object createProxy() {
// 1、 创建Enhancer对象
Enhancer enhancer = new Enhancer();
// 2、 cglib创建代理,对目标对象,创建子类对象
enhancer.setSuperclass(target.getClass());
// 3、传入 callback对象,对目标增强
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("记录日志......");
// 按照JDK编程
return method.invoke(target, args);
}
}
Cglib 创建代理思想: 对目标类创建子类对象
设置 superClass 对哪个类创建子类 (类似 JDK代理 接口)
设置 callback 实现增强代码 (类似 JDK代理 InvocationHandler )
在cglib的callback函数中,要执行被代理对象的方法
method.invoke(target, args); 等价于 methodProxy.invokeSuper(proxy, args);
优先对接口代理 (使用JDK代理),如果目标没有接口,才会使用cglib代理 !
Spring AOP 编程
传统SpringAOP 通知类型(Spring1.2版本 开始)
首先: AOP联盟定义 Advice 通知接口
org.aopalliance.aop.Interface.Advice
然后: Spring AOP 在Advice 接口基础上,扩充为五种通知类型
通过AspectJ 引入Pointcut切点定义
第一步: 实现aop编程,在项目引入 jar包
?com.springsource.org.aopalliance-1.0.0.jar AOP联盟定义规范jar包
?spring-aop-3.2.0.RELEASE.jar Spring对象AOP Advice扩展
?com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar AspectJ框架jar包
?spring-aspects-3.2.0.RELEASE.jar Spring对AspectJ支持 jar包
第二步: 配置文件 ,引入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">
第三步: 编写Advice
/**
* 自定义环绕通知 (传统spring AOP Advice)
*
* @author seawind
*
*/
public class MyMehtodInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("环绕前增强====================");
// 调用目标方法
Object result = methodInvocation.proceed();
System.out.println("环绕后增强====================");
return result;
}
}
第四步: 定义PointCut 切面
execution(* cn.itcast.service...(..)) 拦截service包包含子包下所有类所有方法
execution(* .s(..)) 拦截以s开头的方法
<!-- 配置目标对象 -->
<bean id="orderService" class="cn.itcast.spring.c_aop.OrderServiceImpl" />
<!-- 配置自定义Advice(传统) -->
<bean id="mymethodadvice" class="cn.itcast.spring.c_aop.MyMehtodInterceptor" />
<!-- 配置切面 -->
<!-- 进行aop相关配置 -->
<aop:config>
<!--
aop:advisor: 定义spring传统AOP的切面的, 只支持一个PointCut和 一个Advice
aop:aspect : 定义AspectJ框架切面的 ,可以包含多个PointCut 和 多个Advice
aop:pointcut : 切点定义
-->
<aop:pointcut expression="execution(* cn.itcast.spring.c_aop.OrderServiceImpl.*(..))" id="mypointcut"/>
<aop:advisor advice-ref="mymethodadvice" pointcut-ref="mypointcut"/>
</aop:config>
总结:
AOP Advisor ==== 传统Spring AOP Advice(一个) + 切点(一个)
AspectJ AOP 编程
AspectJ 是一个框架 (第三方AOP框架 ),提供切面编程 ,编写一个Aspect 支持多个Advice和多个PointCut 。
AspectJ 通知类型
相比Spring通知类型,多了一种 After 最终通知
Before前置通知
在目标方法执行前 进行增强代码
AspectJ 提供Advice无需实现任何借口, 可以将很多通知代码 写入一个类 (切面类)
前置通知定义方法: 无返回值,可以传入参数 JoinPoint 连接点
public class MyAspect {
public void before1() {
System.out.println("前置通知 1.....");
}
细节:
1、 默认不能阻止目标方法执行,如果抛出异常,目标方法无法执行
2、 可以传入JoinPoint 连接点参数 , 通过该参数可以获得当前拦截对象和方法信息
<!-- 配置目标 -->
<bean id="customerService" class="cn.itcast.spring.d_aspectj.CustomerService" />
<!-- 配置切面Bean -->
<bean id="myAspect" class="cn.itcast.spring.d_aspectj.MyAspect" />
<!-- AOP切面配置 -->
<aop:config>
<!-- ref 引用定义切面类 -->
<aop:aspect ref="myAspect">
<aop:pointcut expression="execution(* cn.itcast.spring.d_aspectj.CustomerService.*(..))" id="mypointcut2"/>
<aop:before method="before1" pointcut-ref="mypointcut2"/>
<aop:before method="before2" pointcut-ref="mypointcut2"/>
</aop:aspect>
</aop:config>
AfterReturning 后置通知
在目标业务方法执行后,进行代码增强
// 可以在后置通知传入两个参数 1、 连接点对象 2、目标方法返回值
public void afterReturning(JoinPoint joinPoint, Object result) {
System.out.println("后置通知.... 目标方法运行返回值 :" + result);
}
<!-- returning参数,定义后置通知 接收目标方法返回值 参数名称 -->
<aop:after-returning method="afterReturning" returning="result2" pointcut-ref="mypointcut2"/>
细节:
后置通知 可以获得方法的返回值 , 在配置文件定义返回值参数名 必须要和方法参数名一致 。
Around 环绕通知
在目标方法执行前后,进行代码增强 (阻止目标方法的执行 )
环绕通知实现任何通知效果
// 环绕通知 (需要将目标方法 返回值 返回), 传入参数 可执行的连接点
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("环绕通知 方法前执行 ....");
Object result = proceedingJoinPoint.proceed(); // 执行目标方法
System.out.println("环绕通知 方法后执行.... 方法返回值:" + result);
return result;
}
<!-- 环绕通知 -->
<aop:around method="around" pointcut-ref="mypointcut2" />
AfterThrowing 抛出通知
在目标方法出现异常后,通知方法会得到执行 —– 错误日志记录
// 日志记录器
private static final Logger LOG = Logger.getLogger(MyAspect.class);
// 抛出通知
// 第一个参数 JointPoint 连接点
// 第二个参数 目标方法出现异常后,捕获到异常对象
public void afterThrowing(JoinPoint joinPoint, Throwable ex) {
System.out.println("哪个方法出现异常:" + joinPoint.toLongString());
// 调用日志记录API,将异常对象 写入日志文件
LOG.error(ex.getMessage(), ex);
}
方法接收两个参数 连接点和异常对象
<!-- 异常通知 -->
<!-- throwing属性,配置发生异常后,捕获的异常对象参数名称 (和方法一致) -->
<aop:after-throwing method="afterThrowing" throwing="ex" pointcut-ref="mypointcut2"/>
After 最终通知
无论目标方法是否出现异常,该通知都会执行 —— 类似 finally 代码块
应用场景 : 释放资源
// 最终通知
public void after(JoinPoint joinPoint) {
System.out.println("最终通知... 释放资源.... ");
}
<!-- 最终通知 -->
<aop:after method="after" pointcut-ref="mypointcut2" />