初识Spring
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了初识Spring相关的知识,希望对你有一定的参考价值。
接触过Java开发的小伙伴们,应该都或多或少听说过Spring框架(Spring框架通常指Spring Framework),而且大部分的公司都是使用着Spring全家桶框架。之所以说是全家桶,是因为现在Spring系列的框架很多,常见的例如Spring Boot、SpringMVC、Spring Cloud、Spring Security等等。但是学习Spring系列的框架,我们都是先从 Spring Framework 开始学起的,至于为什么要先从 Spring Framework 开始学起,可以参考我之前写的一篇关于JavaWeb程序架构模式的演进的文章。
闲话不多说,我们先来看看Spring是什么,有着什么样的故事,以及它解决了一些开发中的什么问题:
Spring Framework 是一个开源的Java/Java EE全功能栈(full-stack)的应用程序框架,以Apache许可证形式发布,也有.NET平台上的移植版本。该框架基于 Expert One-on-One Java EE Design and Development(ISBN 0-7645-4385-7)一书中的代码,最初由Rod Johnson和Juergen Hoeller等开发。Spring Framework提供了一个简易的开发方式,这种开发方式,可以避免那些可能导致使底层代码变得繁杂混乱的大量的属性文件和工具类。
众所周知,Java是一门面向对象的编程语言,所以我们在开发中经常要设计许多的类,而且总是需要在不同的地方实例化这些类的对象,既然需要实例化很多类的对象,就不可避免的会想到使用工厂方法模式来帮我们创建并管理这些对象,工厂方法可以隐藏对象创建的细节,以降低子类之间的依赖、耦合。对工厂方法不是很熟悉的话,可以参考我之前写的一篇关于工厂方法模式的文章。
以上我们提到了可能需要实例化很多类的对象,如果一个类的对象在多个地方重复的实例化,就会导致系统存在大量的重复的实例对象,这样就会耗费系统资源以及加大了GC回收的压力。这时,我们都会不约而同的想到单例模式,想要让类也 “计划生育” —— 单例模式让一个类只能有一个实例化对象。这样就可以避免存在大量的重复实例化的对象。
想着是美滋滋,但是这就意味着我们每个类都得重复的去编写单例模式的代码,而且这些代码还很容易和我们的业务代码耦合,虽然我们可以将单例模式的代码写在工厂类里,而且这也的确是一个不错的解决办法,不过我们依旧需要自己重复的去编写单例模式的代码。
就在其他程序员都埋头写代码的时候,有一个程序员就产生了一个大胆的想法,我们是否可以建立一个通用的池子或者工厂这样的东西,将我们的需要被管理的类都放入到这个池子中统一进行管理,而且这个池子可以保证取出来的实例对象都是单例的,这样我们在使用某个对象的时候直接从这个池子里边取就行了,我们就不需要总是自己去造轮子、编写重复的样板式代码的!于是Spring就诞生了,而这个程序员就是Rod Johnson,以上我们提到的池子就是Spring容器的雏形。
在当时虽然也已经有Java的企业级框架EJB,但是过于臃肿,属于重量级的框架,说白了就是麻烦又难用,所以Rod Johnson在2002年编写的《Expert One-to-One J2EE Design and Development》一书,Rod 在本书中对J2EE正统框架(EJB)臃肿、低效、脱离现实的种种学院派做法提出了质疑,并以此书为指导思想,编写了interface21框架,也就是后来的Spring。基于最优方法并适用于各种应用类型的Spring框架的建立要归功于Rod Johnson。这些想法也在他的书中得以阐述。书发表后,基于读者的要求,源代码在开源使用协议下得以提供。
Spring Framework的第一版由 Rod Johnson 开发,并在2002年10月发布在 Expert One-on-One J2EE Design and Development 一书中。2003年6月,Spring Framework 第一次发布在 Apache 2.0 许可证下。2004年3月,发布了里程碑的版本1.0,2004年9月以及2005年3月,又发布了新的里程碑版本。2006年,Spring Framework 获得了 Jolt 生产力奖 和 JAX 创新奖。
2006年10月发布Spring 2.0,2007年11月 Spring 2.5,2009年12月 Spring 3.0,2011年 Spring 3.1,2013年11月 Spring 3.2.5,2013年12月发布了4.0版本。值得注意的是,Spring 4.0 版本中增加了对 Java SE 8, Groovy 2, Java EE7 的一些方面以及 WebSocket 的支持。
Spring官网地址:
Spring框架图:
Spring中包含的关键特性一览:
- 强大的基于JavaBeans的采用控制反转(Inversion of Control,IoC)原则的配置管理,使得应用程序的组建更加快捷简易。
- 一个可用于从applet到Java EE等不同运行环境的核心Bean工厂。
- 数据库事务的一般化抽象层,允许宣告式(Declarative)事务管理器,简化事务的划分使之与底层无关。
- 内建的针对JTA和单个JDBC数据源的一般化策略,使Spring的事务支持不要求Java EE环境,这与一般的JTA或者EJB CMT相反。
- JDBC 抽象层提供了有针对性的异常等级(不再从SQL异常中提取原始代码),简化了错误处理,大大减少了程序员的编码量。再次利用JDBC时,你无需再写出另一个‘终止‘(finally)模块。并且面向JDBC的异常与Spring通用数据访问对象(Data Access Object)异常等级相一致。
- 以资源容器,DAO实现和事务策略等形式与Hibernate,JDO和iBATIS SQL Maps集成。利用众多的翻转控制方便特性来全面支持,解决了许多典型的Hibernate集成问题。所有这些全部遵从Spring通用事务处理和通用数据访问对象异常等级规范。
- 灵活的基于核心Spring功能的MVC网页应用程序框架。开发者通过策略接口将拥有对该框架的高度控制,因而该框架将适应于多种呈现(View)技术,例如JSP、FreeMarker、Velocity、Tiles、iText以及POI。值得注意的是,Spring中间层可以轻易地结合于任何基于MVC框架的网页层,例如Struts、WebWork或Tapestry。
- 提供诸如事务管理等服务的面向方面编程框架。
Spring的主要目的是为了简化Java的开发,是为了解决企业级应用开发的复杂性而创建的,而且不是给我们的开发带来复杂性的,为了降低Java开发的复杂性,Spring采取了以下4种关键策略:
- 基于POJO的轻量级和最小侵入性编程
- 通过依赖注入和面向接口实现松耦合
- 基于切面和惯例进行声明式编程
- 通过切面和模板减少样板式代码
在工程中配置并使用Spring
关于Spring的口水理论就先到这里,如果需要比较全面了解Spring可以买一些书来看,推荐几本还不错的书:精通Spring 4.x企业应用开发实战、Spring实战(第4版)、Spring 揭秘以及Spring的官方文档。
接下来我们就来实际看看如何在工程中配置并使用Spring,我这里使用的是Maven工程,pom.xml配置内容如下:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.14.RELEASE</version>
</dependency>
</dependencies>
然后需要在resource目录下创建一个ApplicationContext.xml文件作为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">
</beans>
这样我们就完成了基本Spring工程配置,但是如果需要通过Spring来创建对象的话,我们还需要将对应的类的相关信息配置到Spring的配置文件里,毕竟Spring也不是先知,你不告诉他类在哪的话,怎么帮你创建,例如我这里有一个StudentDAO类,代码如下:
package org.zero01.dao;
import org.zero01.pojo.Student;
public class StudentDAO implements DAO {
public StudentDAO() {
System.out.println("This is StudentDAO class");
}
public int insert() {
System.out.println("StudentDAO类的 insert 方法被执行");
return 0;
}
public Student select() {
System.out.println("StudentDAO类的 select 方法被执行");
return null;
}
public int delete() {
System.out.println("StudentDAO类的 delete 方法被执行");
return 0;
}
public int update() {
System.out.println("StudentDAO类的 update 方法被执行");
return 0;
}
}
在Spring的配置文件里则需要添加这样一句配置:
<!-- class属性的值为该类的全名,id属性为该类在Spring容器中的唯一标识符,就好比工厂里的产品都有自己的id一样,这个值可以自定义,只要是唯一的就可以 -->
<bean class="org.zero01.dao.StudentDAO" id="studentDAO" />
如果你使用的开发工具是IDEA的话,会发现配置完以上这句内容后,会发现StudentDAO里多了一个豆子的图标,表示这个类已经被Spring管理了:
在Spring配置文件里配置完StudentDAO类的信息后就可以让Spring来帮我们创建想要的对象了,Spring里有两大管理对象可以帮我们创建StudentDAO类的实例对象,这两个对象分别是 BeanFactory 和 ApplicationContext ,这里我们先介绍如何使用 BeanFactory 来创建对象,这个对象看名字也就知道是个工厂了,测试代码如下:
package org.zero01.test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
import org.zero01.dao.DAO;
public class Test {
public static void main(String[] args) {
// 传入Spring配置文件的路径
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
// 传入配置文件里配置的id
DAO studentDAO = (DAO) beanFactory.getBean("studentDAO");
studentDAO.insert();
studentDAO.delete();
studentDAO.select();
studentDAO.update();
}
}
运行结果:
This is StudentDAO class
StudentDAO类的 insert 方法被执行
StudentDAO类的 delete 方法被执行
StudentDAO类的 select 方法被执行
StudentDAO类的 update 方法被执行
如上,在测试代码中可以直观的看到,我们不需要自己去实例化具体的子类对象,只需要通过配置好的id就可以让Spring来帮我们创建对象,而且默认创建的对象都是单例的,我们可以来测试一下:
package org.zero01.test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
import org.zero01.dao.DAO;
public class Test {
public static void main(String[] args) {
// 传入Spring配置文件的路径
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
// 传入配置文件里配置的id
DAO studentDAO1 = (DAO) beanFactory.getBean("studentDAO");
DAO studentDAO2 = (DAO) beanFactory.getBean("studentDAO");
System.out.println(studentDAO1 == studentDAO2);
}
}
运行结果:
This is StudentDAO class
true
从运行结果可以看到,构造器只被执行了一次,而且这两个对象的内存地址也是一样的,所以比较运算的结果值为true。
如果你不希望创建的对象是单例的话,可以在配置文件里将scope属性的值设置为prototype:
<!-- scope属性有两个值,分别是prototype和singleton,默认值为singleton -->
<bean class="org.zero01.dao.StudentDAO" id="studentDAO" scope="prototype" />
这时候每次创建的就是新对象了,测试代码和之前一样,略。运行结果如下:
This is StudentDAO class
This is StudentDAO class
false
从运行结果可以看到,构造器被执行了两次,证明对象被重复实例化了。
以上我们提到了Spring的两大管理对象,但是目前我们只用到了其中一个BeanFactory对象,所以接下来要介绍的是另一个ApplicationContext 对象的使用方式,以及它们之间的区别,首先是最简单的创建对象:
package org.zero01.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.zero01.dao.DAO;
public class Test2 {
public static void main(String[] args) {
// 传入Spring配置文件的路径
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
// 传入配置文件里配置的id
DAO studentDAO = (DAO) app.getBean("studentDAO");
}
}
BeanFactory 与 ApplicationContext 的一些主要区别:
从代码上可以看到,两个管理对象的使用方式基本上都是差不多的,只不过我们现在一般都是用 ApplicationContext 对象来作为管理对象,BeanFactory 现在不常用了,它的实现类 XmlBeanFactory 都已经属于淘汰了,因为BeanFacotry是Spring中比较原始的Factory,原始的BeanFactory无法支持Spring的许多插件,如AOP功能、Web应用等。
ApplicationContext接口,它由BeanFactory接口派生而来,因而提供BeanFactory所有的功能。ApplicationContext以一种更向面向框架的方式工作以及对上下文进行分层和实现继承。ApplicationContext包还提供了以下的功能:
- MessageSource, 提供国际化的消息访问
- 资源访问,如URL和文件
- 事件传播
- 载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层
BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。
除此之外在对象加载上也有区别,BeanFactroy采用的是延迟加载形式来注入Bean的,所谓的Bean也就是我们交给Spring管理的类,即只有在使用到某个Bean时 ( 调用getBean( ) ) ,才对该Bean进行加载实例化,这样,我们就不能发现一些存在的Spring的配置问题。
而ApplicationContext则相反,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中所存在的配置错误。
这一点我们可以写测试代码来进行验证:
package org.zero01.test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
public class Test {
public static void main(String[] args) {
// 传入Spring配置文件的路径
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
}
}
运行以上这段代码,控制台没有任何的输出。
这次我们换成 ApplicationContext 试试:
package org.zero01.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
// 传入Spring配置文件的路径
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
}
}
运行以上这段代码,控制台输出如下:
This is StudentDAO class
从控制台的输出,就可以知道StudentDAO的构造器被调用了,也就是说这个类被实例化了,从这个实验可以得知在使用ApplicationContext 时容器启动后就会实例化配置文件中配置的类。而 BeanFactory 则不会,它只有在使用某个类的时候才会进行实例化,这个行为称为延迟加载。
配置简单的依赖注入
Spring的 IOC 控制反转模块中包含依赖注入的功能,让我们可以在创建某个对象时就一并将该对象所依赖的所有对象都创建好,所谓的依赖注入就是如此,例如StudentDAO中包含了一个Student属性:
package org.zero01.dao;
import org.zero01.pojo.Student;
public class StudentDAO implements DAO {
private Student student;
public StudentDAO() {
System.out.println("This is StudentDAO class");
}
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
......
如果我们希望 StudentDAO 对象创建时就一并把它所依赖的Student对象也一并实例化出来给它,这就需要在配置文件中修改配置内容如下:
<bean class="org.zero01.pojo.Student" id="student"></bean>
<bean class="org.zero01.dao.StudentDAO" id="studentDAO">
<property name="student" ref="student"></property>
</bean>
测试代码如下:
package org.zero01.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.zero01.dao.StudentDAO;
public class Test2 {
public static void main(String[] args) {
// 传入Spring配置文件的路径
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
// 传入配置文件里配置的id
StudentDAO studentDAO = (StudentDAO) app.getBean("studentDAO");
// 访问StudentDAO所依赖的Student对象
studentDAO.getStudent().setName("小明");
System.out.println(studentDAO.getStudent().getName());
}
}
运行结果:
This is StudentDAO class
小明
从测试代码中可以看到,我们只从Spring容器中取出了StudentDAO 对象,并没有取出 Student 对象,更没有把这个对象赋值给 StudentDAO 对象中的 student 属性。但是由于我们在配置文件中配置了这个依赖对象,所以在 StudentDAO 对象实例化时Spring容器就帮我们把这个依赖的Student对象创建好了,并且把它赋值给了StudentDAO 对象中的 student 属性,这个过程就是依赖注入,但是依赖注入不单止能注入对象,其他类型的数据都能进行注入。
以上是关于初识Spring的主要内容,如果未能解决你的问题,请参考以下文章
初识OpenGL 片段着色器(Fragment Shader)