Spring AOP 详解

情景案例

小明辛苦忙了一全年终于完成了包含300个接口的业务系统项目。项目圆满上线并稳定运行了一段时间了。忽然有一天总监说,对于会形成数据变化的全部接口,咱们必须记录用户的操做日志。而后小明就吭哧吭哧给其中150个接口,挨个加上日志代码,累得真够呛。spring

过了一阵子总监又说,全部变化不多的数据所有都加上缓存,缓存涉及到刷新缓存、获取缓存、删除缓存的问题。因而乎,小明就又吭哧吭哧地给其中的100个接口加上缓存相关的代码。数据库

又过了一阵子总监说,全部涉及充值退款费用相关的接口,须要生成发票单存入数据库。这时候小明又须要吭哧吭哧给涉及到的50个接口,挨个加上发票存储操做。编程

小明每天加班也没在工期内完成任务,而且本来的业务代码已经变得臃肿不堪了。设计模式

本来的代码:缓存

/**
 * 业务方法
 */
public static void method() {

    // 业务操做
    doBusiness();

}
复制代码

通过硬编码添加各类非业务性代码后的业务代码:bash

/**
 * 业务方法
 */
public static void method() {
    // 日志操做
    doLog();
    // 业务操做
    doBusiness();
    // 缓存操做
    doLog();
    // 发票操做
    doReceipt();

}
复制代码

读者应该能明显感觉到在没有AOP代理的状况下的缺点框架

  1. 业务代码和非业务代码混杂在一块儿,本来清晰的业务流程淹没在与业务不相关的代码中。
  2. 增长非业务性的功能时,都须要手工硬编码去实现,费时费力。
  3. 代码变得很差维护,一是代码耦合度高,二是须要经过硬编码的方式去拓展或者修改功能。

AOP是什么?

在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,经过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。主要目标仍是致力于解耦,咱们能够看到解耦这一理念贯穿于咱们的整个编码工做中。咱们经过各类设计模式或者设计原则来使得对象之间解耦。经过Spring IOC容器中利用依赖注入使得对象之间的耦合度更低。而AOP的思想解耦得更完全,经过动态的添加功能来加强实现,而且作到毫无代码的侵入性。利用AOP能够对业务逻辑和非业务逻辑的部分进行隔离,能够提取非业务逻辑的部分,提升程序的可重用性,同时提升了开发的效率。ide

如何理解“切面”二字呢?函数

15703790331.png

咱们的业务流程方法都是自顶向下垂直的,而当咱们须要给这些业务方法统一加上某些非业务功能的话,就会发现这些非业务功能方法在图上会连成一条直线,并与原来的业务流程方法垂直横切。性能

为何使用AOP?

  1. 核心业务代码与切面代码解耦,切面代码对核心业务代码彻底无侵入,遵照单一职责原则,彻底隔离核心业务代码与切面代码。

  2. 低耦合带来可维护性高,修改或者新增一个切面代码仅需集中在一处进行更改。低耦合也意味着切面代码可复用性高。

  3. Spring IOC容器自然地为AOP的实现提供了便利,IOC和AOP的结合使得Spring的解耦能力更强。

AOP例子

先声明切面类:

/**
 *  注解@Aspect标识该类为切面类
 */
@Component
@Aspect
public class PersonAspect {

    // 经过表达式定义切入点
    @Pointcut("execution(* com.valarchie.aop.MeetingServiceImpl.meeting(..))")
    public void conference() {}
 
    // 前置通知
    @Before("meeting()")
    public void takeSeats() {
        System.out.println("开会前,找到位置坐");
    }
 
    // 前置通知
    @Before("meeting()")
    public void silenceCellPhones() {
        System.out.println("开会前,手机调成静音");
    }
 
    // 后置通知
    @After("meeting()")
    public void summary() {
        System.out.println("开会后,写总结报告");
    }
 
}

复制代码

建立要被代理的接口,即MeetingService会议服务

public interface MeetingService {
    void meeting();
}
复制代码

建立MeetingService会议服务的具体实现

@Component
public class MeetingServiceImpl implements MeetingService {
 
    @Override
    public void meeting() {
        System.out.println("会议进行中..");
    }
 
}

复制代码

定义AOP配置

@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ComponentScan("com.valarchie")
public class AppConfig {
 
}
复制代码
  1. 注解@EnableAspectJAutoProxy开启代理;
  2. 若是属性proxyTargetClass默认为false, 表示使用jdk动态代理织入加强;
  3. 若是属性proxyTargetClass设置为true,表示使用Cglib动态代理技术织入加强;
  4. 若是属性proxyTargetClass设置为false,可是目标类没有声明接口, Spring aop仍是会使用Cglib动态代理,也就是说非接口的类要生成代理都用Cglib。

测试AOP:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AppConfig.class)
public class AopTest {
 
    @Autowired
    private MeetingServiceImpl meetingService;
 
    @Test
    public void testAopAnnotation() {
        meetingService.meeting();
    }
 
}

复制代码

运行结果:

开会前,手机调成静音
开会前,找到位置坐
会议进行中..
开会后,写总结报告
复制代码

在这个AOP的例子当中咱们没有在会议服务实现类当中硬编码须要添加的切面功能,而是经过另外新建一个类来描述切面,以及须要在切面上加强的功能。这样的实现是否是更优雅呢?

关于切入点的表达式稍微解析一下:

例如定义切入点表达式  execution (* com.sample.service.impl..*.*(..))

execution()是最经常使用的切点函数,其语法以下所示:

 整个表达式能够分为五个部分:

 一、execution(): 表达式主体。

 二、第一个*号:表示返回类型,*号表示全部的类型。

 三、包名:表示须要拦截的包名,后面的两个句点表示当前包和当前包的全部子包,
 com.sample.service.impl包、子孙包下全部类的方法。

 四、第二个*号:表示类名,*号表示全部的类。

 五、*(..):最后这个星号表示方法名,*号表示全部的方法,后面括弧里面表示方法
 的参数,两个句点表示任何参数。
复制代码

以上的例子当中涉及很多AOP概念,接下来咱们针对这些概念进行逐一解释。

AOP中的概念阐述

  • 链接点(Joinpoint) 程序执行的某个特定位置:如类开始初始化前、类初始化后、类某个方法调用前、调用后、方法抛出异常后。一个类或一段程序代码拥有一些具备边界性质的特定点,这些点中的特定点就称为“链接点”。Spring仅支持方法的链接点,即仅能在方法调用前、方法调用后、方法抛出异常时以及方法调用先后这些程序执行点织入加强。链接点由两个信息肯定:第一是用方法表示的程序执行点;第二是用相对点表示的方位。

  • 切点(Pointcut) 每一个程序类都拥有多个链接点,如一个拥有两个方法的类,这两个方法都是链接点,即链接点是程序类中客观存在的事物。AOP经过“切点”定位特定的链接点。链接点至关于数据库中的记录,而切点至关于查询条件。切点和链接点不是一对一的关系,一个切点能够匹配多个链接点。在Spring中,切点经过org.springframework.aop.Pointcut接口进行描述,它使用类和方法做为链接点的查询条件,Spring AOP的规则解析引擎负责切点所设定的查询条件,找到对应的链接点。其实确切地说,不能称之为查询链接点,由于链接点是方法执行前、执行后等包括方位信息的具体程序执行点,而切点只定位到某个方法上,因此若是但愿定位到具体链接点上,还须要提供方位信息。

  • 加强(Advice) 加强是织入到目标类链接点上的一段程序代码,在Spring中,加强除用于描述一段程序代码外,还拥有另外一个和链接点相关的信息,这即是执行点的方位。结合执行点方位信息和切点信息,咱们就能够找到特定的链接点。

  • 目标对象(Target) 加强逻辑的织入目标类。若是没有AOP,目标业务类须要本身实现全部逻辑,而在AOP的帮助下,目标业务类只实现那些非横切逻辑的程序逻辑,而性能监视和事务管理等这些横切逻辑则可使用AOP动态织入到特定的链接点上。

  • 引介(Introduction) 引介是一种特殊的加强,它为类添加一些属性和方法。这样,即便一个业务类本来没有实现某个接口,经过AOP的引介功能,咱们能够动态地为该业务类添加接口的实现逻辑,让业务类成为这个接口的实现类。

  • 织入(Weaving) 织入是将加强添加对目标类具体链接点上的过程。AOP像一台织布机,将目标类、加强或引介经过AOP这台织布机完美无缺地编织到一块儿。根据不一样的实现技术,AOP有三种织入的方式: a、编译期织入,这要求使用特殊的Java编译器。 b、类装载期织入,这要求使用特殊的类装载器。 c、动态代理织入,在运行期为目标类添加加强生成子类的方式。 Spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。

  • 代理(Proxy) 一个类被AOP织入加强后,就产出了一个结果类,它是融合了原类和加强逻辑的代理类。根据不一样的代理方式,代理类既多是和原类具备相同接口的类,也可能就是原类的子类,因此咱们能够采用调用原类相同的方式调用代理类。

  • 切面(Aspect) 切面由切点和加强(引介)组成,它既包括了横切逻辑的定义,也包括了链接点的定义,Spring AOP就是负责实施切面的框架,它将切面所定义的横切逻辑织入到切面所指定的链接点中。

笔者我的理解,若有错误恳请网友评论指正。

转自个人我的博客 vc2x.com

相关文章
相关标签/搜索