1.Spring简介
Spring是J2EE开发中一个很重要的框架。它主要用来解决下面两个问题。
-
解决大型软件开发中,对象之间由于复杂的依赖关系导致的牵一发而动全身的强耦合问题
.使用IoC思想解决。 -
使用面向切面编程, 解决控制事务的繁琐操作
.使用AOP解决。
根据Spring解决的问题,和其对应的专业术语。我们也说Spring是一个轻量级的DI/IoC
和AOP容器
的开源框架
, 其提倡
以最小侵入式
管理应用中的代码,意味着我们可以随时卸载和安装Spring
.
接下去我们需要理解下Spring以下的专业术语,我就不抄写网络上的术语内容,并不是那么通俗,以我的理解如下。
1.2 Spring术语
-
应用程序(Application)
.- 对于JavaWeb来将,应用程序就是一个Web App.例如OA系统等.
-
框架
:- 框架是一种抽象, 其抽取了开发中可以重用和常见的功能的集合。
-
非侵入式设计
:- 非侵入式是编程中常见的一种设计方式,它保障了别人用你的框架,即使到时候不用了,也不会应用删除你的框架而要更改大量的代码, 可以不用的时候就直接删除,不会带来依赖问题。
- 从代码的角度来说.非侵入式,保证了不用继承框架的类.也就不会和依赖于框架的类.是一种解耦的设计、
-
轻量级和重量级
- 所谓的轻量级是相对于重量级来说。一般是由以下几个特点.
非侵入式
、资源占用少
、部署简单
、易用
.所以所谓的轻是在有重的前提下的对比。
- 所谓的轻量级是相对于重量级来说。一般是由以下几个特点.
-
POJO
:-
Plain Ordinary Java Object / Pure Old Java Object
.所谓的POJO是不继承
Java的任何类
,不实现
任何的接口
,但可以
包含业务逻辑
和持久化逻辑
-
-
容器
:- 容器主要用来
存放对象
.管理对象的整个生命周期
.比如Tomcat是Servlet/JSP容器
.
- 容器主要用来
1.3 Spring的框架构架
之所以要了解Spring构架是在学习之前我们要对一样事务大体的骨架有一个了解。在介绍Spring的构架之前,我们简要的说下Spring的优势。
Spring的优势
- 低侵入、低耦合
- 声明式事务管理
- 方便集成其他框架
- Spring框架包含JavaEE 三层的每一层的解决方案(一站式)
构架
从上之下。主要分为
-
数据访问层(Data Access)
。 -
Web层
。 -
AOP模块
。 -
核心容器层(Core Coniainer)
。Beans、Core、Context、SpEL -
测试层(Test)
。
Spring框架版本
这里简要介绍下Spring各个版本的变化。其中Spring2.5是变化最大的一个版本
,其已经完成了大部分Spring的核心功能。而后续版本都是在其上增加新的语法支持等。
-
Spring2.5
:驱动编程
、支持SimpleJdbcTemplate的命名参数操作 -
Spring3.x
:全面支持泛型
不支持JDK 1.4。支持SpEL、支持WebService的OXM -
Spring4.x
:支持Java8
、支持JavaEE6规范
、泛型限定式依赖注入
、对Hiberante4的集成
和事务管理
提供更好的管理方案.
介绍完Spring的一些基础常识,我们看是以Spring来写一个最简单的HelloWorld,学习基于Spring搭建应用的基本步骤。
2. 基于Spring如何编写程序
关于Spring插件
如果不是用Spring官方提供的Eclipse。需要自己安装SIS插件.
- Help-->Install New Software-->work with 中输入http://dist.springsource.com/release/TOOLS/update/e4.6/, 勾掉Contact All update ,回车等待片刻,选中带spring IDE的四项,一直next直到完成,重启eclipse即可。如下图
2.1 基于Spring的HelloWorld编写步骤
-
导入jar包
。并build到classpath。- spring-beans-4.0.0.RELEASE.jar
- spring-core-4.0.0.RELEASE.jar
- commons-logging-1.1.3.jar
-
将我们的控制权转交给Spring去控制
-
创建bean的xml配置文件
。 -
注册bean到容器
, 进行属性注入
。注册HelloWorld到IoC容器中。
-
-
启动IoC容器, 根据Bean的id
和Bean的类型
, 从Ioc容器中取出对应的bean实例.bean的获取方式又有下面三种.一般我们使用最后一种
,因为最后这种既能获取到bean,又无需强转
.- 根据ID或者name获取bean.
ioc.getBean(String)
- 根据类型获取bean.
ioc.getBean(Class)
- 根据ID和类型获取bean.
ioc.getBean(String, Class)
- 根据ID或者name获取bean.
目录结构
测试代码
public class HelloWorldTest {
@Test
public void testHelloWorld() {
Resource resource = new ClassPathResource("com/sweetcs/_01helloworld/hello.xml");
BeanFactory beanFactory = new XmlBeanFactory(resource);
HelloWorld helloWorld = beanFactory.getBean("helloWorld", HelloWorld.class);
helloWorld.say();
}
}
输出
可以看到我们使用IoC容器成功的获取到对应的Bean对象,并调用其say方法,输出Hello Spring.
2.2 Spring基本配置
使用Spring编写完第一个HelloWorld演示程序,接下来我细致的分享下Spring基本的配置方式
2.2.1 bean的id和name
在Spring的配置文件中, 也就是配置bean的文件中.我们需要给Bean配置属性。其中比较常用,用来定位Bean的用两种方式,一种是id一种是name.
id方式
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="helloWorld" class="com.sweetcs._01helloworld.HelloWorld"></bean>
</beans>
测试代码
@Test
public void testHelloWorldByID() {
Resource resource = new ClassPathResource("com/sweetcs/_01helloworld/hello.xml");
BeanFactory beanFactory = new XmlBeanFactory(resource);
HelloWorld helloWorld = beanFactory.getBean("helloWorld", HelloWorld.class);
helloWorld.say();
}
ID方式输出
name方式
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- <bean id="helloWorld" class="com.sweetcs._01helloworld.HelloWorld"></bean> -->
<bean name="helloWorld,helloWorld2,helloWorld3" class="com.sweetcs._01helloworld.HelloWorld"></bean>
</beans>
测试代码
@Test
public void testHelloWorldByName() {
Resource resource = new ClassPathResource("com/sweetcs/_01helloworld/hello.xml");
BeanFactory beanFactory = new XmlBeanFactory(resource);
HelloWorld helloWorld = beanFactory.getBean("helloWorld", HelloWorld.class);
HelloWorld helloWorld2 = beanFactory.getBean("helloWorld2", HelloWorld.class);
HelloWorld helloWorld3 = beanFactory.getBean("helloWorld3", HelloWorld.class);
System.out.println(helloWorld == helloWorld2);
System.out.println(helloWorld2 == helloWorld3);
}
输出
ID和name的区别
- 如果是ID方式,一个bean只能配置一个id.并且多个bean的ID值不能重复.如果是Name方式, 一个bean能配置多个别名.
2.3 模块化xml配置文件
如果我们项目中有很多的bean需要注册, 那么这时候如果还只是用一个bean的配置文件,那这个文件配置的bean会爆炸性的增长, 这带来的后果就是十分的难以维护这个配置文件。这个时候我们可以将xml进行拆分, 拆分多个xml文件。一般项目都是按模块划分的,我们可以每个模块下都配置一个xml文件。最后再通过一个统一xml文件,将这些xml文件引入。
引入其他XML配置文件
import元素
使用import元素我们可以将其他的xml配置文件导入。其使用resource属性
代表要导入的文件路径,并且提供了classpath:
和file:
协议分别表示从classpath路径寻找
和从文件系统的路径寻找
.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 导入_01helloworld包下的配置文件 -->
<import resource="classpath:com/sweetcs/_01helloworld/hello.xml"/>
</beans>
对应的测试类代码
注解的意思可以先忽略,看完1.6就明白
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class ApplicationContextTest {
@Autowired
BeanFactory ioc;
@Test
public void testHelloSpring() {
HelloWorld bean = ioc.getBean("helloWorld", HelloWorld.class);
bean.say();
}
}
2.4 Spring中的测试
问题:在编写Spring的测试用例中, 我们直接使用Spring提供的Test。因为该Test是包含在容器中, 我们不用每一次都创建容器,关闭容器, 这对系统开销十分大。
2.4.1 测试步骤
- 导入依赖jar包
-
junit4
。需要两个个jar包junit
和hamcrest-core.jar
-
spring
。需要三个jar包spring-test
、spring-aop
、spring-expression
-
- 使用Junit提供的注解
@RunWith
: 用于指定junit运行环境,是junit提供给其他框架测试环境接口扩展.
@ContextConfiguration
: 用于指定容器上下文配置文件。该文件用于指定容器创建的时候需要生成哪些bean对象。 - 编写测试类
// 告诉JVM,Spring测试运行于JVM上.而不是让Junit运行于JVM上.
@RunWith(SpringJUnit4ClassRunner.class)
// 告诉Spring去哪里寻找配置文件.
@ContextConfiguration("classpath:com/sweetcs/_02springtest/HellWoldTest.xml")
public class HelloWorldTest {
// 自动装配IoC容器
@Autowired
BeanFactory ioc;
@Test
public void testHelloSpring() {
HelloWorld helloWorld = ioc.getBean("helloWorld", HelloWorld.class);
helloWorld.say();
}
}
配置文件命名的小技巧
-
测试类的配置文件
名可以命名为所在的类名-context
.放在和测试类一个目录
原理
@ContextConfiguration
也可以不用指定配置文件路径, 如果不指定,其默认配置文件
和所在的类文件
在同一级目录
下,且该配置文件名
为类名-context.xml
.对于上述程序可以的配置文件名就可以改成HellWoroldTest-context
-
务必指定classpath:
.指定该前缀能让其一定去classpath目录寻找.
上述主要讲了
- 如何像IoC容器转交控制权,让IoC容器替我们管理这些对象.而不是我们手动去管理。
- 如何编写Spring中的测试。
接下来我们要比较系统的介绍下IoC容器的内容。
3.IoC
IoC容器, 即Inversion Of Control, 中文翻译就是反转控制。IoC容器反转了哪些控制呢?其实IoC是一种思想,这种思想被引入软件工程学中, 主要是为了解决大型软件项目中的耦合过深问题。
3.1 思想
传统的思维
传统的思维下,我们需要哪个对象就去new一个。比如下面的代码。
public class EmployeeService {
private IEmployeeDAO employeeDAO = new EmployeeDAO();
}
这中代码本省写得并没有错,但是随着软件复杂度的增加, 你可能导出都存在这种代码,对于EmployeeService来说它就依赖
于EmployeeDAO
.如果采用这种方式,可想而知,项目中会存在很多复杂的依赖,而且这些依赖有的又是重复引用的
.这会导致,其中一个依赖要是存了问题,整个软件系统就无法正常的运行。
如下图,是我在网络上找的一张图,传统软件开发思维如下。导致的问题可想而知。齿轮之间相互耦合太强,牵一发而动全身。
IoC反转控制思想
传统的软件设计是我们需要什么对象需要我们自己去创造,如上分析这随着软件复杂度增加,对于系统的维护和变更是十分困难的,因为存在太多耦合。
IoC的思想
IoC的思想是能不能把这种主动的控制权转交出来,当我们需要某个对象的时候才让"第三方"帮我们注入我们需要的对象。这就解除了类之间的耦合关系,类于类之前的联系需要通过这个中间容器即IoC容器来桥接。
如下图, 只要遵守这个"第三方"的规则,所有的齿轮都能够转动。这个第三方就相当于一个粘合剂,将所有齿轮"粘合"起来,保证整个系统的运转。
这种控制权颠倒过来的设计, 我们就是把它称为“控制反转”。通过这种设计,我们可以在Spring中很方便的扩充功能,而且还能让类之间解除耦合。
说了那么多我们先来看下Spring中IoC容器
、容器如何创建Bean
、Bean如何实例化
、Bean的作用域
、已经Bean的初始化和销毁方法
。
3.2 IoC容器
获取IoC容器的方式有两种。一种是BeanFactory
提供了最基本的IoC功能。一种是ApplicatioinContext
, 基于BeanFactory扩展了更强的功能.
- 使用实现
BeanFacory接口
的实现类BeanFacory是Spring底层的接口,只提供了简单的IoC功能,
负责配置
、创建
、管理
bean。应用中一般不实用BeanFactory, 而推荐使用ApplicationContext。 - 使用实现
ApplicationContext接口
的实现类一般开发中都使用
ApplicationContext
来代表容器.ApplicationContext是BeanFactory子接口,除了继承IoC基本功能,其还提供了更多的功能。消息机制
国际化
统一的资源加载
AOP功能
3.2.1 基于ApplicationContext创建IoC容器(Spring容器)
基于BeanFactory的方式在HelloWorld中已经介绍过, 我们就简单介绍下,基于ApplicatioinContext创建Spring容器的方式。由于ApplicationContext也是一个接口,所以我们只能找其实现类。
通过查看类继承结构如下图,可以发现有如下两个类。
3.3 Bean创建时机研究
ApplicatioinContext和BeanFactory对bean的创建时机并不是相同的,我们来研究下他们有什么区别。之所以以下代码不采用Spring提供的Test来自动装配IoC容器,是因为如果使用SpringTest,它会自动加载所有Bean.就看不出ApplicationContext创建Bean的时机和BeanFactory的区别。
以下代码省略了转交控制权的步骤,需要配置xml,可以参考上面的配置步骤。
3.3.1 基于BeanFactory的Bean创建时机研究
- testCreateWithBeanFactory用例,是创建IoC容器,并获取Bean
- testCreateWithBeanFactory2用例, 是只创建IoC容器
@Test
public void testCreateWithBeanFactory() {
Resource resource = new ClassPathResource("com/sweetcs/_03ioc/BeanCreateTest-context.xml");
BeanFactory ioc = new XmlBeanFactory(resource);
User user = ioc.getBean("user", User.class);
System.out.println(user);
/* 输出
User 构造器被调用,正在初始化User对象
User [name=SweetCS]
*/
}
@Test
public void testCreateWithBeanFactory2() {
Resource resource = new ClassPathResource("com/sweetcs/_03ioc/BeanCreateTest-context.xml");
BeanFactory ioc = new XmlBeanFactory(resource);
/*
* 无任何输出
*/
}
使用BeanFacory
方式,Bean只会在需要的时候才创建
3.3.2 基于ApplicationContext的Bean创建时机研究
- testCreateWithApplicationContext用例,是创建IoC容器,并获取Bean
- testCreateWithApplicationContext2用例, 是只创建IoC容器
@Test
public void testCreateWithApplicationContext() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("com/sweetcs/_03ioc/BeanCreateTest-context.xml");
User bean = ctx.getBean("user", User.class);
System.out.println(bean);
/* 输出
User 构造器被调用,正在初始化User对象
User [name=SweetCS]
*/
}
@Test
public void testCreateWithApplicationContext2() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("com/sweetcs/_03ioc/BeanCreateTest-context.xml");
// 输出 User 构造器被调用,正在初始化User对象
}
运行程序后使用ApplicationContext
的方式,无论有没有取获取Bean,Bean都会在容器启动的时候被创建
。
BeanFactory和ApplicationContext的应用场景
ApplicatioinContext
会一次性将所有Bean的创建出来,主要是为了优化服务端的性能,主用应用于Web后端开发
BeanFacory
只会在需要使用Bean的时候在创建,主要用在客户端开发
。因为客户端直接面向用户,关注用户体验。不能再一启动创建大量对象,让应用卡顿半天。
3.3.3 能否配置延迟创建
其实这个需求,一般不回用到。对于我们做后端开发的来说, 最常用的是ApplicationContext, 如果要让其延迟加载Bean也是可以。有两种配置方式。
-
全局配置
,对所有Bean都生效。<beans default-lazy-init="true"> </beans>
-
局部配置
,指对指定Bean生效。<bean lazy-init="true"></bean>
任选一种做测试,会发现在ApplicationContext下的Bean,如果没获取也不会创建.
3.4 Bean的实例化方式
之所以要聊Bean的实例化方式,是因为我们可能会在不同的情况下需要使用不同的方式,让Spring帮我们注入对象。所以我们需要来研究下让Spring帮我们注入对象的四种方式, 其对应的就是Bean的实例化方式。主要有以下四种方式
通过无参构造器方式,最标准, 使用最多(重点)
转交控制权
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 1.最标准的方式,通过无参够照器方式创建 -->
<bean id="user" class="com.sweetcs._04beaninstance.construtor.User"></bean>
</beans>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class BeanInstanceTest {
@Autowired
ApplicationContext ctx;
@Test
public void testInstanceBeanWithNoArgConstrutor() {
User bean = ctx.getBean("user", User.class);
System.out.println(bean);
}
}
输出
通过静态工厂实例化方式
其主要解决一些系统留下来的问题。例如有可能你接触的项目并不是通过最标准的创建Bean的方式创建,它可能都是使用静态工程创建,那我们就应该在接入Spring的时候,为了兼容性,使用静态工厂实例化方式。
SomeBean2StaticFactory
配置bean,id, class ,factory-method
@Test
public void testInstanceBeanWithStaticFactory() {
SomeBean someBean = ctx.getBean("someBean", SomeBean.class);
System.out.println(someBean);
}
输出
通过实例工厂实例化方式
SomeBean2
package com.sweetcs._04beaninstance.instance_factory;
public class SomeBean2 {
}
SomeBean2Factory
package com.sweetcs._04beaninstance.instance_factory;
public class SomeBean2Factory {
public SomeBean2 getSomeBean2() {
return new SomeBean2();
}
}
转交控制权
<!-- 3.通过实例工厂实例化 -->
<bean id="someBean2Factory" class="com.sweetcs._04beaninstance.instance_factory.SomeBean2Factory"></bean>
<bean id="someBean2" factory-bean="someBean2Factory" factory-method="getSomeBean2"></bean>
测试用例
@Test
public void testInstanceBeanWithInstanceFactory() {
SomeBean2 someBean2 = ctx.getBean("someBean2",SomeBean2.class);
System.out.println(someBean2);
}
需要new出这个工厂
先要转交实例工厂对象创建权
再要转交Bean对象创建权
实现FacotryBean接口实例化。实例工厂的变种,主要为了集成其他框架。
如果发现了这个对象是实现这个接口,Spring容器会自动条用getObject方法返回创建的对象。
package com.sweetcs._04beaninstance.facotry_bean;
import org.springframework.beans.factory.FactoryBean;
public class MyBean {
}
package com.sweetcs._04beaninstance.facotry_bean;
import org.springframework.beans.factory.FactoryBean;
public class MyBeanFactory implements FactoryBean<MyBean>{
@Override
public MyBean getObject() throws Exception {
// TODO Auto-generated method stub
return new MyBean();
}
@Override
public Class<?> getObjectType() {
// TODO Auto-generated method stub
return MyBean.class;
}
@Override
public boolean isSingleton() {
// TODO Auto-generated method stub
return false;
}
}
测试用例
@Test
public void testInstanceBeanWithImplementsFactoryBean() {
MyBean myBean = ctx.getBean("myBean", MyBean.class);
System.out.println(myBean);
}
输出
3.5 Bean的作用域
Bean的作用域研究表示Bean可以存活多久, Bean的标签里有一个scope属性,要来表示Bean的作用域。这边只介绍用得最多的两种方式singleton(单例)和prototype(多例)。
默认方式
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="myBean" class="com.sweetcs._05beanscope.MyBean"></bean>
</beans>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class MyBeanTest {
@Autowired
ApplicationContext ctx;
@Test
public void testDefaultScope() {
MyBean myBean = ctx.getBean("myBean", MyBean.class);
MyBean myBean2 = ctx.getBean("myBean", MyBean.class);
MyBean myBean3 = ctx.getBean("myBean", MyBean.class);
System.out.println(myBean + "\n" + myBean2 + "\n" + myBean3);
}
}
输出
Singleton方式
更改bean的配置的,添加scope属性为singleton
<bean id="myBean" class="com.sweetcs._05beanscope.MyBean" scope="singleton"></bean>
再运行刚才的用例,输出
prototype方式
更该bean的配置为prototype
<bean id="myBean" class="com.sweetcs._05beanscope.MyBean" scope="prototype"></bean>
所有作用域的总结
最常用的是singleton和prototype作用域。其他的可以参考以下
作用域 | 描述 |
---|---|
singleton | 该作用域将 bean 的定义的限制在每一个 Spring IoC 容器中的一个单一实例(默认)。 |
prototype | 该作用域将单一 bean 的定义限制在任意数量的对象实例。 |
request | 该作用域将 bean 的定义限制为 HTTP 请求。只在 web-aware Spring ApplicationContext 的上下文中有效。 |
session | 该作用域将 bean 的定义限制为 HTTP 会话。 只在web-aware Spring ApplicationContext的上下文中有效。 |
global-session | 该作用域将 bean 的定义限制为全局 HTTP 会话。只在 web-aware Spring ApplicationContext 的上下文中有效。 |
3.6 Bean的初始化和销毁方法
bean标签中提供了init-method
属性和destory-method
属性。分别会在bean创建之前调用,在bean销毁之前做收尾操作。
3.6.1 DataSource需要回收
比如DataSource
最终都需要关闭资源
, 在Bean销毁
之前,都要调用close
方法。
MyDataSourceBean
package com.sweetcs._06bean_init_destory;
public class MyDataSourceBean {
public void doingSomeWork() {
System.out.println("处理业务逻辑");
}
public void init() {
System.out.println("初始化配置,准备连接数据库");
}
public void close() {
System.out.println("释放connection");
}
}
转交控制权给Spring容器
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="myDataSource" class="com.sweetcs._06bean_init_destory.MyDataSourceBean"
init-method="init" destroy-method="close"></bean>
</beans>
MyDataSourceBeanTest
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class MyDataSourceBeanTest {
@Autowired
ApplicationContext ctx;
@Test
public void testCreateMyDataSourceBean() {
MyDataSourceBean dataSource = ctx.getBean("myDataSource", MyDataSourceBean.class);
dataSource.doingSomeWork();
}
}
输出
3.6.2 对于多例无效
如果Bean标签的scope属性配置成scope="prototype"
。则容器不负责销毁控制,而且这个bean也不会放入Spring容器里取管理, 而是一创建就交给程序员,让程序员自己去负责。
<bean id="myDataSource" class="com.sweetcs._06bean_init_destory.MyDataSourceBean"
init-method="init" destroy-method="close"
scope="prototype">
</bean>
4.DI
DI又称为依赖注入.DI是IoC思想的一种实现,在Spring容器中就采用了DI技术来实现IoC, 即采用DI实现了反转控制。所以我们要来探讨下如何注入,注入的几种方式。记下来我们主要来研究自动装配
、属性注入
、构造器注入
、属性占位符的使用
4.1 自动装配
自动装配Spring中主要提供了两种方式。如下
- 基于XML配置的自动装配.
Bean标签
中配置autowire
属性 - 基于注解配置的自动装配.
@Autowirsed
注解
基于XML的自动装配
我们创建两个类分别是ABean和CBean,让ABean依赖于CBean.代码如下
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="aBean" class="com.sweetcs._07di.autowise_xml.ABean" autowire="byType"></bean>
<bean id="cBean" class="com.sweetcs._07di.autowise_xml.CBean"></bean>
</beans>
ABean
public class ABean {
private CBean cBean;
public void setcBean(CBean cBean) {
this.cBean = cBean;
}
@Override
public String toString() {
return "ABean [cBean=" + cBean + "]";
}
}
CBean
public class CBean {
}
测试代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class AutoWiseWithXMLTest {
@Autowired
ApplicationContext ctx;
@Test
public void testAutoWiseWithXML() {
ABean bean = ctx.getBean("aBean", ABean.class);
System.out.println(bean);
}
}
输出
如上输出, 基于XML方式
的自动装配
.自动将CBean注入到了ABean当中。使用的是byType的方式,其实还可以使用byName的方式。下面说下二者区别
byName和byType的区别
- byName是按照属性名去容器中找id为这个属性名的对象,如果找不到则抛出异常。
- byType是会根据对应要注入的字段的类型,去容器里找。如果找到一个匹配类型的对象则直接注入。如果找到多个则抛出异常。
4.2 注入值
4.2.1 setter注入
一般我们不会在xml中使用自动装配
,应用这样很难看出这个bean有哪些属性
。取而代之的是有两种方法
- 使用属性注入方式。可以很清晰的看出bean的属性。
- 使用注解装配方式。
注入简单类型-使用字符串
<bean id="employee" class="com.sweetcs._07di.property.Employee">
<property name="name" value="SweetCS"></property>
<property name="url" value="http://www.baidu.com"></property>
<property name="age" value="1"></property>
</bean>
测试用例
public class Employee {
private String name;
private URL url;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public URL getUrl() {
return url;
}
public void setUrl(URL url) {
this.url = url;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Employee [name=" + name + ", url=" + url + ", age=" + age + "]";
}
}
@Test
public void testDIWithProperyMethod() {
Employee employee = ctx.getBean("employee", Employee.class);
System.out.println(employee);
}
注入引用类型
public class EmployeeDAO {
public void save() {
System.out.println("开始持久化数据到数据库");
}
}
public class EmployeeService {
private EmployeeDAO employeeDAO;
public void setEmployeeDAO(EmployeeDAO employeeDAO) {
this.employeeDAO = employeeDAO;
}
@Override
public String toString() {
return "EmployeeDAO [employeeDAO=" + employeeDAO + "]";
}
public void save() {
employeeDAO.save();
}
}
xml配置
<bean id="employeeService" class="com.sweetcs._07di.property.EmployeeService" autowire="byType"></bean>
<bean id="employeeDAO" class="com.sweetcs._07di.property.EmployeeDAO"></bean>
注入集合类型
public class CollectionBean {
private Set set;
private List list;
private String[] array;
private Properties properties;
private Map map;
public void setArray(String[] array) {
this.array = array;
}
public void setProperties(Properties p) {
this.properties =p;
}
public void setSet(Set set) {
this.set = set;
}
public void setList(List list) {
this.list = list;
}
public void setMap(Map map) {
this.map = map;
}
@Override
public String toString() {
return "CollectionBean [set=" + set + ", list=" + list + ", arrayList=" + array + ", map=" + map + "]";
}
}
xml配置
<bean id="collectionBean" class="com.sweetcs._07di.property.CollectionBean">
<property name="set">
<set>
<value>"1"</value>
<value>"2"</value>
<value>3</value>
</set>
</property>
<property name="list">
<list>
<value>"1"</value>
<value>"2"</value>
<value>3</value>
</list>
</property>
<property name="array">
<array>
<value>"1"</value>
<value>"2"</value>
<value>"3"</value>
</array>
</property>
<property name="properties">
<value>
key1=value1
key2=value2
key3=value3
</value>
</property>
<property name="map">
<map>
<entry key="key1" value="value1"></entry>
<entry key="key2" value="value2"></entry>
<entry key="key3" value="value3"></entry>
</map>
</property>
</bean>
4.2.2 构造器注入
构造器注入其实和属性注入差不多, 主要有两个差别,具体就不演示了。
- 需要为相应的Bean类
提供有参的构造方法
(无参构造也要提供) - 需要使用
constructor-arg
标签替换property
标签。
4.3 property place holder(配置数据库连接池)
属性占位符是十分有用的一个功能。开发中一般我们都将有关数据库的配置做成一个配置文件,方便和运维人员解耦,运维人员只要拿着配置文件取做部署,无需关心程序员的代码实现细节。
步骤
- 使用属性占位符初始化上下文的环境变量。
<context:property-placeholder location="classpath:配置文件位置" />
- 使用属性注入方式,配置相关信息。
XML配置, 转交控制权
<context:property-placeholder location="classpath:config.properties"/>
<bean id="ds" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="driverClassName" value="${jdbc.driverClassName}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="initialSize" value="${jdbc.initialSize}"></property>
<property name="maxActive" value="${jdbc.maxActive}"></property>
<property name="minIdle" value="${jdbc.minIdle}"></property>
</bean>
测试用例
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class DruidCreateTest {
@Autowired
ApplicationContext ctx;
@Test
public void testGetConnWithDruid() {
DruidDataSource ds = ctx.getBean("ds", DruidDataSource.class);
Connection connection = null;
try {
connection = ds.getConnection();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(connection);
}
}
输出