Spring AOP就是这么简单啦

前言

只有光头才能变强

上一篇已经讲解了Spring IOC知识点一网打尽!,这篇主要是讲解Spring的AOP模块~java

以前我已经写过一篇关于AOP的文章了,那篇把比较重要的知识点都讲解过了一篇啦:Spring【AOP模块】就这么简单,很荣幸被开源中国推荐过~~spring

  • 若是没有AOP的基础,建议先看看上面那篇文章~
  • 若是没有代理模式基础,建议先看看:给女友讲解什么是代理模式这篇文章
  • 若是都看过了,这篇就放心食用吧!

这篇文章主要是补充和强化一些比较重要的知识点,并会把上面的两本书关于AOP的知识点整理出来并画成一个思惟导图来全面了解Spring AOP的知识点!编程

那么接下来就开始吧,若是有错的地方但愿能多多包涵,并不吝在评论区指正!微信

1、Spring AOP全面认知

结合《Spring 实战 (第4版)》和《精通Spring4.x 企业应用开发实战》两本书的AOP章节将其知识点整理起来~编辑器

1.1AOP概述

AOP称为面向切面编程,那咱们怎么理解面向切面编程??post

咱们能够先看看下面这段代码:性能

咱们学Java面向对象的时候,若是代码重复了怎么办啊??能够分红下面几个步骤:测试

  • 1:抽取成方法
  • 2:抽取类

抽取成类的方式咱们称之为:纵向抽取spa

  • 经过继承的方式实现纵向抽取

可是,咱们如今的办法不行:即便抽取成类仍是会出现重复的代码,由于这些逻辑(开始、结束、提交事务)依附在咱们业务类的方法逻辑中翻译

如今纵向抽取的方式不行了,AOP的理念:就是将分散在各个业务逻辑代码中相同的代码经过横向切割的方式抽取到一个独立的模块中!

上面的图也很清晰了,将重复性的逻辑代码横切出来其实很容易(咱们简单可认为就是封装成一个类就行了),但咱们要将这些被咱们横切出来的逻辑代码融合到业务逻辑中,来完成和以前(没抽取前)同样的功能!这就是AOP首要解决的问题了!

1.2Spring AOP原理

被咱们横切出来的逻辑代码融合到业务逻辑中,来完成和以前(没抽取前)同样的功能

没有学Spring AOP以前,咱们就可使用代理来完成。

  • 若是看过我写的给女友讲解什么是代理模式这篇文章的话,必定就不难理解上面我说的那句话了
  • 代理能干吗?代理能够帮咱们加强对象的行为!使用动态代理实质上就是调用时拦截对象方法,对方法进行改造、加强

其实Spring AOP的底层原理就是动态代理

来源《精通Spring4.x 企业应用开发实战》一段话:

Spring AOP使用纯Java实现,它不须要专门的编译过程,也不须要特殊的类装载器,它在 运行期经过代理方式向目标类织入加强代码。在Spring中能够无缝地将Spring AOP、IoC和AspectJ整合在一块儿。

来源《Spring 实战 (第4版)》一句话:

Spring AOP构建在动态代理基础之上,所以, Spring对AOP的支持局限于方法拦截

在Java中动态代理有两种方式:

  • JDK动态代理
  • CGLib动态代理

JDK动态代理是须要实现某个接口了,而咱们类未必所有会有接口,因而CGLib代理就有了~~

  • CGLib代理其生成的动态代理对象是目标类的子类
  • Spring AOP默认是使用JDK动态代理,若是代理的类没有接口则会使用CGLib代理

那么JDK代理和CGLib代理咱们该用哪一个呢??在《精通Spring4.x 企业应用开发实战》给出了建议:

  • 若是是单例的咱们最好使用CGLib代理,若是是多例的咱们最好使用JDK代理

缘由:

  • JDK在建立代理对象时的性能要高于CGLib代理,而生成代理对象的运行性能却比CGLib的低。
  • 若是是单例的代理,推荐使用CGLib

看到这里咱们就应该知道什么是Spring AOP(面向切面编程)了:将相同逻辑的重复代码横向抽取出来,使用动态代理技术将这些重复代码织入到目标对象方法中,实现和原来同样的功能

  • 这样一来,咱们就在写业务时只关心业务代码,而不用关心与业务无关的代码

1.3AOP的实现者

AOP除了有Spring AOP实现外,还有著名的AOP实现者:AspectJ,也有可能你们没据说过的实现者:JBoss AOP~~

咱们下面来讲说AspectJ扩展一下知识面:

AspectJ是 语言级别的AOP实现,扩展了Java语言,定义了AOP语法,可以在 编译期提供横切代码的织入,因此它有 专门的编译器用来生成遵照Java字节码规范的Class文件。

而Spring借鉴了AspectJ不少很是有用的作法,融合了AspectJ实现AOP的功能。但Spring AOP本质上底层仍是动态代理,因此Spring AOP是不须要有专门的编辑器的~

1.4AOP的术语

嗯,AOP搞了好几个术语出来~~两本书都有讲解这些术语,我会尽可能让你们看得明白的:

链接点(Join point):

  • 可以被拦截的地方:Spring AOP是基于动态代理的,因此是方法拦截的。每一个成员方法均可以称之为链接点~

切点(Poincut):

  • 具体定位的链接点:上面也说了,每一个方法均可以称之为链接点,咱们具体定位到某一个方法就成为切点

加强/通知(Advice):

  • 表示添加到切点的一段逻辑代码,并定位链接点的方位信息

    • 简单来讲就定义了是干什么的,具体是在哪干
    • Spring AOP提供了5种Advice类型给咱们:前置、后置、返回、异常、环绕给咱们使用!

织入(Weaving):

  • 加强/通知添加到目标类的具体链接点上的过程。

引入/引介(Introduction):

  • 引入/引介容许咱们向现有的类添加新方法或属性。是一种特殊的加强!

切面(Aspect):

  • 切面由切点和加强/通知组成,它既包括了横切逻辑的定义、也包括了链接点的定义。

在《Spring 实战 (第4版)》给出的总结是这样子的:

通知/加强包含了须要用于多个应用对象的横切行为;链接点是程序执行过程当中可以应用通知的全部点;切点定义了通知/加强被应用的具体位置。其中关键的是切点定义了哪些链接点会获得通知/加强。

总的来讲:

  • 这些术语可能翻译过来不太好理解,但对咱们正常使用AOP的话影响并无那么大~~看多了就知道它是什么意思了。

1.5Spring对AOP的支持

Spring提供了3种类型的AOP支持:

  • 基于代理的经典SpringAOP

    • 须要实现接口,手动建立代理
  • 纯POJO切面

    • 使用XML配置,aop命名空间
  • @AspectJ注解驱动的切面

    • 使用注解的方式,这是最简洁和最方便的!

2、基于代理的经典SpringAOP

这部分配置比较麻烦,用起来也很麻烦,这里我就主要整理一下书上的内容,你们看看了解一下吧,咱们实际上使用Spring AOP基本不用这种方式了!

首先,咱们来看一下加强接口的继承关系图:

能够分红五类加强的方式:

Spring提供了六种的切点类型

切面类型主要分红了三种

  • 通常切面
  • 切点切面
  • 引介/引入切面

通常切面,切点切面,引介/引入切面介绍:


对于切点切面咱们通常都是直接用就行了,咱们来看看引介/引入切面是怎么一回事:

  • 引介/引入切面是引介/引入加强的封装器,经过引介/引入切面,能够更容易地为现有对象添加任何接口的实现

继承关系图:

引介/引入切面有两个实现类:

  • DefaultIntroductionAdvisor:经常使用的实现类
  • DeclareParentsAdvisor:用于实现AspectJ语言的DeclareParent注解表示的引介/引入切面

实际上,咱们使用AOP每每是Spring内部使用BeanPostProcessor帮咱们建立代理

这些代理的建立器能够分红三类:

  • 基于Bean配置名规则的自动代理建立器:BeanNameAutoProxyCreator
  • 基于Advisor匹配机制的自动代理建立器:它会对容器全部的Advisor进行扫描,实现类为DefaultAdvisorAutoProxyCreator
  • 基于Bean中的AspectJ注解标签的自动代理建立器:AnnotationAwareAspectJAutoProxyCreator

对应的类继承图:

嗯,基于代理的经典SpringAOP就讲到这里吧,其实我是不太愿意去写这个的,由于已经几乎不用了,在《Spring 实战 第4版》也没有这部分的知识点了。

  • 可是经过这部分的知识点能够更加全面地认识Spring AOP的各类接口吧~

3、拥抱基于注解和命名空的AOP编程

Spring在新版本中对AOP功能进行了加强,体如今这么几个方面:

  • 在XML配置文件中为AOP提供了aop命名空间
  • 增长了AspectJ切点表达式语言的支持
  • 能够无缝地集成AspectJ

那咱们使用@AspectJ来玩AOP的话,学什么??其实也就是上面的内容,学如何设置切点、建立切面、加强的内容是什么...

具体的切点表达式使用仍是前往:Spring【AOP模块】就这么简单看吧~~

对应的加强注解:

3.1使用引介/引入功能实现为Bean引入新方法

其实前置啊、后置啊这些很容易就理解了,整篇文章看下来就只有这个引介/引入切面有点搞头。因而咱们就来玩玩吧~

咱们来看一下具体的用法吧,如今我有个服务员的接口:

public interface Waiter {

    // 向客人打招呼
    void greetTo(String clientName);

    // 服务
    void serveTo(String clientName);
}

一位年轻服务员实现类:

public class NaiveWaiter implements Waiter {
    public void greetTo(String clientName) {
        System.out.println("NaiveWaiter:greet to " + clientName + "...");
    }

    @NeedTest
    public void serveTo(String clientName) {
        System.out.println("NaiveWaiter:serving " + clientName + "...");
    }

}

如今我想作的就是:想这个服务员能够充当售货员的角色,能够卖东西!固然了,我确定不会加一个卖东西的方法到Waiter接口上啦,由于这个是暂时的~

因此,我搞了一个售货员接口:

public interface Seller {

  // 卖东西
  int sell(String goods, String clientName);
}

一个售货员实现类:

public class SmartSeller implements Seller {

    // 卖东西
    public int sell(String goods,String clientName) {
        System.out.println("SmartSeller: sell "+goods +" to "+clientName+"...");
        return 100;
    }
    
}

此时,咱们的类图是这样子的:

如今我想干的就是:借助AOP的引入/引介切面,来让咱们的服务员也能够卖东西

咱们的引入/引介切面具体是这样干的:

@Aspect
public class EnableSellerAspect {
    
    @DeclareParents(value = "com.smart.NaiveWaiter",  // 指定服务员具体的实现
            defaultImpl = SmartSeller.class) // 售货员具体的实现
    public Seller seller; // 要实现的目标接口
    
}

写了这个切面类会发生什么??

  • 切面技术将SmartSeller融合到NaiveWaiter中,这样NaiveWaiter就实现了Seller接口!!!!

是否是很神奇??我也以为很神奇啊,咱们来测试一下:

咱们的bean.xml文件很简单:

<?xml version="1.0" encoding="UTF-8" ?>
<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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
    <aop:aspectj-autoproxy/>
    <bean id="waiter" class="com.smart.NaiveWaiter"/>
    <bean class="com.smart.aspectj.basic.EnableSellerAspect"/>
</beans>

测试一下:

public class Test {
    public static void main(String[] args) {


        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("com/smart/aspectj/basic/beans.xml");
        Waiter waiter = (Waiter) ctx.getBean("waiter");

        // 调用服务员原有的方法
        waiter.greetTo("Java3y");
        waiter.serveTo("Java3y");

        // 经过引介/引入切面已经将waiter服务员实现了Seller接口,因此能够强制转换
        Seller seller = (Seller) waiter;
        seller.sell("水军", "Java3y");

    }
}

具体的调用过程是这样子的:

当引入接口方法被调用时,代理对象会把此调用委托给实现了新接口的某个其余对象。实际上,一个Bean的实现被拆分到多个类中

3.2在XML中声明切面

咱们知道注解很方便,可是,要想使用注解的方式使用Spring AOP就必需要有源码(由于咱们要在切面类上添加注解)。若是没有源码的话,咱们就得使用XML来声明切面了~

其实就跟注解差很少的功能:

咱们就直接来个例子终结掉它吧:

首先咱们来测试一下与传统的SpringAOP结合的advisor是怎么用的:

实现类:

xml配置文件:

.......

一个一个来说解仍是太花时间了,我就一次性用图的方式来说啦:

最后还有一个切面类型总结图,看完就几乎懂啦:

3、总结

看起来AOP有不少不少的知识点,其实咱们只要记住AOP的核心概念就行啦。

下面是个人简要总结AOP:

  • AOP的底层其实是动态代理,动态代理分红了JDK动态代理和CGLib动态代理。若是被代理对象没有接口,那么就使用的是CGLIB代理(也能够直接配置使用CBLib代理)
  • 若是是单例的话,那咱们最好使用CGLib代理,由于CGLib代理对象运行速度要比JDK的代理对象要快
  • AOP既然是基于动态代理的,那么它只能对方法进行拦截,它的层面上是方法级别的
  • 不管经典的方式、注解方式仍是XML配置方式使用Spring AOP的原理都是同样的,只不过形式变了而已。通常咱们使用注解的方式使用AOP就行了。
  • 注解的方式使用Spring AOP就了解几个切点表达式,几个加强/通知的注解就完事了,是否是贼简单...使用XML的方式和注解其实没有很大的区别,很快就能够上手啦。
  • 引介/引入切面也算是一个比较亮的地方,能够用代理的方式为某个对象实现接口,从而可以使用借口下的方法。这种方式是非侵入式的~
  • 要加强的方法还能够接收与被代理方法同样的参数、绑定被代理方法的返回值这些功能...

最后,将咱们上一次IOC的思惟导图补充AOP的知识点上去吧~~~

参考资料:

  • 《Spring 实战》
  • 《精通Spring4.x 企业应用开发实战》
若是文章有错的地方欢迎指正,你们互相交流。习惯在微信看技术文章,想要获取更多的Java资源的同窗,能够 关注微信公众号:Java3y。为了你们方便,刚新建了一下 qq群:742919422,你们也能够去交流交流。谢谢支持了!但愿能多介绍给其余有须要的朋友

文章的目录导航

相关文章
相关标签/搜索