依赖注入、页面切换和应用上下文----Spring实战(第4版)读书笔记记录

我们来看一个老式Java对象POJO的例子:

package com.habuma.spring;

public class HelloWorldBean{

   public String sayHelllo(){

 return "Hello World";

}

}

DI

spring的一项重要作用就是依赖注入,我们可以DI来装配上面的POJO式代码,来帮助应用对象彼此之间保持松散耦合。

它的意义在于:在实际的应用中,有两个或者两个以上的类组成,它们需要相互协作来完成特定的功能。每个对象负责管理与自己

相互协作的对象(即它所依赖的对象)的引用,这样会导致高度耦合和难以测试代码。

我们来看一个紧耦合的例子:

package com.springinaction.knight;

public class DamselRescuingKnight implements knight{

private RescueDameselQuest quest;

public DamselRescuingKnight(){

this.quest=new RescueDamselQuest();//与RescueDamselQuest紧耦合

}

public void embarkOnQuest(){

quest.embark();

}

}

这样的紧耦合有两个缺点,一方面DamselRescuingKnight与RescueDameselQuest耦合在一起,这样限制了这个骑士的能力。另一方面耦合的代码

难以测试,难以复用和理解。

这时DI可以很好的解决这两个问题,依赖注入会将所依赖的关系自动交给目标对象,而不是让对象自己去获取依赖。我们来看下面的例子:

package com.springinaction.knights;

public class BraveKnight implements Knight{

private Quest quest

public BraveKnight(Quest quest){//Quest被注入进来,BraveKnight没有自行创建探险任务,而是把它作为构造器参数注入,这就是构造器注入。

//这里的任务类型是Quest,这样所有探险任务都实现一个接口,BraveKnight就可以做RescueDamselQuest、SlayDragonQuest等任意Quest的实现。

this.quest=quest;

}

public void embarkOnQuest(){

quest.embark();

}

}

对依赖进行替换最常用的方法就是在测试的时候使用mock实现。测试代码就不详细说明了。

让我们来看如何把特定的探险任务传给骑士:

package com.springination.knights;

import java.io.printStream;

public class SlayDragonQuest implements Quest{

private printstream stream;

public SlayDragonQuest(printstream stream){

this.stream=stream;

}

public void embark(){

stream.println("Embarking on quest to slay the dragon!");

}

}

装配

创建应用组件之间协作的行为我们称为装配。Spring有多种装配bean的方式,采用XML是很常见的一种装配方式。

我们来看下面这个配置文件knights.xml,将BraveKnight、SlayDragonQuest和PrintsStream装配到一起。

<?xml version="1.0" encoding=='UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www-instance.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.springframework.org/schema/beans

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

<bean id="knight" class="com.springinaction.knights.BraveKnight">

<constructor-arg ref="quest"/>//注入Quest bean

</bean>

<bean id="quest" class="com.springinaction.knights.SlayDragonQuest">//以下语句来创建SlayDragonQuest

<constructor-arg value="#{T(System).out}"/>

</bean>

</bean>

当然Spring还支持java来描述配置。

package com.spring.knights.config;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotion.Configuration;

import com.springinaction.knights.BraveKnight;

import com.springinaction.knights.eKnight;

import com.springinaction.knights.Quest;

import com.springinaction.knights.SlayDragonQuest;

@Configuration

public class knightConfig{

@Bean

public knight knight(){

return new Braveknight(quest());

}

@Bean

public Quest quest(){

return new SlayDragonQuest(System.out);

}

}

Spring通过上下文装载bean的定义并组装它们。

XML文件一般用ClassPathXmlApplicationContext作为上下文,我们看下面的例子:

package com.springinaction.knights;

import org.springframework.context.support.

                           ClassPathXmlApplicationContext;

public satic void main(String[]args)throws Exception{

   ClassPathXmlApplicationContext context=    //加载Spring上下文

              new ClassPathXmlApplicationContext("META-INF/spring/knights.xml");

 knight knight=context.getBean(knight.class);//获取knight bean

 knight.embarkQuest();使用knight

  context.close();

}

}

应用切面

DI能够让相互协作的软件组织保持松耦合,应用切面(AOP)可以把遍布应用各处的功能分离出来形成可重用的组件。

系统的组件除了实现自身核心的功能之外,这些组件还经常承担额外的职责。诸如日志、事务管理和安全这样的系统服务我们称为横切关注点。

AOP的作用就是能够将这些服务模块化,以声明的方式应用到所需的组件中去。

假设我们需要吟游诗人来记载骑士的所以事迹,我们使用Minstrel类。看下面的例子:

package com.springinaction.knights;

import java.io.printstream;

public class Minstrel{

  private PrintStream stream;

  public Minstrel(PrintStream stream){

   this.stream=stream;

}

public void singBeforeQuest(){//探险之前被调用

   stream.println("Tee hee hee,the brave knight is so brave!");

}

public void singAfterQuest(){//探险之后调用

     stream.println("Tee hee hee,the brave knight"+"did embark on a quest!");

}

}

我们做一下适当调整让BraveKnight可以使用在Minstrel。

 

package com.springinaction.knights;

public class BraveKnight implements Knight{

private Quest quest;

private Minstrel minstrel;

public BraveKnight(Quest quest,Minstrel ministrel){

this.quest=quest;

this.ministrel=ministrel;

}

public void embarkOnQuest() throws QuestException{

minstrel.singBeforeQuest();

quest.embark();

ministrel.singAfterQuest();

}

}

 这里的一个问题是吟游诗人的行为受到了骑士的影响,为何骑士还需要提醒吟游诗人做他份内的事?

现在我们将Minstrel抽象为一个切面,只需在Spring配置文件中声明它。

<?xml version="1.0" encoding=='UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www-instance.w3.org/2001/XMLSchema-instance"

xmlns:aop="http://wwwspringframework.org/schema/beans"

xsi:schemaLocation="http://www.springframework.org/schema/aop

 http://www.springframework.org/schema/beans/spring-aop-3.2.xsd

http://www.springframework.org/schema/beans

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

<bean id="knight" class="com.springinaction.knights.BraveKnight">

<constructor-arg ref="quest"/>//注入Quest bean

</bean>

<bean id="quest" class="com.springinaction.knights.SlayDragonQuest">//以下语句来创建SlayDragonQuest

<constructor-arg value="#{T(System).out}"/>

</bean>

<bean id="ministrel" class="com.springinaction.knights.Minstrel">//声明Minstrel bean

<constructor-arg value="#{T(System).out}"/>

</bean>

<aop:config>

<aop:aspect ref="minstrel">

 <aop:pointcut id="embark"

               expression="execution(*   *.embarkOnQuest(..))"/>//定义切点

<aop:before pointcut-ref="embark"//声明前置通知

  method="singAfterQuest"/>

<aop:after pointcut-ref="embark"//声明前置通知

  method="singAfterQuest"/>

</aop:aspect>

</aop:config>

</beans>

最后我们简单介绍几种应用上下文:

AnnotationConfigApplicationContext:从一个或多个基于Java的配置类中加载Spring应用上下文

AnnotationConfigWebApplicationContext:从一个或多个基于Java的配置类中加载Spring Web应用上下文

ClassPathXmlApplicationContext:从类路径下的一个或多个XML配置文件中加载上下文定义,把应用上下文的定义文件作为类资源

FileSystemXmlapplicationContext: 从文件系统下的一个或者多个XML配置文件中加载上下文定义

xmlWebApplicationContext:从web应用下的一个或者多个XML配置文件中加载上下文定义。

 

 

 

 

posted @ 2016-09-28 11:31  JosephHu  阅读(574)  评论(0编辑  收藏  举报