标签:
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(切面): 是切入点和通知(引介)的结合
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代理 !
传统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框架 ),提供切面编程 ,编写一个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" />标签:
原文地址:http://blog.csdn.net/zxiang248/article/details/51792633