基础框架:Spring Framework ——是其他项目的根基
简化、加速开发:Spring Boot
分布式开发:Spring Cloud
学习路线:
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.9</version>
</dependency>
<!-- JDBC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.9</version>
</dependency>
参考:spring maven依赖 - 烟火里的尘埃 - 博客园 (cnblogs.com)
代码书写现状:耦合度偏高
解决方案:使用对象时,在程序中不要主动使用 new 产生对象,而是转换为由外部提供对象。
使用对象时,由程序主动 new 产生对象转换为由外部提供对象,此过程中对象的创建控制权也由程序转移到外部,这种思想称为控制反转。
目标:
最终效果:使用对象是不仅可以从 IOC 容器中获取,并且获取到的 bean 中已经绑定了所有的依赖关系。
IOC入门思路分析:
<?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">
<!-- 1.导入spring坐标spring-context,对应版本是5.2.10.RELEASE-->
<!-- 2.配置bean -->
<!-- bean 标签表示配置 bean
id 属性表示给 bean 起名字,在同一个上下文中不能重复
class 属性表示给 bean 定义类型 -->
<bean id="bookDao" class="com.wlplove.dao.impl.BookDaoImpl"/>
</beans>
在容器中建立 bean 与 bean 之间的依赖关系的整个过程称为依赖注入
DI 入门案例思路分析:
<!-- bean 标签表示配置 bean
id 属性表示给 bean 起名字,在同一个上下文中不能重复
class 属性表示给 bean 定义类型,后面接的是全路径类名 -->
<bean id="bookDao" class="com.wlplove.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.wlplove.service.impl.BookServiceImpl">
<!-- 配置service与dao的关系 -->
<!-- property 标签表示配置当前 bean 的属性,需要其设置相应的set方法
name 属性表示配置哪一个具体的属性,是当前属性的名称
ref 属性表示参照哪一个 bean,是当前容器中存在的 bean -->
<property name="bookDao" ref="bookDao"/>
</bean>
类别 | 描述 |
---|---|
名称 | bean |
类型 | 属性 |
所属 | beans 标签 |
功能 | 定义 Spring 核心容器管理的对象 |
格式 | |
属性列表 | id:bean 的 id,使用容器可以通过 id 值获取对应的 bean,在一个 IoC 容器中 id 值唯一class:bean 的类型,即配置的 bean 的全路径类名 |
范例 |
<!-- name 属性表示别名 -->
<bean id="bookService" name="service service2 bookEbi" class="com.wlplove.service.impl.BookServiceImpl"/>
类别 | 描述 |
---|---|
名称 | name |
类型 | 属性 |
所属 | bean 标签 |
功能 | 定义 bean 的别名,可定义多个,使用逗号、分号、空格分开 |
范例 |
控制创建的 bean 实体的数量(是否单例)
<!-- name 属性表示别名 -->
<bean id="bookService" name="service service2 bookEbi" class="com.wlplove.service.impl.BookServiceImpl"/>
类别 | 描述 |
---|---|
名称 | scope |
类型 | 属性 |
所属 | bean 标签 |
功能 | 定义 bean 的作用范围,可选:singleton(单例,默认)、prototype(非单例) |
范例 |
bean 的实例化过程
主要解决两部分内容,分别是
构造方法
,静态工厂
和 实例工厂
在讲解这三种创建方式之前,我们需要先确认一件事:
bean 本质上就是对象,对象在 new 的时候会使用构造方法完成,那创建bean也是使用构造方法完成的。
提供可访问的构造方法:
public interface BookDao {
public void save();
}
public class BookDaoImpl implements BookDao {
private BookDaoImpl() {
System.out.println("book dao constructor is running ....");
}
public void save() {
System.out.println("book dao save ...");
}
}
配置:
<bean id="orderDao" class="com.wlplove.factory.OrderDaoFactory"
factory-method="getOrderDao"/>
//静态工厂创建对象
public class OrderDaoFactory {
// 工厂方法
public static OrderDao getOrderDao(){
return new OrderDaoImpl();
}
}
配置:
<bean id="userDao" class="com.wlplove.factory.UserDaoFactoryBean" />
FactoryBean(实用,较重要)
public class UserDaoFactoryBean implements FactoryBean {
//代替原始实例工厂中创建对象的方法
public UserDao getObject() throws Exception {
return new UserDaoImpl();
}
public Class getObjectType() {
return UserDao.class;
}
}
配置:
小结:
(1)关于Spring中对bean生命周期控制提供了两种方式:
init-method
和destroy-method
属性InitializingBean
与DisposableBean
接口,这种方式了解下即可。(2)整个生命周期:
(3)关闭容器的两种方式:
分为 简单类型 与 引用类型 的注入
类中需要提供可访问的 set 方法
public void setConnectionNum(int connectionNum) {
this.connectionNum = connectionNum;
}
public void setDatebaseName(String datebaseName) {
this.datebaseName = datebaseName;
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void setBookDao(BookDaoImpl bookDao) {
this.bookDao = bookDao;
}
xml文件配置:注入标签需要用 “”
<!-- 简单类型 (通过 value 属性注入普通类型值) -->
<bean id="bookDao" class="com.wlplove.dao.impl.BookDaoImpl">
<property name="datebaseName" value="mysql"></property>
<property name="connectionNum" value="100"></property>
</bean>
<!-- 引用类型 (通过 ref 属性注入引用类型值) -->
<bean id="bookService" class="com.wlplove.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"></property>
<property name="userDao" ref="userDao"></property>
</bean>
同样地,分为 简单类型 与 引用类型 的注入
类似于 setter 注入需要提供 set 方法,构造方法注入需要在类中提供构造方法:
public BookDaoImpl(int connectionNum, String datebaseName) {
this.connectionNum = connectionNum;
this.datebaseName = datebaseName;
}
public BookServiceImpl(BookDao bookDao, UserDaoImpl userDao) {
this.bookDao = bookDao;
this.userDao = userDao;
}
xml 文件中的配置:注入标签需要用 “”
<!-- 普通类型 (通过 value 属性注入引用类型值,name属性的值是构造方法形参的值)-->
<bean id="bookDao" class="com.wlplove.dao.impl.BookDaoImpl">
<constructor-arg name="connectionNum" value="666"></constructor-arg>
<constructor-arg name="datebaseName" value="mysql"></constructor-arg>
</bean>
<bean id="userDao" class="com.wlplove.dao.impl.UserDaoImpl"></bean>
<!-- 引用类型 (通过 ref 属性注入引用类型值,name 属性的值是构造方法形参的值)-->
<bean id="bookService" class="com.wlplove.service.impl.BookServiceImpl">
<constructor-arg name="bookDao" ref="bookDao"/>
<constructor-arg name="userDao" ref="userDao"/>
</bean>
配置中使用 constructor-arg 标签、type 属性,设置按构造方法的形参类型注入:
<!--type方式,解决 name 属性绑定了形参名称的问题-->
<bean id="bookDao" class="com.wlplove.dao.impl.BookDaoImpl">
<constructor-arg type="int" value="666"></constructor-arg>
<constructor-arg type="java.lang.String" name="datebaseName" value="mysql"></constructor-arg>
</bean>
配置中使用 constructor-arg 标签 index 属性设置按形参类型注入
<!-- index索引方式,解决参数类型重复问题,使用索引进行参数匹配对应 -->
<bean id="bookDao" class="com.wlplove.dao.impl.BookDaoImpl">
<constructor-arg index="0" value="6"></constructor-arg>
<constructor-arg index="1" name="datebaseName" value="mysql"></constructor-arg>
</bean>
IoC 容器根据 bean 所依赖的资源在容器中自动查找并注入到 bean 中的过程称为自动装配。
自动装配有这么几种方式:
自动装配时需要注意:
自动装配只需要修改applicationContext.xml配置文件即可:
如果容器中只存在一个属性类型的 bean,则让属性自动装配。 如果存在多个,则会引发异常,这表明不能为该 bean 使用 byType 自动装配。 如果没有匹配的 bean,则不会发生任何事情(未设置属性)。
会自动在容器上下文中查找,和对象 set() 方法后面的值对应的 bean 的 id
需要保证所有 bean 的类型唯一,并且这个 bean 需要和自动注入的属性的 set()
方法的值一致。
按照类型自动装配时,如果在 Spring 的 IoC 容器中如果找到多个对象,会报 NoUniqueBeanDefinitionException
异常
<bean id="bookDao" class="com.wlplove.dao.impl.BookDaoImpl"/>
<!-- autowire="byType" 属性按类型自动装配,甚至都可以不指定 id 属性 -->
<bean id="bookService" class="com.wlplove.service.impl.BookServiceImpl" autowire="byType"/>
<bean id="bookDao" class="com.wlplove.dao.impl.BookDaoImpl"/>
<!-- autowire="byName" 按名称自动装配 -->
<bean id="bookService" class="com.wlplove.service.impl.BookServiceImpl" autowire="byName"/>
最后对于依赖注入,需要注意一些其他的配置特征:
接下来的例子以 setter 注入为例进行说明,构造器注入也是类似的,将 标签修改为 即可。
注入数组对象:
<!-- name 属性设置将数据注入哪一个数组 -->
<property name="test_array">
<!-- 此处 array 与 list 可以混用 -->
<array>
<value>123</value>
<value>456</value>
<value>789</value>
<!-- 引用类型可以这样写:-->
<!-- <ref bean="beanId"></ref>-->
</array>
</property>
注入 Set 对象:
<property name="test_set">
<set>
<value>set_qwe1</value>
<value>set_asd2</value>
<value>set_zxc3</value>
<!-- 重了会自动过滤 -->
<value>set_zxc3</value>
</set>
</property>
注入 List 对象:
<property name="test_list">
<!-- 此处 array 与 list 可以混用 -->
<list>
<value>list_qwe1</value>
<value>list_asd2</value>
<value>list_zxc3</value>
</list>
</property>
注入 Map 对象:
<property name="test_map">
<map>
<entry key="contry" value="China"></entry>
<entry key="province" value="Gansu"></entry>
<entry key="city" value="Lanzhou"></entry>
</map>
</property>
<property name="test_properties">
<props>
<prop key="contry">China</prop>
<prop key="province">Gansu</prop>
s<prop key="city">Lanzhou</prop>
</props>
</property>
说明:
第一步:xml文件中添加 context 命名空间:
原文件:
<?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>
通过复制修改过的新文件:
<?xml version="1.0" encoding="UTF-8"?>
<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
http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/context
+ http://www.springframework.org/schema/context/spring-context.xsd
">
</beans>
第二步:在 xml 文件中添加,使用 context 空间加载指定 properties 文件
<context:property-placeholder location="jdbc.properties"/>
<!-- 可以加载多个文件 -->
<context:property-placeholder location="jdbc.properties,jdbc2.properties"/>
<!-- 或者用通配符加载所有文件 -->
<context:property-placeholder location="*.properties"/>
<!-- 用 system-properties-mode="NEVER" 禁用系统属性 -->
<context:property-placeholder location="classpath:*.properties" system-properties-mode="NEVER"/>
<!-- 从类路径或 jar 包中搜索并加载所有 properties 文件 -->
<context:property-placeholder location="classpath*:*.properties"/>
最规范的写法可以这样:
<context:property-placeholder location="jdbc.properties"/>
<!-- 可以加载多个文件 -->
<context:property-placeholder location="jdbc.properties,jdbc2.properties"/>
<!-- 或者用通配符加载所有文件 -->
<context:property-placeholder location="*.properties"/>
<!-- 用 system-properties-mode="NEVER" 禁用系统属性 -->
<context:property-placeholder location="classpath:*.properties" system-properties-mode="NEVER"/>
<!-- 从类路径或 jar 包中搜索并加载所有 properties 文件 -->
<context:property-placeholder location="classpath*:*.properties"/>
第三步:使用 ${} 读取加载的属性值
properties 的文件内容为:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/spring_db
jdbc.username=root
jdbc.password=root
则这样写:
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
方式一:通过类路径来加载配置文件的方式创建容器
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
方式二:通过文件路径来加载配置文件的方式创建容器
ApplicationContext ctx = new FileSystemXmlApplicationContext("E:\\applicationContext.xml");
以上两种方式也都支持加载多个配置文件:
ApplicationContext ctx = new ClassPathXmlApplicationContext("bean1.xml","bean2.xml");
ApplicationContext ctx = new FileSystemXmlApplicationContext("E:\\bean1.xml","E:\\bean2.xml");
方式一:使用 bean 名称获取
// 需要设置类型转换
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
方式二:使用 bean 名称获取并指定类型
BookDao bookDao = ctx.getBean("bookDao",BookDao.class);
方式三:使用 bean 类型获取
// 注意:参数是 类.class
BookDao bookDao = ctx.getBean(BookDao.class);
BeanFactory是延迟加载,只有在获取 bean 对象的时候才会去创建
ApplicationContext 是立即加载,容器加载的时候就会创建 bean 对象
ApplicationContext 要想成为延迟加载,只需要按照如下方式进行配置,设置 “ lazy-init="true" ”
<?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="bookDao" class="com.itheima.dao.impl.BookDaoImpl" lazy-init="true"/>
</beans>