最近在玩SpringBoot,因为要做分享,所以拿出Spring去做个对比,才能感受到使用SpringBoot的简单和快速性。
一.Spring定义
Spring是一个开源框架,为了解决企业应用开发的复杂性而创建的,但现在已经不止应用与企业应用。
Spring是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的容器框架。
--从大小与开销方面看来,Spring都是轻量级的;
--通过控制反转IOC的技术达到松耦合的目的(把控制权交出去,在使用的时候直接用);
--提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务进行内聚性的开发;
--包含并管理应用对象的配置和生命周期,这个意义上是一种容器;
--将简单的组件配置组合成复杂的应用,这个意义上是框架。
二.Spring作用
- 容器;
- 提供了多种技术的支持(JMS,MQ支持,UnitTest等);
- AOP(事务管理,日志管理等);
- 提供了众多方便应用的辅助类(JDBC Template等);
- 对主流应用框架提供了良好的支持(Hibernate等)。
三.适用范围
- 构建企业级应用;
- 单独使用Bean容器(Bean管理);
- 单独使用AOP进行切面管理;
- 其他的Spring功能,如对消息的支持等;
- 在互联网中的应用。
四.IOC控制反转
控制权的转移,应用程序本身不负责依赖对象的创建和维护,而是由外部容器来负责创建和维护,DI(依赖注入)是其一种实现方式。
目的:创建对象并组装对象间的关系。
依赖注入:由IOC容器在运行中,动态的将某种依赖关系注入对象中。
五.Spring的常用注入方式
Spring注入是指在启动spring容器加载bean配置时,完成对变量的赋值注入。
(1)设值注入(set注入):
配置文件如下:
property 表示InjectionServiceImpl有一个属性(成员变量)injectDAO
ref表示引用id
通过set注入,自动调用set方法,InjectServiceImpl中一定有一个setInjectDAO方法。
<?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="injectionService" class="com.spring.beananocation.InjectionServiceImpl">
<property name="injectionDAO" ref="injectionDAO"></property>
</bean>
<bean id="injectionDAO" class="com.spring.beananocation.InjectionDAOImpl"></bean>
</beans>
使用代码如下:
package com.spring.beananocation;
/**
* DAO接口
*/
public interface InjectionDAO {
String save(String string);
}
package com.spring.beananocation;
/**
* DAO实现类
*/
public class InjectionDAOImpl implements InjectionDAO {
@Override
public String save(String string) {
return "这是一个保存:" + string;
}
}
package com.spring.beananocation;
/**
* service 接口
*/
public interface InjectionService {
void save(String arg);
}
package com.spring.beananocation;
/**
* service 实现类
*/
public class InjectionServiceImpl implements InjectionService {
/**
* 和配置文件中property同名的成员变量
*/
private InjectionDAO injectionDAO;
/**
* set方法
*/
public void setInjectionDAO(InjectionDAO injectionDAO) {
this.injectionDAO = injectionDAO;
}
@Override
public void save(String arg) {
//模拟业务操作
System.out.println("service接收参数:" + arg);
arg = arg + " :" + this.hashCode();
System.out.println(injectionDAO.save(arg));
}
}
单元测试方法:
@Test
public void testSetter() {
InjectionServiceImpl injectionService = super.getBean("injectionService");
injectionService.save("这是要保存的数据");
}
可以看到单元测试执行成功,injectionDAO被成功注入,执行结果如下:
(2)构造注入:
使用构造方法创建类的实例化时,把相应的injectionDAO注入。
配置文件如下:
<?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="injectionService" class="com.spring.beananocation.InjectionServiceImpl">
<constructor-arg name="injectionDAO" ref="injectionDAO"></constructor-arg>
</bean>
<bean id="injectionDAO" class="com.spring.beananocation.InjectionDAOImpl"></bean>
</beans>
将InjectServiceImpl的set方法变为构造方法来注入:
package com.spring.beananocation;
/**
* service 实现类
*/
public class InjectionServiceImpl implements InjectionService {
/**
* 和配置文件中property同名的成员变量
*/
private InjectionDAO injectionDAO;
/**
* 构造方法
*/
public InjectionServiceImpl(InjectionDAO injectionDAO) {
this.injectionDAO = injectionDAO;
}
@Override
public void save(String arg) {
//模拟业务操作
System.out.println("service接收参数:" + arg);
arg = arg + " :" + this.hashCode();
System.out.println(injectionDAO.save(arg));
}
}
单元测试方法和结果同设置注入。
建议以设值注入为主,构造注入为辅的方式来进行注入。
对于依赖关系无需变化的注入,尽量采用构造注入;而其他依赖关系的注入,则优先考虑设值注入。
五.Bean的配置项
- id:在整个ioc容器中,这个Bean的唯一标识;
- class:具体要实例化的哪一个类;
- scope:指范围,作用域;
- Constructor arguments:构造器的参数(构造注入用)。
- Properties:属性(设值注入用)。
- AutoWiring mode:自动装配模式。
- lazy-initialization mode:懒加载模式。
- Initialization/destruction method:初始化和销毁方法。
六.Bean的作用域
- singleton :单例模式,默认选项,指一个Bean容器中只存在一份。(启动时就创建。)
- prototype:每次请求(每次使用)创建新的实例,destory方法不生效,因为请求完成后,实例就被垃圾回收器回收了,不存在了。(在用到对象时才创建。)
- request:每次http请求创建一个实例且仅在当前request内有效。
- session:同request,每次http请求创建,当前session内有效。
- global session:基于portlet的web中有效(portel定义了global session),如果是在web中,同session。(有时候做系统和应用的集成,单点登录等。)
在配置文件中加scope配置项:
<?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="injectionService" class="com.spring.beananocation.InjectionServiceImpl" scope="singleton">
<constructor-arg name="injectionDAO" ref="injectionDAO"></constructor-arg>
</bean>
<bean id="injectionDAO" class="com.spring.beananocation.InjectionDAOImpl"></bean>
</beans>
修改单元测试用例如下,由于我们打出了hashcode:
/**
* 单元测试
*/
@Test
public void testSetter() {
InjectionServiceImpl injectionService = super.getBean("injectionService");
injectionService.save("这是要保存的数据");
InjectionServiceImpl injectionService2 = super.getBean("injectionService");
injectionService2.save("这是要保存的数据");
}
所以 当 scope="singleton"或者不加scope配置项时,为单例模式。
所以两次输出的hashcode为相同的,如下:
而当scope="prototype",每次使用都是一个新的实例。输出的hashcode不一致,如下:
七.Bean 的生命周期
- 定义:在Spring的bean的xml文件中配置的bean,定义了一个id和class;
- 初始化:当ioc容器在启动的时候去加载bean配置文件里面的bean并初始化,生成bean的实例。
- 使用:从bean容器中取出一个bean的实例,去调用它的方法。
- 销毁:在bean容器停止的时候,去销毁由当前这个bean创建的所有实例。
Bean初始化和销毁的三种方式:
(1)配置init-method和destroy-method方法。
<?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="beanLifeCycle" class="com.spring.ioc.impl.BeanLifeCycle" init-method="init"
destroy-method="beanDestroy"></bean>
</beans>
BeanLifeCycle类:
package com.spring.ioc.impl;
public class BeanLifeCycle {
public void init() {
System.out.println("bean start");
}
public void beanDestroy() {
System.out.println("bean stop");
}
public void hello() {
System.out.println("hello~");
}
}
单元测试方法:
@Test
public void beanLifeCycle(){
BeanLifeCycle beanLifeCycle = super.getBean("beanLifeCycle");
beanLifeCycle.hello();
}
单元测试执行结果如下:
可以看到是先初始化,再使用,再销毁。
(2)实现InitializingBean和DisposableBean接口,并重写他们的afterPropertiesSet()和destroy()方法。不用修改配置文件。
package com.spring.ioc.impl;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
public class BeanLifeCycle implements InitializingBean, DisposableBean {
public void hello() {
System.out.println("hello~");
}
@Override
public void destroy() throws Exception {
System.out.println("bean DisposableBean");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("bean InitializingBean");
}
}
(3)在配置文件配置全局默认初始化销毁方法:
配置default-init-method和default-destroy-method属性。
<?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"
default-init-method="defaultInit" default-destroy-method="defaultDestory">
<bean id="beanLifeCycle" class="com.spring.ioc.impl.BeanLifeCycle"></bean>
</beans>
BeanLifeCycle类:
package com.spring.ioc.impl;
public class BeanLifeCycle{
public void defaultInit() {
System.out.println("defaultInit start");
}
public void defaultDestory() {
System.out.println("defaultDestroy stop");
}
public void hello() {
System.out.println("hello~");
}
}
当3种方式都实现的时候,先执行实现接口的方法,再执行单独配置的init,destory方法,全局默认的方法不实现。
(只要实现了单独配置的init和destory方法,就不再实现全局默认方法了。)
八.Bean的自动装配(Autowiring)
- NO:不做任何配置(默认选项);
- byName:根据属性名自动装配。此选项将检查容器并根据名字查找与属性完全一致的bean,并将其与属性自动装配。bean id重复,会启动失败,提示同一个XML下不能存在相同的bean。
- byType:如果容器种存在一个与指定属性类型相同的bean,那么将与该属性自动装配,如果存在多个该类型的bean,那么抛出异常,并指出不能使用byType方式进行自动装配,如果没找到相匹配的bean,则什么事都不发生。
- Constructor:与byType方式类似,不同之处在于它应用于构造器参数。如果容器种没有找到与构造器参数类型一致的bean,那么抛出异常。
在配置文件中加入配置default-autowire="byName"或者default-autowire="byType",都是通过set方法来赋值的。
<?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"
default-autowire="byName">
<bean id="autoWiringService" class="com.spring.ioc.autowiring.AutoWiringService"></bean>
<bean id="autoWiringDAO" class="com.spring.ioc.autowiring.AutoWiringDAO"></bean>
</beans>
AutoWiringService:
package com.spring.ioc.autowiring;
public class AutoWiringService {
private AutoWiringDAO autoWiringDAO;
public void setAutoWiringDAO(AutoWiringDAO autoWiringDAO) {
System.out.println("走set方法");
this.autoWiringDAO = autoWiringDAO;
}
public void say(String words) {
this.autoWiringDAO.say(words);
}
}
AutoWiringDAO:
package com.spring.ioc.autowiring;
public class AutoWiringDAO {
public void say(String word) {
System.out.println("AutoWiringDAO:" + word);
}
}
单元测试:
@Test
public void autoWiringServiceTest() {
AutoWiringService autoWiringService = super.getBean("autoWiringService");
autoWiringService.say("This is a test!");
}
执行结果:
在配置文件中加入配置default-autowire="constructor"或者default-autowire="byType",是通过构造方法来赋值的。
AutoWiringService:
package com.spring.ioc.autowiring;
public class AutoWiringService {
private AutoWiringDAO autoWiringDAO;
public AutoWiringService(AutoWiringDAO autoWiringDAO) {
System.out.println("走构造方法");
this.autoWiringDAO = autoWiringDAO;
}
public void say(String words) {
this.autoWiringDAO.say(words);
}
}
九.Bean装配之Resources
Resources为针对资源文件的统一接口
- UrlResource:URL对应的资源,根据一个URL地址即可构建。
- ClassPathResource:获取类路径下的资源文件。
- FileSystemResource:获取文件系统里面的资源。
- ServletContextResource:ServletContext封装的资源,用于访问ServletContext环境下的资源。
- InputStreamResource:针对于输入流封装的资源。
- ByteArrayResource:针对于字节组封装的资源。