六、Spring框架
6.1 Spring框架的概念
Spring 是众多开源java项目中的一员,基于分层的javaEE应用一站式轻量级开源框架,主要核心是 IOC(控制反转/依赖注入)与 AOP(面向切面)两大技术,实现项目在开发过程中的轻松解耦,提高项 目的开发效率。
Spring框架是一个开放源代码的J2EE应用程序框架,由[Rod Johnson](https://baike.baidu.com/item/Rod Johnson/1423612)发起,是针对bean的生命周期进行管理的轻量级容器(lightweight container)。 Spring解决了开发者在J2EE开发中遇到的许多常见的问题,提供了功能强大IOC、AOP及Web MVC等功能。Spring可以单独应用于构筑应用程序,也可以和Struts、Webwork、Tapestry等众多Web框架组合使用,并且可以与 Swing等桌面应用程序AP组合。因此, Spring不仅仅能应用于J2EE应用程序之中,也可以应用于桌面应用程序以及小应用程序之中。Spring框架主要由七部分组成,分别是 Spring Core、 Spring AOP、 Spring ORM、 Spring DAO、Spring Context、 Spring Web和 Spring Web MVC。
- 特点:
- 分布式
- 轻量级
- 开源
6.1.2 Spring框架的搭建
- 添加 Spring 框架的依赖坐标
Maven仓库:https://mvnrepository.com/
-
编写 Bean 对象
package com.yjxxt.service; public class UserService { public void test(){ System.out.println("Hello Spring!"); } }
-
添加 Spring 配置文件
-
在项目的src下创建文件夹 resources(Alt+insert)
-
将 resources 标记为资源目录
-
在 src\main\resources 目录下新建 spring.xml 文件,并拷贝官网文档提供的模板内容到 xml 中。配置 bean 到 xml 中,把对应 bean 纳入到 Spring 容器来管理spring.xml
<?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 https://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- xmlns 即 xml namespace xml使用的命名空间 xmlns:xsi 即xml schema instance xml 遵守的具体规范 xsi:schemaLocation 本文档xml遵守的规范 官方指定 --> </beans>
- 在 spring.xml 中配置 Bean 对象
<!-- id:bean对象的id,唯一标识。一般是Bean对象的名称的首字母小写 class:bean对象的类路径 --> <bean id="userService" class="com.xxxx.service.UserService"> </bean>
-
-
加载配置文件,获取实例化对象
public class App { public static void main(String[] args) { // 获取Spring上下文环境 (加载配置文件) ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml"); // 通过getBean方法得到Spring容器中实例化好的Bean对象 (实例化Bean对象) // userService代表的是配置文件中bean标签的id属性值 UserService userService = (UserService) ac.getBean("userService"); // 调用方法 (使用实例化对象) userService.test(); } }
6.2 Spring IOC容器 Bean 对象实例化
6.2.1 构造器实例化
-
步骤:
-
设置配置文件 spring.xml
<bean id="userService" class="com.xxxx.service.UserService"> </bean>
-
获取实例化对象
ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml"); UserService userService = (UserService) ac.getBean("userService"); userService.test();
-
-
注意:构造器实例化是通过默认构造器创建的, 空构造方法必须存在 ,否则创建失败,或者必须按照构造器中给定的参数来写
6.2.2 静态工厂实例化(了解)
当我们指定Spring使用静态工厂方法来创建Bean实例时,Spring将先解析配置文件,并根据配置文件指定的信息,通过反射调用静态工厂类的静态工厂方法,并将该静态工厂方法的返回值作为Bean实例**,在这个过程中,Spring不再负责创建Bean实例,** Bean 实例是由用户提供的静态工厂方法提供的。
-
步骤:
-
定义静态工厂类
package com.xxxx.factory; import com.xxxx.service.UserService; /*** 定义静态工厂类 */ public class StaticFactory { /*** 定义对应的静态方法,返回实例化对象 * @return */ public static UserService createUserService() { return new UserService(); } }
-
设置配置文件 spring.xml
<!--静态工厂--> <bean id="userService" class="com.xxxx.factory.StaticFactory" factory- method="createUserService"> </bean>
-
获取实例化对象
ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml"); UserService userService = (UserService) ac.getBean("userService"); userService.test();
-
-
注意:
- 要有该工厂类及工厂方法
- 工厂方法为静态的
6.2.3 实例化工厂实例化(了解)
6.2.4 Spring三种实例化Bean的方式比较
-
方式一:通过bean的缺省构造函数创建,
- 当各个bean的业务逻辑相互比较独立的时候或者和外界关联较少的时候可以使用。
-
方式二:利用静态factory方法创建,
- 可以统一管理各个bean的创建,如各个bean在创建之前需要相同的初始化处理,则可用这个factory方法险进行统一的处理等等。
-
方式三:利用实例化factory方法创建,
- 即将factory方法也作为了业务bean来控制,1可用于集成其他框架的bean创建管理方法,2能够使bean和factory的角色互换。
-
注意:
开发中项目一般使用一种方式实例化bean,项目开发基本采用第一种方式,交给Spring托管,使用时直接拿来使用即可。另外两种了解
6.3 Spring IOC 注入
6.3.1 Spring IOC 手动装配(注入)
Spring 支持的注入方式共有四种:set 注入、构造器注入、静态工厂注入、实例化工厂注入。
1. set 方法注入
-
对于JavaBean的实现步骤
-
属性字段提供set方法
public class UserService { // 业务对象UserDao set注入(提供set方法) private UserDao userDao; public void setUserDao(UserDao userDao) { this.userDao = userDao; } }
-
配置spring.xml文件的bean标签设置property标签
<!-- IOC通过property标签手动装配(注入): Set方法注入 name:bean对象中属性字段的名称 ref:指定bean标签的id属性值 name:bean对象中属性字段的名称 value:具体的值(基本类型 常用对象|日期 集合) --> <bean id="userDao" class="com.xxxx.dao.UserDao"> </bean> <bean id="userService" class="com.xxxx.service.UserService"> <!--业务对象 注入--> <property name="userDao" ref="userDao"/> </bean>
-
-
对于常用对象和基本类型的实现步骤
-
属性字段提供set方法
public class UserService { // 常用对象String set注入(提供set方法) private String host; public void setHost(String host) { this.host = host; } // 基本类型Integer set注入(提供set方法) private Integer port; public void setPort(Integer port) { this.port = port; } }
-
配置spring.xml文件的bean标签设置property标签
<!-- IOC通过property标签手动装配(注入): Set方法注入 name:bean对象中属性字段的名称 value:具体的值(基本类型 常用对象|日期 集合) --> <bean id="userService" class="com.xxxx.service.UserService"> <!--常用对象String 注入--> <property name="host" value="127.0.0.1"/> <!--基本类型注入--> <property name="port" value="8080"/> </bean>
-
-
集合类型和属性对象
-
属性字段提供set方法
public class UserService { // List集合 set注入(提供set方法) public List<String> list; public void setList(List<String> list) { this.list = list; } public void printList(){ list.forEach(k -> System.out.println(k)); } // Map set注入(提供set方法) private Map<String,Object> map; public void setMap(Map<String, Object> map) { this.map = map; } public void printMap(){ map.forEach((k,v) -> System.out.println(k+v)); } // Properties set注入(提供set方法) private Properties properties; public void setProperties(Properties properties) { this.properties = properties; } public void printProperties(){ properties.forEach((k,v) -> System.out.println(k+"----"+v)); } }
-
配置spring.xml文件的bean标签设置property标签
<bean id="userService" class="com.xxxx.service.UserService" > <property name="userDao" ref="userDao"></property> <property name="port" value="8081"></property> <property name="name" value="张三"></property> <!--注入 List--> <property name="list"> <list> <value>123</value> <value>456</value> <value>789</value> </list> </property> <!--注入 Map--> <property name="map"> <map> <entry> <key><value>卢本伟</value></key> <value>五五开</value> </entry> <entry> <key><value>PDD</value></key> <value>拼到底</value> </entry> </map> </property> <!--注入 properties--> <property name="properties"> <props> <prop key="UZI">永远的神</prop> <prop key="麻辣香锅">莽夫</prop> </props> </property> </bean>
-
2.构造器注入
提供带参构造器
3.静态工厂注入
4.实例化工厂注入
6.3.2 Spring IOC 自动装配(注入)
注解方式注入 Bean
对于 bean 的注入,除了使用 xml 配置以外,可以使用注解配置。注解的配置,可以简化配置文件,提高开发的速度,使程序看上去更简洁。对于注解的解释,Spring对于注解有专门的解释器,对定义的注解进行解析,实现对应bean对象的注入。通过反射技术实现。
1. @Resource注解
- 实现步骤
-
修改配置文件
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
-
开启自动化注入
通过<context:annotation-config/>
标签来实现自动化注入
<!--开启自动化装配(注入)-->
<context:annotation-config/>
<bean id="userDao" class="com.yjxxt.dao.UserDao"></bean>
<bean id="userService" class="com.yjxxt.service.UserService"></bean>
-
给注入的bean对象添加注解
有两种添加注解的方式,一种是在bean对象的属性前加注解
@Resource
,另一种是加注解@Autowired
通过注解实现自动注入(反射)
-
注意:
-
默认根据属性字段名称查找对应的bean对象 (属性字段的名称与bean标签的id属性值相等)
-
如果属性字段名称未找到,则会通过类型(Class类型)查找
-
属性可以提供set方法,也可以不提供set方法
-
注解可以声明在属性级别 或 set方法级别
-
可以设置name属性,name属性值必须与bean标签的id属性值一致;如果设置了name属性值,就只会按照name属性值查找bean对象 (如下)
public class UserService { @Resource(name = "userDao") // name属性值与配置文件中bean标签的id属性值一致 private UserDao ud; public void test() { // 调用UserDao的方法 ud.test(); } }
-
当注入接口时,如果接口只有一个实现则正常实例化;如果接口存在多个实现,则需要使用name属性指定需要被实例化的bean对象
-
2. @Autowired注解
-
默认通过类型(Class类型)查找bean对象 与属性字段的名称无关
-
属性可以提供set方法,也可以不提供set方法
-
注解可以声明在属性级别 或 set方法级别
-
可以添加@Qualifier结合使用,通过value属性值查找bean对象(value属性值必须要设置,且值要与bean标签的id属性值对应)
6.3.3 Spring IOC 扫描器
-
介绍:
实际的开发中,bean的数量非常多,采用手动配置bean的方式已无法满足生产需要,Spring这时候同样提供了扫描的方式,对扫描到的bean对象统一进行管理,简化开发配置,提高开发效率。
-
作用:
bean对象统一进行管理,简化开发配置,提高开发效率 ,实现bean对象的自动配置
-
实现步骤:
-
设置自动化扫描的范围
如果bean对象未在指定包范围,即使声明了注解,也无法实例化
-
在声明类的上方使用指定的注解
bean对象的id属性默认是 类的首字母小写
-
-
不同类级别的声明规定
- Dao层:@Repository
- Service层: @Service
- Controller层: @Controller
- 任意类: @Component
6.4 Bean的作用域与生命周期
6.4.1 Bean的作用域
默认情况下,我们从Spring容器中拿到的对象均是单例的,对于bean的作用域类型如下:
1. singleton 作用域
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T9fjcKpq-1650899661295)(JavaEE.assets/1650862223345.png)]
注意: lazy-init是懒加载,如果等于true时作用是指Spring容器启动的时候不会去实例化这个bean, 而是在程序调用时才去实例化,默认是false即Spring容器启动时实例化。
-
lazy-init属性(懒加载)
如果为false,则在IOC容器启动时会实例化bean对象,默认false
如果为true,则IOC容器启动时不会实例化Bean对象,在使用bean对象时才会实例化
lazy-init设置为alse有什么好处?
1)可以提前发现潜在的配置问题
2)Bean 对象存在于缓存中,使用时不用再去实例化bean,加快程序运行效率
2. prototype 作用域
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hyidnbsP-1650899661297)(JavaEE.assets/1650862548626.png)]
通过scope=“prototype” 设置bean的类型 ,每次向Spring容器请求获取Bean都返回一个全新的Bean,相对于"singleton"来说就是不缓存Bean,每次都是一个根据Bean定义创建的全新Bean。
3. Web 应用中的作用域
-
request 作用域
表示每个请求需要容器创建一个全新Bean。比如提交表单的数据必须是对每次请求新建一个Bean
来保持这些表单数据,请求结束释放这些数据。
-
session 作用域
表示每个会话需要容器创建一个全新Bean。比如对于每个用户一般会有一个会话,该用户的用户
信息需要存储到会话中,此时可以将该Bean作用域配置为session级别。
-
globalSession 作用域
类似于session作用域,其用于portlet(Portlet是基于Java的Web组件,由Portlet容器管理,并由容
器处理请求,生产动态内容)环境的web应用。如果在非portlet环境将视为session作用域。
注意:
- 配置方式和基本的作用域相同,只是必须要有web环境支持,并配置相应的容器监听器或拦截器从而能应用这些作用域,目前先熟悉概念,后续集成web时讲解具体使用,大家只需要知道有这些作用域就可以了。
6.4.2 Bean的生命周期
对比已经学过的servlet 生命周期(容器启动装载并实例化servlet类,初始化servlet,调用service方法,销毁servlet)。
同样对于Spring容器管理的bean也存在生命周期的概念
在Spring中,Bean的生命周期包括Bean的定义、初始化、使用和销毁4个阶段
1 .Bean 的定义
在Spring中,通常是通过配置文档的方式来定义Bean的。
在一个配置文档中,可以定义多个Bean。
2. Bean 的初始化
默认在IOC容器加载时,实例化对象。
Spring bean 初始化有两种方式:
-
方式一:
-
在配置文档中通过指定 init-method 属性来完成
public class RoleService { // 定义初始化时需要被调用的方法 public void init() { System.out.println("RoleService init..."); } }
<!-- 通过init-method属性指定方法 --> <bean id="roleService" class="com.yjxxt.service.RoleService" init-method="init"> </bean>
-
-
方式二:
-
实现 org.springframework.beans.factory.InitializingBean 接口。
public class RoleService implements InitializingBean { @Override public void afterPropertiesSet() throws Exception { System.out.println("RoleService init..."); } }
<bean id="roleService" class="com.yjxxt.service.RoleService" ></bean>
Bean对象实例化过程是在Spring容器初始化时被实例化的,但也不是不可改变的,可以通过 lazy-init=“true” 属性延迟bean对象的初始化操作,此时再调用getBean 方法时才会进行bean的初始化操作
-
3. Bean 的使用
-
方式一:
-
使用 BeanFactory
// 得到Spring的上下文环境 BeanFactory factory = new ClassPathXmlApplicationContext("spring.xml"); RoleService roleService = (RoleService) factory.getBean("roleService");
-
-
方式二:
-
使用 ApplicationContext
// 得到Spring的上下文环境 ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml"); RoleService roleService = (RoleService) ac.getBean("roleService");
-
4. Bean 的销毁
实现销毁方式(Spring容器会维护bean对象的管理,可以指定bean对象的销毁所要执行的方法)。
-
步骤一:
-
实现销毁方式(Spring容器会维护bean对象的管理,可以指定bean对象的销毁所要执行的方法)
<bean id="roleService" class="com.yjxxt.service.RoleService" destroy- method="destroy"> </bean>
-
-
步骤二:
-
通过 AbstractApplicationContext 对象,调用其close方法实现bean的销毁过程
AbstractApplicationContext ctx=new ClassPathXmlApplicationContext("spring.xml"); ctx.close();
-
-
IOC/DI-控制反转和依赖注入
将对象实例化的创建过程转交给外部容器(IOC容器 充当工厂角色)去负责;属性赋值的操作;