1. Spring简介
Spring
是于2003年兴起的一个轻量级(对应于ejb)的Java 开发框架,由
Rod Johnson 在其著作Expert One-On-One J2EE Development and Design中
阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为
J2EE
应用程序开发提供集成的框架。Spring使用基本的
JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于
服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。Spring的核心是
控制反转(
IoC)和面向切面(
AOP)。简单来说,Spring是一个分层的JavaSE/EE full-stack(一站式)
轻量级开源框架。他的核心是ioc(Inversion of Control)/di(dependency injection),即通过配置文件的方式来完成java类依赖关系的自动装配。将在下一节课进行学习,本节课主要了解spring中一些常用的接口和spring的配置文件说明并完成一个helloworld级别的spring程序。
2. Spring 框架概述
Spring框架包含许多特性,并被很好地组织在下图所示的七个模块中。本节将依次介绍每个模块。
3. 框架特性
Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。
Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。
轻量——轻量是针对重量级容器(EJBjndi)来说的。从大小与开销两方面而言完整的Spring框架可以在一个大小只有1MB多的JAR文件里发布。并且Spring所需的处理开销也是微不足道的。
Spring是非侵入式的:典型地,Spring应用中的对象不依赖于Spring的特定类。
控制反转(di)——Spring通过一种称作控制反转(IoC)的技术促进了松耦合。当应用了IoC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。你可以认为IoC与JNDI相反——不是对象从容器中查找依赖,而是容器在对象初始化时不等对象请求就主动将依赖传递给它。
面向切面(aop)——Spring提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务(transaction)管理)进行内聚性的开发。应用对象只实现它们应该做的——完成业务逻辑——仅此而已。它们并不负责(甚至是意识)其它的系统级关注点,例如日志或事务支持。
容器(Context)——Spring包含并管理应用对象的配置和生命周期,在这个意义上它是一种容器,你可以配置你的每个bean如何被创建——基于一个可配置原型(prototype),你的bean可以创建一个单独的实例或者每次需要时都生成一个新的实例——以及它们是如何相互关联的。然而,Spring不应该被混同于传统的重量级的EJB容器,它们经常是庞大与笨重的,难以使用。Spring有两种不同的容器
l Bean工厂(org.springframework.beans.factory.BeanFactory)
最简单的容器(是Spring基本的IOC容器),提供了基础的依赖注入支持。而BeanFactory容器在Bean的Scope为prototypeo时实例化后并不会自动实例化Bean,只有当Bean被使用时BeanFactory容器才会对该Bean进行实例化与依赖关系的装配。
l ApplicationContext应用上下文(org.springframework.context.ApplicationContext)
建立在Bean工厂基础之上,提供了系统构架服务 , ApplicationContext容器实例化后会自动对所有的单实例Bean进行实例化与依赖关系的装配,使之处于待用状态,ApplicationContext容器还可以提供其它的一些功能比如:jms等
框架——Spring可以将简单的组件配置、组合成为复杂的应用。在Spring中,应用对象被声明式地组合,典型地是在一个XML文件里。Spring也提供了很多基础功能(事务管理、持久化框架集成等等),将应用逻辑的开发留给了你。
所有Spring的这些特征使你能够编写更干净、更可管理、并且更易于测试的代码。它们也为Spring中的各种模块提供了基础支持。
4. Spring jar包说明 :
Myeclipse10.0自带spring最高版本3.0版本, spring目前已经托管到github可从hithub上下载到最新版本的spring.
Spring的安装比较简单,直接将下载的jar包导入到web工程的lib目录即可。
l spring-core.jar:包含SpringIOC和全局使用的类, ApplicationContext接口在此包中,spring开发必须的包
l spring-beans.jar:spring的Bean工厂的包,包含BeanFactory接口spring开发必须的包
l spring-context.jar:提供IOC高级功能,JNDI、EJB的支持等spring开发必须的包
l spring-aop.jar:Spring的AOP框架,如果需要使用spring的aop特性需要引入该包,比如使用声明式事务管理等。
l spring-dao.jar:提供了对数据库的访问抽象,提供了对JDBC和数据库事务的支持
l spring-orm.jar:提供了对Hibernate, myBATIS等持久层框架的支持
l spring-struts.jar:与struts整合开发时必须的包
l spring-mybatis.jar:与mybatis整合开发时必须的包
l spring-expression:Spring3.0引入一个全新的功能,表达式语言(EL)简称SpELspring表达式支持的包,因为spring的配置文件使用了表达式所以这个包是spring3.0以上版本开发必须的包spring3.0以上版本开发必须的包
l spring-web.jar:提供了对web的支持,如果你的项目是web程序那么必须要引入此包,org.springframework.web.context.ContextLoaderListener就在此包中
l spring-webmvc.jar:Spring自己的Web框架,如果使用spring与其它前台框架(struts2)进行集成开发不必引入此包
l spring-test.jar:提供了nunit进行测试的包junit
5. Spring常用接口及实现类:
ApplicationContext 接口:
l 在ApplicationContext接口的众多实现类中,有3个是我们经常用到的(见表1-1),并且使用这3个实现类也基本能满足我们Java EE应用开发中的绝大部分需求。
表1-1 ApplicationContext接口的常用实现类介绍
类 名 称 | 功 能 描 述 |
ClassPathXmlApplicationContext | 从类路径ClassPath中寻找指定的XML配置文件,找到并装载完成ApplicationContext的实例化工作。 例如: //装载单个配置文件实例化ApplicationContext容器 ApplicationContext cxt = new ClassPathXmlApplicationContext("applicationContext.xml"); //装载多个配置文件实例化ApplicationContext容器 String[] configs = {"bean1.xml","bean2.xml","bean3.xml"}; ApplicationContext cxt = new ClassPathXmlApplicationContext(configs); 【注意】此处是已项目的类路径为根(src目录或者说是classes目录),上例中的所有的.xml文件都应该在src目录下。【常用】 |
FileSystemXmlApplicationContext | 从指定的文件系统路径中寻找指定的XML配置文件,找到并装载完成ApplicationContext的实例化工作。 例如: //装载单个配置文件实例化ApplicationContext容器 ApplicationContext cxt = new FileSystemXMLApplicationContext("beans.xml"); //装载多个配置文件实例化ApplicationContext容器 String[] configs = {"beans1.xml","c:/beans2.xml"}; ApplicationContext cxt = new FileSystemXmlApplicationContext(configs); 【注意】:此处是以文件系统为路径,如果使用绝对路径那么会在磁盘的对应位置读取配置文件,如果以相对路径会在项目的根目录下读取, 图中的配置文件可通过:new FileSystemXmlApplicationContext(“ApplicationContext.xml”);读取【不常用】 |
XmlWebApplicationContext | 从Web应用中寻找指定的XML配置文件,找到并装载完成ApplicationContext的实例化工作。这是为Web工程量身定制的,使用 WebApplicationContextUtils类的getRequiredWebApplicationContext方法 可在JSP与Servlet中取得IoC容器的引用,就可以得到容器中所有的bean【在servlet中得到bean工厂时常用】 |
这些实现类的主要区别就是装载Spring配置文件实例化ApplicationContext容器的方式不同,在ApplicationContext实例化后,同样通过getBean方法从ApplicationContext容器中获取装配好的Bean实例以供使用。
与BeanFactory不同的是,ApplicationContext容器实例化后会自动对所有的单实例Bean进行实例化与依赖关系的装配,使之处于待用状态。而BeanFactory容器实例化后并不会自动实例化Bean,只有当Bean被使用时BeanFactory容器才会对该Bean进行实例化与依赖关系的装配。
在Java项目中通过ClassPathXmlApplicationContext类手动实例化ApplicationContext容器通常是不二之选。但对于Web项目就不行了,Web项目的启动是由相应的Web服务器负责的,因此,在Web项目中ApplicationContext容器的实例化工作最好交给Web服务器来完成。
【注意】:这里读取配置文件的方式只用于开发java程序或者是测试的时候,在开发web应用程序的时配置文件是由spring来自动读取的。读取的方式在开发web应用程序的时候做介绍
6. web应用使用spring
Spring
为此提供了两种解决方案,一种是基于ContextLoaderListener实现的(此方案只适用于Servlet2.4及以上规范的Servlet容器)。例如,在web.xml中加入如下代码:
<!-- 指定Spring的配置文件,多个配置文件以逗号分隔 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<!-- 指定以Listener方式启动Spring容器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
【注意这里的配置文件在WEB-INF目录下】
另外一种方案则是基于ContextLoaderServlet实现的。例如,在web.xml中加入如下代码:
<!-- 指定Spring的配置文件,多个配置文件以逗号分隔 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-config/applicationContext.xml</param-value>
</context-param>
<!-- 指定以Servlet方式启动Spring容器 -->
<servlet>
<servlet-name>context</servlet-name>
<servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
从servlet容器启动时加载组件的顺序来看,Listener组件是优先于Servlet组件的。基于Servlet方式的加载方案主要是为了兼容Servlet2.3及以下规范的Servlet容器。以Tomcat为例,Tomcat5.x都已经支持Servlet2.4规范了,因此,基于Listener方式启动Spring容器是目前的主流选择。[1]
对于application.xml配置文件会在以后章节进行详细说明。
7. bean容器(ioc容器)
Spring的所有对象都是通过bean容器来进行管理的。spring在启动的时候会读取配置文件来实例化应用中的所有对象,并进行对象的依赖注入【注意如果你的Bean需要由spring进行管理,一定不要通过代码来实例化(不要在代码中通过new关键字实例化)】,spring的bean是由xml文件进行配置的,spring的bean配置主要使用bean、property
Bean:
spring中的对象是通过bean节点来进行配置的,表示一个类下表为bean节点的主要属性【注意其中最常用的属性是id、class/scope】
属性名 | 是否必须 | 属性值 | 默认值 | 属性描述 |
Id | 是 |
|
| Bean的唯一标识名。它必须是合法的XML ID,在整个XML文档中唯一用来定义bean的唯一id,如果其它类中依赖这个类,可以通过id进行引用 |
name | 否 |
| 无 | 用来为id创建一个或多个别名。它可以是任意的字母符合。多个别名之间用逗号或空格分开【注意别名主要用于其它一些bean的属性当中,不建议使用】 |
class | 否 | 类的全限定名 | 无 | Spring根据class属性通过反射机制来实例化对象只有子类Bean不用定义该属性【注意,不建议使用子类的方式来定义bean,最好直接将class定义为类的全限定类名】 |
|
|
|
|
|
scope | 否 | Singleton:单例 Prototype:不是单例,每次getBean都是一个新的实例 Request:用于web开发,每一次请求中是同一个实例 Session:每次会话中都是同一个实例 request和session不常用 | singleton | 如果类中的全局变量会有线程安全方面的问题,就可以使用prototype类型 |
Abstract | 否 | True: false | false | 表示是抽象类applicationContext预实例化时忽略所有的抽象Bean的定义一般与parent属性一起使用 |
parent | 否 | Bean标签中abstract属性为true的Bean的ID |
| 【不常用】 |
Lazy-init | 否 | True false | false | 值为true表示延迟加载,也就是IoC容器将第一次被用到时才开始实例化【注意每次通过property标签引用bean的时候spring内部都会调用一次getBean方法】 |
autowire | 否 | byName bytype constructor autodetect no | byName | byName 根据Bean定义时的“id"属性上指定的别名与Setter名称是否一致进行自动装配 byType 根据PoJo的setXXX()方法所接受的类型判断bean定义文件是否定义有类似的类型对象进行自动装配 constructor Spring容器比对容器中的Bean实例类型及相关的构造方法上的参数类型是否符合进行自动装配 autodetect 先进行constructor自动装配,若缺省,则进行byType自动装配 no不进行自动装配【不常用】
|
depends-on | 否 | Beanid | 无 | 若A depends-on B 意思是实例化A之前必须先实例化B,但A不需要持有B的实例【不常用】 |
dependency-check | 否 | simple objects none | none | simple 只检查简单的属性是否完成依赖关系 objects 检查对象类型的属性是否完成依赖关系 all检查全部的属性是否完成依赖关系 none默认值,表示不检查依赖性【不常用】 |
factory-bean | 否 | 无 | 无 | 通过实例工厂方法创建bean,class属性必须为空,factory-bean属性必须指定一个bean的名字,这个bean一定要在当前的bean工厂或者父bean工厂中,并包含工厂方法。而工厂方法本身通过factory-method属性设置。【不常用】 |
factory-method | 否 | 无 | 无 | 定义工厂方法,若是class属性指向工厂类,该工厂类包含的工厂方法须是static【不常用】 |
Property:
Property标签主要用来对类的依赖对象进行注入,常用的属性包括name,ref,class
属性名 | 是否必须 | 属性值 | 默认值 | 描述 |
name | 是 | 无 | 无 | 对应java类中全局变量的变量名称。【必须有set方法】 |
Class | 否 | 无 | 无 | 全限定类名 |
Ref | 否 | 无 | 无 | 对应spring配置文件中bean的id,表示会将id对应的bean注入。 |
8. 第一个spring 程序:
使用spring的步骤:
1、 导入jar包,
2、 写一个xml配置文件ApplicationContext.xml
下面我们通过一个spring程序来实现java对象的注入,大家回想一下在之间的java学习当中如果一个类需要使用另一个类,我们是怎么做的,比方说userServlet如果要访问数据库,那么他里面就可能会需要使用一个类似于名字为userDao的对象。那么在userServlet里面是不是需要类似下面这样的一行代码:
UserDao userDao= new UserDao();
这样的关系我们把他称为依赖,就是说我们的userServlet类在进行逻辑运算的过程中依赖于userDao访问数据库的功能。大家想一下这样做有没有什么弊端。如果说我们换数据库了,或者是不使用数据库了,而是从远程服务器上得到userServlet中需要的资源比如用户信息等。那么我们是不是需要去改动userservlet中的代码,而我们的程序上线后往往都是打成jar文件了,这样改动起来就会比较麻烦,下面我们看看spring是怎么处理这种关系的。
程序说明:userService类依赖userDao,在userService的sayHello方法中调用userDao的getUser(Stringid)方法得到user对象。然后输出XX用户,,你好!程序比较简单,我们主要从这个小小的程序当中体会一下spring他到底干了些什么,这么干有什么用,怎样使用spring来构建一个最基本的应用。
程序文件清单
Java文件:UserDao,UserService,User
Xml:applicationcontext.xml(spring默认的配置文件)
测试文件:test.java
Usedao:
package com.spring.dao;
import com.spring.pojo.User;
public class UserDao {
public UserDao(){
System.out.println("userDao");
}
public void init(){
System.out.println("init()");
}
public User getUserByid(String id){
User user = new User();
user.setPassword("pwd");
user.setUsername("itjob");
return user;
}
public void destroy(){
System.out.println("destroy");
}
}
userService:
package com.spring.service;
import com.spring.dao.UserDao;
import com.spring.pojo.User;
public class UserService {
private UserDao userDao;
public UserDao getUserDao() {
return userDao;
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void sayHello(){
User user = userDao.getUserByid("a");
System.out.println(user.getUsername() + " 用户,hello!");
}
}
spring.xml
<bean id="userDao" lazy-init="true" class="com.spring.dao.UserDao" ></bean>
<!—定义userService-->
<bean id="userService" class="com.spring.service.UserService">
<!-- 注意这里的name 必须要和UserService类中的userDao的 名称一致如下,
private UserDao userDao;
并且必须有setUserDao方法:
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
-->
<property name="userDao" ref="userDao"></property>
</bean>
测试类
public static void main(String[] args) {
//通过classPathXmlApplication 类来实例化spring的上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//从上下文容器中得到userService
/**
* 注意 这里得到的userService是经过spring 注入了userDao的userService实例
* 因为getBean方法得到的是一个Object类型的对象,所以需要进行强制类型转换
*/
UserService userService = (UserService)context.getBean("userService");
userService.sayHello();
}