• Spring 框架的核心功能之AOP技术


    1. AOP 的概述

    • AOP, Aspect Oriented Programming, 面向切面编程;
    • 通过预编译方式和运行期动态代理实现程序功能的统一维护的技术;
    • AOP 采取横向抽取机制,取代了传统纵向继承体系重复性代码(性能监视,事务管理,安全检查,缓存);
    • AOP 可以在不修改源代码的前提下,对程序进行增强;

    2. AOP 的底层实现

    1. Spring 框架的AOP技术底层采用的是代理技术,代理方式分为:

      • 基于JDK的动态代理:必须是面向接口的,只有实现了具体接口的类才能生成代理对象;
      • 基于CGLIB的动态代理: 对于没有实现接口的类,采用生成类的子类的方式;
    2. 基于JDK的动态代理的原理,请参考"动态代理入门"

    3. 基于CGLIB的代理技术(了解)

    // 1. 引入Spring 核心开发包, 其中包含CGLIB的开发 jar包,
    
    // 2. 编写相关代码
    
    // CGLIB 代理类
    public class MyCglibUtils{
        public static BookDaoImpl getProxy(){
            // 创建 CGLIB 的核心类
            Enhancer enhancer = new Enhancer();
    
            // 设置父类
            enhancer.setSuperclass(BookDaoImpl.class);
    
            // 设置回调函数, 参数为匿名内部类
            enhancer.setCallback(new MethodInterceptor(){
                @Override
                public Object intercept(Object obj, Method method, Object[] args,
                                        MethodProxy methodProxy) throws Throwable{
                    if("save".equals(method.getName())){
                        // 添加新功能(例如记录日志)
                        System.out.println("开始记录日志...");
                    }
    
                    // 目标对象的方法正常执行
                    return methodProxy.invokeSuper(obj,args);
                    }
            });
    
            // 生成代理对象
            BookDaoImpl proxy = (BookDaoImpl)enhancer.create();
            return proxy;
        }
    }
    
    
        public class BookDaoImpl{
    
            public void save(){
                System.out.println("保存图书...");
            }
        }
    
        // 测试方法
        public class Demo{
    
            // 没有使用代理之前
            public void fun2(){
                BookDaoImpl dao = new BookDaoImpl();
                dao.save();
            }
    
            // 使用CGLIB代理
            public void fun3(){
    
                BookDaoImpl proxy = MyCglibUtils.getProxy();
                proxy.save();
            }
        }
    

    3. Spring 基于AspectJ的AOP开发

    1. AOP 的相关术语

    • Joinpoint(连接点): 指那些被拦截到的点,在Spring中,这些点指的是类中的所有方法;
    • Pointcut(切入点): 指我们要对哪些Joinpoint进行拦截,也就是需要增强的方法;
    • Advice(通知/增强): 指拦截到Joinpoint之后,所要做的事情,分为前置通知,后置通知,异常通知,最终通知,环绕通知;
    • Introduction(引介): 是一种特殊的通知; 在不修改类代码的前提下,introduction 可以在运行期为类动态的
      添加一些方法或Field;
    • Target(目标对象): 代理的目标对象;
    • Weaving(织入):是指把增强添加到目标对象,创建新的代理对象的过程;
    • Proxy(代理): 一个类被AOP织入增强后,就产生一个结果代理类;
    • Aspect(切面): 是切入点和通知的结合;
    • 其中,通知需要自己来编写,切入点需要自己来配置;

    2. AspectJ的XML方式完成AOP的开发

    2.1 导入 jar 包
    • Spring 框架的基本开发包(6个);
    • Spring 的传统AOP的开发包
      • spring-aop-4.3.10.RELEASE
      • org.aopalliance-1.10.0 (在 Spring 依赖包中)
    • aspectJ 的开发包
      • org.aspectj.weave-1.6.8.RELEASE.jar (在 Spring 依赖包中)
      • spring-aspects-4.3.10.RELEASE.jar
    2.2 编写 applicationContext.xml 配置文件
    // 需要引入具体的AOP的schema约束
    <?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">
    
        <!-- 配置 customerDao -->
        <bean id="customerDao" class="cn.itcast.demo.CustomerDaoImpl"/>
    
        <!-- 配置切面类 -->
        <bean id="myAspectXml" class="cn.itcast.demo.MyAspectXml"/>
    
        <!-- 配置 AOP -->
        <aop:config>
            <!-- 配置切面类: 包含了切入点和通知(类型) -->
            <aop:aspect ref="myAspectXml">
                <!-- 配置前置通知 -->
                <!-- 切入点表达式:
                     execution(public void cn.itcast.demo.CustomerDaoImpl.save()) -->
            <aop:before method="log"
                pointcut="execution(public void cn.itcast.demo.CustomerDaoImpl.save())"/>
    
            </aop:aspect>
        </aop:config>
    </beans>
    
    2.3 创建包结构,编写具体的接口和实现类
    • cn.itcast.demo
      • CustomerDao: 接口
      • CustomerDaoImpl: 实现类
    // CustomerDao.java  接口
        public interface CustomerDao{
            public void save();
            public void update();
        }
    
    // CustomerDaoImpl.java 实现类
        public class CustomerDaoImpl implements CustomerDao{
            public void save(){
                System.out.println("保存客户...");
            }
    
            public void update(){
                System.out.println("修改客户...");
            }
        }
    
    // 测试类
        @RunWith(SpringJUnit4ClassRunner.class)
        @ContextConfiguration("classpath:applicationContext.xml")
        public void Demo{
    
            @Resource(name="customerDao")
            private CustomerDao customerDao;
    
            @Test
            public void fun(){
                customerDao.save();
                customerDao.update();
            }
        }
    
    // 需求: 使用AOP技术,在不改变源代码的情况下,增强 save() 方法
    // 创建切面类
        public class MyAspect{
    
            // 通知(具体的增强)
            public void log(){
                System.out.println("记录日志...");
            }
        }
    
    // 然后在 applicationContext.xml 中配置切面类, 即可完成增强
    

    3. AOP 的切入点表达式

    1. 格式: execution([修饰符] 返回值类型 包名.类名.方法名(参数))
      • 修饰符 public 可以省略不写;
      • 返回值类型不能省略的,可以使用 * 代替,表示返回值为任意类型;
      • 包名,以 cn.itcast.demo.CustomerDaoImpl.save() 为例
        • cn是不能省略的,可以使用 * 代替;
        • 中间的包名可以使用 * 代替;
        • 如果向省略掉中间的包名,可以使用"..",表示任意包下的 save 方法;
      • 类名可以使用 * 代替,也有类似的写法 *DaoImpl;
      • 方法可以使用 * 代替,也有类似的写法 save*()
      • 参数如果是一个参数可以使用 * 代替,如果向代表任意参数,可以使用".."

    4. AOP 的通知类型

    1. 前置通知
      • 在目标类的方法执行之前执行;
      • 具体配置格式: <aop:before method="方法名" pointcut="被增强的方法"/>
      • 应用: 可以对方法的参数来做校验;
    2. 最终通知
      • 在目标类的方法执行之后执行,如果程序出现了异常,最终通知也会执行;
      • 具体配置格式: <aop:after method="方法名" pointcut="被增强的方法"/>
      • 应用:释放资源;
    3. 后置通知
      • 方法正常执行后的通知;
      • 具体配置格式: <aop:after-returning method="方法名" pointcut="被增强的方法"/>
      • 应用: 可以修改方法的返回值;
    4. 异常抛出通知
      • 在抛出异常后的通知;
      • 具体配置格式: <aop:after-throwing method="方法名" pointcut="被增强的方法"/>
      • 应用:包装异常信息;
    5. 环绕通知
      • 方法的执行前后执行;
      • 具体配置格式: <aop:around method="方法名" pointcut="被增强的方法"/>
      • 默认情况下,目标对象的方法不能执行,需要手动让目标对象的方法执行;
    // 环绕通知的增强方法
        public void around(ProceedingJoinPoint joinPoint){
            System.out.println("环绕通知1....");
    
            // 手动执行目标方法
            try{
                joinPoint.proceed();
            }catch(Exception e){
                throw new RuntimeException(e);  
            }
    
            System.out.println("环绕通知2....");
        }
    

    参考资料

  • 相关阅读:
    C#2.0泛型学习--入门篇
    [转自MSDN]如何映射 HRESULT 和异常
    js创建hashtable
    滑雪在日本 之 新泻篇 3
    就算神游 之四:富士山和富士游乐园 2
    就算神游 之四:富士山和富士游乐园 9
    滑雪在日本 之 新泻篇 9
    滑雪在日本 之 新泻篇 14
    就算神游 之四:富士山和富士游乐园 4
    挤地铁的感悟
  • 原文地址:https://www.cnblogs.com/linkworld/p/7719841.html
Copyright © 2020-2023  润新知