• AOP--Aspect Oriented Programming


    1.AOP概念:Aspect Oriented Programming 面向切面编程
           Aspect Oriented Programming(AOP)。AOP主要实现的目的是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。比如我们最常见的就是日志记录了,举个例子,我们现在提供一个查询学生信息的服务,但是我们希望记录有谁进行了这个查询。
          如果按照传统的OOP的实现的话,那我们实现了一个查询学生信息的服务接口(StudentInfoService)和其实现 类 (StudentInfoServiceImpl.java)同时为了要进行记录的话,那我们在实现类(StudentInfoServiceImpl.java)中要添加其实现记录的过程。这样的话,假如我们要实现的服务有多个呢?那就要在每个实现的类都添加这些记录过程。这样做的话就会有点繁琐,而且每个实现类都与记录服务日志的行为紧耦合,违反了面向对象的规则【面向对象的设计准则:1、模块化  2、抽象   3、信息隐藏   4、弱耦合   5、强内聚   6、可重用 】。那么怎样才能把记录服务的行为与业务处理过程中分离出来呢?看起来好像就是查询学生的服务自己在进行,但却是背后日志记录对这些行为进行记录,并且查询学生的服务不知道存在这些记录过程,这就是我们要讨论AOP的目的所在。
         AOP的编程,好像就是把我们在某个方面的功能提出来与一批对象进行隔离,这样与一批对象之间降低了耦合性,可以就某个功能进行编程。
     
    2.作用:本质上来说是一种简化代码的方式
        继承机制
        封装方法
        动态代理 :BaseMath bean = (BaseMath) ioc.getBean("testmath"); ioc调用实现类的ID去创建接口的对象,进而调用实现类实现的方法
        ……
     
    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.AOP术语![参见图例和doc文档]
        AOP概述
            ●AOP(Aspect-Oriented Programming,面向切面编程):是一种新的方法论,
             是对传统 OOP(Object-Oriented Programming,面向对象编程)的补充。
            ●参见图例和doc文档解释AOP的各个术语!
            ●Spring的AOP实现方法:①xml配置的方法
                                                    ②利用注解的方法  
     
     
    5.在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
     
        ③开启基于注解的AOP功能 ,在此之前也要注意,需要在XML中插入能够扫描包的代码 
        <context:component-scan base-package="com.neuedu.aop.mathtest"/> <!-- 扫描包的代码 -->
        <aop:aspectj-autoproxy/>   <!-- 开启注解aop功能  -->
            
        ④声明一个切面类,并把这个切面类加入到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方法结束");
                }
            }
     
      ⑥被代理的对象也需要加入IOC容器
           
     @Component//加入IOC容器
            public class MathCalculatorImpl {
     
                public int add(int i,int j){
                    int result = i+j;
                    return result;
                }
                public int sub(int i,int j){
                    int result = i-j;
                    return result;
                }
                public int multi(int i,int j){
                    int result = i*j;
                    return result;
                }
                public int divide(int i,int j){
                    int result = i/j;
                    return result;
                }
            }
    

      

    6.切入点表达式:
       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([权限修饰符] [返回值类型] [简单类名/全类名] [方法名]([参数列表]))
     
          1.任意参数,任意类型
          2.任意返回值
          3.用@PointCut注解统一声明,然后在其它通知中引用该统一声明即可!
                 需要注意的是:权限是不支持写通配符的,当然你可以写一个*表示所有权限所有返回值!
     
         最详细的切入点表达式:
            execution(public int com.neuedu.aop.target.MathCalculatorImpl.add(int, int))
         最模糊的切入点表达式:
            execution (* *.*(..))
     
    7.统一声明切入点表达式
     
         @Pointcut(value= "execution(public int com.atguigu.aop.target.EazyImpl.add(int,int))")
         public void myPointCut(){}
         //调用上面的函数,就可以代替重复的很多代码
    @AfterReturning (value="myPointCut()", returning= "result")
    

      

    8.通知方法的细节
        ①在通知中获取目标方法的方法名和参数列表
            [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)
     
    9.根据接口类型获取target对象时,实际上真正放在IOC容器中的对象是代理对象,而并不是目标对象本身!
     
     
    10.环绕通知:@Around
        1.环绕通知需要在方法的参数中指定JoinPoint的子接口类型ProceedingJoinPoint为参数
      
     
         @Around(value="pointCut()")
            public void around(ProceedingJoinPoint joinPoint){
            }
    
        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;
            }
     
    

      

    11.切面的优先级
        对于同一个代理对象,可以同时有多个切面共同对它进行代理。
        可以在切面类上通过@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;
                }
            }
     
    12.注意:上面的AOP都是通过注解实现的,AOP实际上也可以通过xml配置的方式实现!
        
    <!-- 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>
    
        需要知道的是:事务的管理是和AOP是有很大关系的,即声明式事务的底层是用事务实现的!
  • 相关阅读:
    boost::asio发送与传输相关的几个函数,备忘
    当ASIO::ASYNC_READ与SOCKET的ASYNC_READ_SOME的区别
    boost::asio keepalive
    boost asio ——深入框架
    Boost.Asio和ACE之间关于Socket编程的比较
    OCP-1Z0-052-V8.02-15题
    JavaScript对象和数组
    Oracle 11g New 使用RMAN 增强功能
    Flex中集合的类型
    Flex中基于列表的一系列MX组件及相互关系
  • 原文地址:https://www.cnblogs.com/WangKaitao/p/7450086.html
Copyright © 2020-2023  润新知