zoukankan      html  css  js  c++  java
  • 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....");
        }
    

    参考资料

  • 相关阅读:
    Java实现 蓝桥杯VIP 算法训练 一元三次方程
    Java实现 蓝桥杯VIP 算法训练 乘法表
    Java实现 蓝桥杯VIP 算法训练 矩阵加法
    Java实现 蓝桥杯VIP 算法训练 一元三次方程
    Java实现 蓝桥杯VIP 算法训练 平方计算
    Java实现 蓝桥杯VIP 算法训练 平方计算
    Java实现 蓝桥杯VIP 算法训练 平方计算
    Java实现 蓝桥杯VIP 算法训练 乘法表
    Java实现 蓝桥杯VIP 算法训练 乘法表
    监管只是压倒网盘业务的一根稻草,但不是主要原因(答案只有一个:成本!)
  • 原文地址:https://www.cnblogs.com/linkworld/p/7719841.html
Copyright © 2011-2022 走看看