欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring学习之AOP基础

程序员文章站 2022-05-24 23:44:09
...

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配置文件如下


    <bean id="userServiceTarget" class="cn.xuhuanfeng.aop.UserServiceImpl"/>
    <bean id="advice" class="cn.xuhuanfeng.aop.LogManager"/>
    <!--指定代理类,用于为目标类进行增强-->
    <bean id="userService" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!--配置对应的目标类,也就是要对其进行增强的类-->
        <property name="target" ref="userServiceTarget"/>
        <!--配置对应的增强类-->
        <property name="interceptorNames" value="advice"/>
        <!--是否使用CGLib动态代理,如果没有配置接口,则默认是-->
        <property name="optimize" value="true"/>
    </bean>

测试结果如下所示


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配置方式的简便性。