Spring简介
Spring是什么
Spring是分层的Java SE/EE应用full-stack轻量级开源框架,以IoC(Inverse Of Control:反转控制)和AOP(Aspect Oriented Programming:面向切面编程)为内核。
提供了展现层 SpringMVC 和持久层Spring JDBCTemplate 以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的Java EE 企业应用开源框架。
Spring的优势
- 方便解耦,简化开发
- 通过 Spring 提供的IoC容器,可以将对象间的依赖关系交由 Spring 进行控制,避免硬编码所造成的过度耦合。用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。
- AOP 编程的支持
- 通过 Spring的 AOP 功能,方便进行面向切面编程,许多不容易用传统 OOP 实现的功能可以通过 AOP 轻松实现。
- 声明式事务的支持
- 可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务管理,提高开发效率和质量。
- 方便程序的测试
- 可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情
- 方便集成各种优秀框架
- Spring对各种优秀框架(Struts、Hibernate、Hessian、Quartz等)的支持。
- 降低 JavaEE API 的使用难度
- Spring对 JavaEE API(如 JDBC、JavaMail、远程调用等)进行了薄薄的封装层,使这些 API 的使用难度大为降低。
- Java 源码是经典学习范例
- Spring的源代码设计精妙、结构清晰、匠心独用,处处体现着大师对Java 设计模式灵活运用以及对 Java技术的高深造诣。它的源代码无意是 Java 技术的最佳实践的范例。
Spring的体系结构
Spring入门
Spring程序开发步骤
之前开发是在业务层调用DAO层来new
对象,现在借助Spring框架来实现解耦合:先将DAO配置到xml
文件中,然后通过Spring框架来读取xml
配置文件,最后在业务层通过Spring提供的API来得到对象。
简而言之,就是将对象的创建权交给了Spring。
- 导入 Spring 框架对应的jar包
- 编写 Dao 接口和实现类
- 创建 xml 配置文件
- 在 xml 配置文件中配置 UserDaoImpl
- 使用 Spring 的 API 获得 Bean 实例
解耦的意义:当业务对象改变时,只需要改变xml
配置文件,而不需要修改业务层代码
使用IDEA实现Sping解耦
创建javaweb项目
借助Maven来进行项目构建和依赖管理。不会使用Maven的小伙伴,可以看我之前的Maven笔记:传送门
这里不做赘述
导入Spring框架对应的jar包
借助Maven来进行依赖管理,在pom.xml
文件中写入spring开发的基本包坐标
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.5.RELEASE</version> </dependency> </dependencies>
编写Dao接口和实现类
在main\java
下新建UserDao
接口类
package com.wyatt.dao; public interface UserDao { public void save(); }
在com.wyatt.dao
下新建UserDaoImpl
实现类
package com.wyatt.dao.impl; import com.wyatt.dao.UserDao; public class UserDaoImpl implements UserDao { public void save() { System.out.println("save running..."); } }
在 xml 配置文件中配置 UserDaoImpl
在main\resources
下新建xml
配置文件,取名叫applicationContext
<?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="userDao" class="com.wyatt.dao.impl.UserDaoImpl"></bean> </beans>
使用 Spring 的 API 获得 Bean 实例
在main\java
中新建一个测试类UserDaoDemo
,在该类的方法中使用 Spring 的 API 来获得 Bean 实例
package com.wyatt.demo; import com.wyatt.dao.UserDao; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class UserDaoDemo { public static void main(String[] args) { ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml"); UserDao userDao = (UserDao) app.getBean("userDao"); userDao.save(); } }
在控制台可以看到打印save running...
,说明通过Spring框架提供的API调用UserDao
对象的save()
方法成功。
Spring配置文件
Bean标签基本配置
用于将配置对象交由Spring来创建。
默认情况下它调用的是类中的无参构造函数,如果没有无参构造函数则不能创建成功。
基本属性:
id
:Bean实例在Spring容器中的唯一标识class
:Bean的全限定名称
<bean id="userDao" class="com.wyatt.dao.impl.UserDaoImpl"></bean>
Bean标签范围配置
scope
:指对象的作用范围,取值如下:
取值范围 | 说明 |
singleton | 默认值,单例的 |
prototype | 多例的 |
request | WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中 |
session | WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中 |
global session | WEB 项目中,应用在 Portlet 环境,如果没有 Portlet 环境那么globalSession 相当于 session |
验证singleton
在src\test\java
下新建测试类SpringTest
借助junit
来进行单元测试,在pom.xml
中引入junit
的相关依赖
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency>
在SpringTest
测试类中编写一个测试方法test1()
测试singleton
的作用范围
public class SpringTest { @Test public void test1(){ // 获取Spring客户端 ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml"); // 调用Spring客户端的getBean(id标识)方法 UserDao userDao1 = (UserDao) app.getBean("userDao"); UserDao userDao2 = (UserDao) app.getBean("userDao"); System.out.println(userDao1); System.out.println(userDao2); } }
从控制台的打印结果可以看出,两次getBean()
获得的对象地址相同,是同一个对象。这意味如果Bean
的作用范围配置为singleton
,则在Spring容器中该Bean
对象只有一个。
验证prototype
在xml
配置文件中配置Bean
作用范围为prototype
<bean id="userDao" class="com.wyatt.dao.impl.UserDaoImpl" scope="prototype"></bean>
上面验证singleton
的代码不变,重新执行
从控制台的打印结果可以看出,两次getBean()
获得的对象地址不同,不是同一个对象。这意味如果Bean
的作用范围配置为prototype
,则在Spring容器中该Bean
对象可以有多个。
小结
当scope的取值为singleton时
- Bean的实例化个数:1个
- Bean的实例化时机:当Spring核心文件被加载时,实例化配置的Bean实例
- Bean的生命周期:
- 对象创建:当应用加载,创建容器时,对象就被创建了
- 对象运行:只要容器在,对象一直活着
- 对象销毁:当应用卸载,销毁容器时,对象就被销毁了
当scope的取值为prototype时
- Bean的实例化个数:多个
- Bean的实例化时机:当调用
getBean()
方法时实例化Bean
- 对象创建:当使用对象时,创建新的对象实例
- 对象运行:只要对象在使用中,就一直活着
- 对象销毁:当对象长时间不用时,被 Java 的垃圾回收器回收了
Bean生命周期配置
init-method
:指定类中的初始化方法名称destroy-method
:指定类中销毁方法名
Bean实例化三种方式
- 无参构造方法实例化
- 工厂静态方法实例化
- 工厂实例方法实例化
工厂是一个java类,专门用来生产对象
Bean的依赖注入入门
之前的实践都是借助Spring在Dao层产生对象,而在实际开发中,业务层和web层也需要产生对象。
在main\java\com\wyatt
下新建业务层中的UserService
接口
package com.wyatt.service; public interface UserService { public void getDaoObject(); }
在main\java\com\wyatt\service
下创建接口的实现类UserServiceImpl
在业务层中通过Spring获取dao层对象,并调用它的save()
方法
public class UserServiceImpl implements UserService { public void getDaoObject() { // 获取Spring客户端 ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml"); // 调用Spring客户端的getBean(id标识)方法 UserDao userDao = (UserDao) app.getBean("userDao"); userDao.save(); } }
在main\java\com\wyatt\demo
中新建一个模拟web层中的类UserController
在web层调用业务层来获取dao层对象
public class UserController { public static void main(String[] args) { UserService userService = new UserServiceImpl(); userService.getDaoObject(); } }
更进一步,将业务层的对象也配置到xml
文件中
<bean id="userService" class="com.wyatt.service.impl.UserServiceImpl"></bean>
在web层借助Spring来获取service层对象,并调用service层对象的getDaoObject()
方法,在service层借助Spring来获取dao层对象,并调用dao层对象的save()
方法
public class UserController { public static void main(String[] args) { ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml"); UserService userService = (UserService) app.getBean("userService"); userService.getDaoObject(); } }
Bean的依赖注入分析
上例中的UserService实例和UserDao实例都存在于Spring容器中,当前的做法是在容器外部获得UserService实例和UserDao实例,然后在容器外部将Dao组装到Service中。
因为UserService和UserDao都在Spring容器中,而web层最终程序直接使用的是UserService,所以可以在Spring容器中,将UserDao设置到UserService内部。
Bean的依赖注入概念
依赖注入(Dependency Injection):它是 Spring 框架核心 IOC 的具体实现。
在编写程序时,通过控制反转,把对象的创建交给了 Spring,但是代码中不可能出现没有依赖的情况。IOC 解耦只是降低他们的依赖关系,但不会消除。例如:业务层仍会调用dao层的方法。
那这种业务层和dao层的依赖关系,在使用 Spring 之后,就让 Spring 来维护了。简单的说,就是坐等框架把dao层对象传入业务层,而不用我们自己去获取。
Bean的依赖注入方式
将UserDao注入到UserService内部
- 构造方法
- set方法
set方法注入
- 在
UserServiceImpl
内部创建一个UserDao
属性,设置该属性的set()
方法。 - 因为在依赖注入后,Spring容器内部会自动把dao层对象传入业务层,而不用我们自己去获取,所以删除业务层手动获取dao对象的语句。
public class UserServiceImpl implements UserService { private UserDao userDao; public void setUserDao(UserDao userDao) { this.userDao = userDao; } public void getDaoObject() { userDao.save(); } }
在xml
配置文件中进行依赖注入,告诉Spring各个对象间的依赖关系
<bean id="userDao" class="com.wyatt.dao.impl.UserDaoImpl"></bean> <bean id="userService" class="com.wyatt.service.impl.UserServiceImpl"> <property name="userDao" ref="userDao"></property> </bean>
构造方法注入
在UserServiceImpl
内写一个有参构造方法和一个无参构造方法
public class UserServiceImpl implements UserService { private UserDao userDao; public UserServiceImpl(UserDao userDao) { this.userDao = userDao; } public UserServiceImpl() { } public void getDaoObject() { userDao.save(); } }
在xml
配置文件中进行依赖注入,告诉Spring各个对象间的依赖关系
<bean id="userDao" class="com.wyatt.dao.impl.UserDaoImpl"></bean> <bean id="userService" class="com.wyatt.service.impl.UserServiceImpl"> <constructor-arg name="userDao" ref="userDao"></constructor-arg> </bean>
Bean依赖注入的数据类型
上面的操作,都是注入的引用Bean,除了对象的引用可以注入,普通数据类型,集合等都可以在容器中进行注入。
可以注入三种数据类型
- 引用数据类型
- 普通数据类型
- 集合数据类型
之前的操作都是对UserDao
对象的引用进行注入的。下面将以set
方法注入为例,演示普通数据类型和集合数据类型的注入。
普通数据类型的注入
public class UserDaoImpl implements UserDao { private String username; private int age; public void setUsername(String username) { this.username = username; } public void setAge(int age) { this.age = age; } public void save() { System.out.println(username+"===="+age); System.out.println("save running..."); } }
<bean id="userDao" class="com.wyatt.dao.impl.UserDaoImpl"> <property name="username" value="wyatt"></property> <property name="age" value="20"></property> </bean>
集合数据类型的注入
方法大体同上例,不再赘述。具体代码可以在我的GitHub仓库中查看。
引入其他的配置文件
实际开发中,Spring的配置内容非常多,这就导致Spring配置很繁杂且体积很大,所以,可以将部分配置拆解到其他配置文件中,而在Spring主配置文件通过import
标签进行加载。
<import resource="applicationContext-xxx.xml"/>
Spring配置文件小结
<bean>标签 id属性:在容器中Bean实例的唯一标识,不允许重复 class属性:要实例化的Bean的全限定名 scope属性:Bean的作用范围,常用是Singleton(默认)和prototype <property>标签:属性注入 name属性:属性名称 value属性:注入的普通属性值 ref属性:注入的对象引用值 <list>标签 <map>标签 <properties>标签 <constructor-arg>标签 <import>标签:导入其他的Spring的分文件
Spring相关API
ApplicationContext的继承体系
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
ApplicationContext
:接口类型,代表应用上下文,可以通过其实例获得 Spring 容器中的 Bean 对象
ApplicationContext的三个实现类
ClassPathXmlApplicationContext
:它是从类的根路径下加载配置文件,推荐使用这种。FileSystemXmlApplicationContext
:它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。AnnotationConfigApplicationContext
:当使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解。
getBean方法的使用
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); // 方法1 容器中的一个全限定名可以有多个Bean UserService userService1 = (UserService) applicationContext.getBean("userService");// id标识 // 方法2 容器中的一个全限定名只能有一个Bean UserService userService2 = applicationContext.getBean(UserService.class); // 对象类型