Spring学习记录(02)——Spring AOP

###0.前言
《Java EE互联网轻量级框架整合开发——SSM框架(Spring MVC+Spring+MyBatis)和Redis实现》

本文主要记录Spring AOP的学习笔记。

###1. AOP (面向切面编程)
将程序中的横切关注点模块化形成切面,通过切点匹配程序中的连接点,将切面织入到目标对象,即创建目标对象的代理对象。代理对象可对原对象的功能进行拦截修改和增强(引入)。

概念描述:

  • 原有对象:真实的对象

  • 横切关注点:是一些具有横越多个模块的行为,使用传统的软件开发方法不能够达到有效的模块化的一类特殊关注点。(eg:日志、事务等)

  • 切面(Aspect):横切关系封装进切面来保持程序的模块化。

  • 通知(Advice):切面中的方法,主要用于增强原有对象的方法。

    • 前置通知(before):原有对象方法执行前调用
    • 后置通知(after):原有对象方法执行后调用
    • 返回通知(afterReturning):原有方法执行后正常返回(无异常),则执行此通知
    • 异常通知(afterThrowing):原有方法执行后产生异常则执行此通知
    • 环绕通知(around):可取代原有对象方法,通常会提供原有对象的方法的作为参数。


  • 织入(Weaving):将切面中的功能加入到原有对象方法的流程中,通过代理对象实现。

  • 连接点(join Point):原有对象(真实对象)中需要织入切面内容的位置。

  • 切点(Pointcut):通过一定规则判断哪些方法是连接点。通常使用正则匹配。

  • 引入(Introduction):增强原有对象(真实对象)的功能,通常采用动态代理的getInterfaces()实现。




通过 <切点>的限定规则 找到 <连接点>,对 <原有对象>存在的<连接点> 的方法进行 <切面> 的 <织入>。

  • <切面>描述了需要做的事情,事情的内容根据功能划分为各种通知(before、after、around等)
  • <切点>描述了哪些地方需要插入这些事情,即通过一定规则拿到连接点

###2.Spring AOP的使用
Spring框架中的AOP是基于方法拦截的的AOP(即不支持参数拦截),主要基于AspectJ框架

开发具体流程:

  1. 启用AspectJ框架的自动代理
  2. 创建切面,定义切点
  3. 切面织入

通过注解方式使用AOP:

  1. 启用AspectJ的自动代理功能:@EnableAspectJAutoProxy

    //AopConfig.class
    @Configuration
    @EnableAspectJAutoProxy //开启AspectJ的自动代理功能
    @ComponentScan(basePackages = "com.ssm.chapter11.aop")
    public class AopConfig {
    
        @Bean    //注册切面实例到Spring IoC容器中
        public RoleAspect getRoleAspect() {
            return new RoleAspect(); 
        }
    }
    
  1. 创建切面类,定义切点(标记着@Aspect的类)

    //RoleAspect.class
    @Aspect    //注明为切面类
    public class RoleAspect {
    
        @DeclareParents(value= "com.ssm.chapter11.aop.service.impl.RoleServiceImpl+", defaultImpl=RoleVerifierImpl.class)
        public RoleVerifier roleVerifier; //引入,功能增强
    
        //切点的定义
        @Pointcut("execution(* com.ssm.chapter11.aop.service.impl.RoleServiceImpl.printRole(..))")
        public void print() {
        }
    
        //前置通知
        @Before("print()")
        public void before() {
            System.out.println("before ....");
        }
    
        //后置通知
        @After("print()")
        public void after() {
            System.out.println("after ....");
        }
    
        //完成结果通知
        @AfterReturning("print()")
        // @AfterReturning("execution(*
        // com.ssm.chapter11.aop.service.impl.RoleServiceImpl.printRole(..))")
        public void afterReturning() {
            System.out.println("afterReturning ....");
        }
    
        //完成异常通知
        @AfterThrowing("print()")
        public void afterThrowing() {
            System.out.println("afterThrowing ....");
        }
    
        //环绕通知        
        @Around("print()")
        public void around(ProceedingJoinPoint jp) {
            System.out.println("around before ....");
            try {
                jp.proceed(); //执行原方法
            } catch (Throwable e) {
                e.printStackTrace();
            }
            System.out.println("around after ....");
        }
    
        //参数附加
        @Before("execution(* com.ssm.chapter11.aop.service.impl.RoleServiceImpl.printRole(..)) " + "&& args(role, sort)")
        public void before(Role role, int sort) {
            System.out.println("before ....");
        }
    }
    
    //引入的接口,用于增强代理类功能
    public interface RoleVerifier {
        public boolean verify(Role role);
    }
    
  2. 切面织入

    //AopConfig.class
    @Configuration
    @EnableAspectJAutoProxy //开启AspectJ的自动代理功能
    @ComponentScan(basePackages = "com.ssm.chapter11.aop")
    public class AopConfig {
    
        @Bean    //注册切面实例到Spring IoC容器中
        public RoleAspect getRoleAspect() {
            return new RoleAspect(); 
        }
    }
    

    主要将切面类添加到Spring IoC容器中即可,Spring IoC会根据切点自动织入。

通过XML使用AOP:

//spring-config.xml

//需要引入模板文件
http://www.springframework.org/schema/aop 
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd

<aop:aspectj-autoproxy /> <!--开启AspectJ的自动代理功能-->
<bean id="xmlAspect" class="com.ssm.chapter11.xml.aspect.XmlAspect" />
<bean id="roleService" class="com.ssm.chapter11.xml.service.impl.RoleServiceImpl" />
<aop:config>
    <aop:aspect ref="xmlAspect">
        <!-- 自定义切点 -->
        <aop:pointcut id="printRole"
            expression="execution(* com.ssm.chapter11.xml.service.impl.RoleServiceImpl.printRole(..))" />
        <!-- 定义通知 -->
        <aop:before method="before" pointcut-ref="printRole" />
        <aop:after method="after" pointcut-ref="printRole" />
        <aop:after-throwing method="afterThrowing"
            pointcut-ref="printRole" />
        <aop:after-returning method="afterReturning"
            pointcut-ref="printRole" />
        <aop:around method="around" pointcut-ref="printRole" />
        <!-- 引入增强 -->
        <aop:declare-parents
            types-matching="com.ssm.chapter11.xml.service.impl.RoleServiceImpl+"
            implement-interface="com.ssm.chapter11.aop.verifier.RoleVerifier"
            default-impl="com.ssm.chapter11.aop.verifier.impl.RoleVerifierImpl" />
    </aop:aspect>
</aop:config>

多切面配置执行顺序:
默认为无序执行,即责任链下多个切面的代理顺序是无序的。

  1. 注解方式:通过切面类上标注@Order(Number)配置。 Number小的优先执行
  2. xml方式:<aop:aspect ref="xmlAspect" order="Number">

切面执行顺序图

责任链模式

###3.总结
AOP(面向切面编程)是一种思想,主要是在不破坏原有模块的纯粹性和单一功能原则的情况下,扩展模块功能,同时对切面进行模块化管理。在实际使用中还得因地制宜。

END

–Nowy

–2018.12.12

分享到