Spring入门之面向切面的Spring

1、面向切面编程(AOP所要解决的问题):

将横切关注点与业务逻辑相分离。

2、横切关注点:

散布于应用中多处的相同功能被称为横切关注点,如日志、安全和事务管理等。

3、依赖注入(DI)

有助于应用对象之间的解耦,而AOP可以实现横切关注点与他们所影响的对象之间的解耦。

4、切面

横切关注点可以被模块化为特殊的类,这些类被称为切面(aspect)。

5、使用AOP有两个好处:

  • a、每个关注点都集中于一个地方,而不是分散到多处代码中;

  • b、服务模块更简洁,因为它们只包含核心功能的代码,次要关注点的代码被转移到切面中了。

6、AOP术语

描述切面的常用术语有通知(advice)、切点(pointcut)和连接点(joinpoint)。

6.1、通知(Advice)

通知定义了切面是什么以及何时使用。 Spring切面可以应用5种类型的通知:

  • a、前置通知(Before):

在目标方法被调用之前调用通知功能;

  • b、后置通知(After):

在目标方法完成之后调用通知,此时不会关心方法的输出是什么;

  • c、返回通知(After-returning):

在目标方法成功执行之后调用通知;

  • d、异常通知(After-throwing):

在目标方法抛出异常后调用通知;

  • e、环绕通知(Around):

通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。

6.2连接点(Join point)

我们的应用有很多时机应用通知。这些时机被称为连接点。连接点是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时,甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。

6.3切点(Poincut)

如果让一位抄表员访问电力公司所服务的所有住户,那肯定是不现实的。实际上,电力公司为每一个抄表员都分别指定某一块区域的住户。类似地,一个切面并不需要通知应用的所有连接点。切点有助于缩小切面所通知的连接点范围。
如果说通知定义了切面的“什么”和“何时”的话,那么切点就定义了“何处”。切点的定义会匹配通知所要织入的一个或多个连接点。我们通常使用明确的类和方法名称,或是利用正则表达式定义所匹配的类和方法名称来指定这些切点。有些AOP框架允许我们创建动态的切点,可以根据运行时的决策(比如方法的参数值)来决定是否应用通知。

6.4切面(Aspect)

当抄表员开始一天的工作时,他知道自己要做的事情(报告用电量)和从哪些房屋收集信息。因此,他知道要完成工作所需要的一切东西。切面是通知和切点的结合。通知和切点共同定义了切面的全部内容——它是什么,在何时和何处完成其功能。

6.5引入(Introduction)

引入允许我们向现有的类添加新方法或属性。例如,我们可以创建一个Auditable通知类,该类记录了对象最后一次修改时的状态。这很简单,只需要一个方法,setLastModified(Date),和一个实例变量来保存这个状态。然后,这个新方法和实例变量就可以被引入到现有的类中,从而可以在无需修改这些现有的类的情况下,让它们具有新的行为和状态。

6.6织入(Weaving)

织入是把切面应用到目标对象并创建新的代理对象的过程。切面在指定的连接点被织入到目标对象中。在目标对象的生命周期里有多个点可以进行织入:

  • a、编译期:切面在目标类编译时别织入。这种方式需要特殊的编译器。AspectJ的织入编译器就是以这种方式织入切面的。

  • b、类加载期:切面在目标类加载到JVM时被织入。这种方式需要特殊的类加载器,它可以在目标类被引入应用之前增加该目标类的字节码。AspectJ5的加载时织入就支持这种方式。

  • c、运行期:切面在应用运行的某个时候被织入。一般情况下,在织入切面时,AOP容器会为目标对象动态地创建一个代理对象。Spring
    AOP就是以这种方式织入切面的。

通知包含了需要用于多个应用对象的横切行为;连接点是程序执行过程中能够应用通知的所有点;切点定义了通知被应用的具体位置(在哪些连接点)。其中关键的概念是切点定义了哪些连接点会得到通知。

7、Spring提供了4种类型的AOP支持:

  • a、基于代理的经典Spring AOP;

  • b、纯POJO切面;

  • c、@AspectJ注解驱动的切面;

  • d、注入时AspectJ切面(适用于Spring各版本);

SpringAOP构建在动态代理基础上,因此,Spring对AOP的支持局限于方法拦截。Spring经典的AOP过于繁杂和笨重(直接使用ProxyFactory Bean会让人感觉厌烦),二声明式AOP和基于注解的AOP更为简单。

8、借助Spring的aop命名空间,我们可以将纯POJO转化为切面。

实际上,这些POJO只是提供了满足切点条件时所要调用的方法。遗憾的是,这种技术需要XML配置,但这的确是声明式地将对象转换为切面的简便方式(项目中可以考虑混合配置);基于@AspectJ注解驱动的AOP,本质上仍然是基于代理的AOP,但是编程模型几乎与编写成熟的AspectJ注解切面完全一致。这种AOP风格的好处在于能够不使用XML来完成功能。

9、AspectJ实现切面

如果你的AOP需求超过了简单的方法调用(如构造器和属性拦截),那么你需要考虑使用AspectJ来实现切面。

10、通知

Spring所创建的通知都是使用标准的Java类编写的,定义通知所应用的切点通常会使用注解或在Spring配置文件里采用XML来编写,这两种语法对Java开发者来说都是相当熟悉的。
AspectJ与之相反。虽然AspectJ现在支持基于注解的切面,但AspectJ最初是以Java语言扩展的方式来实现的。这种方式有缺点也有优点。通过特有的AOP语言可以获得更强大和细粒度的控制,以及更丰富的AOP工具集,但是我们需要额外学习新的工具和语法。

11、织入时机

Spring运行时才创建代理对象,我们不需要特殊的编译器来织入SpringAOP的切面。

12、Spring切面实现原理

Spring的切面由包裹了目标对象的代理类实现。代理类处理方法的调用,执行额外的切面逻辑,并调用目标方法。

13、Spring只支持方法级别的连接点。

14、切点表达式语言

Spring借助AspectJ的切点表达式语言来定义Spring切面,且Spring仅支持部分AspectJ的指示器。其中,只有execution()指示器是实际执行匹配的,其他指示器都是用来限制匹配的。这说明execution指示器是我们在编写切点定义时最主要使用的指示器,再次基础上,我们使用其他指示器来限制所匹配的切点。

15、切点

切点用于准确定位应该在什么地方应用切面的通知。通知和切点是切面的最基本元素。

16、编写切点

我们定义一个Performance接口来作为切点的目标:

package concert;
public interface Performance{
    public void perform();
}

Performance可以代表任何类型的现场表演,假设我们想编写Performance的perform()方法触发的通知。下面的表达式能够设置当perform()方法执行时出发通知的调用。

execution(* concert.Performance.perform(..))

我们使用execution()指示器选择Performance的perform()方法。方法表达式以” * “号开始,表明了我们不关心方法返回值的类型。然后,我们制定了权限定类名和方法名。对于方法参数列表,我们使用两个点号(..)表明切点要选择任意的perform()方法,无论该方法的入参是什么。
如果我们要求配置的切点仅匹配concert包,那么表达式应该是这样:

execution(* concert.Performance.perform(..) and within(concert.*) )

and等同于&&,同理or、not等同于||和!。

17、在切点中选择bean

除了AspectJ中的指示器外,Spring还引入了一种新的bean()指示器,它允许我们在切点表达式中使用bean的ID来表示bean。该指示器的一种典型应用是:在特定场景下,切面的通知会被编址到所有ID不为指定ID的bean中:

execution(* concert.Performance.perform(..) and !bean('woodstock'))

在此场景下,切面的通知会被编织到所有ID不为woodstock的bean中。这些就是编写切点的基础知识,接下来我们了解如何编写通知和使用这些切点声明切面。

18、使用注解创建切面

package concert;
@Aspect
public class Audience{
    @Pointcut("execution(** concert.Performance.perform(..))")
    public void performance(){}

    @Before("performance()")
    public void silenceCellPhones(){
    System.out.println("Silencing cell phones");
    }
    @Before("performance()")
    public void takeSeats(){
      System.out.println("Taking Seats");
    }

    @AfterReturning("performance()")
    public void applause(){
      System.out.println("CLAP CLAP CLAP");
    }
    @AfterThrowing("performance")
    public void demandRefund(){
      System.out.println("Demanding a refund");
    }
}

在JavaConfig中启用AspectJ注解的自动代理

package concert;
@Configuration
@EnableAspectJAutoProxy            //启用AspectJ自动代理
@ComponentScan
public class ConcertConfig{

    @Bean
    public Audience audience(){
      return new Audience();
    }    
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值