前言
本章概括
这部分文章主要是用于SSM框架的复习笔记,对于SSM框架的视频博主看的是黑马的SSM框架视频,在SSM的学习中个人感觉重点在于SpringBoot,Mybatis-Plus这两个部分的学习,所以想要快速复习SSM的小伙伴可以直接阅读关于这两个部分的文章SSM学习目录
- Spring(一)-SSM框架(了解即可(๑・̀ㅂ・́)و✧ ) 点击我查看(当前位置)
- Spring(二)-SSM框架(了解即可ヾ (≧▽≦*) o) 点击我查看
- Spring(三)-SSM框架(了解即可(^▽^)) 点击我查看
- SpringMVC-SSM框架(~(〃∀`)ノ) 点击我查看
Spring框架的主要优势在于简化开发和框架整合上.
简化开发: Spring框架中提供了两个核心技术来实现
IOC(控制反转)AOP(面向切面编程)
IOC(控制反转)是Spring框架的核心功能之一,它允许开发人员将应用程序的依赖关系从代码中分离出来,并使用配置文件来管理这些依赖关系.
AOP(面向切面编程)是Spring框架的另一个核心功能,它允许开发人员将横切关注点(如日志记录,事务管理等)从业务逻辑中分离出来,并使用配置文件来管理这些横切关注点.
框架整合: Spring框架可以与许多其他框架和技术进行整合,包括
- MyBatis
- MyBatis Plus
- Spring MVC
- Spring Security
- Spring Boot
- Spring Cloud
- …….
Spring概念
Spring框架
Spring框架是一个开源的Java应用程序框架,它提供了一种轻量级的开发方式,用于开发企业级应用程序.
学习Spring框架主要是学习Sping Framework,Spring Boot,Spring Cloud等技术.
- Spring Framework: Spring Framework是一个开源的Java应用程序框架,它提供了一组用于构建企业级应用程序的组件和工具,是Spring的基座.
- Spring Boot: Spring Boot是一个用于创建独立的,基于Spring的应用程序的简化框架,能更加快速的进行开发.
- Spring Cloud: Spring Cloud是一个用于构建分布式系统的框架,主要用于微服务的开发.
除了上面的这三个技术外,还有很多其他的技术,也比较流行,如SpringData,SpringSecurity等,这些都可以被应用在我们的项目中,我们所学习的Spring其实指的是Spring Framework.
Spring Framework主要包括
核心层(Core Container):核心容器,这个模块是Spring最核心的模块,其他的都需要依赖该模块AOP层(切面编程):这个模块主要是用于实现AOP编程,Aspects是AOP的实现数据访问层(Data Access Object):这个模块主要是用于实现数据访问,包括JDBC,ORM,事务管理等Web层(Web):这个模块主要是用于实现Web开发,包括MVC,REST等集成层(Integration):这个模块主要是用于集成其他框架,如MyBatis,Quartz等测试层(Test):这个模块主要是用于测试,包括单元测试,集成测试等
IOC(控制反转)
IOC(控制反转)是Spring框架的核心功能之一,它允许开发人员将应用程序的依赖关系从代码中分离出来,并使用配置文件来管理这些依赖关系.
IOC的主要思想是将对象的创建和管理交给Spring容器(IOC容器)来完成,这样开发人员就不需要自己创建对象,也不需要自己管理对象的生命周期.
IOC容器是一个对象容器,它负责创建对象,管理对象生命周期,并注入对象之间的依赖关系.
IOC容器的主要优点是
- 降低了代码的耦合度,使得代码更加灵活,易于维护
- 提高了代码的可测试性,因为对象的创建和管理由IOC容器来完成,可以通过模拟对象来进行测试
- 提高了代码的可扩展性,因为对象的创建和管理由IOC容器来完成,可以通过配置文件来进行扩展
IOC容器的主要实现方式有两种
- 基于XML配置文件的实现方式
- 基于注解的实现方式
- 基于XML配置文件的实现方式是将对象的创建和管理交给Spring容器来完成,Spring容器会读取配置文件中的配置信息,并根据配置信息来创建对象,管理对象的生命周期,并注入对象之间的依赖关系.例如:
查看代码
1.定义了一个接口HelloWorld及其实现类HelloWorldImpl
2.在applicationContext.xml中配置 Bean 和属性注入
3.通过ClassPathXmlApplicationContext加载配置文件并获取 Bean
4.调用 Bean 的方法输出结果1
2
3
4
5package com.example.demo;
public interface HelloWorld {
void sayHello();
}
1 | package com.example.demo; |
1 |
|
1 | package com.example.demo; |
- 基于注解的实现方式是将对象的创建和管理交给Spring容器来完成,Spring容器会扫描指定的包,并根据注解来创建对象,管理对象的生命周期,并注入对象之间的依赖关系.例如:
查看代码
1.使用@Component注解标记实现类,使其被 Spring 自动发现
2.通过@Configuration和@ComponentScan注解配置组件扫描
3.使用AnnotationConfigApplicationContext加载 Java 配置类
4.从容器中获取 Bean 并调用方法1
2
3
4
5package com.example.demo;
public interface HelloWorld {
void sayHello();
}
1 | package com.example.demo; |
1 | package com.example.demo; |
1 | package com.example.demo; |
DI(依赖注入)
DI(依赖注入)是Spring框架的另一个核心功能,它允许开发人员将对象之间的依赖关系从代码中分离出来,并使用配置文件来管理这些依赖关系.
DI的主要思想是将对象的创建和管理交给Spring容器(IOC容器)来完成,这样开发人员就不需要自己创建对象,也不需要自己管理对象的生命周期.
DI的主要实现方式有两种
- 基于XML配置文件的实现方式
- 基于注解的实现方式
- 基于XML配置文件的实现方式是将对象的创建和管理交给Spring容器来完成,Spring容器会读取配置文件中的配置信息,并根据配置信息来创建对象,管理对象的生命周期,并注入对象之间的依赖关系.例如:
查看代码
1.定义了MessageService接口及其实现类EmailService
2.创建了MessageConsumer类,支持构造器注入和 Setter 注入
3.在 XML 配置文件中:
定义了emailService Bean
使用
使用
从容器中获取不同方式注入的 Bean 并调用方法1
2
3
4
5package com.example.service;
public interface MessageService {
String getMessage();
}
1 | package com.example.service; |
1 | package com.example.consumer; |
1 |
|
1 | package com.example; |
- 基于注解的实现方式是将对象的创建和管理交给Spring容器来完成,Spring容器会扫描指定的包,并根据注解来创建对象,管理对象的生命周期,并注入对象之间的依赖关系.例如:
查看代码
1.使用@Service注解标记服务实现类,使其被 Spring 自动发现
2.通过@Autowired注解实现依赖注入(支持构造器注入、Setter 注入)
3.使用@Qualifier指定具体使用哪个实现类(当有多个实现时)
4.通过@Configuration和@ComponentScan启用组件扫描
5.从容器中获取 Bean 并调用方法1
2
3
4
5package com.example.service;
public interface MessageService {
String getMessage();
}
1 | package com.example.service; |
1 | package com.example.service; |
1 | package com.example.consumer; |
1 | package com.example; |
核心小结
介绍完Spring的IOC和DI的概念后,我们会发现这两个概念的最终目标就是:==充分解耦==,具体实现靠:
- 使用IOC容器管理bean
- 在IOC容器内将有依赖关系的bean进行关系绑定
- 最终结果为:使用对象时不仅可以直接从IOC容器中获取,并且获取到的bean已经绑定了所有的依赖关系.
(1)什么IOC/DI思想?
- IOC:控制反转,控制反转的是对象的创建权
- DI:依赖注入,绑定对象与对象之间的依赖关系
(2)什么是IOC容器?
Spring创建了一个容器用来存放所创建的对象,这个容器就叫IOC容器(3)什么是Bean?
容器中所存放的一个个对象就叫Bean或Bean对象
IOC(控制反转)
bean
bean基础配置
对于bean的基础配置为
id: bean的id,用于标识bean,bean的id不能重复,如果重复,会报错.
class: bean的全类名,用于指定bean的类型.1
<bean id="beanId" class="bean的全类名">
bean的name属性
bean的name属性用于指定bean的名称,可以使用别名,多个别名之间用逗号分隔,例如:1
<bean id="userService" class="com.example.UserService" name="userService,userServiceImpl">
获取bean时,可以使用id或name来获取,例如:1
2UserService userService = (UserService) context.getBean("userService");
UserService userServiceImpl = (UserService) context.getBean("userServiceImpl");
bean的scope属性
bean的scope属性用于指定bean的作用域,可以使用singleton:单例(默认),prototype:非单例,例如:1
<bean id="userService" class="com.example.UserService" scope="singleton"/>
scope使用的后续思考:
单例模式:在整个应用程序中,只有一个实例,所有的请求都共享同一个实例.
非单例模式:每次请求都会创建一个新的实例.
单例模式的优点:
- 减少了内存的开销,因为只有一个实例
- 可以共享数据,因为所有的请求都共享同一个实例
- 避免了线程安全问题,因为所有的请求都共享同一个实例
非单例模式的优点:- 可以避免数据共享问题,因为每个请求都有自己的实例
- 可以避免数据共享问题,因为每个请求都有自己的实例
bean的实例化
在上面对象已经交给了Spring容器管理,那么Spring容器如何创建对象呢?
所以我们就要研究bean的实例化过程,在这块内容我们需要解决两部分内容,分别是
bean是如何创建的
实例化bean的三种方式: 构造方法,静态工厂方法,实例工厂方法
构造方法实例化bean
我们准备一个BookDao类与BookImpl类1
2
3
4
5
6
7
8
9
10public interface BookDao {
public void save();
}
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ...");
}
}
我们在applicationContext.xml中配置BookDaoImpl的bean1
<bean id="bookDao" class="com.example.BookDaoImpl">
我们在测试类中获取BookDaoImpl的bean1
2
3
4
5
6
7public class App {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao = (BookDao) context.getBean("bookDao");
bookDao.save();
}
}
在Spring中,bean的实例化过程就是通过反射创建对象的,所以,在BookDaoImpl类中添加一个无参构造方法,就可以实现bean的实例化过程,如下:1
2
3
4
5
6
7
8
9
10public class BookDaoImpl implements BookDao {
public BookDaoImpl() {
System.out.println("book dao constructor...");
}
public void save() {
System.out.println("book dao save...");
}
}
我们将无参构造换成有参构造,如下:1
2
3
4
5
6
7
8
9
10public class BookDaoImpl implements BookDao {
public BookDaoImpl(String name) {
System.out.println("book dao constructor...");
}
public void save() {
System.out.println("book dao save...");
}
}
此时我们再次运行程序便会报错,这说明Spring容器在创建对象时,默认调用无参构造创建对象,如果创建对象时需要传入参数,则需要使用有参构造创建对象,此时Spring容器无法创建对象,会报错.如果我们没有提供任何构造方法时,Spring容器会默认提供无参构造,此时Spring容器可以创建对象.
静态工厂方法实例化bean
静态工厂方法实例化bean的方式为:
- 定义一个静态工厂类,在静态工厂类中定义一个静态方法,该方法返回一个对象.
- 在applicationContext.xml中配置bean,使用factory-method属性指定静态工厂方法.
我们准备一个BookDaoFactory类,在BookDaoFactory类中定义一个静态方法,该方法返回一个BookDaoImpl对象.
1 | public class BookDaoFactory { |
我们在applicationContext.xml中配置BookDaoFactory的bean
1 | <bean id="bookDao" class="com.example.BookDaoFactory" factory-method="getBookDao"> |
我们在测试类中获取BookDaoImpl的bean
1 | public class App { |
此时我们运行程序,可以看到输出的是BookDaoImpl的类名,说明Spring容器通过静态工厂方法创建了对象.
实例工厂方法实例化bean
实例工厂方法实例化bean的方式为:
- 定义一个实例工厂类,在实例工厂类中定义一个方法,该方法返回一个对象.
- 在applicationContext.xml中配置bean,使用factory-bean属性指定实例工厂类,使用factory-method属性指定实例工厂方法.
我们准备一个BookDaoFactory类,在BookDaoFactory类中定义一个方法,该方法返回一个BookDaoImpl对象.
1 | #注意此处与静态工厂方法的区别在于,静态工厂方法是静态方法,实例工厂方法是普通方法. |
我们在applicationContext.xml中配置BookDaoFactory的bean
1 | <bean id="bookDaoFactory" class="com.example.BookDaoFactory"> |
我们在测试类中获取BookDaoImpl的bean
1 | public class App { |
在此过程中配置过程较为复杂而Spring提供了一种简化的配置方式,即FactoryBean(工厂方法实例化bean).
FactoryBean(工厂方法实例化bean)
FactoryBean是Spring提供的一个接口,该接口提供了一个getObject()方法,该方法返回一个对象,该对象就是我们要创建的对象.
我们准备一个BookDaoFactory类,在BookDaoFactory类中实现FactoryBean接口,并重写getObject()方法,该方法返回一个BookDaoImpl对象.
1 | public class BookDaoFactory implements FactoryBean<BookDao> { |
我们在applicationContext.xml中配置BookDaoFactory的bean
1 | <bean id="bookDao" class="com.example.BookDaoFactory" scope="prototype"/> |
我们在测试类中获取BookDaoImpl的bean
我们在applicationContext.xml中配置BookDao的bean,并使用FactoryBean的bean
1 | public class App { |
此时我们运行程序,可以看到输出的是BookDaoImpl的类名,说明Spring容器通过FactoryBean创建了对象.
FactoryBean的作用:
- 可以将复杂的创建过程封装起来,简化配置过程.
- 可以将创建过程中的参数注入到FactoryBean中,从而实现参数化的创建过程.
bean的生命周期
关于bean的相关知识还有最后一个是bean的生命周期,对于生命周期,我们主要围绕着bean生命周期控制来讲解:
首先理解下什么是生命周期?
从创建到消亡的完整过程,例如人从出生到死亡的整个过程就是一个生命周期。
bean生命周期是什么?
bean对象从创建到销毁的整体过程。
bean生命周期控制是什么?
在bean创建后到销毁前做一些事情。
环境准备
我们先准备一下环境,如下: (1)项目中添加BookDao、BookDaoImpl、BookService和BookServiceImpl类 查看代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26public interface BookDao {
public void save();
}
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ...");
}
}
public interface BookService {
public void save();
}
public class BookServiceImpl implements BookService{
private BookDao bookDao;
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
}
(2)resources下提供spring的配置文件1
2
3
4
5
6
7
<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="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
</beans>
(3)编写AppForLifeCycle运行类,加载Spring的IOC容器,并从中获取对应的bean对象1
2
3
4
5
6
7
8public class AppForLifeCycle {
public static void main( String[] args ) {
ApplicationContext ctx = new
ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
}
}
此时我们运行程序,可以看到输出的是book dao save…,说明Spring容器创建了对象.
bean的生命周期设置
我们在applicationContext.xml中添加一个bean,接下来,在上面这个环境中来为BookDao添加生命周期的控制方法,具体的控制有两个阶段:
1.bean创建之后,想要添加内容,比如用来初始化需要用到资源
2.bean销毁之前,想要添加内容,比如用来释放用到的资源
步骤1:添加初始化和销毁方法
针对这两个阶段,我们在BooDaoImpl类中分别添加两个方法1
2
3
4
5
6
7
8
9
10
11
12
13public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ...");
}
//表示bean初始化对应的操作
public void init(){
System.out.println("init...");
}
//表示bean销毁前对应的操作
public void destory(){
System.out.println("destory...");
}
}
步骤2:配置生命周期
在配置文件添加配置,如下:1
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" init-method="init" destroy-method="destory"/>
步骤3:运行程序
此时我们运行程序,可以看到输出的是init…、book dao save…,说明Spring容器创建了对象,并执行了初始化方法,并在销毁前执行了销毁方法.
从结果中可以看出,init方法执行了,但是destroy方法却未执行,这是为什么呢?
- Spring的IOC容器是运行在JVM中
- 运行main方法后,JVM启动,Spring加载配置文件生成IOC容器,从容器获取bean对象,然后调方法执行
- main方法执行完后,JVM退出,这个时候IOC容器中的bean还没有来得及销毁就已经结束了,所以没有调用对应的destroy方法
解决办法
close关闭容器
我们可以在main方法中添加关闭容器的代码,如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18public class AppForLifeCycle {
public static void main( String[] args ) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
//关闭容器
/* ApplicationContext中没有close方法
需要将ApplicationContext更换成ClassPathXmlApplicationContext
这里是进行了强转*/
//方法一:
((ClassPathXmlApplicationContext) ctx).close();
System.out.println("关闭容器");
//方法二:
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
ctx.close();
System.out.println("关闭容器");
}
}
在调用ctx.()的方法时,就能执行destroy方法的内容了
注册钩子关闭容器
在容器未关闭之前,提前设置好回调函数,让JVM在退出之前回调此函数来关闭容器,调用ctx的registerShutdownHook()方法1
2
3
4
5
6
7
8
9
10public class AppForLifeCycle {
public static void main( String[] args ) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
//注册钩子
//注意:registerShutdownHook在ApplicationContext中也没有
((ClassPathXmlApplicationContext) ctx).registerShutdownHook();
}
}
两种方式介绍完后,close和registerShutdownHook选哪个?
相同点:这两种都能用来关闭容器
不同点:close()是在调用的时候关闭,registerShutdownHook()是在JVM退出前调用关闭。
分析上面的实现过程,会发现添加初始化和销毁方法,即需要编码也需要配置,实现起来步骤比较多也比较乱。
InitializingBean和DisposableBean
Spring提供了两个接口来完成生命周期的控制,好处是可以不用再进行配置init-method和destroy-method
接下来在BookServiceImpl完成这两个接口的使用:
修改BookServiceImpl类,添加两个接口InitializingBean, DisposableBean并实现接口中的两个方法afterPropertiesSet和destroy1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16public class BookServiceImpl implements BookService, InitializingBean, DisposableBean {
private BookDao bookDao;
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
public void destroy() throws Exception {
System.out.println("service destroy");
}
public void afterPropertiesSet() throws Exception {
System.out.println("service init");
}
}
这样当我们启动项目时,就会执行init和destroy方法,并且会执行save方法,其中对于InitializingBean接口中的afterPropertiesSet方法,翻译过来为属性设置之后,所以afterPropertiesSet()的执行是在setBookDao()方法执行之后的.
生命周期总结
(1)关于Spring中对bean生命周期控制提供了两种方式:
在配置文件中的bean标签中添加init-method和destroy-method属性
类实现InitializingBean与DisposableBean接口,这种方式了解下即可。
(2)对于bean的生命周期控制在bean的整个生命周期中所处的位置如下:
初始化容器
1.创建对象(内存分配)
2.执行构造方法
3.执行属性注入(set操作)
4.执行bean初始化方法
使用bean
1.执行业务操作
关闭/销毁容器
1.执行bean销毁方法(3)关闭容器的两种方式:
ConfigurableApplicationContext是ApplicationContext的子类
close()方法
registerShutdownHook()方法
DI(依赖注入)
前面我们已经完成了bean相关操作的讲解,接下来就进入第二个大的模块DI(依赖注入),首先来介绍下Spring中有哪些注入方式?
我们先来思考
向一个类中传递数据的方式有几种?
- 普通方法(set方法)
- 构造方法
依赖注入描述了在容器中建立bean与bean之间的依赖关系的过程,如果bean运行需要的是数字或字符串呢?
- 引用类型
- 简单类型(基本数据类型与String)
Spring就是基于上面这些知识点,为我们提供了两种注入方式,分别是:
setter注入
- 简单类型
- 引用类型
构造器注入
- 简单类型
- 引用类型
依赖注入的方式已经介绍完,接下来挨个学习下:
setter注入
对于setter方式注入引用类型的方式之前已经学习过,快速回顾下:
- 在bean中定义引用类型属性,并提供可访问的set方法
1 | public class BookServiceImpl implements BookService { |
- 配置中使用==property==标签==ref==属性注入引用类型对象
1 | <bean id="bookService" class="com.itheima.service.impl.BookServiceImpl"> |
环境准备
我们先准备一下环境,如下:
查看代码
(1)项目中添加BookDao、BookDaoImpl、UserDao、UserDaoImpl、BookService和BookServiceImpl类1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34public interface BookDao {
public void save();
}
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ...");
}
}
public interface UserDao {
public void save();
}
public class UserDaoImpl implements UserDao {
public void save() {
System.out.println("user dao save ...");
}
}
public interface BookService {
public void save();
}
public class BookServiceImpl implements BookService{
private BookDao bookDao;
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
}
(2)resources下提供spring的配置文件1
2
3
4
5
6
7
8
9
10
<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="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/>
</bean>
</beans>
(3)编写AppForDI运行类,加载Spring的IOC容器,并从中获取对应的bean对象1
2
3
4
5
6
7public class AppForDISet {
public static void main( String[] args ) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookService bookService = (BookService) ctx.getBean("bookService");
bookService.save();
}
}
注入引用数据类型
需求:在bookServiceImpl对象中注入userDao
1.在BookServiceImpl中声明userDao属性
2.为userDao属性提供setter方法
3.在配置文件中使用property标签注入
步骤1:声明属性并提供setter方法
在BookServiceImpl中声明userDao属性,并提供setter方法
1 | public class BookServiceImpl implements BookService{ |
步骤2:配置文件中配置
在applicationContext.xml配置文件中使用property标签注入
1 |
|
步骤3:运行程序
运行AppFroDISet类,查看结果,说明userDao注入成功,book service save…、book dao save…、user dao save…
注入简单数据类型
需求:给BookDaoImpl注入一些简单数据类型的数据
参考引用数据类型的注入,我们可以推出具体的步骤为:
1.在BookDaoImpl类中声明对应的简单数据类型的属性
2.为这些属性提供对应的setter方法
3.在applicationContext.xml中配置
思考:
引用类型使用的是<property name="" ref=""/>,简单数据类型还是使用ref么?
ref是指向Spring的IOC容器中的另一个bean对象的,对于简单数据类型,没有对应的bean对象,该如何配置?
步骤1:声明属性并提供setter方法
在BookDaoImpl类中声明对应的简单数据类型的属性,并提供对应的setter方法
1 | public class BookDaoImpl implements BookDao { |
步骤2:配置文件中进行注入配置
1 |
|
说明:
value:后面跟的是简单数据类型,对于参数类型,Spring在注入的时候会自动转换,但是不能写成
1 | <property name="connectionNum" value="abc"/> |
这样的话,spring在将abc转换成int类型的时候就会报错。
注意:两个property注入标签的顺序可以任意。
对于setter注入方式的基本使用就已经介绍完了,
- 对于引用数据类型使用的是
<property name="" ref=""/> - 对于简单数据类型使用的是
<property name="" value=""/>
构造器注入
环境准备
构造器注入也就是构造方法注入,学习之前,还是先准备下环境:
查看代码
(1)项目中添加BookDao、BookDaoImpl、UserDao、UserDaoImpl、BookService和BookServiceImpl类1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38public interface BookDao {
public void save();
}
public class BookDaoImpl implements BookDao {
private String databaseName;
private int connectionNum;
public void save() {
System.out.println("book dao save ...");
}
}
public interface UserDao {
public void save();
}
public class UserDaoImpl implements UserDao {
public void save() {
System.out.println("user dao save ...");
}
}
public interface BookService {
public void save();
}
public class BookServiceImpl implements BookService{
private BookDao bookDao;
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
}
(2)resources下提供spring的配置文件1
2
3
4
5
6
7
8
9
10
<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="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/>
</bean>
</beans>
(3)编写AppForDIConstructor运行类,加载Spring的IOC容器,并从中获取对应的bean对象1
2
3
4
5
6
7public class AppForDIConstructor {
public static void main( String[] args ) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookService bookService = (BookService) ctx.getBean("bookService");
bookService.save();
}
}
构造器注入引用数据类型
需求:将BookServiceImpl类中的bookDao修改成使用构造器的方式注入。
1.将bookDao的setter方法删除掉
2.添加带有bookDao参数的构造方法
3.在applicationContext.xml中配置
步骤1:删除setter方法并提供构造方法
在BookServiceImpl类中将bookDao的setter方法删除掉,并添加带有bookDao参数的构造方法
1 | public class BookServiceImpl implements BookService{ |
步骤2:配置文件中配置
在applicationContext.xml配置文件中使用constructor-arg标签注入
1 |
|
说明:
标签<constructor-arg>中
name属性对应的值为构造函数中方法形参的参数名,必须要保持一致。
ref属性指向的是spring的IOC容器中其他bean对象。
构造器注入多个引用数据类型
需求:将BookServiceImpl类中的bookDao和userDao修改成使用构造器的方式注入。
1.将bookDao和userDao的setter方法删除掉
2.添加带有bookDao和userDao参数的构造方法
3.在applicationContext.xml中配置
步骤1:提供多个属性的构造函数
在BookServiceImpl声明userDao并提供多个参数的构造函数
1 | public class BookServiceImpl implements BookService{ |
步骤2:配置文件中配置多参数注入
在applicationContext.xml中配置注入
1 |
|
构造器注入多个简单数据类型
需求:在BookDaoImpl中,使用构造函数注入databaseName和connectionNum两个参数。
参考引用数据类型的注入,我们可以推出具体的步骤为:
1.提供一个包含这两个参数的构造方法
2.在applicationContext.xml中进行注入配置
步骤1:添加多个简单属性并提供构造方法
修改BookDaoImpl类,添加构造方法
1 | public class BookDaoImpl implements BookDao { |
步骤2:配置完成多个属性构造器注入
在applicationContext.xml中进行注入配置
1 |
|
上面已经完成了构造函数注入的基本使用,但是会存在一些问题:
- 当构造函数中方法的参数名发生变化后,配置文件中的name属性也需要跟着变
- 这两块存在紧耦合,具体该如何解决?
在解决这个问题之前,需要提前说明的是,这个参数名发生变化的情况并不多,所以上面的还是比较主流的配置方式,下面介绍的,大家都以了解为主。
方式一:删除name属性,添加type属性,按照类型注入1
2
3
4<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<constructor-arg type="int" value="10"/>
<constructor-arg type="java.lang.String" value="mysql"/>
</bean>
这种方式可以解决构造函数形参名发生变化带来的耦合问题,但是如果构造方法参数中有类型相同的参数,这种方式就不太好实现
方式二:删除type属性,添加index属性,按照索引下标注入,下标从0开始1
2
3
4<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<constructor-arg index="1" value="100"/>
<constructor-arg index="0" value="mysql"/>
</bean>
这种方式可以解决参数类型重复问题,但是如果构造方法参数顺序发生变化后,这种方式又带来了耦合问题
介绍完两种参数的注入方式,具体我们该如何选择呢?
- 强制依赖使用构造器进行,使用setter注入有概率不进行注入导致null对象出现
- 强制依赖指对象在创建的过程中必须要注入指定的参数
- 可选依赖使用setter注入进行,灵活性强
- 可选依赖指对象在创建过程中注入的参数可有可无
- Spring框架倡导使用构造器,第三方框架内部大多数采用构造器注入的形式进行数据初始化,相对严谨
- 如果有必要可以两者同时使用,使用构造器注入完成强制依赖的注入,使用setter注入完成可选依赖的注入
- 实际开发过程中还要根据实际情况分析,如果受控对象没有提供setter方法就必须使用构造器注入
- 自己开发的模块推荐使用setter注入
自动配置
前面我们花费了大量时间的时间去思考如何解决构造函数参数名发生变化带来的耦合问题,其实Spring框架已经为我们提供了很好的解决方式,即自动配置
自动配置:自动配置指的是Spring框架在创建对象时,会自动根据构造函数的参数类型,自动为构造函数的参数注入值。
自动装配的方式:按类型(常用),按名称,按构造方法,不启用自动装配
环境准备
查看代码
(1)项目中添加BookDao、BookDaoImpl、UserDao、UserDaoImpl、BookService和BookServiceImpl类1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29public interface BookDao {
public void save();
}
public class BookDaoImpl implements BookDao {
private String databaseName;
private int connectionNum;
public void save() {
System.out.println("book dao save ...");
}
}
public interface BookService {
public void save();
}
public class BookServiceImpl implements BookService{
private BookDao bookDao;
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
}
(2)resources下提供spring的配置文件1
2
3
4
5
6
7
8
9
10
<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="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/>
</bean>
</beans>
(3)编写AppForAutoware运行类,加载Spring的IOC容器,并从中获取对应的bean对象1
2
3
4
5
6
7public class AppForAutoware {
public static void main( String[] args ) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookService bookService = (BookService) ctx.getBean("bookService");
bookService.save();
}
}
完成自动装配的配置
自动装配只需要修改applicationContext.xml配置文件即可:
1.删除标签
2.在标签中添加autowire=属性
首先来实现按照类型注入的配置1
2
3
4
5
6
7
8
9
10
<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 class="com.itheima.dao.impl.BookDaoImpl"/>
<!--autowire属性:开启自动装配,通常使用按类型装配-->
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl" autowire="byType"/>
</beans>
注意事项:
- 需要注入属性的类中对应属性的setter方法不能省略
- 被注入的对象必须要被Spring的IOC容器管理
- 按照类型在Spring的IOC容器中如果找到多个对象,会报
NoUniqueBeanDefinitionException异常
一个类型在IOC中有多个对象,还想要注入成功,这个时候就需要按照名称注入,配置方式为:1
2
3
4
5
6
7
8
9
10
<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 class="com.itheima.dao.impl.BookDaoImpl"/>
<!--autowire属性:开启自动装配,通常使用按类型装配-->
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl" autowire="byName"/>
</beans>
注意:
两种方式介绍完后,以后用的更多的是按照类型注入。
最后对于依赖注入,需要注意一些其他的配置特征:
- 自动装配用于引用类型依赖注入,不能对简单类型进行操作
- 使用按类型装配时(byType)必须保障容器中相同类型的bean唯一,推荐使用
- 使用按名称装配时(byName)必须保障容器中具有指定名称的bean,因变量名与配置耦合,不推荐使用
- 自动装配优先级低于setter注入与构造器注入,同时出现时自动装配配置失效
集合注入
前面我们已经能完成引入数据类型和简单数据类型的注入,但是还有一种数据类型集合,集合中既可以装简单数据类型也可以装引用数据类型,对于集合,在Spring中该如何注入呢?
环境准备
查看代码
(1)项目中添加添加BookDao、BookDaoImpl类1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34public interface BookDao {
public void save();
}
public class BookDaoImpl implements BookDao {
public class BookDaoImpl implements BookDao {
private int[] array;
private List<String> list;
private Set<String> set;
private Map<String,String> map;
private Properties properties;
public void save() {
System.out.println("book dao save ...");
System.out.println("遍历数组:" + Arrays.toString(array));
System.out.println("遍历List" + list);
System.out.println("遍历Set" + set);
System.out.println("遍历Map" + map);
System.out.println("遍历Properties" + properties);
}
//setter....方法省略,自己使用工具生成
}
}
(2)resources下提供spring的配置文件,applicationContext.xml1
2
3
4
5
6
7
<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="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
</beans>
(3)编写AppForDIConstructor运行类,加载Spring的IOC容器,并从中获取对应的bean对象1
2
3
4
5
6
7public class AppForDICollection {
public static void main( String[] args ) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
}
}
下面的所以配置方式,都是在bookDao的bean标签中使用1
2
3
4
5
6
7
8
9
<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="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
</bean>
</beans>
注入数组类型数据
1 | <property name="array"> |
注入List类型数据
1 | <property name="list"> |
注入Set类型数据
1 | <property name="set"> |
注入Map类型数据
1 | <property name="map"> |
注入Properties类型数据
1 | <property name="properties"> |
说明:
property标签表示setter方式注入,构造方式注入constructor-arg标签内部也可以写
<array>、<list>、<set>、<map>、<props>标签
List的底层也是通过数组实现的,所以<list>和<array>标签是可以混用
集合中要添加引用类型,只需要把<value>标签改成<ref>标签,这种方式用的比较少



