最近的开发中,感觉对Spring框架的掌握还不够熟练,重新翻出了《Spring实战》这本书,重新学习一遍,以此记录。
任何一个有实际意义的应用都是由两个或多个类组成,这些类相互之间进行协作来完成特定的业务逻辑。通常,每个对象负责管理与自己相互协作的对象(即它所依赖的对象)的引用。这将导致高度耦合和难以测试的代码。
因高度耦合而执行特定任务的knight类:
package com.springinaction.knights;
public class DamselrescuingKnight implements Knight{
private RescueDamselQuest quest;
public DamselRescuingKnight(){
quest = new RescueDamselQuest();
}
public void embarkOnQuest() throws QuestException{
quest.embark();
}
}
DamselrescuingKnight 类在它的构造器中自行创建了RescueDamselQuest。这使得DamselrescuingKnight紧密地与RescueDamselQuest耦合到了一起,因此极大地限制了这个类的能力,只能执行对应的救援(RescueDamselQuest)任务,无法执行其他任务。
优化后足够灵活且强大的BraveKnight类:
package com.springinaction.knights;
public class braveKnight implements Knight{
private Quest quest;
public BraveKnight(Quest quest){
this.quest = quest; //<--Quest被注入进来
}
public void embarkOnQuest() throws QuestException{
quest.embark();
}
}
正如看到的那样,不同于之前的DamselrescuingKnight,BraveKnight没有自行创建任务(即没有在构造器中new一个新的具体对象),而是在构造时把任务(Quest)作为构造器参数传入。这是依赖注入的方式之一,即构造器注入。
更重要的是,它被传入的探险类型是Quest,也就是一个所有任务都必须实现的接口。所以,BraveKnight能够响应RescueDamselQuest、SlayDragon-Quest、MakeRoundTableRoundQuest等任意一种Quest接口的具体实现,且BraveKnight没有与任何特定的Quest实现发生耦合。
创建应用组件之间协作的行为通常称为装配。spring有多种装备bean的方式,采用XML配置通常是最常见的装配方式
使用Spring配置文件将SlayDragonQuest注入到BraveKnight中:
//向BraveQuest的构造器注入quest bean
//创建SlayDragonQuest bean
Spring通过应用上下文(Application Context)装载Bean的定义并把它们组装起来。Spring应用上下文全权负责对象的创建和组装。Spring自带了几种应用上下文的实现,它们之间主要的区别仅仅是如何加载它们的配置。
加载包含Knight的Spring上下文:
package com.springinaction.knights;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class KnightMain{
public static void main(String[] args){
ApplicationContext context = new ClassPathXmlApplicationContext("knights.xml"); //加载Spring上下文
Knight knight = (Knight) context.getBean("knight"); //获取knight Bean
knight.embarkOnQuest(); //使用knight
}
}
这个类完全不知道knight接受哪种任务,而且也没有体现出来这是由BraveKnight来执行的。
依赖注入让相互协作的软件组件保持松散耦合,而AOP编程允许把遍布应用各处的功能分离出来形成可重用的组件。
利用AOP,可以使用各种功能层去包裹核心业务层。这些层以声明的方式灵活应用到系统中,甚至核心应用根本不知道他们的存在。这是一个非常强大的理念,可以将安全、事务和日志关注点与你的核心业务逻辑相分离。
每一个人都熟知骑士的任何事情,这是因为吟游诗人用诗歌记载了骑士的事迹并将其进行传颂。Minstrel(吟游诗人)类记载了骑士的所有事迹。
package com.springinaction.knights;
public class Minstrel{
public void springBeforeQuest{
//骑士执行任务之前调用
System.out.println("Fa la la;The knight is so brave!");
}
public void singAfterQuest(){
//骑士执行任务后调用
System.out.println("Tee hee he;The brave knight did embark on a quest!");
}
}
适当地调整BraveKnight可以使用Minstrel,BraveKnight必须调用Minstrel方法:
package com.springinaction.knights;
public class BraveKnight implements Knight{
private Quest quest;
private Minstrel minstrel;
public BraveKnight(Quest quest,Minstrel minstrel){
this.quest = quest;
this.minstrel = minstrel;
}
public void embarkOnQuest() throws QuestException{
minstrel.singBeforeQuest();
quest.embark();
minstrel.singAfterQuest();
}
}
这样虽然可以达到预期效果,但管理吟游诗人不应该是骑士的职责工作,骑士只需要负责执行任务,吟游诗人需要自主记录传颂。
利用AOP,可以声明吟游诗人必须歌颂骑士的事迹,而骑士就不再直接访问吟游诗人的方法了。把Minstrel抽象为一个切面,然后在Spring配置文件中声明它即可。
Minstrel被声明为一个切面:
//声明minstrel Bean
//定义切面
//声明前置通知
//声明后置通知
在前置通知,后置通知这两种方式中都引用了名为embank的切入点。该切入点是在前边的
Bean工厂(bean factories,由org.springframework.beans.factory.BeanFactory接口定义)是最简单的容器,提供基本的DI支持。
应用上下文(application)由org.springframework.context.ApplicationContext接口定义)基于BeanFactory之上构建,并提供面向应用的服务。
ClassPathXmlApplicationContext:从类路径下的XML配置文件中加载上下文定义,把应用上下文定义文件当作类资源。
FileSystemXmlapplicationcontext:读取文件系统下的XML配置文件并加载上下文定义。
XmlWebApplicationContext:读取Web应用下的XML配置文件并装载上下文定义。
加载一个FileSystemXmlapplicationcontext:
ApplicationContext context = new FileSystemXmlApplictionContext("c:/foo.xml");
类似地,加载一个ClassPathXmlApplicationContext:
ApplicationContext context = new ClassPathXmlApplicationContext("foo.xml");
两者区别在于FileSystemXmlApplicationContext在指定的文件系统路径下查找foo.xml文件。
而ClassPathXmlApplicationContext是在所有的类路径(包含jar文件)下查找foo.xml文件。
1、Spring对Bean进行实例化。
2、Spring将值和Bean的引用注入进Bean对应的属性中。
3、如果Bean实现了BeanNameAware接口,Spring将Bean的ID传递给setBeanName()接口方法。
4、如果Bean实现了BeanFactoryAware接口,Spring将调用setBeanFactory()接口方法,将BeanFactory容器实例传入。
5、如果Bean实现了ApplicationContextAware接口,Spring将调用setApplicationContext()接口方法,将应用上下文的引用传入。
6、如果Bean实现了BeanPostProcessor接口,Spring将调用它们的postProcessBeforeInitialization()接口方法。
7、如果Bean实现了Initialization接口,Spring将调用它们的afterPropertiesSet()接口方法。类似地,如果Bean使用init-method声明了初始化方法,该方法也会被调用。
8、如果Bean实现了BeanPostProcessor接口,Spring将调用它们的postPoressAfterInitialization()方法。
9、此时,Bean已经准备就绪,可以被使用了,它们将一直驻留在应用上下文中,知道该应用上下文被销毁。
10、如果Bean实现了DisposableBean接口,Spring将调用它的destroy()接口方法。同样,如果bean使用destroy-method声明了销毁方法,该方法也会被调用。
1、核心Spring容器(Core Spring container)
2、Spring的AOP模块
3、数据访问与集成
4、Web和远程调用
5、测试