Spring学习之AOP基础

Spring学习之AOP基础

前言

最近在学Spring,这两天碰到AOP这个概念,一开始不是很理解其背后的思想,经过这两天的学习,基本上大致理解了其含义以及目的,故将学习过程的笔记整理出来,以供日后回顾使用,以及与各位正在学习Spring的朋友分享

AOP的介绍

AOP,全程是Apsect Orientation Programming,翻译过来就是面向切面的编程,说到面向切面,首先需要谈到的就是OOP,也就是比较熟悉的面向对象编程,在面向对象编程中,当重复性的代码出现比较多次的时候,一般我们就会将公共部分其抽取出来,形成父类,这样,通过继承的手段,就能极大地减少代码的重复。然而,在实际开发过程中却有一些代码是冗余是,但是通过面向对象开发的方式却是无法将其抽取出来,比如说,上一篇文章中提到的,日志管理,或者常用的事务管理等,这些类型的代码有具有一个很明显的特点,那就是他们紧紧围绕在业务代码,或者说目标代码的周围(前/后),这种形式的代码采用面向对象的方式是无法进行抽取的。于是,需求诞生了解决方案,面向切面的方式就开始投入了使用。

所谓的面向切面编程,其实就是从横向的角度来处理冗余的代码(面向对象的编程方式基本都是纵向的角度),比如说,日志管理,由于日志管理代码紧紧围绕在业务代码中,从横向的角度来看这些代码,日志管理代码就好像是一层外衣,紧紧地包围着业务代码,而面向切面编程的核心思想,就是将这些具有横向特征的代码抽取出来,并且将他们集中在特定的类中,然后通过织入的方式,将他们织入到对应的业务方法中,这样子,这些代码就好像是一层独立与业务代码的代码了,而将他们抽取出来并且在需要的时候将他们整合进去的方式,就是面向切面编程了。

这里需要注意一点,面向切面编程的出现,不是要替代面向对象编程,而是对其进行补充,扩展面向对象编程的能力。在前面的一小节,动态代理中也提到了,动态代理技术是Spring中AOP应用的背景,也就是说,Spring是通过动态代理技术来实现面向切面编程的,有了动态代理技术的基础,接下来我们就来学习面向切面编程

AOP编程常用术语

在具体学习AOP编程之间,有几个比较重要的概念需要弄清楚,这几个概念是面向切面编程的基础,也是其核心。

  • 连接点 Jointpoint
    • 所谓的连接点,指的是代码中可以进行增强的点,比如说方法调用前、方法调用后、方法调用前后、类初始化前、类初始化后、异常抛出后
  • 切点 Cutpoint
    • 切点,是指要进行增强的点,这里需要注意区分连接点和切点,连接点是所有符合条件的点,而切点是所要进行织入的点,连接点包含了切点,但同时一个切点可能匹配多个连接点
  • 增强 Advice
    • 增强,指的是所要在切点上执行的代码,也就是具体的想要增强的代码,一般还包含了方位信息(方法前/后、返回等,但不包含具体的方法信息,也就是不包含切点信息)
  • 目标类 Target
    • 目标类,指的是所要进行增强的类,这个比较好理解
  • 引介 Introduction:
    • 引介,一种特殊的增强,主要作用于类级别上,通常用于为类动态实现接口等的操作,也就是作用于类上的增强
  • 织入 Weaving
    • 织入,指的是将通过切点信息,将对象的增强添加到目标类的过程
  • 代理 Proxy
    • 代理,这个比较好理解,指的就是通过增强之后所产生的对象,也就是原来目标类经过增强之后的代理类
  • 切面 Aspect:
    • 切面,由切点和增强组成,包含了横切逻辑和方位的定义,一个切点可以描述多个连接点,加上增强,就形成了面,也就是所谓的切面了。

原始的Spring AOP编程方式

这里通过比较原始的Spring AOP编程方式来详细讲解上面所提到的概念,以增强对上面的概念的认识,需要注意的是,通过这种方式的目的是为了更好地理解AOP编程的概念,在日常的开发过程中,由于这种方式比较底层,而且操作麻烦,基本是不怎么使用的,还有一点需要注意的是,Spring仅支持方法的增强,也就是说,Spring中的切点只能描述方法,增强只能作用在方法上。


/**
 * 方法调用前增强
 */
class LogManager implements MethodBeforeAdvice{

    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("log something");
    }
}

/**
 * UserService接口
 */
interface UserService{

    void login();
    void logout();
}

/**
 * UseService实现类
 */
class UserServiceImpl implements UserService{

    @Override
    public void login() {
        System.out.println("someone login....");
    }

    @Override
    public void logout() {
        System.out.println("someone logout....");
    }
}

对应的Spring配置文件如下


    
    
    
    
        
        
        
        
        
        
    

测试结果如下所示


log something
someone login....
log something
someone logout....

从上面的结果可以看出,我们已经成功为UserService的方法配置增强,并且UserService对此并不知情

接下来再看另外几个例子


/**
 *  方法调用后增强
 */
class LogManagerAfter implements AfterReturningAdvice{

    @Override
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        System.out.println("after returning");
    }
}

/**
 * 环绕增强
 */
class LogManagerAround implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        System.out.println("before ...");
        Object object = methodInvocation.proceed();
        System.out.println("after ...");
        return object;
    }
}

在配置文件中进行配置的具体代码基本同上面的内容,这里就不进行展示了,从上面的内容可以看出,如果想对一个对象应用AOP方式进行增强,首先需要为其定义对应的增强,同时为其描述对应的切点(上面没有明确指出是切点,所以默认是对所有的方法进行增强),以及对应的目标类,然后通过Spring的ProxyFactorybean来负责进行织入,产生对应的增强类。

不过,从上面的声明增强的方式中,我们也可以看出通过这种方式配置的缺点

  • 只能通过硬编码指定增强,并且需要为每个需要增强的类创建增强类
  • 无法明确指定切点,只能通过硬编码进行编写,且编写过程繁琐

由于通过这种方式的配置有着无法忍受的缺点,所以一般我们在实际开发过程中也没有这么做,至于一般怎么做,将在后面两个小节进行介绍

总结

由于面向对象编程本身存在着的不可避免的问题,于是提出了面向切面编程的概念,并且,随着前辈们的努力,AOP编程技术现在已经变得非常成熟了。在AOP中编程中,有几个比较重要的概念,切点,增强 ,引介,切面,织入,只有对这几个概念比较熟悉,才能更好地使用AOP技术来提高效率,在原始的Spring AOP技术中,如果需要声明一个增强,则需要实现对应的接口,这种方式是不太方便的,因为通过这种方式没有办法明确指定对应的切点,而且基本上必须为需要增强的对象编写对应的增强,在后面我们将看到,经过努力之后的Spring AOP配置方式的简便性。

你可能感兴趣的:(Spring学习之AOP基础)