• Spring-AOP


    1.AOP概念

    Aspect Oriented Programming 面向切面编程

    AOP概述

    ●AOP(Aspect-Oriented Programming,面向切面编程):是一种新的方法论,
    是对传统 OOP(Object-Oriented Programming,面向对象编程)的补充。
    ●参见图例和doc文档解释AOP的各个术语!
    ●Spring的AOP既可以使用xml配置的方式实现,也可以使用注解的方式来实现!

    2.作用:本质上来说是一种简化代码的方式

    继承机制

    封装方法

    动态代理

    ……

    3.情景举例


    ①数学计算器接口[MathCalculator]

    int add(int i,int j);
    int sub(int i,int j);
    int mul(int i, int j);
    int div(int i,int j

    ②提供简单实现[EasyImpl]


    ③在简单实现的基础上让每一个计算方法都能够打印日志[LoginImpl]

    ④缺陷

    [1]手动添加日志繁琐,重复

    [2]统一修改不便
    [3]对目标方法本来要实现的核心功能有干扰,使程序代码很臃肿,不易于开发维护

    ⑤使用动态代理实现

    [1]创建一个类,让这个类能够提供一个目标对象的代理对象
    [2]在代理对象中打印日志

    4.在Spring中使用AOP实现日志功能


    ①Spring中可以使用注解或XML文件配置的方式实现AOP。
    ②导入jar包

    com.springsource.net.sf.cglib -2.2.0.jar
    com.springsource.org.aopalliance-1.0.0 .jar
    com.springsource.org.aspectj.weaver-1.6.8 .RELEASE.jar
    commons-logging-1.1.3. jar
    spring-aop-4.0.0.RELEASE.jar
    spring-aspects-4.0.0.RELEASE.jar
    spring-beans-4.0.0.RELEASE.jar
    spring-context-4.0.0.RELEASE.jar
    spring-core-4.0.0.RELEASE.jar
    spring-expression-4.0.0.RELEASE. jar
    View Code

    ③开启基于注解的AOP功能
      < aop:aspectj-autoproxy />
    ④声明一个切面类,并把这个切面类加入到IOC容器中
      @Aspect//表示这是一个切面类
      @Component//加入IOC容器
      public class LogAspect {}

    ⑤在切面类中声明通知方法
      [1]前置通知:@Before
      [2]返回通知:@AfterReturning
      [3]异常通知:@AfterThrowing
      [4]后置通知:@After
      [5]环绕通知:@Around :环绕通知是前面四个通知的集合体!

    @Aspect//表示这是一个切面类
    @Component//将本类对象加入到IOC容器中!
    public class LogAspect {
    @Before(value="execution(public int com.neuedu.aop.target.MathCalculatorImpl.add(int, int))")
    public void showBeginLog(){
    System.out.println("AOP日志开始");
    }
    @After(value="execution(public int com.neuedu.aop.target.MathCalculatorImpl.add(int, int))")
    public void showReturnLog(){
    System.out.println("AOP方法返回");
    }
    @AfterThrowing(value="execution(public int com.neuedu.aop.target.MathCalculatorImpl.add(int, int))")
    public void showExceptionLog(){
    System.out.println("AOP方法异常");
    }
    @AfterReturning(value="execution(public int com.neuedu.aop.target.MathCalculatorImpl.add(int, int))")
    public void showAfterLog(){
    System.out.println("AOP方法结束");
    }
    }
    View Code

    ⑥被代理的对象也需要加入IOC容器

    5.切入点表达式

    1)上述案例通过junit测试,会发现,我们调用目标类的四个方法只有add方法被加入了4个通知,如果想所有的方法都加上这些通知,可以
    在切入点表达式处,将execution(public int com.neuedu.aop.target.MathCalculatorImpl.add(int, int)) 换成:
    execution(public int com.neuedu.aop.target.MathCalculatorImpl.*(int, int))这样只要是有两个参数,且
    参数类型为int的方法在执行的时候都会执行其相应的通知方法!

    2)切入点表达式的语法格式 
      execution([权限修饰符] [返回值类型] [简单类名/全类名] [方法名]([参数列表]))

    最详细的切入点表达式:
      execution(public int com.neuedu.aop.target.MathCalculatorImpl.add(int, int))
    最模糊的切入点表达式:
      execution (* *.*(..))

    6.统一声明切入点表达式


    @Pointcut(value= "execution(public int com.atguigu.aop.target.EazyImpl.add(int,int))")
    public void myPointCut(){}

    7.通知方法的细节

    ①在通知中获取目标方法的方法名和参数列表
      [1]在通知方法中声明一个JoinPoint类型的形参
      [2]调用JoinPoint对象的getSignature()方法获取目标方法的签名
      [3]调用JoinPoint对象的getArgs()方法获取目标方法的实际参数列表

    ②在返回通知中获取方法的返回值
      [1]在@AfterReturning注解中添加returning属性
        @AfterReturning (value="myPointCut()", returning= "result")
      [2]在返回通知的通知方法中声明一个形参,形参名和returning属性的值一致
        showReturnLog(JoinPoint joinPoint, Object result)
    ③在异常通知中获取异常对象
      [1]在@ AfterThrowing注解中添加throwing属性
      @AfterThrowing (value="myPointCut()",throwing= "throwable" )
    [2]在异常通知的通知方法中声明一个形参,形参名和throwing属性值一致
      showExceptinLog(JoinPoint joinPoint, Throwable throwable)

       根据接口类型获取target对象时,实际上真正放在IOC容器中的对象是代理对象,而并不是目标对象本身!

    八、环绕通知:@Around

      1.环绕通知需要在方法的参数中指定JoinPoint的子接口类型ProceedingJoinPoint为参数      

     @Around(value="pointCut()")
            public void around(ProceedingJoinPoint joinPoint){
            }
    View Code

        2.环绕通知会将其他4个通知能干的,自己都给干了!

              注意:@Around修饰的方法一定要将方法的返回值返回!本身相当于代理!
            
         

      @Around(value="pointCut()")
            public Object around(ProceedingJoinPoint joinPoint){
                Object[] args = joinPoint.getArgs();
                Signature signature = joinPoint.getSignature();
                String methodName = signature.getName();
                List<Object> list = Arrays.asList(args);
                Object result = null;
                try {
                    //目标方法之前要执行的操作
                    System.out.println("[环绕日志]"+methodName+"开始了,参数为:"+list);
                    //调用目标方法
                    result = joinPoint.proceed(args);
                    
                    //目标方法正常执行之后的操作
                    System.out.println("[环绕日志]"+methodName+"返回了,返回值为:"+result);
                } catch (Throwable e) {
                    //目标方法抛出异常信息之后的操作
                    System.out.println("[环绕日志]"+methodName+"出异常了,异常对象为:"+e);
                    throw new RuntimeException(e.getMessage());
                }finally{
                    //方法最终结束时执行的操作!
                    System.out.println("[环绕日志]"+methodName+"结束了!");
                }
                return result;
            }
    View Code

    九、切面的优先级

       对于同一个代理对象,可j以同时有多个切面共同对它进行代理。
       可以在切面类上通过@Order (value=50)注解来进行设置,值越小优先级越高!

    @Aspect
    @Component
    @Order(value=40)
    public class TxAspect {
    @Around(value="execution(public * com.neuedu.aop.target.MathCalculatorImpl.*(..))")
    public Object around(ProceedingJoinPoint joinPoint){
    Object[] args = joinPoint.getArgs();
    Signature signature = joinPoint.getSignature();
    String methodName = signature.getName();
    List<Object> list = Arrays.asList(args);
    Object result = null;
    try {
    //目标方法之前要执行的操作
    System.out.println("[事务日志]"+methodName+"开始了,参数为:"+list);
    //调用目标方法
    result = joinPoint.proceed(args);
    
    //目标方法正常执行之后的操作
    System.out.println("[事务日志]"+methodName+"返回了,返回值为:"+result);
    } catch (Throwable e) {
    //目标方法抛出异常信息之后的操作
    System.out.println("[事务日志]"+methodName+"出异常了,异常对象为:"+e);
    throw new RuntimeException(e.getMessage());
    }finally{
    //方法最终结束时执行的操作!
    System.out.println("[事务日志]"+methodName+"结束了!");
    }
    return result;
    }
    }
    View Code

    注意:上面的AOP都是通过注解实现的,AOP实际上也可以通过xml配置的方式实现!

    事务的管理是和AOP是有很大关系的,即声明式事务的底层是用事务实现的!

    <!-- 1.将需要加载到IOC容器中的bean配置好 -->
    <bean id="logAspect" class="com.neuedu.aop.proxy.LogAspect"></bean>
    <bean id="txAspect" class="com.neuedu.aop.target.TxAspect"></bean>
    <bean id="calculator" class="com.neuedu.aop.target.MathCalculatorImpl"></bean>
    
    <!-- 2.配置AOP,需要导入AOP名称空间 -->
    <aop:config>
    <!-- 声明切入点表达式 -->
    <aop:pointcut expression="execution(* com.neuedu.aop.target.MathCalculatorImpl.*(..))" id="myPointCut"/>
    <!-- 配置日志切面类,引用前面的类 ,通过order属性控制优先级-->
    <aop:aspect ref="logAspect" order="25">
    <!-- 通过method属性指定切面类的切面方法,通过pointcut-ref指定切入点表达式 -->
    <aop:before method="showBeginLog" pointcut-ref="myPointCut"/>
    <aop:after method="showAfterLog" pointcut-ref="myPointCut"/>
    <aop:after-throwing method="showExceptionLog" pointcut-ref="myPointCut" throwing="ex"/>
    <aop:after-returning method="showReturnLog" pointcut-ref="myPointCut" returning="result"/>
    <aop:around method="around" pointcut-ref="myPointCut"/>
    </aop:aspect>    
    <!-- 配置事务切面类,引用前面的类 -->
    <aop:aspect ref="txAspect" order="20">
    <aop:around method="around" pointcut-ref="myPointCut"/>
    </aop:aspect>
    </aop:config>
    View Code

     十、批处理(batch)

       1、概念

    - 批处理指的是一次操作中执行多条SQL语句
    - 批处理相比于一次一次执行效率会提高很多

     2、主要步骤

          1)将要执行的SQL语句保存
           2)执行SQL语句

         - Statement和PreparedStatement都支持批处理操作,这里我们只需要掌握PreparedStatement的批处理方式:
      - 方法:
        void addBatch()
          - 将要执行的SQL先保存起来,先不执行
          - 这个方法在设置完所有的占位符之后调用
        int[] executeBatch()
          - 这个方法用来执行SQL语句,这个方法会将批处理中所有SQL语句执行

      - mysql默认批处理是关闭的,所以我们还需要去打开mysql的批处理:

     rewriteBatchedStatements=true
    View Code

       我们需要将以上的参数添加到mysql的url地址中
      - 注意:低版本的mysql-jdbc驱动也不支持批处理,一般都是在修改的时候使用批处理,查询的时候不使用!


    案例演示:
      1)创建一张新的数据表

    CREATE TABLE t_emp(
    id INT PRIMARY KEY AUTO_INCREMENT,
    NAME VARCHAR(50)
    )
    View Code

      2)反复打开数据库客户端,插入语句【相当于每次获取一个connection连接,执行executeUpdate语句】

    INSERT INTO t_emp(NAME) VALUES('张三');
    SELECT * FROM t_emp;
    View Code

      3)引出批处理--->执行效率高,资源利用率好!

    @Test//测试批处理
    public void testBatch(){
    //向t_emp表中插入10000条数据
    
    //准备两个变量
    Connection connection = null;
    PreparedStatement ps = null;
    
    try {
    //获取数据库连接
    connection=JDBCUtil.getConnection();
    //准备SQL模板
    String sql = "INSERT INTO t_emp(NAME) VALUES(?)";
    //获取PrepareStatement
    ps = connection.prepareStatement(sql);
    //创建一个for循环,来设置占位符
    for(int i = 0; i < 10000 ;i++){
    //填充占位符
    ps.setString(1,"emp"+i);
    //添加到批处理方法中,调用无参的,有参的是Statement来调用的!
    ps.addBatch();
    } 
    //获取一个时间戳
    long start = System.currentTimeMillis();
    //执行批处理
    ps.executeBatch();
    //获取一个时间戳
    long end = System.currentTimeMillis();
    System.out.println("共花费了:"+(end-start));
    } catch (SQLException e) {
    e.printStackTrace();
    }
    }
    View Code







  • 相关阅读:
    叉姐的魔法训练(第十课)---- 幽默大师卫冕战
    叉姐的魔法训练(第⑨课)---- 幽默大师职业赛
    叉姐的魔法训练(第八课)---- 幽默术
    叉姐的魔法训练(第七课)---- 在沙漠中的长途旅行
    叉姐的魔法训练(第六课)---- 暴雨术入门
    叉姐的魔法训练(第五课)---- 如何成为一名合格的小学生
    叉姐的魔法训练(第四课)---- 风系魔法的基本要领
    叉姐的魔法训练(第三课)---- 火球术入门
    叉姐的魔法训练(第二课)---- 冰系魔法入门
    叉姐的魔法训练(第一课)---- 初级魔法练习
  • 原文地址:https://www.cnblogs.com/bkyy/p/8024660.html
Copyright © 2020-2023  润新知