1. 核心概念
- IoC(Inversion of Control)控制反转
- 使用对象时,由主动new产生对象转换为由外部提供对象,此过程中对象创建控制权由程序转移到外部,此思想称为控制反转
- Spring技术对IoC思想进行了实现
- Spring提供了一个容器,称为IoC容器,用来充当IoC思想中的“外部”
- IoC容器负责对象的创建、初始化等一系列工作,被创建或被管理的对象在IoC容器中统称为Bean
- DI(Dependency Injection)依赖注入
- 在容器中建立bean与bean之间的依赖关系的整个过程,称为依赖注入
- 目标:充分解耦
2. IoC入门案例
1. 导入Spring坐标
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>spring_01_quickstart</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
2. 定义Spring管理的类(接口)
public 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 = new BookDaoImpl();
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
}
3. 创建Spring配置文件,配置对应类作为Spring管理的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">
<!--bean标签标示配置bean
id属性标示给bean起名字
class属性表示给bean定义类型
-->
<bean id="bookDao" class="com.lenyoo.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.lenyoo.service.impl.BookServiceImpl"/>
</beans>
注意:bean定义时id属性在同一个上下文中不能重复
4. 初始化IoC容器(Spring核心容器/Spring容器),通过容器获取bean
package com.lenyoo;
import com.lenyoo.dao.BookDao;
import com.lenyoo.service.BookService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
// 3. 获取IOC容器
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
// 4. 获取bean
// BookDao bookDao = (BookDao) ctx.getBean("bookDao");
// bookDao.save();
BookService bookService = (BookService) ctx.getBean("bookService");
bookService.save();
}
}
3. DI入门案例
1. 删除使用new的形式创建对象的代码
public class BookServiceImpl implements BookService {
// 5. 删除业务层中使用new的方式创建的dao对象
private BookDao bookDao /*= new BookDaoImpl()*/;
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
2. 提供依赖对象对应的setter方法
public class BookServiceImpl implements BookService {
// 5. 删除业务层中使用new的方式创建的dao对象
private BookDao bookDao;
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
// 6. 提供对应的set方法
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
}
3. 配置service与dao之间的关系
<?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标签标示配置bean
id属性标示给bean起名字
class属性表示给bean定义类型
-->
<bean id="bookDao" class="com.lenyoo.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.lenyoo.service.impl.BookServiceImpl">
<!--7. 配置service与dao的关系-->
<!--property标签表示配置当前bean的属性
name表示配置哪一个具体的属性
ref属性表示参照哪一个bean-->
<property name="bookDao" ref="bookDao"/>
</bean>
</beans>
注意:配置中的两个bookDao的含义是不一样的
3. IoC相关内容
3.1 bean的基础配置
3.1.1 bean的name属性
3.1.2 bean的作用范围scope配置
3.2 bean实例化
对象已经能交给Spring的IOC容器来创建了,但是容器是如何来创建对象的呢?就需要研究下bean的实例化过程
。
3.2.1 实例化bean的三种方式——构造方法(常用)
- 提供可访问的构造方法
public class BookDaoImpl implements BookDao {
public BookDaoImpl() {
System.out.println("book dao constructor is running ...");
}
public void save() {
System.out.println("book dao save ...");
}
}
- 配置
<!--方式一:构造方法实例化bean-->
<bean id="bookDao" class="com.lenyoo.dao.impl.BookDaoImpl"/>
- 无参构造方法如果不存在,将抛出异常BeanCreationException
3.2.2 实例化bean的三种方式——静态工厂(了解)
- 静态工厂
public class OrderDaoFactory {
public static OrderDao getOrderDao(){
System.out.println("factory setup...");
return new OrderDaoImpl();
}
}
- 配置
<!--方式二:使用静态工厂实例化bean-->
<bean id="orderDao" class="com.lenyoo.factory.OrderDaoFactory" factory-method="getOrderDao"/>
3.2.3 实例化bean的三种方式——实例工厂(了解)
- 实例工厂
public class UserDaoFactory {
public UserDao getUserDao(){
return new UserDaoImpl();
}
}
- 配置
<!--方式三:使用实例工厂实例化bean-->
<bean id="userFactory" class="com.lenyoo.factory.UserDaoFactory"/>
<bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/>
3.2.4 实例化bean的第四种方式——FactoryBean(实用)
- FactoryBean
public class UserDaoFactoryBean implements FactoryBean<UserDao> {
// 代替原始实例工厂中创建对象的方法
@Override
public UserDao getObject() throws Exception {
return new UserDaoImpl();
}
@Override
public Class<?> getObjectType() {
return UserDao.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
- 配置
<!--方式四:使用FactoryBean实例化bean-->
<bean id="userDao" class="com.lenyoo.factory.UserDaoFactoryBean"/>
3.3 bean的生命周期
(1)关于Spring中对bean生命周期控制提供了两种方式:
- 在配置文件中的bean标签中添加
init-method
和destroy-method
属性
<bean id="bookDao" class="com.lenyoo.dao.impl.BookDaoImpl"
init-method="init" destroy-method="destroy"/>
- 类实现
InitializingBean
与DisposableBean
接口,这种方式了解下即可。
package com.lenyoo.service.impl;
import com.lenyoo.dao.BookDao;
import com.lenyoo.service.BookService;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
public 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();
}
@Override
public void destroy() throws Exception {
System.out.println("service destroy");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("service init");
}
}
(2)对于bean的生命周期控制在bean的整个生命周期中所处的位置如下:
- 初始化容器
- 1.创建对象(内存分配)
- 2.执行构造方法
- 3.执行属性注入(set操作)
- 4.执行bean初始化方法
- 使用bean
- 1.执行业务操作
- 关闭/销毁容器
- 1.执行bean销毁方法
(3)关闭容器的两种方式:
- ConfigurableApplicationContext是ApplicationContext的子类
- close()方法
- registerShutdownHook()方法
import com.lenyoo.dao.BookDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AppForLifeCycle {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
// ctx.registerShutdownHook();
ctx.close();
}
}
4. DI相关内容
依赖注入方式,分别是:
- setter注入
- 简单类型
- 引用类型
- 构造器注入
- 简单类型
- 引用类型
4.1 Setter注入
4.1.1 Setter注入——引用类型
- 在bean中定义引用类型属性,并提供可访问的set方法
public class BookServiceImpl implements BookService {
private BookDao bookDao;
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
public void save() {
System.out.println("book service save ...");
bookDao.save();
userDao.save();
}
}
- 配置中使用property标签ref属性注入引用类型对象
<bean id="bookDao" class="com.lenyoo.dao.impl.BookDaoImpl"/>
<bean id="userDao" class="com.lenyoo.dao.impl.UserDaoImpl"/>
<bean id="bookService" class="com.lenyoo.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/>
<property name="userDao" ref="userDao"/>
</bean>
4.1.2 Setter注入——简单类型
- 在bean中定义简单类型属性并提供可访问的set方法
public class BookDaoImpl implements BookDao {
private int connectionNum;
private String databaseName;
public void setConnectionNum(int connectionNum) {
this.connectionNum = connectionNum;
}
public void setDatabaseName(String databaseName) {
this.databaseName = databaseName;
}
public void save() {
System.out.println("book dao save ..." + databaseName + "," + connectionNum);
}
}
- 配置中使用property标签value属性注入简单类型数据
<bean id="bookDao" class="com.lenyoo.dao.impl.BookDaoImpl">
<property name="databaseName" value="mysql"/>
<property name="connectionNum" value="100"/>
</bean>
4.2 构造器注入(了解)
4.2.1 构造器注入——引用类型(了解)
- 在bean中定义引用类型属性并提供可访问的构造方法
public class BookServiceImpl implements BookService {
private BookDao bookDao;
private UserDao userDao;
public BookServiceImpl(BookDao bookDao, UserDao userDao) {
this.bookDao = bookDao;
this.userDao = userDao;
}
- 配置中使用constructor-arg标签ref属性注入引用类型对象
<bean id="bookDao" class="com.lenyoo.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.lenyoo.service.impl.BookServiceImpl">
<constructor-arg name="bookDao" ref="bookDao"/>
<constructor-arg name="userDao" ref="userDao"/>
</bean>
4.2.2 构造器注入——简单类型(了解)
- 在bean中定义简单类型属性并提供可访问的构造方法
public class BookDaoImpl implements BookDao {
private String databaseName;
private int connectionNum;
public BookDaoImpl(String databaseName, int connectionNum) {
this.databaseName = databaseName;
this.connectionNum = connectionNum;
}
public void save() {
System.out.println("book dao save ..." + databaseName + "," + connectionNum);
}
}
- 配置中使用constructor-arg标签value属性注入简单类型数据
<bean id="bookDao" class="com.lenyoo.dao.impl.BookDaoImpl">
<constructor-arg name="databaseName" value="mysql"/>
<constructor-arg name="connectionNum" value="10"/>
</bean>
4.3 依赖自动装配
- IoC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称为自动装配
- 自动装配方式
- 按类型(常用)
- 按名称
- 按构造方法
- 不启用自动装配
- 配置中使用bean标签autowire属性设置自动装配的类型
<bean id="bookDao" class="com.lenyoo.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.lenyoo.service.impl.BookServiceImpl" autowire="byType"/>
注意
- 自动装配用于引用类型依赖注入,不能对简单类型进行操作
- 使用按类型装配时(byType)必须保障容器中相同类型的bean唯一,推荐使用
- 使用按名称装配时(byName)必须保障容器中具有指定名称的bean,因变量名与配置耦合,不推荐使用
- 自动装配优先级低于setter注入与构造器注入,同时出现时自动装配配置失效
4.4 集合注入
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 setArray(int[] array) {
this.array = array;
}
public void setList(List<String> list) {
this.list = list;
}
public void setSet(Set<String> set) {
this.set = set;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public void setProperties(Properties properties) {
this.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);
}
}
<bean id="bookDao" class="com.lenyoo.dao.impl.BookDaoImpl">
<property name="array">
<array>
<value>100</value>
<value>200</value>
<value>300</value>
</array>
</property>
<property name="list">
<list>
<value>itcast</value>
<value>itheima</value>
<value>boxuegu</value>
</list>
</property>
<property name="set">
<set>
<value>itcast</value>
<value>itheima</value>
<value>boxuegu</value>
<value>chuanzhihui</value>
</set>
</property>
<property name="map">
<map>
<entry key="country" value="china"/>
<entry key="province" value="zhejiang"/>
<entry key="city" value="hangzhou"/>
</map>
</property>
<property name="properties">
<props>
<prop key="country">china</prop>
<prop key="province">zhejiang</prop>
<prop key="city">hangzhou</prop>
</props>
</property>
</bean>
5. IOC/DI配置管理第三方bean
5.1 案例:数据源对象管理
该案例使用数据源Druid(德鲁伊)
和C3P0
来配置学习
步骤1:导入第三方数据源依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.16</version>
</dependency>
步骤2:配置第三方bean
<bean class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/spring_db"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring_db"/>
<property name="user" value="root"/>
<property name="password" value="root"/>
<property name="maxPoolSize" value="1000"/>
</bean>
步骤3:从IOC容器中获取对应的bean对象
public class App {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
DataSource dataSource = (DataSource) ctx.getBean("dataSource");
System.out.println(dataSource);
}
}
5.2 加载properties文件
- 开启context命名空间
<!--1. 开启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"
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>
- 使用context空间,加载指定properties文件
<!--2. 使用context空间加载properties文件-->
<context:property-placeholder location="jdbc.properties"/>
- 使用属性占位符${}读取properties文件中的属性
<!--3. 使用属性占位符${}读取properties文件中的属性-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<bean id="bookDao" class="com.lenyoo.dao.impl.BookDaoImpl">
<property name="name" value="${jdbc.driver}"/>
</bean>
当有多个properties配置文件需要被加载,该如何配置?
- 不加载系统属性
<context:property-placeholder location="jdbc.properties" system-properties-mode="NEVER"/>
- 加载多个properties文件
<context:property-placeholder location="jdbc.properties,jdbc2.properties"/>
- 加载所有properties文件
<context:property-placeholder location="*.properties"/>
- 加载properties文件标准格式
<context:property-placeholder location="classpath:*.properties" system-properties-mode="NEVER"/>
- 从类路径或jar包中搜索并加载properties文件
<context:property-placeholder location="classpath*:*.properties" system-properties-mode="NEVER"/>
6. 核心容器
6.1 容器的创建方式
- 方式一:类路径加载配置文件
// 1. 加载类路径下的配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
- 方式二:文件路径加载配置文件
// 2. 从文件系统下加载配置文件
ApplicationContext ctx = new FileSystemXmlApplicationContext("//Users/lenyoo/Documents/code/spring-app/spring_10_container/src/main/resources/applicationContext.xml");
6.2 bean的三种获取方式
- 方式一:使用bean名称获取
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
- 方式二:使用bean名称获取并指定类型
BookDao bookDao = ctx.getBean("bookDao", BookDao.class);
- 方式三:使用bena类获取
BookDao bookDao = ctx.getBean(BookDao.class);
6.3 BeanFactory
- 使用BeanFactory创建的容器是延迟加载
- 使用ApplicationContext创建的容器是立即加载
- 具体BeanFactory如何创建只需要了解即可。