1 Spring 能做什么?
1)官方网站
Home
2)个人总结
- Spring 的核心是 IoC(控制反转)和 AOP(面向切面编程),SpringMVC 是需要依赖 Spring 的基础;
- SpringMVC 其实简化了 Web 开发,但是,实际开发中依然需要配置很多东西,可以用 xml 或者 java 代码配置,这无疑是一个繁重的操作,因此 SpringBoot 诞生了,简化 SpringWeb 的配置开发;
- Spring 旨在简化 J2EE 企业应用程序开发。Spring Boot 旨在简化 Spring 开发(减少配置文件,开箱即用!)
3)Spring 学习准备工作
Maven 仓库spring-context
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
2 IoC 思想
2.1 什么是控制反转?
主要是为了降低程序的耦合度,在之前书写 JavaWeb 项目的时候,我们经常需要自己手动 new 一个 service 对象的实现类等等操作,这会提高代码的耦合度,在大型项目中这是不利于我们维护项目的。Spring 中引入 IoC 控制反转的概念,简单来讲就是不用手动 new 具体的实现类对象,Spring 会有一个称为 IoC 容器的东西,帮我们维护这这些实例对象,当我们需要使用的时候,Spring 将这个示例对象直接给我们即可。
Spring 中的 IoC 容器是对 IoC 思想的具体实现,不是只有在 Spring 中特有的。
**控制:**对象的实例化、管理的权力;
**反转:**将控制权力交给外部;
代码自己不会实例化对象,而是将实例化对象的权力交给 IoC 容器,当需要对象的时候,只需要向 IoC 容器索要即可,IoC 容器会维护一堆相应的对象;
Spring 中的 IoC 容器是 IoC 思想的具体实现,其实 IoC 容器就是一个 Map(Key,Value)的形式,容器中存放这各种各样的 bean 对象;
2.2 代码体验
1)通过配置 xml 文件实现 bean 对象的控制反转
<?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">
<!-- 使用xml配置bean对象 -->
<!--
id: 表示唯一识别的名字
class: 具体的类的全类名
-->
<bean id="bookDao" class="cn.edu.njust.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="cn.edu.njust.service.impl.BookServiceImpl"/>
</beans>
package cn.edu.njust;
import cn.edu.njust.dao.BookDao;
import cn.edu.njust.service.BookService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* 体验IoC容器如何获取Bean对象
*/
public class TestDemo01 {
public static void main(String[] args) {
// 获取IoC容器对象
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
/*
* 使用这个IoC对象获取我们的Bean对象
* getBean: 根据xml文件中的id属性来获取bean对象
* */
// 1.获取BookDao
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
System.out.println("------------------------");
// 2.获取BookService对象
BookService bookService = (BookService) ctx.getBean("bookService");
bookService.save();
}
}
3 Spring 中对 bean 对象的管理
Spring 中 bean 对象就是 IoC 容器中管理的对象,任何可以 new 出来的实例都可以作为 bean 对象管理;不仅是自己写的类可以使用 bean 对象管理,引入的第三方的实体类也可以交给 IoC 容器进行管理,比如引入数据库连接池 Druid 或者 C3P0 等等;
Spring 中使用 IoC 容器将 bean 对象交给容器管理,开发者不需要自己手动 new 对象实例,这样可以降低程序的耦合度,那 IoC 容器是如何帮我们实例化对象的呢?
Spring 的实例化有几种方式,主要是:构造器、静态工厂和实例化工厂。
3.1 通过构造器实例化 bean
简单的说,就是虽然我们不手动的 new 对象实例,并且通过这种 xml 文件配置的方式将 new 实例的工作交给 Spring 的 IoC 容器。
3.1.1 默认使用无参构造
<bean id="userDao" class="cn.edu.njust.dao.impl.UserDaoImpl"/>
<bean id="orderDao" class="cn.edu.njust.dao.impl.OrderDaoImpl"/>
类似这样使用标签配置的 bean 对象,Spring 底层使用默认的无参构造器来实例化 bean 对象;
若是想要检验是否调用了午餐构造,可以自己添加无参构造器并输出日志查看验证。
3.1.2 使用有参构造
<bean id="orderService" class="cn.edu.njust.service.impl.OrderServiceImpl">
<!-- 使用 constructor-arg 标签配置简单数据类型的注入 -->
<constructor-arg name="name" value="TIANSU"/>
<constructor-arg name="num" value="1"/>
</bean>
除了无参构造,还可以使用有参构造,当然,此时必须保证class
指定的类中有相应的构造器;
参数使用constructor-arg
来指定;
3.2 通过静态工厂实例化 bean
主要是和传统的使用静态工厂实例化对象方式对接。
(1)构造一个静态工厂
public class UserDaoFactory {
public static UserDao getUserDao() {
return new UserDaoImpl();
}
}
(2)配置 bean 标签
<!-- 使用静态工程实例化bean -->
<bean id="userDao" class="cn.edu.njust.factory.UserDaoFactory" factory-method="getUserDao"/>
- bean 标签中 class 属性指定的类是这个静态工厂类
- 使用 factory-method 属性指定构造实例化对象的静态方法;
3.3 通过实例化工厂实例化 bean
主要是和传统的使用实例化工厂实例化对象的方式对接。
(1)实例化工厂
public class UserServiceFactory {
public UserService getUserServiceDao() {
return new UserServiceImpl();
}
}
(2)配置 bean 标签
<!-- 使用示例工程实例化bean -->
<!-- 使用这种方式,首先需要先制造工厂的bean对象 -->
<bean id="userServiceFactory" class="cn.edu.njust.factory.UserServiceFactory"/>
<!-- 在获取bean对象 -->
<bean id="userService" factory-bean="userServiceFactory" factory-method="getUserServiceDao"/>
- 首先配置一个实例工厂的 bean 对象;
- 其次配置目的 bean 对象的 bean 标签:标签中使用 factory-bean 指定实例工厂的 bean 对象,属性值是实例工厂 bean 对象的 id 属性;同时需要使用 factory-method 属性指定获取目的实例化对象的方法;
(3)总结:这种方式中,可以看到实例化工厂的 bean 对象就是一个“中间件”,其作用就是为了配置目的 bean;因此 Spring 提供了另外一种实例化工厂获取 bean 对象的方式;
3.4 通过实例化工厂实例化 bean(pro)
(1)实现 FactoryBean
package cn.edu.njust.factory;
import cn.edu.njust.service.UserService;
import cn.edu.njust.service.impl.UserServiceImpl;
import org.springframework.beans.factory.FactoryBean;
/**
* -- coding: UTF-8 -- *
*
* @author wangs
* @date 2023/11/9 20:30
* @description 实例化工厂的升级版
*/
public class UserServiceFactoryBean implements FactoryBean<UserService> {
/**
* 代替原始实例化对象中的工厂方法
* @return
* @throws Exception
*/
@Override
public UserService getObject() throws Exception {
return new UserServiceImpl();
}
/**
* 获取对象的类型
* @return
*/
@Override
public Class<?> getObjectType() {
return UserService.class;
}
}
- 实现 FactoryBean,指定需要实例化的对象;
- 实现 getObject 方法,返回实例化对象
- 实现 getObjectType 方法,返回实例化对象的 class 文件;
(2)配置 bean 标签
<!-- 实例化工厂变种方式 -->
<bean id="userService" class="cn.edu.njust.factory.UserServiceFactoryBean"/>
只需要正常配置即可,id 属性指明需要维护的 bean 对象,class 属性直接指向 FactoryBean 的实现类;
4 依赖注入
4.1 什么是依赖注入 DI?
在容器中建立 bean 和 bean 之间的关系的过程,称之为依赖注入。IoC 为我们实现控制反转,如 JavaWeb 中,service 层需要使用到 dao/mapper 层的 bean 对象,这两个对象之间存在着某种联系,DI 就是根据这样的特性来将 IoC 容器中的 bean 对象自动的关联起来。方便使用。
Spring 使用 IoC 和 DI 最终实现了充分解耦。
1) 依赖注入实例:
<?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">
<!-- 使用xml配置bean对象 -->
<!--
id: 表示唯一识别的名字
class: 具体的类的全类名
name: 配置bean的别名,可以配置多个
scope: 配置这个类是单例模式还是非单例模式
singleton: 单例,默认值
prototype: 非单例
-->
<bean id="bookDao" name="bookDao2,dao" class="cn.edu.njust.dao.impl.BookDaoImpl" scope="singleton"/>
<!-- 配置依赖 -->
<!--
因为在 BookService 中使用到了 BookDao 的实例对象,所以在 BookService 中配置
-->
<bean id="bookService" class="cn.edu.njust.service.impl.BookServiceImpl">
<!-- 属性说明:
1.name: 这个属性对应的是具体示例的名称,在此处,BookService中的示例名称为 bookDao
2.ref: 关联bean对象,此处的bookDao指的是本文件中配置的 id 为 bookDao 的bean对象
-->
<property name="bookDao" ref="bookDao"/>
</bean>
</beans>
2)Spring 中 DI 注入的几种方式:setter 注入、构造器注入、简单数据类型注入、集合类型数据注入
4.2 setter 注入
(1)示例实现类
package cn.edu.njust.service.impl;
import cn.edu.njust.dao.OrderDao;
import cn.edu.njust.dao.UserDao;
import cn.edu.njust.service.UserService;
public class UserServiceImpl implements UserService {
private UserDao userDao;
private OrderDao orderDao;
public UserServiceImpl() {
}
/**
* 提供给构造器注入的方式使用
* @param orderDao
*/
public UserServiceImpl(OrderDao orderDao) {
this.orderDao = orderDao;
}
@Override
public void run() {
System.out.println("UserService is running...");
userDao.run();
}
@Override
public void ordered() {
orderDao.run();
}
/**
* 构造器,提供给setter注入使用
* @param userDao
*/
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
(2)bean 标签配置
<!-- 配置要使用到的 bean 对象 -->
<bean id="userDao" class="cn.edu.njust.dao.impl.UserDaoImpl"/>
<bean id="userService" class="cn.edu.njust.service.impl.UserServiceImpl">
<!-- 使用 property 标签配置 setter 注入 -->
<property name="userDao" ref="userDao"/>
</bean>
- 使用 property 标签指定需要注入的 bean 对象
- 必须要有相应的 bean 对象的 setter
4.3 构造器注入
构造器注入主要就是在相应发类中先提供对应参数的构造器,然后在 xml 配置文件中使用 constructor-arg 标签配置
<!-- 配置要使用到的 bean 对象 -->
<bean id="userDao" class="cn.edu.njust.dao.impl.UserDaoImpl"/>
<bean id="orderDao" class="cn.edu.njust.dao.impl.OrderDaoImpl"/>
<bean id="userService" class="cn.edu.njust.service.impl.UserServiceImpl">
<!-- 使用 property 标签配置 setter 注入 -->
<property name="userDao" ref="userDao"/>
<!-- 使用 constructor-arg 标签配置构造器注入-->
<constructor-arg name="orderDao" ref="orderDao"/>
</bean>
- 使用 constructor-arg 标签指定注入的参数;
- 必须提供相应的构造器;
4.4 简单数据类型注入
简单数据类型的注入和 bean 对象的注入方式极为相似,区别就是 bean 对象使用 ref 关联 bean 的实例,而简单数据类型使用 vlaue 赋值。
<bean id="orderService" class="cn.edu.njust.service.impl.OrderServiceImpl">
<!-- 使用 constructor-arg 标签配置简单数据类型的注入 -->
<constructor-arg name="name" value="TIANSU"/>
<constructor-arg name="num" value="1"/>
</bean>
4.5 bean 对象自动配置
在 xml 中程序员根据各个 bean 之间的依赖关系而先设定一些配置,是比较麻烦的操作,Spring 为提供了一种便捷的操作方式,使用自动配置,IoC 容器会按照一定的规则,当需要使用某个 bean 对象的时候,自己在容器中寻找,并建立依赖关系。
主要的自动配置方式有:按类型自动配置、按名称自动配置。
4.5.1 按类型自动装配
<!--
自动配置
ByType: 按类型自动配置中待配置的bean对象的id还可以省略
ByName: 按名称自动配置中,待配置的bean对象id不能省略
-->
<bean id="bookDao" class="cn.edu.njust.dao.impl.BookDaoImpl"/>
<!-- <bean class="cn.edu.njust.dao.impl.BookDaoImpl"/>-->
<bean id="bookService" class="cn.edu.njust.service.impl.BookServiceImpl" autowire="byType"/>
- 使用 autowire 属性指定注入的方式
- byType:按类型自动配置,IoC 容器会根据需要的 bean 对象的类型,在容器中寻找,然后建立依赖;
- 这种方式中,别配置的 bean 对象的 id 属性可以省略,如上面的
<bean id="bookDao" class="cn.edu.njust.dao.impl.BookDaoImpl"/>
中可以省略 id 属性; - 如果 IoC 容器中某一类型的 bean 对象有多个,会出现错误,此时可以使用按名称自动配置解决;
4.5.2 按名称自动配置
<!--
自动配置
ByType: 按类型自动配置中待配置的bean对象的id还可以省略
ByName: 按名称自动配置中,待配置的bean对象id不能省略
-->
<bean id="bookDao" class="cn.edu.njust.dao.impl.BookDaoImpl"/>
<!-- <bean class="cn.edu.njust.dao.impl.BookDaoImpl"/>-->
<bean id="bookService" class="cn.edu.njust.service.impl.BookServiceImpl" autowire="byName"/>
- 这种方式被配置的 bean 对象必须指明 id 属性,否则报错
4.5.3 自动装配与其他装配方式的注意事项
1)按类型自动配置:
1.1)自动配置的 bean 对象的 setter 必须存在;
1.2)配置文件中存在多个同类型的 bean 对象,即 IoC 容器中同一种类型的 bean 对象有多个,就会报错;
1.3)被注入的 bean 对象必须在配置文件中声明;
1.4)被注入的 bean 对象的 id 属性可以省略;
2)按名称自动配置:
2.1)被注入 bean 对象必须在配置文件中声明;
2.2)被注入的 bean 对象的 id 不能省略;
2.3)被注入对象的 setter 必须按规矩生成;因为按名称注入的方式是将 setter 方法的 set 之后的名称首字母小写后作为注入对象的名称,不统一的情况下很可能出错;
2.4)这种方式支持一个 IoC 容器中存在多个同种类型的 bean 对象。
注意:自动配置的优先级低于 setter 注入和构造器注入,当这些方式同时存在的时候,自动配置失效。
5 知识汇总
5.1 标签汇总
5.2 常用方法
**方法原型 ** | 说明 |
---|---|
Object getBean(String var1) throws BeansException | 根据 xml 文件中 id 属性名称获取 bean 对象 |