zoukankan      html  css  js  c++  java
  • aop原理及理解

    • 概念

    Aspect Oriented Programming,面向切面编程,实际上它是一个规范、一种设计思路,总之是抽象的。

    先上图

    • 使用目的

    从项目结构上来说

    对业务逻辑的各个部分进行隔离,降低业务逻辑的耦合度

    从开发的角度上说

    减少重复编码,提高程序的可重用性,提高了开发的效率

    总结简单的讲:就是那些与业务无关,却为业务模块所共同调用的逻辑封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。

    • 使用场景

    日志记录,性能统计,安全控制,事务处理,异常处理等

    • 概念理解(仅指java中)

    先上图

    圆柱:原有的业务流程,从上至下执行

    蓝色面:即切面,为指定的业务流程增加功能;交界处即切点,意味着将增加的功能插入到指定位置(单位是方法)

    AOP核心概念

    1、横切关注点

    对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点,即公共封装的业务逻辑,如日志记录

    2、切面(aspect)

    类是对物体特征的抽象,切面就是对横切关注点的抽象

    3、连接点(joinpoint)

    被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器,在此不做深究

    4、切入点(pointcut)

    对连接点进行拦截的定义

    5、通知(advice)

    所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类

    6、目标对象

    代理的目标对象

    7、织入(weave)

    将切面应用到目标对象并导致代理对象创建的过程

    8、引入(introduction)

    在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段

    • spring aop实现方法

     通常我们采用2种

    基于注解

    /**
     * 系统服务组件Aspect切面Bean*/
    //声明这是一个组件
    @Component
    //声明这是一个切面Bean
    @Aspect
    public class ServiceAspect {
    
        private final static Log log = LogFactory.getLog(ServiceAspect.class);
    
        //配置切入点,该方法无方法体,主要为方便同类中其他方法使用此处配置的切入点
        @Pointcut("execution(* com.hyq.aop..*(..))")
        public void aspect(){    }
    
        /*
         * 配置前置通知,使用在方法aspect()上注册的切入点
         * 同时接受JoinPoint切入点对象,可以没有该参数
         */
        @Before("aspect()")
        public void before(JoinPoint joinPoint){
            System.out.println("执行before.....");
        }
    
        //配置后置通知,使用在方法aspect()上注册的切入点
        @After("aspect()")
        public void after(JoinPoint joinPoint){
            System.out.println("执行after.....");
        }
    
        //配置环绕通知,使用在方法aspect()上注册的切入点
        @Around("aspect()")
        public void around(JoinPoint joinPoint){
            long start = System.currentTimeMillis();
            try {
                ((ProceedingJoinPoint) joinPoint).proceed();
                long end = System.currentTimeMillis();
                if(log.isInfoEnabled()){
                    log.info("around " + joinPoint + "	Use time : " + (end - start) + " ms!");
                }
            } catch (Throwable e) {
                long end = System.currentTimeMillis();
                if(log.isInfoEnabled()){
                    log.info("around " + joinPoint + "	Use time : " + (end - start) + " ms with exception : " + e.getMessage());
                }
            }
        }
    
        //配置后置返回通知,使用在方法aspect()上注册的切入点
        @AfterReturning("aspect()")
        public void afterReturn(JoinPoint joinPoint){
            if(log.isInfoEnabled()){
                log.info("afterReturn " + joinPoint);
            }
        }
    
        //配置抛出异常后通知,使用在方法aspect()上注册的切入点
        @AfterThrowing(pointcut="aspect()", throwing="ex")
        public void afterThrow(JoinPoint joinPoint, Exception ex){
            if(log.isInfoEnabled()){
                log.info("afterThrow " + joinPoint + "	" + ex.getMessage());
            }
        }
    
    }

    基于配置

    <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"
           xmlns:context="http://www.springframework.org/schema/context"
           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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    
        <!--<context:component-scan base-package=""-->
    
        <!-- 定义目标对象 -->
        <bean name="productDao" class="com.xxx.spring.springAop.dao.daoimp.ProductDaoImpl" />
    
        <!-- 定义切面 -->
        <bean name="myAspectXML" class="com.xxx.spring.springAop.AspectJ.MyAspectXML" />
        <!-- 配置AOP 切面 -->
        <aop:config>
            <!-- 定义切点函数 -->
            <aop:pointcut id="pointcut" expression="execution(* com.xxx.spring.springAop.dao.ProductDao.add(..))" />
    
            <!-- 定义其他切点函数 -->
            <aop:pointcut id="delPointcut" expression="execution(* com.xxx.spring.springAop.dao.ProductDao.delete(..))" />
    
            <!-- 定义通知 order 定义优先级,值越小优先级越大-->
            <aop:aspect ref="myAspectXML" order="0">
                <!-- 定义通知
                method 指定通知方法名,必须与MyAspectXML中的相同
                pointcut 指定切点函数
                -->
                <aop:before method="before" pointcut-ref="pointcut" />
    
                <!-- 后置通知  returning="returnVal" 定义返回值 必须与类中声明的名称一样-->
                <aop:after-returning method="afterReturn" pointcut-ref="pointcut"  returning="returnVal" />
    
                <!-- 环绕通知 -->
                <aop:around method="around" pointcut-ref="pointcut"  />
    
                <!--异常通知 throwing="throwable" 指定异常通知错误信息变量,必须与类中声明的名称一样-->
                <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="throwable"/>
    
                <!--
                     method : 通知的方法(最终通知)
                     pointcut-ref : 通知应用到的切点方法
                    -->
                <aop:after method="after" pointcut-ref="pointcut"/>
            </aop:aspect>
        </aop:config>
    </beans>

    重点介绍下pointcut(切入点) expression表达式解析及配置

    方法参数匹配

    args()

    @args()

    方法描述匹配

    execution()

    当前AOP代理对象类型匹配

    this()

    目标类匹配

    target()

    @target()

    within()

    @within()

    标有此注解的方法匹配

    @annotation()

    其中execution 是用的最多的,其格式为:

    execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)throws-pattern?) 

    ret-type-pattern,name-pattern, parameters-pattern是必须的. 

    ret-type-pattern:可以为*表示任何返回值,全路径的类名等. 

    name-pattern:指定方法名, *代表所有 

    如:set*代表以set开头的所有方法. 

    parameters-pattern:指定方法参数(声明的类型),(..)代表所有参数,(*)代表一个参数 

    (*,String)代表第一个参数为任何值,第二个为String类型.

     更多配置细节可查看官方文档:https://docs.spring.io/spring/docs/4.3.9.RELEASE/spring-framework-reference/html/aop.html#aop-introduction

    • 原理分析

     简单的讲,即使用jdk或cglib的动态代理功能,对目标类生成代理类,然后在代理类中调用或执行额外的新增功能。

    先上图

    在此推荐看https://blog.csdn.net/luanlouis/article/details/51095702

    详细说明了spring aop实现的大致逻辑和机制

    另外推荐https://blog.csdn.net/luanlouis/article/details/51155821

    通过解析spring源码来深入理解spring aop的实现

  • 相关阅读:
    LeetCode 128. 最长连续序列
    MySQL的information_schema
    maven项目板块的pom.xml配置
    mybatis打印SQL日志
    MySQL的时间字段转换
    mysql的csv数据导入与导出
    一致性协议
    分布式事务
    事务基本信息
    分布式系统定义及特点
  • 原文地址:https://www.cnblogs.com/wish5714/p/9577720.html
Copyright © 2011-2022 走看看