zoukankan      html  css  js  c++  java
  • Spring之AOP

    一、AOP介绍

    1、AOP理解

    OOP(Object Oriented Programming,面向对象编程)引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系对于其他类型的代码,如安全性、异常处理和透明的持续性也都是如此,这种散布在各处的无关的代码被称为横切(crosscutting),在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

    AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP的补充和完善。它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。

    AOP把软件系统分为两个部分:核心关注点横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事物。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。

    参考自:http://www.cnblogs.com/xrq730/p/4919025.html

    2、AOP中的概念

    • Joinpoint(连接点)

      所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点

    • Pointcut(切入点)

      所谓切入点是指我们要对哪些Joinpoint进行拦截的定义

    • Advice(通知/增强)

      所谓通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)

    • Introduction(引介)

      引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field

    • Target(目标对象)

      代理的目标对象

    • Weaving(织入)

      是指把增强应用到目标对象来创建新的代理对象的过程

    • Proxy(代理)

      一个类被AOP织入增强后,就产生一个结果代理类

    • Aspect(切面)

      是切入点和通知的结合,以后咱们自己来编写和配置的

    • Advisor(通知器、顾问)

      和Aspect很相似

    二、AOP之AspectJ

    AspectJ是一个Java实现的AOP框架,它能够对java代码进行AOP编译(一般在编译期进行),让java代码具有AspectJ的AOP功能(当然需要特殊的编译器)。

    AspectJ是目前实现AOP框架中最成熟,功能最丰富的语言,它与java程序完全兼容,几乎是无缝关联。

    1、织入介绍

    AspectJ应用到Java代码的过程称为织入,对于织入,可以简单理解为aspect(切面)应用到目标函数(类)的过程。织入的过程,一般分为动态织入和静态织入。

    • 动态织入

      动态织入的方式是在运行时动态将要增强的代码织入到目标类中,它一般是通过动态代理技术完成的,如Java JDK的动态代理(Proxy,底层通过反射实现)或CGLIB的动态代理(底层通过继承实现),Spring AOP采用的就是基于运行时增强的代理技术。

    • 静态织入

      ApectJ采用的就是静态织入的方式。ApectJ主要采用的是编译期织入,在这个期间使用AspectJ的acj编译器(类似javac)把aspect类编译成class字节码后,在java目标类编译时织入,即先编译aspect类再编译目标类。

    2、AspectJ切入点表达式

    切入点表达式的格式如下所示:

    execution([修饰符] 返回值类型 包名.类名.方法名(参数))
    /*
     *表达式参数说明:
     *
     *execution:必须要
     *修饰符:可省略
     *返回值类型:必须要,但是可以使用*通配符
     *包名:
     *	多级包之间使用.分割
     *	包名可以使用*代替,多级包名可以使用多个*代替
     *	如果想省略中间的包名可以使用 ..
     *类名:
     *	可以使用*代替
     *	也可以写成*DaoImpl(以DaoImpl结尾)
     *方法名:
     *	可以使用*好代替
     *	也可以写成add*(以add开头)
     *参数:
     *	参数使用*代替
     *	如果有多个参数,可以使用..代替
     */
    

    execution(void com.luis.dao.UserDaoImpl.addUser(int id,String name))可写作:

    execution(* *..*.*DaoImpl.*(..))

    3、AspectJ通知类型

    通知类型分为五种:前置通知、后置通知、最终通知、环绕通知、异常抛出通知

    1、前置通知

    • 执行时机:目标对象方法之前执行通知
    • 配置文件:<aop:before method="before" pointcut-ref="myPointcut"/>
    • 对应注解:@Before
    • 应用场景:方法开始时可以进行校验

    2、后置通知

    • 执行时机:目标对象方法之后执行通知,有异常则不执行了
    • 配置文件:<aop:after-returning method="afterReturning" pointcut-ref="myPointcut"/>
    • 对应注解:@After
    • 应用场景:可以修改方法的返回值

    3、最终通知

    • 执行时机:目标对象方法之后执行通知,有没有异常都会执行
    • 配置文件:<aop:after method="after" pointcut-ref="myPointcut"/>
    • 对应注解:@AfterReturning
    • 应用场景:例如像释放资源

    4、异常抛出通知

    • 执行时机:在抛出异常后通知
    • 配置文件:<aop:after-throwing method=" afterThrowing " pointcut- ref="myPointcut"/>
    • 对应注解:@AfterThrowing
    • 应用场景:包装异常

    5、环绕通知

    • 执行时机:目标对象方法之前和之后都会执行。
    • 配置文件:<aop:around method="around" pointcut-ref="myPointcut"/>
    • 对应注解:@Around
    • 应用场景:事务、统计代码执行时机

    注意:环绕通知对上述四种的功能都能实现,其必须有返回值

    4、AspectJ的使用

    Spring已经将AspectJ收录其中,底层织入采取的动态织入方式

    4.1、使用准备

    添加依赖:

    <!-- 基于AspectJ的aop依赖 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>5.0.7.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>aopalliance</groupId>
        <artifactId>aopalliance</artifactId>
        <version>1.0</version>
    </dependency>
    

    编写接口和实现类(目标对象):如 UserService接口和UserServiceImpl实现类

    4.2、基于xml的方式

    配置目标类,并将目标类交给spring IoC容器管理。

    <context:component-scan base-package="com.luis.dao" />
    

    编写通知类,并配置给IOC容器管理,并配置AOP切面。代码如下:

    <!-- 配置通知类 -->
    <bean name="message" calss="com.luis.util.Message"/>
    
    <aop:config>
        <!-- method:配置增强类 -->
        <!-- pointcut:配置切入点 -->
    	<aop:aspect ref="message">
        	<!--<aop:before method="log" pointcut="execution(void com.luis.dao.UserDaoImpl.addUser())"/> -->
            <aop:before method="log" pointcut="execution(* *..*.*DaoImpl.add*())"/>
        </aop:aspect>
    </aop:config>
    

    4.3、基于注解的方式

    配置目标类,并将目标类交给spring IoC容器管理。

    编写切面类,指定切入点,并将切面类交给spring IoC容器管理。

    @Component
    @Aspect
    public class MyAspect{
        //配置前置通知及切入点表达式
        @Before(value="execution(* *..*.*DaoImpl.add*())")
        public void log(){
            System.out.println("日志记录...");
        }
    }
    

    开启AOP自动代理

    <aop:aspectj-autoproxy/>
    

    注意:

    1、当有返回值时,环绕通知可实现其他四种。

    @Around(value = "execution(* *..*.*DaoImpl.add*())")
    public Object around(ProceedingJoinPoint pjp) {
        String methodName = pjp.getSignature().getName();
        Object result = null;
        try {
            // 获取参数
            Object[] args = pjp.getArgs();
            // before
            System.out.println("method:  " + methodName + "  begins");
            // 执行方法
            result = pjp.proceed(args);
            // AfterReturning
            System.out.println("method:  " + methodName + "  return result:" + result);
        } catch (Throwable e) {
            // AfterThrowing
            System.out.println("method:  " + methodName + "occurs exception:" + e);
        }
        // After
        System.out.println("method:  " + methodName + "  ends");
        return result;
    }
    

    2、当配置多个切入点的表达式一样时可采用通用的切入点配置方式:@Pointcut,代码如下:

    @Aspect
    public class MyAspect{
        //配置前置通知及切入点表达式
       @Before(value="MyAspect.point()")
        public void log1(){
            System.out.println("日志记录...");
        }
        
        @Before(value="MyAspect.point()")
        public void log2(){
            System.out.println("日志记录...");
        }
        
        //通用切入点
        @Pointcut(value="execution(* *..*.*DaoImpl.add*())"))
        public void point(){
            System.out.println("日志记录...");
        }
    }
    

    三、AOP之Spring AOP

    Spring AOP是通过动态代理技术实现的,而动态代理是基于反射设计的。动态代理技术的实现方式有两种:基于接口的JDK动态代理和基于继承的CGLib动态代理

    Spring AOP可通过纯注解的方式进行配置:通过注解@EnableAspectJAutoProxy在配置类中进行配置。

    @Configuration
    @ComponentScan(basePackages="com.luis")
    @EnableAspectJAutoProxy
    public class SpringConfiguration {
        
    }
    

    至于这里提到的两种动态代理技术不是本章的重点,会在设计模式系列的动态代理中进行说明。

  • 相关阅读:
    SharePoint 2013 APP 开发示例 (六)服务端跨域访问 Web Service (REST API)
    麦咖啡导致电脑不能上网
    SharePoint 2013 Central Admin 不能打开
    SharePoint 2013 APP 开发示例 (五)跨域访问 Web Service (REST API)
    SharePoint 2013 APP 开发示例 系列
    synthesize(合成) keyword in IOS
    Git Cmd
    简单的正则匹配
    Dropbox
    SQL Server Replication
  • 原文地址:https://www.cnblogs.com/liuyi6/p/10222312.html
Copyright © 2011-2022 走看看