(更新中…)2017.4.6
@(Java读书笔记)
Pro Java EE Spring Patterns
Best Practices and Design Strategies Implementing Java EE Pattern with the Spring Framework
[印] Dhrubojyoti Kayal 著
张平 龚波 李平芳 等译
本章内容:
简要介绍了Spring应用框架及其重要性,然后将详细介绍Spring框架的每一个组成部分。
Spring框架是个开源的应用程序框架,最初用于Java平台,现在也移植到.NET平台。
Java EE平台的目标是降低分布式应用程序开发的复杂性,旨在建立基于组件模型的应用程序,提高代码复用。
EJB目的:开发EJB的初衷是为了降低事务和分布式应用程序的开发难度。
EJB缺点:但过多的使用EJB,特别是会话bean来简化业务逻辑,必将导致在应用程序组件模型中引入分布式特性,这些特性非常复杂,且会消耗更多的CPU资源,也会导致产生与元数据相关的大量重复性代码,并且访问分布式应用程序组件需要网络往返传输数据,以及大规模数据集的组装和分解。因此,即使是一个非常简单的应用,滥用分布式对象也会导致不理想的执行结果。
Java EE包含大量本来就很复杂的技术和API。例如:完全掌握实体bean API是很难的,并且带来的价值又不大。由于Java EE组件在应用程序服务器容器内部运行,所以很难对其做单元测试,从而无法执行测试驱动开发(TDD)。
这种种限制直接导致了开发团队尝试寻求替代方法,继而出现了基于不同Java EE API所构建的框架。Spring框架同样也能解决与Java EE应用程序开发相关的复杂难题,它的优势在于:
1、Spring提供了一个全面的多层框架,可在应用程序的所有层使用。这一特性,有助于整合整个应用程序和现成的组件,以及集成最适合的单层框架(如Hibernate或struts等)
2、Spring框架提供一个基于POJO的简单编程模型,并且由于这些组件可在服务器容器之外运行,所以测试起来非常容易。
3、IOC容器是整个框架的核心,有助于粘合应用程序的不同部分从而形成一个整体
4、通过Spring的各种远程选项,这些POJO业务组件可以成为分布式对象,或者,可以使用POJO业务组件来开发和连接分布式EJB组件。
5、使用Spring AOP可向POJO组件透明的应用系统服务,如事务、安全和检测。
6、Spring的安全机制是一种全面的解决方案,足以满足任何企业级应用程序的安全需求。
Spring是一个应用程序框架,可以分成若干个模块或者组件,每个模块都可以提供一组特定的功能,并且运行时具有一定的独立性。开发人员可以选择最适合当前问题域的模块,所以系统非常灵活。Spring框架的组成如图:
Core模块是整个Spring的核心,所有其他模块依赖于它。
“好莱坞原则”是IOC的最佳描述:“不要给我们打电话,我们会给你打电话”。这一点在所有高内聚和低耦合的软件开发以及应用程序的流程控制中都非常重要。考虑一个简单的例子:应用程序要使用log4j执行某种计算操作,并输出最终结果。在这个例子中应用程序负责流程控制,在必要的时候再调用log4j API的方法。
IOC是所有框架的基础,借助IOC,应用程序对象被注册到框架,而框架负责在合适的时机或事件发生时,调用被注册对象的方法。简言之,IOC的基本原理是允许其他对象或框架在相关事件发生时调用自己应用程序对象中的方法。
之所以叫控制反转,这个反转体现在,不是应用程序调用了框架API,而是框架API调用了应用程序代码。
DI描述一个对象如何解析或查找提供所需方法的对象。存在多种DI实现方式,IOC只是其中的一种策略。
public class FormulaOneDriver{
public Car getCar(){
Car car = new FerrariCar();
return car;
}
}
缺点:
* 增加耦合度
* 导致对象创建代码分散在应用程序中,增加代码维护和单元测试难度。
public class FormulaOneDriver{
public Car getCar(){
Car car = CarFactory.getInstance("FERARI");
return car;
}
}
工厂是一种对象设计的最佳实战,也被成为面向接口编程。该原理规定,具体对象必须实现一个在调用程序中使用但不再具体对象中使用的接口。这样做的优点是可以轻易地使用其他替代实现方法,却不会对客户端产生任何影响。换句话说,由于并不直接依赖于具体实现,因此会降低耦合。
借助工厂助手,也能够支持对象创建的可配置性。
直接实例化和工厂助手,都属于拉(pull)DI,因为依赖对象都是由最终使用它的对象“拉进来”的。因此严格意义上说并不是“注入”,而是一种依赖的解决方案。
IOC可以实现真正的DI,即推(push)DI,在这种方法中,外部容器或应用程序框架会创建依赖对象,并将依赖对象传递给需要它的对象。依赖对象需要构造器或者setter方法创建。
org.springframework.beans.factory.BeanFactory 是Spring的IOC容器或bean工厂的基础。
它是GOF工厂方法设计模式的高级实现,可以创建、缓冲、融合和管理应用程序对象。
由于Spring改进了POJO编程模型,这些对象被成为bean。
Spring提供bean工厂的多种实现方式,其中一种就是类XmlBeanFactory。
下面代码就是一个简单的Spring bean 配置文件,spring-config.xml:
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd ">
<bean class="com.apress.simpleapp.service.CarServiceImpl"/>
beans>
配置bean后,下面开始介绍IOC容器,如下面代码所示,SpringInitializer.java:
Resource res = new FileSystemResource("spring-config.xml");
BeanFactory factory = new XmlBeanFactory(res);
现在Spring容器已经存在并在运行中,所以可以从bean工厂中检索能够在应用程序中执行某些实用操作的bean,有多种方式:
如下面代码所示,CarServiceLocator.java:
CarService service = (CarService)factory.getBean("carService");
可以实现,但是不推荐。因为它通过指定的键在Spring bean工厂或IOC容器中检索CarService对象,但是CarService可能存在不同变种,因此每次有不同需要时都需要调用getBean方法。
为解决上述问题,建议使用以下两种push DI。
对象在Spring IOC容器中通过调用无参构造函数来创建,然后,依赖对象作为参数传递给setter方法。如下述代码:
CarServiceImpl.java
public class CarServiceImpl implements CarService{
private CarDao carDao;
public void setCarDao(CarDao carDao){
this.carDao = carDao;
}
public void save(){
carDao.save(car);
}
}
通过配置文件,让Spring知道如何解析和注入依赖(通过setter方式)
spring-config.xml:
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd ">
<bean nam="carService" class="com.apress.simpleapp.service.CarServiceImpl">
<property name="carDao" ref="carDao"/>
bean>
<bean nam="carDao" class="com.apress.simpleapp.dao.CarDaoImpl" />
beans>
在这种方式中,依赖对象作为构造器调用的一部分被传递。如:
CarServiceImpl.java
public class CarServiceImpl implements CarService{
private CarDao carDao;
public void CarServiceImpl(CarDao carDao){
this.carDao = carDao;
}
public void save(){
carDao.save(car);
}
}
要实现构造器注入,对应配置文件也应该修改为:
spring-config.xml:
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd ">
<bean nam="carService" class="com.apress.simpleapp.service.CarServiceImpl">
<constructor-arg>
<ref bean="carDao"/>
constructor-arg>
bean>
<bean nam="carDao" class="com.apress.simpleapp.dao.CarDaoImpl" />
beans>