第一章 Spring的核心(4节)

来源:互联网 发布:csr2车辆调试数据 编辑:程序博客网 时间:2024/06/04 00:33
 
1.4 应用aop编程
尽管DI可以把软件组件通过松散的方式组合起来,AOP让你抓住在软件系统中可重用的组件。
1.4.1介绍aop
Aop经常被介绍成为一个编程的技术,用来促进软件系统的概念区别。系统由一些组件组成,每个负责一些制定的功能。尽管经常他们总是提供一些多余的功能。日志,事务管理和安全等系统服务经常在其他一些模块中发现。这些系统服务通常指的是横向的概念,因为它们经常横向在你的其他组件中。
在其他组件中展开这个些系统服务,你把下面2个级别的复杂的东西添加到代码中。
l         实现系统级别关系的代码在多个组件是复用的。这就意味着,你要改变这些关系,你需要改变所有使用这些代码的地方。就算你把它抽象到一个分离的模块,程序调用仅仅需要一个方法的调用,这个方法的调用也是不断拷贝的。
l         这些代码垃圾,不能跟核心功能写在一起。一个往地址薄里面加入东西的方法应该只包含如何加入和安全及事务方面的事情。
下图实例了这种复杂。左边的业务对象同系统服务联系太紧密了。每个对象不仅知道log,安全,包含在事务上下文中,而且每个对象负责它们中的这些服务。

AOP使这些服务模块化,然后在应用它们的时候不需要请求。这使那些对象跟紧密,而且专注于自己需要的地方,完全忽视它们需要执行的系统服务。简短地说,AOP使POJO类保持简单。

 

可以理解为aspect是一个毯子,覆盖在系统的一些组件上,如下图所示。它中心是一些系统组件,用来执行业务逻辑。通过AOP,你可以把你的核心模块用一些功能层来覆盖。这些功能层不需要你的组件知道就可以灵活的执行,也不用声明。这是个强力概念,哈哈,它把安全等东西从杂乱的业务中拿出来。

1.4.2 实战AOP

       假设你的程序已经给了市场部,他们返回了一些新需求,一个诗人必须跟一个骑士一起走,编写骑士的行为,写成一首诗。

       Hmm…一个唱着骑士之歌的诗人,eh??听起来不难。下面写下了诗人class

package com.springinaction.chapter01.knight;

 

import org.apache.log4j.Logger;

 

public class Minstrel {

    private static final Logger SONG = Logger.getLogger(Minstrel.class);

    public void singBefore (Knight knight){

       SONG.info("Fa la la; Sir " + knight.getName() + " is so brave!");

    }

   

    public void singAfter(Knight knight){

       SONG.info("Tee-hee-he; Sir " + knight.getName() + " did embark a quest!");

    }

}

       DI的思想,KnightOfRoundTable应该做些修改,添加minstrel进去。

    private Minstrel minstrel;

    ……

    public void setMinstrel(Minstrel minstrel) {

       this.minstrel = minstrel;

    }

……

    public Object embarkOnQuest() throws GrailFailedException{

       minstrel.singBefore(this);

       HolyGrail grail = quest.embark();

       minstrel.singAfter(this);

       return grail;

    }

    应该就是这样的!等等还有个小问题。每个knight要停下来告诉minstrel他要进行quest了,然后才能继续。如下图所示。Quest后,knight必须告诉minstrel唱他的歌。记得告诉minstrel唱歌会阻止knight进行quest-embark

    理想情况,minstrel要自动的开始唱歌。Knight不需要知道自己的行为写入了诗中。毕竟,你不想knight为了告诉minstrel而耽误quest

简短的说,minstrel的行为超越了knight的行为。另外一个说法是:minstrel的服务(唱歌)同knight的行为正交了。这就需要把minstrel变成一个aspect,然后用来写诗。然后,minstrel的服务就覆盖了knight的行为,所有的都不需要knight知道有个minstrel在那里。下图所示:

    就像看到的,通过SpringAOP很容易把minstrel变成一个aspect

组织aspect

    Spring中有几个方法来实现aspect,我们在第四章专研这些。但为了下面的例子,我们介绍Spring2.0中提供的AOP命名空间。为了使用aop,那么要通过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-2.0.xsd

       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.1.xsd">

……………

</beans>

    声明了命名空间,我们就可以创建aspect。下面xml中,在Spring上下文中的minstrel作为一个bean,接着就可以同knight写作的aspect

    <bean id="minstrel" class="com.springinaction.chapter01.knight.Minstrel">

       <aop:config>

           <aop:aspect ref="minstrel">

              <aop:pointcut id="questPointcut" expression="execution(* *.embarkQuest(..)) and target(bean)"/>

              <aop:before method="singBefore" pointcut-ref="questPointcut" arg-names="bean"/>

              <aop:after-returning method="singAfter" pointcut-ref="questPointcut" arg-names="bean"/>

           </aop:aspect>

       </aop:config>

    </bean>

上面的代码有很多要了解的。

l         第一行,声明minstrel bean,它没有任何的依赖关系。

l         aop:config,大部分的aop配置都要在这个元素内。

l         aop:aspect 元素声明了一个aspect,ref指向了需要生成的类的id

l         aop:pointcut一个aspectpointcut组成。Pointcut是指哪里使用aspect的功能。这个元素定义了执行embarkQuest时触发的一个pointcut

l         aop:before 定义了在pointcut之前要调用的方法

l         aop:after 定义了在pointcut之后调用的方法

这就是所有的。我们现在把minstrel变成了一个Springaspect。不要担心不理解这些,第四章有足够的例子让你明白Spring AOP的。现在,有两个重要点。

第一,Minstrel仍然是个POJO类,没有什么表面它是个aspect,但在Spring上下文中,它已经是个aspect了。

第二,更重要的,knight不需要告诉minstrel歌唱他的事迹了。作为一个aspectminstrel自动处理这些。实际上,knight再也不用知道minstrel存在了,相应的,KnightOfRoundTable中之前添加的代码将删掉。

public class KnightOfRoundTable implements Knight{

    private String name;

    private Quest quest;

 

    public KnightOfRoundTable(String name) {

       this.name = name;

    }

    public Object embarkOnQuest() throws GrailFailedException{

       return quest.embark();

    }

    public void setQuest(Quest quest){

       this.quest = quest;

    }  

}

    使用AOP来记录knight的事迹很有趣,但Spring AOP可以做更多有用的事。以后将看到,Spring 使用AOP来提供企业级服务,比如声明事务(第六章)和安全(第七章)。

 

原创粉丝点击