Spring框架 AOP(三)

来源:互联网 发布:手机淘宝哪里看直播 编辑:程序博客网 时间:2024/05/22 20:25

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

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 接口基础上,扩充为五种通知类型
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.xsdhttp://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 切面
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 通知类型

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" />
0 0