一. Spring概述
1.1 什么是Spring
Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson创建。简单来说,Spring是一个分层的JavaSE/EE (一栈式) 轻量级开源框架。
- JEE分层
- 表现层(页面数据显示、页面跳转调度)jsp/servlet
- 业务层(业务处理和功能逻辑、事务控制)-service
- 持久层(数据存取和封装、和数据库打交道)dao
- 一站式
Spring提供了JavaEE各层的解决方案:
表现层:Spring MVC,持久层:JdbcTemplate、ORM框架整合,业务层:IoC、AOP、事务控制。
- 轻量级:Spring的出现取代了EJB的臃肿、低效、繁琐复杂、脱离现实。
1.2 Spring的核心
IoC(Inverse of Control 反转控制): 将对象创建权利交给Spring工厂进行管理。
AOP(Aspect Oriented Programming 面向切面编程),基于动态代理的功能增强方式。
1.3 Spring的优点
- 方便解耦,简化开发
- Spring就是一个大工厂,可以将所有对象创建和依赖关系维护,交给Spring管理
- AOP编程的支持
- Spring提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能
- 声明式事务的支持
- 只需要通过配置就可以完成对事务的管理,而无需手动编程
- 方便程序的测试
- Spring对Junit4支持,可以通过注解方便的测试Spring程序
- 方便集成各种优秀框架
- Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如:Struts2、Hibernate、MyBatis、Quartz等)的直接支持
- 降低JavaEE API的使用难度
- Spring 对JavaEE开发中非常难用的一些API(JDBC、JavaMail、远程调用等),都提供了封装,使这些API应用难度大大降低
关于框架的特性,我们也会俗称Spring为开发架构的粘合剂。
二. Spring框架的快速入门
2.1 搭建环境
- 下载开发包,导jar包
下载网址:http://repo.spring.io/libs-release-local/org/springframework/spring/
开发包目录结构:
docs : api文档和开发规范
libs : 开发需要的jar包(源码)
schema : 开发需要的约束schema
-----------------------------------------------------------------------------
1. 新建web项目
2. Spring项目的核心容器最基本的jar包(4个):
1. Beans 2. Core 3. Context 4.Expression Language
3. Spring框架需要的日志包(2个,依赖库中找)
1. apache commons-logging(JCL)日志框架
2. log4j的日志实现
3. log4j的配置文件 log4j.properties
2.2 业务代码
模拟用户保存。
传统写法 :
UserServiceImpl :
// 模拟用户注册
public void save() {
// 传统方式
System.out.println("业务层........UserServiceImpl用户注册...");
UserDAO dao = new UserDAOImpl();
userDAO.save();
}
UserDAO :
//模拟用户注册
public void save() {
System.out.println("持久层.......UserDAOImpl用户注册...");
}
web层(这里是测试test):
@Test
public void test() {
UserService service = new UserServiceImpl();
service.save();
}
传统的方式代码过于耦合,上层代码过于依赖下层代码,如果业务有改动,要改变DAO的实现类时,需要改动代码:
UserDAO userDAO = new UserDaoImpl();因此需要采取方式进行解耦合。
解决方案:采用IoC(Inverse of Control)控制反转的思想进行解耦合.
简单的说就是引入工厂(第三者),将原来在程序中手动创建管理的依赖的UserDAO对象,交给工厂来创建管理。
在Spring框架中,这个工厂就是Spring中的工厂,因此,也可以说,将创建管理UserDAO对象的控制权被反转给了Spring框架了。
-----------------------------------------------------------------------------
概念:IoC中文翻译为控制反转,指以前程序自己创建对象,现在将创建对象的控制权交给了第三方(Spring)了。
IoC底层实现:工厂(设计模式)+反射(机制) + 配置文件(xml)。
IoC是一种思想,控制反转的思想、解耦合的思想。
Spring的IoC是该思想的一种实现。因此Spring容器也通常称之为IoC容器。
2.3 IoC控制反转的实现
传统是自己创建对象,现在将创建对象交给Spring容器,我们获取就行了.
这种方式,即使更换实现类,也只需要修改配置文件中的实现类的路径。
2.3.1 Spring的核心配置文件编写applicationContext.xml
1. 习惯上: 在src建立applicationContext.xml (位置:src目录或者 WEB-INF目录)
2. 引入xml的头部信息bean schema约束,可以参考规范文档中的的xsd-config.html
3. 配置实现类的映射bean
2.3.2 通过Spring的工厂获取Bean完成相关操作
读取配置文件,获取
Spring的Bean
工厂-
通过
Spring的Bean
工厂获取对象// Spring解耦合,使用配置文件:创建工厂+反射创建UserDAO对象 // 加载配置文件,获取工厂对象 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); // 实例化对象 UserDAO dao = (UserDAO) applicationContext.getBean("userDAO"); userDAO.save();
2.4 DI依赖注入的实现
DI:Dependency Injection 依赖注入,在Spring框架负责创建Bean对象时,动态的将依赖对象注入到Bean组件(简单的说,可以将另外一个bean对象动态的注入到另外一个bean中。)
耦合代码变成依赖注入代码的方法:
即:Spring创建了Service、DAO对象,在配置中将DAO传入Servcie,那么Service对象就包含了DAO对象的引用。
在Service对象创建调用时,也会产生一个DAO对象,并通过Service内提供的setter方法将该对象的引用注入进去。
applicationContext.xml核心配置文件:
Service层:
声明注入的对象,提供setter方法
private UserDAO userDAO;
//提供setter方法Spring方法进行动态注入userDAO
public void setUserDAO(UserDAO userDAO) {
this.userDAO = userDAO;
}
@Override
// 模拟用户注册
public void save() {
//Spring框架创建了userDAO对象并注入进来,因此不会是空
userDAO.save();
}
web层:
//加载配置,获取Spring工厂(容器)
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取bean对象
UserService userService = (UserService) applicationContext.getBean("userService");
//调用业务逻辑
userService.save();
小结:
IoC:是一个大的思想,将某件事情(对象创建权利等)的控制前交给第三方管理。
DI:还是IoC的思想,将对象依赖注入权利交给第三方管理。
2.5 Spring的工厂
ApplicationContext用来加载Spring框架的配置文件,来构建Spring对象的工厂对象,它被称为Spring框架的上下文,
也被称为Spring的容器。
ApplicationContext是BeanFactory(Bean工厂,Bean就是一个java对象)的一个子接口。
为什么不直接使用顶层接口对象来操作呢?
因为ApplicationContext是对BeanFactory的扩展,它的功能更强。
* 国际化处理
* 事件传递
* Bean的自动装配
* 不同应用层的Context的实现
2.5.1 获取Spring工厂的两种方式
src:开发的时候,工程里的一个目录,存放的文件,会在编译发布后,放入classes下
- 如果
applicationContext.xml 在 src下, ClassPathXmlApplication
读取 -
如果applicationContext.xml 在WEB-INF下,FileSystemXmlApplicationContext
读取
三. IoC容器装配Bean_基于XML配置方式
3.1 实例化Bean的四种方式(了解)
- 无参构造/带参构造方式
- 静态工厂方式
- 实例工厂方式
- FactoryBean方式
1. 无参构造/带参构造方式:
public class Bean1 {
//无参构造
public Bean1() {
System.out.println("Bean1被创建了............无参构造");
}
}
public class Bean2 {
private Integer id;
private String name;
//提供带参构造
public Bean2(Integer id, String name) {
System.out.println("Bean2被创建了..........带参构造");
this.id = id;
this.name = name;
}
}
配置文件applicationContext.xml:
2. 静态工厂方式 : 通过静态工厂的静态方法创建bean对象
要被反转控制的Bean类:
public class Bean3 {
public Bean3() {
System.out.println("Bean3被创建了.....静态工厂方式");
}
}
静态工厂类提供静态方法:
public class StaticBean3Factory {
public static Bean3 getBean3() {
//在实例化时,可以进行其它操作,例如逻辑判断等
return new Bean3();
}
}
配置文件:
3. 实例化工厂方式:
要被反转控制的Bean类:
public class Bean4 {
public Bean4() {
System.out.println("Bean4被创建了.......实例化工厂方式");
}
}
实例化工厂类提供创建Bean的方法:
public class Bean4Factory {
// 实例化工厂方式创建Bean对象
public Bean4 initBean() {
// 可以在new Bean4之前进行很多的逻辑判断
// 例如判断new哪一个对象
return new Bean4();
}
}
配置文件:
4. FactoryBean方式:
实现接口,实现getObject方法,返回要创建的Bean类.Spring检查到实现了FactoryBean接口时,会在实例化
FactoryBean时自动调用getObject方法获取Bean对象。
public class FactoryBean5 implements FactoryBean {
@Override
public Bean5 getObject() throws Exception {
return new Bean5();
}
小结:
四种方式:
第一种最常用,第二、三、一些框架初始化的时候用的多、第四种spring底层用的多。
---------------------------------------------------------------------------
BeanFactory和FactoryBean的区别?
BeanFactory(ApplicationContext):
是一个工厂(其实是构建了一个spring上下文的环境,容器),用来管理和获取很多Bean对象.
FactoryBean:
是一个Bean生成工具,是用来获取一种类型对象的Bean,它是构造Bean实例的一种方式。
3.2 Bean的作用域
项目开发中通常会使用:singleton 单例、 prototype多例 。
Singleton: 在一个spring容器中,对象只有一个实例。(默认值)
Prototype: 在一个spring容器中,存在多个实例,每次getBean 返回一个新的实例。
单例是默认值,如果需要单例对象,则不需要配置scope。
3.3 在xml配置Bean的初始化和销毁方法(了解)
- 说明:Spring初始化bean或销毁bean时,有时需要作一些处理工作,因此spring可以在创建和拆卸bean的时候调用bean的两个生命周期方法
- init-method -- 当bean被载入到容器的时候调用init-method属性指定的方法
- destroy-method -- 当bean从容器中删除的时候调用destroy-method属性指定的方法
想查看destroy-method的效果,有如下条件
1. 单例(singleton)的bean才可以手动销毁。
2. web容器中会自动调用,但是main函数或测试用例需要手动调用(需要使用ClassPathXmlApplicationContext的close()方法)
public class LifeCycleBean {
public LifeCycleBean() {
System.out.println("实例化................");
}
public void init() {
System.out.println("初始化...............");
}
public void destroy() {
System.out.println("销毁................");
}
}
配置文件:
测试:
3.4 Bean的属性依赖注入
3.4.1 属性依赖注入的三种方式
- 构造器参数注入
- setter方法属性注入
- 接口注入(了解)
Spring 框架规范中通过配置文件配置的方式,只支持构造器参数注入和setter方法属性注入,不支持接口注入 !
3.4.2 构造器参数注入constructor-arg
当Spring初始化Car时,如果发现有constructor-arg
标签,会自动调用带参构造,而不会使用无参构造。
constructor-arg
的属性:
- name : 根据属性名称定位属性
- index : 根据索引定位属性
- type : 根据属性的类型定位属性
- ================================
- value : 简单值,数字,字符串,其他对象等等
- ref : 复杂的对象(就是指bean),值:bean的引用名字
Bean类:
public class Car {
private Integer id;
private String name;
private Double price;
// 必须提供带参构造,用于属性注入
public Car(Integer id, String name, Double price) {
super();
this.id = id;
this.name = name;
this.price = price;
}
@Override
public String toString() {
return "Car [id=" + id + ", name=" + name + ", price=" + price + "]";
}
}
配置文件applicationContext.xml:
测试:
@Test
public void test() {
// 加载配置文件,获取Spring工厂
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取Bean对象
Car car = (Car) applicationContext.getBean("car");
System.out.println(car);
}
结果:
Car [id=1, name=宝马, price=999999.0]
补充:
1. 定位属性的标签,可以混用
即:
2. 自标签的属性赋值问题,可以使用子标签的value,效果和value属性一样
如:
宝马
3.4.3.setter方法属性注入 property【推荐】
使用的默认的构造器(new Bean()),但必须提供属性的setter方法。
Spring创建出Bean对象,再通过setter方法属性注入值。
两步:在类中加入setter方法,在配置文件中使用property
Bean类:
public class Person {
private Integer pid;
private String name;
private Car car;
//必须提供setter方法,用于属性注入
public void setPid(Integer pid) {
this.pid = pid;
}
public void setName(String name) {
this.name = name;
}
public void setCar(Car car) {
this.car = car;
}
@Override
public String toString() {
return "Person [pid=" + pid + ", name=" + name + ", car=" + car + "]";
}
}
配置文件applicationContext.xml:
测试:
@Test
public void test() {
// 加载配置文件,获取Spring工厂
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取Bean对象
//将创建对象与属性注入的权利交给Spring,我们直接从容器拿到这个对象就可以了(已经包含了属性的值)
Person person = (Person) applicationContext.getBean("person");
System.out.println(person);
}
结果:
Person [pid=1, name=jack, car=Car [id=1, name=宝马, price=999999.0]]
setter方法属性注入的属性:
property : 用于setter方式进行属性注入的标签
name : 与Bean类中的setter方法对应.例如setCar --- name就是car.
value : 简单值.
ref : 复杂值。要注入的Bean对象的id/name名字
3.4.4.p名称空间的使用(了解)
什么是名称空间?
作用:Schema区分同名元素。(有点类似于java的包)
xmlns="http://www.springframework.org/schema/beans"
Xmlns没有前缀是默认的名称空间.
p名称空间的作用是为了简化setter方法属性依赖注入配置的,它不是真正的名称空间。
它的使用方法:
p:<属性名>="" 引入常量值
p:<属性名>-ref =""引入其他Bean对象
具体使用步骤:
1. 引入p名称空间
2. 将 子元素 简化为 元素的属性 (以上面的person为例)
结果:
Person [pid=2, name=coco, car=Car [id=1, name=宝马, price=999999.0]]
配置时不需要 子元素,简化了配置 .
3.4.5 spEL表达式的使用 –会使用
spEL(Spring Expression Language)
是一种表达式语言,它是spring3.x版本的新特性。
作用:支持在运行时操作和查询对象,其语法类似统一的EL语言,但是SpEL提供了额外的功能,功能更强大。
语法: #{…} , 引用另一个Bean 、属性、 方法
-
#{bean_id}
引用Bean(具体对象) -
#{bean_id.属性}
引用Bean的属性 -
#{bean_id.方法(参数)}
引用Bean的方法
例1:
//修改了p:pid的值为#{2*3},修改p:name为#{car.name},car是创建car对象的id。
car.name相当于调用了它的getName()方法,因此Car的Bean类中必须提供getName方法。
测试结果:
Person [pid=6, name=宝马, car=Car [id=1, name=宝马, price=999999.0]]
例2:
//修改了p:pid的值为#{car.id},修改p:name为#{car.name},car是创建car对象的id。
car.id相当于调用了它的getId()方法,因此Car的Bean类中必须提供getId方法。
测试结果:
Person [pid=1, name=BMW, car=Car [id=1, name=宝马, price=999999.0]]
3.4.6.集合类型属性注入 (了解-使用时查看即可)
作用:主要用于框架整合配置。
Spring为集合提供了对应的标签:
注入 list元素
注入 set元素
Bean类:提供四种集合,List,Map,Set,Properties,使用setter方式进行属性注入:
public class CollectionBean {
private List list;
private Set set;
private Map map;
private Properties properties;
// 提供setter方式,供Spring框架属性注入
public void setList(List list) {
this.list = list;
}
public void setSet(Set set) {
this.set = set;
}
public void setMap(Map map) {
this.map = map;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
@Override
public String toString() {
return "CollectionBean [list=" + list + ", set=" + set + ", map=" + map + ", properties=" + properties + "]";
}
}
======================================================================================
applicationContext.xml :
value注入简单类型。 ref注入复杂类型。
1
2
3
4
aa
bb
cc
dd
林心如
李诗诗
测试:
// 加载配置文件,获取Spring工厂
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取Bean对象
CollectionBean collectionBean = (CollectionBean) applicationContext.getBean("collectionBean");
System.out.println(collectionBean);
结果:
CollectionBean [list=[1, 2, 3, 4], set=[aa, bb, cc, dd], map={罗贯中=三国演义, 施耐庵=水浒传, 曹雪芹=红楼梦, 吴承恩=西游记}, properties={霍建华=林心如, 吴奇隆=李诗诗}]
3.5.配置文件分开管理(了解)
在开发中,所有的bean不可能只写在一个配置文件中,如果在src的目录下又多创建了一个配置文件,现在是两个核心的配置文件,那么加载这两个配置文件的方式有两种:
-
主配置文件中包含其他的配置文件:【推荐】
在applicationContext.xml中:
工厂创建的时候直接加载多个配置文件:
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
"applicationContext.xml","applicationContext2.xml");