Spring之面向切面编程(AOP)入门

1、面向切面编程(AOP)

(1)面向切面编程

       DI能够让相互协作的软件组件保持松散耦合,而面向切面编程(aspect-oriented programming, AOP)允许你把遍布应用各处的功能分离出来形成可重用的组件。AOP和OOP互为补充,面向切面编程将程序运行过程分解成各个切面。

       面向切面编程往往被定义为促使软件系统实现关注点的分一项技术。系统由许多不同的组件组成,每一个组件各负责一块特定功能。除了实现自身核心的功能之外,这些组件还经常承担着额外的职责。诸如日志、事务管理和安全这样的系统服务经常融入到自身具有核心业务逻辑的组件中去,这些系统服务通常被称为横切关注点,因为它们会跨越系统的多个组件。

       如果将这些关注点分散到多个组件中去,你的代码将会带来双重的复杂性。

  • 实现系统关注点功能的代码将会重复出现在多个组件中。这意味着如果你要改变这些关注点的逻辑,必须修改各个模块中的相关实现。即使你把这些关注点抽象为一个独立的模块,其他模块只是调用它的方法,但方法的调用还是会重复出现在各个模块中。
  • 组件会因为那些与自身核心业务无关的代码而变得混乱。一个向地址簿增加地址条目的方法应该只关注如何添加地址,而不应该关注它是不是安全的或者是否需要支持事务。

       我们可以把切面想象为覆盖在很多组件之上的一个外壳。应用是由那些实现各自业务功能的模块组成的。借助AOP,可以使用各种功能层去包裹核心业务层。这些层以声明的方式灵活地应用到系统中,你的核心应用甚至根本不知道它们的存在。这是一个非常强大的理念,可以将安全、事务和日志关注点与核心业务逻辑相分离。

(2)AOP实现可分为两类

  1. 静态AOP实现: AOP框架在编译阶段对程序进行修改,即实现对目标类的增强,生成静态的AOP代理类,以AspectJ(基于Java语言的AOP框架)为代表。
  2. 动态AOP实现: AOP框架在运行阶段动态生成AOP代理,以实现对目标对象的增强,以Spring AOP为代表。

一般来说,静态AOP实现具有较好的性能,但需要使用特殊的编译器。动态AOP实现是纯Java实现,因此无须特殊的编译器,但是通常性能略差。

(3)关于面向切面编程的一些术语

  • 切面(Aspect): 切面用于组织多个Advice,Advice放在切面中定义。
  • 连接点(Joinpoint): 程序执行过程中明确的点,如方法的调用,或者异常的抛出。在Spring AOP中,连接点总是方法的调用。
  • 增强处理(Advice): AOP框架在特定的切入点执行的增强处理。处理有"around"、"before"和"after"等类型
  • 切入点(Pointcut): 可以插入增强处理的连接点。简而言之,当某个连接点满足指定要求时,该连接点将被添加增强处理,该连接点也就变成了切入点。

(4)Spring的AOP支持

       Spring中的AOP代理由Spring的IoC容器负责生成、管理,其依赖关系也由IoC容器负责管理。为了在应用中使用@AspectJ支持,Spring需要添加三个库:

  • aspectjweaver.jar
  • aspectjrt.jar
  • aopalliance.jar

并在Spring配置文件中做如下配置:

<!--启动@AspectJ支持-->
<aop:aspectj-autoproxy/>

<!--指定自动搜索Bean组件、自动搜索切面类-->
<context:component-scan base-package="edu.shu.sprint.service">
    <context:include-filter type="annotation" expression="org.aspectj.lang.annotation.Aspect"/>
</context:component-scan>

 

2、实例

package main.java.sia.knights;

public class BraveKnight implements Knight {
    private Quest quest;
    private Minstrel minstrel;  // 吟游诗人类

    public BraveKnight(Quest quest, Minstrel minstrel) {   // Quest和Minstrel被注入(构造器注入)
        this.quest = quest;
        this.minstrel = minstrel;
    }

    public void embarkOnQuest() {
        minstrel.singBeforeQuest();
        quest.embark();
        minstrel.singAfterQuest();
    }
}

       在以上的例子中将吟游诗人类(Minstrel )注入到骑士类(BraveKnight )中,导致代码变得复杂。但利用AOP,你可以声明吟游诗人必须歌颂骑士的探险事迹,而骑士本身并不用直接访问Minstrel的方法。添加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/aop
      http://www.springframework.org/schema/aop/spring-aop.xsd
      http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="knight" class="main.java.sia.knights.BraveKnight">
    <constructor-arg ref="quest" />
</bean>

<bean id="quest" class="main.java.sia.knights.SlayDragonQuest">
    <constructor-arg value="#{T(System).out}" />
</bean>

<!--    声明 Minstrel bean-->
<bean id="minstrel" class="main.java.sia.knights.Minstrel">
    <constructor-arg value="#{T(System).out}" />
</bean>

<aop:config>
<!-- 引用Minstrel Bean-->
    <aop:aspect ref="minstrel">
<!--    定义切点-->
        <aop:pointcut id="embark"
                      expression="execution(* *.embarkOnQuest(..))"/>
<!--    声明前置通知-->
        <aop:before pointcut-ref="embark"
                    method="singBeforeQuest"/>

<!--    声明后置通知-->
        <aop:after pointcut-ref="embark"
                   method="singAfterQuest"/>
    </aop:aspect>
</aop:config>

</beans>

<!--<?xml version="1.0" encoding="UTF-8"?>-->
<!--<beans xmlns="http://www.springframework.org/schema/beans"-->
<!--       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"-->
<!--       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">-->

<!--</beans>-->

       这里使用了Spring的aop配置命名空间把Minstrel bean声明为一个切面。首先,需要把Minstre1声明为- -个bean,然后在<aop: aspect>元素中引用该bean。为了进一步定义切面, 声明(使用<aop:before>)在embar kOnQuest ()方法执行前调用Minstrel的singBeforeQuest ()方法。这种方式被称为前置通知(before advice)。同时声明(使用<aop:after>)在embarkOnQuest ()方法执行后调用singAfter Quest() 方法。这种方式被称为后置通知( after advice)。

       在这两种方式中,pointcut-ref属性都引用了名字为embank的切入点。该切入点是在前边的<pointcut>元素中定义的,并配置expression,属性来选择所应用的通知。表达式的语法采用的是AspectJ的切点表达式语言。

       这就是我们需要做的所有的事情!通过少量的XML配置,就可以把Minstrel声明为一个Spring切面。

       首先,Minstrel 仍然是-一个POJO,没有任何代码表明它要被作为-一个切面使用。当我们按照上面那样进行配置后,在Spring的上下文中,Minstrel实际上已经变成一个切面了。

       其次,也是最重要的,Minstrel 可以被应用到BraveKnight中,而BraveKnight不 需要显式地调用它。实际上,BraveKnight完全不知道Minstrel的存在。

       必须还要指出的是,尽管我们使用Spring魔法把Mi nstrel转变为一个切面,但首先要把它声明为一个Spring bean.能够为其他Spring bean做到的事情都可以同样应用到Spring切面中,例如为它们注入依赖。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值