spring基础

Spring基础

1. 教学目标
了解Spring的基本情况(网站、项目历史、和其他框架的关系) 
掌握Spring类库的手工安装 
理解工厂模式基本原理 
熟练掌握Spring的通用工厂功能 
熟练掌握Spring的IOC功能 

2. Spring介绍

2.1. Spring资源

Spring官方网址:http://www.springframework.org 

Spring的投资方BEA:http://www.bea.com 

BEA已经被Oracle收购:http://www.oracle.com 

2.2. Spring的主要功能

官方网站上的概括: 

... Spring Framework, the leading full-stack Java/JEE application framework ...

Spring是用来替代笨重的EJB容器(特指EJB1/2)。 

《J2EE Development without EJB》 

中文版:http://product.dangdang.com/product.aspx?product_id=9037541 

Spring是这本书中的思想的具体化 

基于Spring的开发比笨重的EJB开发方式提高很大的生产效率 

Spring能够替代EJB的关键因素:AOP和应用AOP的事务管理 

Spring的其他主要功能: 
能够方便的集成开发者的各个功能模块/组件和类库,基于Spring的工厂和IOC以及AOP功能 

Spring内置了很多框架和API的帮助类,比如Struts1/Struts2/webwork/JSF/Hibernate/iBatis/JDO/JavaMail/JMS... 

2.3. 和Spring功能类似的框架

Google Guice:http://code.google.com/p/google-guice/ 

HiveMind:http://hivemind.apache.org/ 

2.4. Spring分发包

当前Spring最新版本是:2.5.2 

下载地址:http://sourceforge.net/project/showfiles.php?group_id=73357&package_id=173644&release_id=578115 

介绍分发包的内容。 

编写Spring程序所需的类库: 

dist/spring.jar 
lib/jakarta-commons/commons-logging.jar

通过Maven构建需要的配置: 

  <dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring</artifactId>
  <version>2.5.2</version>
  </dependency>

3. Spring的工厂功能

3.1. 工厂模式

示例见:http://spring2demo.googlecode.com/svn/branches/core_factory 

3.1.1. 工厂模式的作用

接口的使用者不需要实例化具体实现的类。 

安排实验进一步体会: 
Servlet/Action直接创建DAO类 
Servlet/Action使用DAO接口类型,并创建DAO类 
Servlet/Action通过工厂获得DAO接口类型的实例 
最好两个人进行实验,一个人编写Servlet/Action,另外一个人编写DAO 

3.1.2. 最简单的工厂模式

见:com.googlecode.spring2demo.product.SingletonFactory 

切换行号显示切换行号显示 
  1 public abstract class SimpleFactory {
  2 public static ProductDao getProductDao() {
  3 return new ProductDaoImpl();
  4 }
  5 ...

3.1.3. 增加单例模式的简单工厂模式

见:com.googlecode.spring2demo.product.SimpleSingletonFactory 

切换行号显示切换行号显示 
  1 private static ProductDao productDao;
  2 
  3 public static ProductDao getProductDao() {
  4 if (productDao == null) {
  5 productDao = new ProductDaoImpl();
  6 }
  7 
  8 return productDao;
  9 }
  10 
  11 

3.1.4. 通用的工厂模式

工厂代码可以复用,而不像上面仅能供ProductDao使用。 

通过配置文件读取,达到通用性。 

代码:com.googlecode.spring2demo.product.CommonFactory 

切换行号显示切换行号显示 
  1 private static Properties properties;
  2 
  3 public static Object getInstance(String className) {
  4 if (properties == null) {
  5 properties = new Properties();
  6 try {
  7 properties.load(Thread.currentThread().getContextClassLoader()
  8 .getResourceAsStream("factory.properties"));
  9 } catch (IOException e) {
  10 throw new RuntimeException(e);
  11 }
  12 }
  13 
  14 try {
  15 return Class.forName(properties.getProperty(className))
  16 .newInstance();
  17 } catch (Exception e) {
  18 throw new RuntimeException(e);
  19 }
  20 

配置文件:factory.properties 

com.googlecode.spring2demo.product.ProductDao=com.googlecode.spring2demo.product.ProductDaoImpl

3.1.5. 带单例模式的通用工厂

单例(单件)模式的作用: 对于创建代价比较昂贵,并且可以并发使用的对象,单例模式减少系统开销。 

类代码:com.googlecode.spring2demo.product.SingletonFactory 

切换行号显示切换行号显示 
  1 private static Properties properties;
  2 
  3 private static Map<String, Object> instanceMap;
  4 
  5 public static Object getInstance(String className) {
  6 if (properties == null) {
  7 properties = new Properties();
  8 try {
  9 properties.load(Thread.currentThread().getContextClassLoader()
  10 .getResourceAsStream("factory.properties"));
  11 } catch (IOException e) {
  12 throw new RuntimeException(e);
  13 }
  14 }
  15 
  16 if (instanceMap == null) {
  17 instanceMap = new HashMap<String, Object>();
  18 }
  19 
  20 try {
  21 Object returnValue = instanceMap.get(className);
  22 
  23 if (returnValue == null) {
  24 returnValue = Class.forName(properties.getProperty(className))
  25 .newInstance();
  26 instanceMap.put(className, returnValue);
  27 }
  28 
  29 return returnValue;
  30 
  31 } catch (Exception e) {
  32 throw new RuntimeException(e);
  33 }

配置文件和上面通用工厂的相同。 

3.2. 使用Spring的通用工厂

示例见:http://spring2demo.googlecode.com/svn/branches/core_factory_spring 

3.2.1. 配置文件

文件名可以自定义。 

spring2.0新式的xsd格式。 

<?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:util="http://www.springframework.org/schema/util"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.0.xsd">
</beans>

直接复制代码就可以创建Spring的配置文件。 

3.2.2. 简单使用Spring的通用工厂功能

通过简单使用Spring的通用工厂功能,替代上文com.googlecode.spring2demo.product.CommonFactory的功能。 

见代码:com.googlecode.spring2demo.product.SimpleSpringDemo 

切换行号显示切换行号显示 
  1 //创建ApplicationContext,即工厂
  2 ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(
  3 "simple.config.xml", SimpleSpringDemo.class);
  4  
  5 ProductDao productDao = (ProductDao) applicationContext
  6 .getBean("productDao");
  7 Product product = new Product();
  8 productDao.save(product);
  9  
  10 applicationContext.close();//关闭ApplicationContext

ClassPathXmlApplicationContext.getBean()方法: 根据名字获取对象. 

ClassPathXmlApplicationContext.close()方法: 关闭(释放)所有Bean对象. 

和CommonFactory的区别: 

CommonFactory是全局的,ApplicationContext是实例的; 

ApplicationContext需要关闭 

Spring的配置文件:simple.config.xml 

<bean id="productDao" class="com.googlecode.spring2demo.product.ProductDaoImpl" />
Bean的概念: 是由Spring容器初始化、装配及被管理的对象. 
xml中id的作用: xml文档节点的唯一标识. 

3.2.3. BeanFactory与ApplicationContext

上文使用了ApplicationContext接口的实现: ClassPathXmlApplicationContext 作为工厂的实现类. 

ApplicationContext接口及实现提供了很多高级功能, 是普遍使用的方式. 

底层还有BeanFactory接口和它的实现, 功能简单,运行速度也更快, 但是并不方便使用. 

 

通过BeanFactory完成类似ApplicationContext的功能。 

见:com.googlecode.spring2demo.product.SimpleSpringFactoryDemo 

切换行号显示切换行号显示 
  1 XmlBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource(
  2 "simple.config.xml", SimpleSpringFactoryDemo.class));
  3 ProductDao productDao = (ProductDao) beanFactory.getBean("productDao");
  4 Product product = new Product();
  5 productDao.save(product);

配置文件和上例相同。 

和ApplicationContext不同的地方: 

默认情况下,ApplicationContext在启动时创建productDao实例(单例),除非设置lazy-init属性 

XmlBeanFactory是在getBean时才创建productDao实例 

XmlBeanFactory不需要关闭 

从日志上看XmlBeanFactory动静更小 

3.2.4. Bean的作用域

默认情况下,bean是单例的。 

应该能够通过下面的测试代码:com.googlecode.spring2demo.ApplicationContextTest 

切换行号显示切换行号显示 
  1 @Test
  2 public void testScope() {
  3 ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(
  4 "scope.config.xml", this.getClass());
  5 ProductDao productDao = (ProductDao) applicationContext
  6 .getBean("productDao");
  7  
  8 assert productDao == applicationContext.getBean("productDao");
  9  
  10 applicationContext.close();
  11 }

这时的配置文件:scope.config.xml 

<bean id="productDao" class="com.googlecode.spring2demo.product.ProductDaoImpl" />

或者可以是: 

<bean id="productDao" class="com.googlecode.spring2demo.product.ProductDaoImpl" scope="singleton"/>

如果配置文件改为: 

<bean id="productDao" class="com.googlecode.spring2demo.product.ProductDaoImpl" scope="prototype"/>

ApplicationContextTest的testScope方法会出错。 

因为: 
默认,singleton,只创建一个实例 
prototype,每次getBean创建新的实例 

作用域总结: 
单例: singleton 
原型: prototype 
其他: spring2.0中加入了request, session, global session和自定义作用域 

3.2.5. Bean定义的继承

bean定义是可以继承的。这样可以减少很多重复的配置。 

见配置文件:ext.config.xml 

  <bean id="abstractProductDao" class="com.googlecode.spring2demo.product.ProductDaoImpl" abstract="true"/>
  <bean id="productDao" parent="abstractProductDao"/>
productDao继承了abstractProductDao的配置属性 
abstract,不能getBean获取,因为是抽象的,有的bean,就是用于减少冗长的配置的,自身不应该被获取 
parent,继承哪个bean 

见代码:com.googlecode.spring2demo.ApplicationContextTest 

  ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(
  "ext.config.xml", this.getClass());
  String error = null;
  ProductDao productDao = null;

  try {
  productDao = (ProductDao) applicationContext
  .getBean("abstractProductDao");//这里肯定会出错,因为abstractProductDao是abstract的
  } catch (Exception e) {
  error = e.getMessage();
  }
   
  assert error!=null;
   
  productDao=(ProductDao) applicationContext.getBean("productDao");
  assert productDao!=null;

  applicationContext.close();

3.2.6. Bean的初始化和关闭

有的时候,Bean对象需要类似下列的情况: 
在创建的时候做一些初始化工作。 

在ApplicationContext关闭前,关闭Bean。 

比如带连接池的DataSource对象。 

见代码:com.googlecode.spring2demo.product.DemoDataSource 

切换行号显示切换行号显示 
  1 public class DemoDataSource {
  2 
  3 /**
  4 * 用于连接池的初始化
  5 */
  6 public void init() {
  7 System.out.println("连接池的初始化");
  8 }
  9 
  10 /**
  11 * 用于关闭连接池,释放连接
  12 */
  13 public void close() {
  14 System.out.println("关闭连接池,释放连接");
  15 }

见配置文件:init.config.xml 

  <bean id="dataSource"
  class="com.googlecode.spring2demo.product.DemoDataSource"
  init-method="init" destroy-method="close" />
init-method属性 
destroy-method属性 
bean不必是接口的实现 
prototype作用域不支持这个属性 

测试代码,见:com.googlecode.spring2demo.ApplicationContextTest 

切换行号显示切换行号显示 
  1 @Test
  2 public void testInitAndClose() {
  3 ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(
  4 "init.config.xml", this.getClass());
  5 DemoDataSource dataSource = (DemoDataSource) applicationContext
  6 .getBean("dataSource");
  7 System.out.println(">>>" + dataSource);
  8 applicationContext.close();

3.2.7. Bean的延时加载

如果bean初始代价较大,而且并非启动时需要,可使用延时加载。 

  <bean id="dataSource"
  class="com.googlecode.spring2demo.product.DemoDataSource" lazy-init="true"
  init-method="init" destroy-method="close" />
lazy-init属性:default|true|false 

<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:util="http://www.springframework.org/schema/util"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.0.xsd"
  default-lazy-init="true">
default-lazy-init:true|false 

4. Spring的IOC功能

示例见:http://spring2demo.googlecode.com/svn/branches/core_ioc/ 

4.1. IOC介绍

IoC(Inversion of Control,控制反转),或者叫做依赖注入(Dependence Injection) 

在Struts2中已经碰到IOC模式的应用,HTTP参数被注入到Action中。 

4.2. Spring IOC的简单使用

现在要创建一个Product对象。 

4.2.1. 不使用IOC的情况

见代码:com.googlecode.spring2demo.product.NonIocDemo 

切换行号显示切换行号显示 
  1 public static void main(String[] args) {
  2 Product product = new Product();
  3 product.setId(1L);
  4 product.setName("chide");
  5 product.setPrice(12);
  6 product.setVendor("vendor");
  7 }

4.2.2. 使用Spring的IOC功能

见代码:com.googlecode.spring2demo.product.IocDemo 

切换行号显示切换行号显示 
  1 public static void main(String[] args) {
  2 ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(
  3 "ioc.config.xml", IocDemo.class);
  4 
  5 Product product = (Product) applicationContext.getBean("product");
  6 assert product.getName() != null;
  7 
  8 applicationContext.close();
  9 }

见配置文件:ioc.config.xml 

  <bean id="product"
  class="com.googlecode.spring2demo.product.Product">
  <property name="id" value="1" />
  <property name="name" value="chide" />
  <property name="price" value="12" />
  <property name="vendor" value="vendor" />
  </bean>
IOC是建立在通用工厂基础上的 
Spring具有自动转型的能力,将字符串值转型为java对应的类型,Struts2的IOC也有类似能力 
IOC一旦配置好后,使用bean变得非常方便 

4.3. Spring IOC常用功能

4.3.1. 通过构造函数注入

刚才的例子是实际上是通过JavaBean的set方法注入的。 

Spring还支持通过构造函数注入。 

比如Product类有这样的构造方法: 

切换行号显示切换行号显示 
  1 public Product(Long id, String name, float price, String vendor) {
  2 super();
  3 this.id = id;
  4 this.name = name;
  5 this.price = price;
  6 this.vendor = vendor;
  7 }

可以通过如下配置:ioc.config.xml 

  <bean id="otherProduct" class="com.googlecode.spring2demo.product.Product">
  <constructor-arg value="1" />
  <constructor-arg value="chide" />
  <constructor-arg value="12" />
  <constructor-arg value="vendor" />
  </bean>

调用方式类似: 

切换行号显示切换行号显示 
  1 product=(Product) applicationContext.getBean("otherProduct");
  2 assert product.getName() != null;

哪种注入方式好呢? 

4.3.2. 注入集合

上面说的都是注入普通值对象,如果是集合,比如List/Array/Set/Map。 

需要注入的Bean,见:com.googlecode.spring2demo.product.CollectionIocDemo 

切换行号显示切换行号显示 
  1 public class CollectionIocDemo {
  2 private List<String> valueList;
  3 
  4 private String[] valueArray;
  5 
  6 private Set<String> valueSet;
  7 
  8 private Map<String, String> valueMap;
  9 
  10 public List<String> getValueList() {
  11 return valueList;
  12 }

配置文件,见:ioc.config.xml 

  <bean id="collectionDemo" class="com.googlecode.spring2demo.product.CollectionIocDemo">
  <property name="valueArray">
  <list value-type="java.lang.String">
  <value>1</value>
  <value>2</value>
  </list>
  </property>
  <property name="valueList">
  <list value-type="java.lang.String">
  <value>1</value>
  <value>2</value>
  </list>
  </property>
  <property name="valueSet">
  <set value-type="java.lang.String">
  <value>1</value>
  <value>2</value>
  </set>
  </property>
  <property name="valueMap" >
  <map key-type="java.lang.String" value-type="java.lang.String">
  <entry key="1" value="1"/>
  <entry key="2" value="2"/>
  </map>
  </property>
  </bean>

调用代码,见:com.googlecode.spring2demo.product.CollectionIocDemo 

切换行号显示切换行号显示 
  1 
  2 CollectionIocDemo collectionIocDemo = (CollectionIocDemo) applicationContext
  3 .getBean("collectionDemo");
  4 assert collectionIocDemo.getValueArray().length == 2;
  5 assert collectionIocDemo.getValueList().size() == 2;
  6 assert collectionIocDemo.getValueSet().size() == 2;
  7 assert collectionIocDemo.getValueMap().size() == 2;
  8 

4.3.3. 注入Bean之间的引用

Bean之间经常相互引用,组成1:1/1:n/m:n的关系。 

比如:商品分类和商品之间,就是1:n的关系。 

见代码:com.googlecode.spring2demo.product.Product 

切换行号显示切换行号显示 
  1 public class Product {
  2 
  3 private Category category;
  4 
  5 public Category getCategory() {
  6 return category;
  7 }
  8 
  9 public void setCategory(Category category) {
  10 this.category = category;
  11 }

见代码:com.googlecode.spring2demo.product.Category 

切换行号显示切换行号显示 
  1 public class Category {
  2 private Long id;
  3  
  4 private String name;

见配置文件:ioc.config.xml 

  <bean id="p1" class="com.googlecode.spring2demo.product.Product">
  <property name="id" value="20" />
  <property name="name" value="康师傅方便面" />
  <property name="price" value="12" />
  <property name="vendor" value="康师傅食品厂" />
  <property name="category" ref="c1" />
  </bean>
   
  <bean id="c1" class="com.googlecode.spring2demo.product.Category">
  <property name="id" value="1" />
  <property name="name" value="食品" />
  </bean>
property的ref属性,表示引用的bean id 

调用代码: 

切换行号显示切换行号显示 
  1 product=(Product) applicationContext.getBean("p1");
  2 assert product!=null;
  3 assert product.getName()!=null;
  4 assert product.getCategory()!=null;
  5 assert product.getCategory().getName()!=null;

4.4. Spring IOC应用示例

演示通过IOC获取DataSource编写Dao代码的示例。 

体会和以前常规获取DataSource和Connection做法有很大不同。 

这里需要准备数据库环境: 

用derby的嵌入方式,这样不需要配置就可以演示。 

感谢Maven,我们只需要在pom.xml文件中加入一下内容即可使用derby类库: 

  <dependency>
  <groupId>org.apache.derby</groupId>
  <artifactId>derby</artifactId>
  <version>10.3.1.4</version>
  <scope>run</scope>
  </dependency>

DataSource的实现: 

这里没有JNDI环境,Spring提供的DriverDataSource(org.springframework.jdbc.datasource.DriverManagerDataSource)。 

它只是简单封装DriverManager为DataSource接口类型(DataSource未必要与JNDI绑定,而且未必带连接池) 

Java源文件说明: 
com.googlecode.spring2demo.dao.Product 

com.googlecode.spring2demo.dao.AbstractDaoSupport 

com.googlecode.spring2demo.dao.ProductDao 

com.googlecode.spring2demo.dao.ProductDaoImpl 

配置文件:config.xml 

  <bean id="abstractDao"
  class="com.googlecode.spring2demo.dao.AbstractDaoSupport"
  abstract="true">
  <property name="dataSource" ref="dataSource" />
  </bean>

  <bean id="dataSource"
  class="org.springframework.jdbc.datasource.DriverManagerDataSource">
  <property name="driverClassName"
  value="org.apache.derby.jdbc.EmbeddedDriver" />
  <property name="url"
  value="jdbc:derby:target/database/test;create=true" />
  <property name="username" value="test" />
  <property name="password" value="test" />
  </bean>

  <bean id="productDao" parent="abstractDao" class="com.googlecode.spring2demo.dao.ProductDaoImpl"/>

测试类代码:com.googlecode.spring2demo.dao.ProductDaoTest 

切换行号显示切换行号显示 
  1 public class ProductDaoTest {
  2 private ProductDao productDao;
  3 
  4 private ClassPathXmlApplicationContext applicationContext;
  5 
  6 @Before
  7 public void init() {
  8 this.applicationContext = new ClassPathXmlApplicationContext(
  9 "config.xml", ProductDaoTest.class);
  10 this.productDao = (ProductDao) this.applicationContext
  11 .getBean("productDao");
  12 }
  13 
  14 @After
  15 public void close() {
  16 this.applicationContext.close();
  17 }
  18  
  19 @Test
  20 public void test(){
  21 Product product=new Product();
  22 this.productDao.save(product);
  23 }

这个示例能够看懂,Spring的核心功能就基本没问题了。 

而且,后面集成Hibernate的代码,也至少能够看懂60&percnt;。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值