源码:ssm-spring-part.rar
这些 Bean 是使用您提供给容器的配置元数据创建的(例如,以 XML 定义的形式)。我们学习,如何通过定义XML配置文件,声明组件类信息,交给 Spring 的 IoC 容器进行组件管理!
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>6.0.6version>
dependency>
<dependency>
<groupId>org.junit.jupitergroupId>
<artifactId>junit-jupiter-apiartifactId>
<version>5.3.1version>
dependency>
dependencies>
当通过构造函数方法创建一个 bean(组件对象) 时,所有普通类都可以由 Spring 使用并与之兼容。也就是说,正在开发的类不需要实现任何特定的接口或以特定的方式进行编码。只需指定 Bean 类信息就足够了。但是,默认情况下,我们需要一个默认(空)构造函数。
package com.atguigu.ioc01;
public class HappyComponent {
//默认包含无参数构造函数
public void doWork() {
System.out.println("HappyComponent.doWork");
}
}
创建携带spring约束的xml配置文件
编写配置文件:
文件:resources/spring-bean-01.xml
<bean id="happyComponent" class="com.atguigu.ioc01.HappyComponent"/>
接下来我们讲解如何定义使用静态工厂方法创建Bean的配置 !
package com.atguigu.ioc01;
public class ClientService {
private static ClientService clientService = new ClientService();
private ClientService() {}
public static ClientService createInstance() {
return clientService;
}
}
文件:resources/spring-bean-01.xml
<bean id="clientService" class="com.atguigu.ioc01.ClientService" factory-method="createInstance"/>
接下来我们讲解下如何定义使用实例工厂方法创建Bean的配置 !
package com.atguigu.ioc01;
public class DefaultServiceLocator {
private static ClientServiceImpl clientService = new ClientServiceImpl();
public ClientServiceImpl createClientServiceInstance() {
return clientService;
}
}
package com.atguigu.ioc;
public class ClientServiceImpl {
}
文件:resources/spring-bean-01.xml
<bean id="serviceLocator" class="com.atguigu.ioc01.DefaultServiceLocator">bean>
<bean id="clientService" factory-bean="serviceLocator" factory-method="createClientServiceInstance"/>
主要涉及注入场景:基于构造函数的依赖注入和基于 Setter 的依赖注入。
基于构造函数的 DI 是通过容器调用具有多个参数的构造函数来完成的,每个参数表示一个依赖项。下面的示例演示一个只能通过构造函数注入进行依赖项注入的类!
package com.atguigu.ioc02;
public class UserDao {
}
package com.atguigu.ioc02;
public class UserService {
private UserDao userDao;
public UserService(UserDao userDao) {
this.userDao = userDao;
}
}
文件:resources/spring-bean-02.xml
<beans>
<bean id="userService" class="com.atguigu.ioc02.UserService">
<constructor-arg ref="userDao"/>
bean>
<bean id="userDao" class="com.atguigu.ioc02.UserDao"/>
beans>
基于构造函数的 DI 是通过容器调用具有多个参数的构造函数来完成的,每个参数表示一个依赖项。下面的示例演示通过构造函数注入多个参数,参数包含其他bean和基本数据类型!
package com.atguigu.ioc03;
public class UserDao {
}
package com.atguigu.ioc03;
public class UserService {
private UserDao userDao;
private int age;
private String name;
public UserService(int age , String name ,UserDao userDao) {
this.userDao = userDao;
this.age = age;
this.name = name;
}
}
spring-bean-03.xml
<beans>
<bean id="userService" class="com.atguigu.ioc03.UserService">
<constructor-arg value="18"/>
<constructor-arg value="赵伟风"/>
<constructor-arg ref="userDao"/>
bean>
<bean id="userDao" class="com.atguigu.ioc03.UserDao"/>
beans>
<beans>
<bean id="userService" class="com.atguigu.ioc03.UserService">
<constructor-arg name="name" value="赵伟风"/>
<constructor-arg name="userDao" ref="userDao"/>
<constructor-arg name="age" value="18"/>
bean>
<bean id="userDao" class="com.atguigu.ioc03.UserDao"/>
beans>
<beans>
<bean id="userService" class="com.atguigu.ioc03.UserService">
<constructor-arg index="1" value="赵伟风"/>
<constructor-arg index="2" ref="userDao"/>
<constructor-arg index="0" value="18"/>
bean>
<bean id="userDao" class="com.atguigu.ioc03.UserDao"/>
beans>
开发中,除了构造函数注入(DI)更多的使用的Setter方法进行注入!下面的示例演示一个只能使用纯 setter 注入进行依赖项注入的类。
package com.atguigu.ioc04;
public class MovieFinder {
}
package com.atguigu.ioc04;
public class SimpleMovieLister {
private MovieFinder movieFinder;
private String movieName;
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
public void setMovieName(String movieName) {
this.movieName = movieName;
}
// business logic that actually uses the injected MovieFinder is omitted...
}
spring-bean-04.xml
<bean id="simpleMovieLister" class="com.atguigu.ioc04.SimpleMovieLister">
<property name="movieFinder" ref="movieFinder" />
<property name="movieName" value="消失的她"/>
bean>
<bean id="movieFinder" class="com.atguigu.ioc04.MovieFinder"/>
总结:
特别注意:
上面的实验只是讲解了如何在XML格式的配置文件编写IoC和DI配置!如图:
想要配置文件中声明组件类信息真正的进行实例化成Bean对象和形成Bean之间的引用关系,我们需要声明IoC容器对象,读取配置文件,实例化组件和关系维护的过程都是在IoC容器中实现的!
package com.atguigu.ioc05;
public class HappyComponent {
// 默认包含无参数构造函数
public void doWork() {
System.out.println("HappyComponent.doWork");
}
}
spring-bean-05.xml
<bean id="happyComponent" class="com.atguigu.ioc05.HappyComponent"/>
测试类
SpringIocTest.java
package com.atguigu.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringIocTest {
public void createIoc() {
// 方式1:实例化并且指定配置文件
// 参数:String...locations 传入一个或者多个配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("spring-bean-05.xml");
// 方式2:先实例化,再指定配置文件,最后刷新容器触发Bean实例化动作 [springmvc源码和contextLoadListener源码方式]
ClassPathXmlApplicationContext context1 = new ClassPathXmlApplicationContext();
// 设置配置配置文件,方法参数为可变参数,可以设置一个或者多个配置
context1.setConfigLocations("spring-bean-05.xml");
// 后配置的文件,需要调用refresh方法,触发刷新配置
context1.refresh();
}
}
测试类
SpringIocTest.java
@Test
public void getBeanFromIoc() {
//先创建IOC容器对象
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext();
applicationContext.setConfigLocations("spring-bean-05.xml");
applicationContext.refresh();
// 方式1: 根据id获取
// 没有指定类型,返回为Object,需要类型转化!
HappyComponent happyComponent1 = (HappyComponent) applicationContext.getBean("happyComponent");
// 使用组件对象
happyComponent1.doWork();
// 方式2: 根据类型获取
// 根据类型获取,但是要求,同类型(当前类,或者之类,或者接口的实现类)只能有一个对象交给IoC容器管理
// 配置两个或者以上出现: org.springframework.beans.factory.NoUniqueBeanDefinitionException 问题
HappyComponent happyComponent2 = applicationContext.getBean(HappyComponent.class);
happyComponent2.doWork();
// 方式3: 根据id和类型获取
HappyComponent happyComponent3 = applicationContext.getBean("happyComponent", HappyComponent.class);
happyComponent3.doWork();
System.out.println(happyComponent1 == happyComponent2);
System.out.println(happyComponent2 == happyComponent3);
}
我们可以在组件类中定义方法,然后当IoC容器实例化和销毁组件对象的时候进行调用!这两个方法我们成为生命周期方法!类似于Servlet的init/destroy方法,我们可以在周期方法完成初始化和释放资源等工作。
package com.atguigu.ioc06;
public class BeanOne {
// 周期方法要求: 方法命名随意,但是要求方法必须是 public void 无形参列表
public void init() {
// 初始化逻辑
System.out.println("BeanOne init");
}
}
package com.atguigu.ioc06;
public class BeanTwo {
public void cleanup() {
// 释放资源逻辑
System.out.println("BeanTwo cleanup");
}
}
spring-bean-06.xml
<beans>
<bean id="beanOne" class="com.atguigu.ioc06.BeanOne" init-method="init" />
<bean id="beanTwo" class="com.atguigu.ioc06.BeanTwo" destroy-method="cleanup" />
beans>
@Test
public void test06(){
// 创建iod容器,自动实例化
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-bean-06.xml");
// 正常结束ioc容器,自动销毁
context.close();
}
这意味着,BeanDefinition与类概念一样,SpringIoC容器可以可以根据BeanDefinition对象反射创建多个Bean对象实例。
具体创建多少个Bean的实例对象,由Bean的作用域Scope属性指定!
取值 | 含义 | 创建对象的时机 | 默认值 |
---|---|---|---|
singleton | 在 IOC 容器中,这个 bean 的对象始终为单实例 | IOC 容器初始化时 | 是 |
prototype | 这个 bean 在 IOC 容器中有多个实例 | 获取 bean 时 | 否 |
如果是在WebApplicationContext环境下还会有另外两个作用域(但不常用):
取值 | 含义 | 创建对象的时机 | 默认值 |
---|---|---|---|
request | 请求范围内有效的实例 | 每次请求 | 否 |
session | 会话范围内有效的实例 | 每次会话 | 否 |
package com.atguigu.ioc06;
public class HappyMachine {
private String machineName;
public String getMachineName() {
return machineName;
}
public void setMachineName(String machineName) {
this.machineName = machineName;
}
}
package com.atguigu.ioc06;
public class HappyComponent {
private String componentName;
public String getComponentName() {
return componentName;
}
public void setComponentName(String componentName) {
this.componentName = componentName;
}
}
spring-bean-06.xml
<bean id="happyMachine8" scope="prototype" class="com.atguigu.ioc06.HappyMachine">
<property name="machineName" value="happyMachine"/>
bean>
<bean id="happyComponent8" scope="singleton" class="com.atguigu.ioc06.HappyComponent">
<property name="componentName" value="happyComponent"/>
bean>
@Test
public void testExperiment08() {
ApplicationContext iocContainer = new ClassPathXmlApplicationContext("spring-bean-06.xml");
HappyMachine8 bean = iocContainer.getBean(HappyMachine8.class);
HappyMachine8 bean1 = iocContainer.getBean(HappyMachine8.class);
//多例对比 false
System.out.println(bean == bean1);
HappyComponent8 bean2 = iocContainer.getBean(HappyComponent8.class);
HappyComponent8 bean3 = iocContainer.getBean(HappyComponent8.class);
//单例对比 true
System.out.println(bean2 == bean3);
}
FactoryBean 接口是Spring IoC容器实例化逻辑的可插拔性点。用于配置复杂的Bean对象,可以将创建过程存储在FactoryBean 的getObject方法!FactoryBean 接口提供三种方法:
package com.atguigu.ioc07;
import com.atguigu.ioc06.HappyMachine8;
import org.springframework.beans.factory.FactoryBean;
// 实现FactoryBean接口时需要指定泛型
// 泛型类型就是当前工厂要生产的对象的类型
public class HappyFactoryBean implements FactoryBean<HappyMachine8> {
private String machineName;
public String getMachineName() {
return machineName;
}
public void setMachineName(String machineName) {
this.machineName = machineName;
}
@Override
public HappyMachine8 getObject() throws Exception {
// 方法内部模拟创建、设置一个对象的复杂过程
HappyMachine8 happyMachine = new HappyMachine8();
happyMachine.setMachineName(this.machineName);
return happyMachine;
}
@Override
public Class<?> getObjectType() {
// 返回要生产的对象的类型
return HappyMachine8.class;
}
}
<bean id="happyMachine7" class="com.atguigu.ioc07.HappyFactoryBean">
<property name="machineName" value="iceCreamMachine"/>
bean>
@Test
public void testExperiment07() {
ApplicationContext iocContainer = new ClassPathXmlApplicationContext("spring-bean-07.xml");
// 注意: 直接根据声明FactoryBean的id,获取的是getObject方法返回的对象
HappyMachine8 happyMachine = iocContainer.getBean("happyMachine7", HappyMachine8.class);
System.out.println("happyMachine7 = " + happyMachine);
// 如果想要获取FactoryBean对象, 直接在id前添加&符号即可! &happyMachine7 这是一种固定的约束
Object bean = iocContainer.getBean("&happyMachine7");
System.out.println("bean = " + bean);
}
**FactoryBean **是 Spring 中一种特殊的 bean,可以在 getObject() 工厂方法自定义的逻辑创建Bean!是一种能够生产其他 Bean 的 Bean。FactoryBean 在容器启动时被创建,而在实际使用时则是通过调用 getObject() 方法来得到其所生产的 Bean。因此,FactoryBean 可以自定义任何所需的初始化逻辑,生产出一些定制化的 bean。
一般情况下,整合第三方框架,都是通过定义FactoryBean实现!!!
BeanFactory 是 Spring 框架的基础,其作为一个顶级接口定义了容器的基本行为,例如管理 bean 的生命周期、配置文件的加载和解析、bean 的装配和依赖注入等。BeanFactory 接口提供了访问 bean 的方式,例如 getBean() 方法获取指定的 bean 实例。
它可以从不同的来源(例如 Mysql 数据库、XML 文件、Java 配置类等)获取 bean 定义,并将其转换为 bean 实例。同时,BeanFactory 还包含很多子类(例如,ApplicationContext 接口)提供了额外的强大功能。
总的来说,FactoryBean 和 BeanFactory 的区别主要在于前者是用于创建 bean 的接口,它提供了更加灵活的初始化定制功能,而后者是用于管理 bean 的框架基础接口,提供了基本的容器功能和 bean 生命周期管理。
搭建一个三层架构案例,模拟查询全部学生(学生表)信息,持久层使用JdbcTemplate和Druid技术,使用XML方式进行组件管理!
CREATE TABLE students (
id INT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
gender VARCHAR(10) NOT NULL,
age INT,
class VARCHAR(50)
);
INSERT INTO students (id, name, gender, age, class)
VALUES
(1, '张三', '男', 20, '高中一班'),
(2, '李四', '男', 19, '高中二班'),
(3, '王五', '女', 18, '高中一班'),
(4, '赵六', '女', 20, '高中三班'),
(5, '刘七', '男', 19, '高中二班'),
(6, '陈八', '女', 18, '高中一班'),
(7, '杨九', '男', 20, '高中三班'),
(8, '吴十', '男', 19, '高中二班');
spring-xml-practice-02
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>6.0.6version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.25version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.2.8version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>6.0.6version>
dependency>
dependencies>
public class Student {
private Integer id;
private String name;
private String gender;
private Integer age;
private String classes;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getClasses() {
return classes;
}
public void setClasses(String classes) {
this.classes = classes;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", gender='" + gender + '\'' +
", age=" + age +
", classes='" + classes + '\'' +
'}';
}
}
为了在特定领域帮助我们简化代码,Spring 封装了很多 『Template』形式的模板类。例如:RedisTemplate、RestTemplate 等等,包括现在要学习的 JdbcTemplate。
jdbc.properties
文件,提取数据库连接信息
atguigu.url=jdbc:mysql://localhost:3306/studb
atguigu.driver=com.mysql.cj.jdbc.Driver
atguigu.username=root
atguigu.password=root
spring-ioc.xml
配置文件
<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 https://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="classpath:jdbc.properties" />
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="${atguigu.url}"/>
<property name="driverClassName" value="${atguigu.driver}"/>
<property name="username" value="${atguigu.username}"/>
<property name="password" value="${atguigu.password}"/>
bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="druidDataSource"/>
bean>
beans>
基于jdbcTemplate的CRUD使用
public class JdbcTemplateTest {
/**
* 使用jdbcTemplate进行DML动作
*/
@Test
public void testDML() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-ioc.xml");
JdbcTemplate jdbcTemplate = applicationContext.getBean(JdbcTemplate.class);
// TODO 执行插入一条学员数据
String sql = "insert into students (id,name,gender,age,class) values (?,?,?,?,?);";
/*
参数1: sql语句
参数2: 可变参数,占位符的值
*/
int rows = jdbcTemplate.update(sql, 9, "十一", "男", 18, "二年三班");
System.out.println("rows = " + rows);
}
/**
* 查询单条实体对象
* public class Student {
* private Integer id;
* private String name;
* private String gender;
* private Integer age;
* private String classes;
*/
@Test
public void testDQLForPojo() {
String sql = "select id , name , age , gender , class as classes from students where id = ? ;";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-ioc.xml");
JdbcTemplate jdbcTemplate = applicationContext.getBean(JdbcTemplate.class);
// 根据id查询
Student student = jdbcTemplate.queryForObject(sql, (rs, rowNum) -> {
// 自己处理结果映射
Student stu = new Student();
stu.setId(rs.getInt("id"));
stu.setName(rs.getString("name"));
stu.setAge(rs.getInt("age"));
stu.setGender(rs.getString("gender"));
stu.setClasses(rs.getString("classes"));
return stu;
}, 2);
System.out.println("student = " + student);
}
/**
* 查询实体类集合
*/
@Test
public void testDQLForListPojo() {
String sql = "select id , name , age , gender , class as classes from students ;";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-ioc.xml");
JdbcTemplate jdbcTemplate = applicationContext.getBean(JdbcTemplate.class);
/*
query可以返回集合!
BeanPropertyRowMapper就是封装好RowMapper的实现,要求属性名和列名相同即可
*/
List<Student> studentList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Student.class));
System.out.println("studentList = " + studentList);
}
}
import com.atguigu.domain.Student;
import java.util.List;
// 接口
public interface StudentDao {
/**
* 查询全部学生数据
*
* @return
*/
List<Student> queryAll();
}
import com.atguigu.dao.StudentDao;
import com.atguigu.domain.Student;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.List;
// 实现类
public class StudentDaoImpl implements StudentDao {
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
/**
* 查询全部学生数据
*
* @return
*/
@Override
public List<Student> queryAll() {
String sql = "select id , name , age , gender , class as classes from students ;";
/*
query可以返回集合!
BeanPropertyRowMapper就是封装好RowMapper的实现,要求属性名和列名相同即可
*/
List<Student> studentList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Student.class));
return studentList;
}
}
import com.atguigu.domain.Student;
import java.util.List;
// 接口
public interface StudentService {
/**
* 查询全部学员业务
*
* @return
*/
List<Student> findAll();
}
import com.atguigu.dao.StudentDao;
import com.atguigu.domain.Student;
import com.atguigu.service.StudentService;
import java.util.List;
// 实现类
public class StudentServiceImpl implements StudentService {
private StudentDao studentDao;
public void setStudentDao(StudentDao studentDao) {
this.studentDao = studentDao;
}
/**
* 查询全部学员业务
*
* @return
*/
@Override
public List<Student> findAll() {
List<Student> studentList = studentDao.queryAll();
return studentList;
}
}
import com.atguigu.domain.Student;
import com.atguigu.service.StudentService;
import java.util.List;
public class StudentController {
private StudentService studentService;
public void setStudentService(StudentService studentService) {
this.studentService = studentService;
}
public void findAll() {
List<Student> studentList = studentService.findAll();
System.out.println("studentList = " + studentList);
}
}
spring-ioc.xml
<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 https://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="classpath:jdbc.properties" />
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="${atguigu.url}"/>
<property name="driverClassName" value="${atguigu.driver}"/>
<property name="username" value="${atguigu.username}"/>
<property name="password" value="${atguigu.password}"/>
bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="druidDataSource"/>
bean>
<bean id="studentDao" class="com.atguigu.dao.impl.StudentDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate" />
bean>
<bean id="studentService" class="com.atguigu.service.impl.StudentServiceImpl">
<property name="studentDao" ref="studentDao" />
bean>
<bean id="studentController" class="com.atguigu.controller.StudentController">
<property name="studentService" ref="studentService" />
bean>
beans>
import com.atguigu.controller.StudentController;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ControllerTest {
@Test
public void testRun() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-ioc.xml");
StudentController studentController = applicationContext.getBean(StudentController.class);
studentController.findAll();
}
}