1 IOC介绍
IOC(Inversion Of Control),即控制反转,是一种能够指导我们设计出松耦合、高可扩展应用程序的设计思想。
- 反转的是什么
正常情况下,我们会主动创建对象,然后再使用,对象的生命周期有我们通过编码控制。
而IOC思想是将对象的创建及对对象之间关系的维护交由第三方容器实现,反转的正式这个方面的内容。 - 实现方式
DI(Dependency Injection),即依赖注入,是实现IOC的一种方式,指Spring创建对象的过程中,将对象依赖属性通过配置进行注入。
IOC 就是一种控制反转的思想, 而 DI 是对IoC的一种具体实现。
2 Spring
Spring框架提供了IOC容器相关的实现方式,Spring通过IOC容器管理Bean及Bean之间的依赖关系,其实现方式包括如下两种:
- BeanFactory
这是 IoC 容器的基本实现,是 Spring 内部使用的接口。面向 Spring 本身,不提供给开发人员使用。 - ApplicationContext
BeanFactory 的子接口,提供了更多高级特性。面向 Spring 的使用者,几乎所有场合都使用 ApplicationContext 而不是底层的 BeanFactory。
类型名 | 说明 |
---|---|
ClassPathXmlApplicationContext | 通过读取类路径下的 XML 格式的配置文件创建 IOC 容器对象 |
FileSystemXmlApplicationContext | 通过文件系统路径读取 XML 格式的配置文件创建 IOC 容器对象 |
- Spring管理bean的两种方式:xml方式和注解
3 Spring基于xml管理bean
3.1 基础环境搭建
创建子模块giser-java-spring6-01,并引入配置文件beans.xml、log4j2.xml,添加如下依赖:
<dependencies>
<!-- Spring依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.1.1</version>
</dependency>
<!--junit5测试-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.10.1</version>
</dependency>
<!--log4j2的依赖-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.19.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j2-impl</artifactId>
<version>2.19.0</version>
</dependency>
</dependencies>
创建实体类:
public class HelloWorld {
public HelloWorld(){
System.out.println("constructor execute...");
}
public void sayHello(){
System.out.println("hello world!");
}
}
beans.xml配置
<?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">
<!--
向Spring容器中注册bean信息
id: 注册的bean的id,作为bean的唯一标识
class: 注册bean的类全路径,用户创建bean对象
-->
<bean id="helloWorld" class="com.giser.spring6.HelloWorld"></bean>
</beans>
log4j配置:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<loggers>
<!--
level指定日志级别,从低到高的优先级:
TRACE < DEBUG < INFO < WARN < ERROR < FATAL
trace:追踪,是最低的日志级别,相当于追踪程序的执行
debug:调试,一般在开发中,都将其设置为最低的日志级别
info:信息,输出重要的信息,使用较多
warn:警告,输出警告的信息
error:错误,输出错误信息
fatal:严重错误
-->
<root level="DEBUG">
<appender-ref ref="spring6log"/>
<appender-ref ref="RollingFile"/>
<appender-ref ref="log"/>
</root>
</loggers>
<appenders>
<!--输出日志信息到控制台-->
<console name="spring6log" target="SYSTEM_OUT">
<!--控制日志输出的格式-->
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss SSS} [%t] %-3level %logger{1024} - %msg%n"/>
</console>
<!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,适合临时测试用-->
<File name="log" fileName="d:/spring6_log/test.log" append="false">
<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
</File>
<!-- 这个会打印出所有的信息,
每次大小超过size,
则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,
作为存档-->
<RollingFile name="RollingFile" fileName="d:/spring6_log/app.log"
filePattern="log/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz">
<PatternLayout pattern="%d{yyyy-MM-dd 'at' HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n"/>
<SizeBasedTriggeringPolicy size="50MB"/>
<!-- DefaultRolloverStrategy属性如不设置,
则默认为最多同一文件夹下7个文件,这里设置了20 -->
<DefaultRolloverStrategy max="20"/>
</RollingFile>
</appenders>
</configuration>
3.2 获取bean的三种方式
public class HelloWorldTest {
private Logger logger = LoggerFactory.getLogger(HelloWorldTest.class);
@Test
public void testSpringBean() {
// 读取配置信息
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
}
}
- 根据bean标签的id获取
// 根据id获取bean
HelloWorld bean1 = (HelloWorld) applicationContext.getBean("helloWorld");
- 根据bean的类型获取
// 根据bean的类型获取bean,此时要求此类型的bean只能存在一个,否则会抛异常
HelloWorld bean2 = applicationContext.getBean(HelloWorld.class);
- 根据id和类型获取bean
HelloWorld bean3 = applicationContext.getBean("helloWorld", HelloWorld.class);
3.3 DI之setter注入
实体:
public class User {
private String userName;
private Integer userAge;
// 无参构造函数
// 全参构造函数
// getter setter toString
}
beans-di.xml
<?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">
<!--
set方法注入
-->
<bean id="user" class="com.giser.spring6.entity.User">
<property name="userName" value="张三"/>
<property name="userAge" value="13" />
</bean>
</beans>
测试
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans-di.xml");
User user = applicationContext.getBean("user", User.class);
3.4 DI之构造器注入
<!--
constructor注入
name: 指定参数名
value: 参数值
index属性:指定参数所在位置的索引(从0开始)
-->
<bean id="user1" class="com.giser.spring6.entity.User">
<constructor-arg name="userName" value="张三" />
<constructor-arg name="userAge" value="23" />
</bean>
测试
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans-di.xml");
User user1 = applicationContext.getBean("user1", User.class);
3.5 DI之特殊值注入
<!--
特殊值处理:字面量赋值
-->
<!-- 使用value属性给bean的属性赋值时,Spring会把value属性的值看做字面量 -->
<bean id="user2" class="com.giser.spring6.entity.User">
<property name="userName" value="张三"/>
<property name="userAge" value="13" />
</bean>
<!--
特殊值处理:null值
-->
<bean id="user3" class="com.giser.spring6.entity.User">
<property name="userName">
<null />
</property>
<property name="userAge" value="13" />
</bean>
<!--
特殊值处理:xml实体
-->
<bean id="user4" class="com.giser.spring6.entity.User">
<property name="userName" value="a < b"></property>
<property name="userAge" value="13" />
</bean>
<!--
特殊值处理:CDATA节
CDATA中的C代表Character,是文本、字符的含义,CDATA就表示纯文本数据;
XML解析器看到CDATA节就知道这里是纯文本,就不会当作XML标签或属性来解析;
所以CDATA节中写什么符号都随意;
-->
<bean id="user5" class="com.giser.spring6.entity.User">
<property name="userName">
<value><![CDATA[A<=B]]></value>
</property>
<property name="userAge" value="13" />
</bean>
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans-di.xml");
User user2 = applicationContext.getBean("user2", User.class);
User user3 = applicationContext.getBean("user3", User.class);
User user4 = applicationContext.getBean("user4", User.class);
User user5 = applicationContext.getBean("user5", User.class);
logger.info("特殊值处理-字面量赋值:{}", user2);
logger.info("特殊值处理-null值:{}", user3);
logger.info("特殊值处理-xml实体:{}", user4);
logger.info("特殊值处理-CDATA节:{}", user5);
3.6 DI之对象类型赋值
包括三种方式:引入外部bean、使用内部bean、级联属性赋值。
实体:
public class Dept {
private String deptName;
// getter setter toString
}
public class Emp {
private String empName;
private Integer empAge;
private Dept dept;
// getter setter toString
}
xml文件
<?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="dept" class="com.giser.spring6.iocpojo.Dept">
<property name="deptName" value="产品研发部" />
</bean>
<bean id="emp1" class="com.giser.spring6.iocpojo.Emp">
<property name="empName" value="小明" />
<property name="empAge" value="32" />
<!-- 对象类型属性,通过ref引入外部bean -->
<property name="dept" ref="dept" />
</bean>
<!--
对象类型赋值方式二:使用内部bean
-->
<bean id="emp2" class="com.giser.spring6.iocpojo.Emp">
<property name="empName" value="小明" />
<property name="empAge" value="32" />
<!-- 对象类型属性,使用内部bean -->
<property name="dept" >
<!-- 内部bean只能用于给属性赋值,不能在外部通过IOC容器获取,因此可以省略id属性 -->
<bean class="com.giser.spring6.iocpojo.Dept">
<property name="deptName" value="董事会" />
</bean>
</property>
</bean>
<!--
对象类型赋值方式三:级联属性赋值
-->
<bean id="dept3" class="com.giser.spring6.iocpojo.Dept">
<property name="deptName" value="产品研发部" />
</bean>
<bean id="emp3" class="com.giser.spring6.iocpojo.Emp">
<property name="empName" value="小明" />
<property name="empAge" value="32" />
<!-- 对象类型属性,级联属性赋值 -->
<property name="dept" ref="dept3" />
<property name="dept.deptName" value="中央办公室" />
</bean>
</beans>
测试:
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans-obj-di.xml");
Emp emp = applicationContext.getBean("emp3", Emp.class);
logger.info("对象类型属性赋值:{}", emp);
3.7 DI之数组类型属性赋值
public class Student {
private String stuName;
private String[] hobbyArr;
// 无参构造函数
// 全参构造函数
// getter setter toString
}
<?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="stu" class="com.giser.spring6.iocarray.Student">
<property name="stuName" value="张三" />
<property name="hobbyArr">
<array>
<value>吃饭</value>
<value>睡觉</value>
<value>敲代码</value>
</array>
</property>
</bean>
</beans>
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans-arr-di.xml");
Student stu = applicationContext.getBean("stu", Student.class);
logger.info("数组类型属性赋值:{}", stu);
3.8 DI之List类型属性赋值
public class Cls {
private List<Stu> stuList;
private String clsName;
// 无参构造函数
// 全参构造函数
// getter setter toString
}
public class Stu {
private String stuName;
private List<String> hobbyList;
// 无参构造函数
// 全参构造函数
// getter setter toString
}
<?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="stu" class="com.giser.spring6.ioclist.Stu">
<property name="stuName" value="张三" />
<property name="hobbyList">
<list>
<value>吃饭</value>
<value>睡觉</value>
<value>敲代码</value>
</list>
</property>
</bean>
<bean id="stu2" class="com.giser.spring6.ioclist.Stu">
<property name="stuName" value="李四" />
<property name="hobbyList">
<list>
<value>烧烤</value>
<value>炸鸡</value>
<value>胡辣汤</value>
</list>
</property>
</bean>
<!--
数组类型属性赋值: 引用类型
-->
<bean id="cls" class="com.giser.spring6.ioclist.Cls">
<property name="clsName" value="中一班" />
<property name="stuList">
<list>
<ref bean="stu" />
<ref bean="stu2" />
</list>
</property>
</bean>
</beans>
private Logger logger = LoggerFactory.getLogger(SpringListDITest.class);
@Test
public void testSpecialVal(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans-list-di.xml");
Stu stu = applicationContext.getBean("stu", Stu.class);
logger.info("List集合类型属性赋值:{}", stu);
}
@Test
public void testSpecialVal2(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans-list-di.xml");
Cls cls = applicationContext.getBean("cls", Cls.class);
logger.info("List集合类型属性赋值:{}", cls);
}
3.9 DI之Map类型属性赋值
public class Family {
private String familyName;
private Map<String, Mem> memMap;
// 无参构造函数
// 全参构造函数
// getter setter toString
}
public class Mem {
private String memName;
private Integer memAge;
// 无参构造函数
// 全参构造函数
// getter setter toString
}
<?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="mem1" class="com.giser.spring6.iocmap.Mem">
<property name="memAge" value="11" />
<property name="memName" value="小李" />
</bean>
<bean id="mem2" class="com.giser.spring6.iocmap.Mem">
<property name="memAge" value="22" />
<property name="memName" value="小张"/>
</bean>
<!--
数组类型属性赋值: 引用类型
-->
<bean id="family" class="com.giser.spring6.iocmap.Family">
<property name="familyName" value="梦想家" />
<property name="memMap">
<map>
<entry>
<key>
<value>1001</value>
</key>
<ref bean="mem1"/>
</entry>
<entry>
<key>
<value>1002</value>
</key>
<ref bean="mem2"/>
</entry>
</map>
</property>
</bean>
<bean id="family2" class="com.giser.spring6.iocmap.Family">
<property name="familyName" value="梦想家" />
<property name="memMap">
<map>
<entry value-ref="mem1">
<key>
<value>1001</value>
</key>
</entry>
<entry value-ref="mem2">
<key>
<value>1002</value>
</key>
</entry>
</map>
</property>
</bean>
</beans>
public class SpringMapDITest {
private Logger logger = LoggerFactory.getLogger(SpringMapDITest.class);
@Test
public void testSpecialVal(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans-map-di.xml");
Family family = applicationContext.getBean("family", Family.class);
logger.info("对象类型属性赋值:{}", family);
Family family2 = applicationContext.getBean("family2", Family.class);
logger.info("对象类型属性赋值:{}", family2);
}
}
3.10 DI之引用集合类型注入
public class Family {
private String familyName;
private Map<String, Mem> memMap;
private List<Mem> memList;
// 无参构造函数
// 全参构造函数
// getter setter toString
public class Mem {
private String memName;
private Integer memAge;
// 无参构造函数
// 全参构造函数
// getter setter toString
<?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:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="family" class="com.giser.spring6.iocref.Family">
<property name="familyName" value="一家人" />
<!--
引用集合类型的bean
-->
<property name="memMap" ref="memMap" />
<property name="memList" ref="memList" />
</bean>
<util:list id="memList">
<ref bean="mem1" />
<ref bean="mem2" />
<ref bean="mem3" />
</util:list>
<util:map id="memMap">
<entry>
<key>
<value>1001</value>
</key>
<ref bean="mem1"/>
</entry>
<entry>
<key>
<value>1002</value>
</key>
<ref bean="mem2"/>
</entry>
</util:map>
<bean id="mem1" class="com.giser.spring6.iocref.Mem">
<property name="memAge" value="11" />
<property name="memName" value="小李" />
</bean>
<bean id="mem2" class="com.giser.spring6.iocref.Mem">
<property name="memAge" value="22" />
<property name="memName" value="小张"/>
</bean>
<bean id="mem3" class="com.giser.spring6.iocref.Mem">
<property name="memAge" value="33" />
<property name="memName" value="小王"/>
</bean>
</beans>
3.11 DI之通过p标签为属性赋值
<?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:util="http://www.springframework.org/schema/util"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
p标签使用
-->
<bean id="family" class="com.giser.spring6.iocref.Family"
p:familyName="一家人"
p:memMap-ref="memMap"
p:memList-ref="memList"
>
</bean>
<util:list id="memList">
<ref bean="mem1" />
<ref bean="mem2" />
<ref bean="mem3" />
</util:list>
<util:map id="memMap">
<entry>
<key>
<value>1001</value>
</key>
<ref bean="mem1"/>
</entry>
<entry>
<key>
<value>1002</value>
</key>
<ref bean="mem2"/>
</entry>
</util:map>
<bean id="mem1" class="com.giser.spring6.iocref.Mem">
<property name="memAge" value="11" />
<property name="memName" value="小李" />
</bean>
<bean id="mem2" class="com.giser.spring6.iocref.Mem">
<property name="memAge" value="22" />
<property name="memName" value="小张"/>
</bean>
<bean id="mem3" class="com.giser.spring6.iocref.Mem">
<property name="memAge" value="33" />
<property name="memName" value="小王"/>
</bean>
</beans>
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans-p-di.xml");
Family family = applicationContext.getBean("family", Family.class);
logger.info("p标签属性赋值:{}", family);
3.12 DI之引入外部属性文件
jdbc.properties
jdbc.user=root
jdbc.password=root
jdbc.url=jdbc:mysql://localhost:3306/db_mybatis?serverTimezone=UTC
jdbc.driver=com.mysql.cj.jdbc.Driver
beans-jdbc.xml
<?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/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
引入外部属性文件
注意:在使用 context:property-placeholder 元素加载外包配置文件功能前,首先需要在 XML 配置的一级标签 <beans> 中添加 context 相关的约束。
-->
<context:property-placeholder location="classpath:jdbc.properties" />
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="${jdbc.url}"/>
<property name="password" value="${jdbc.password}" />
<property name="username" value="${jdbc.user}" />
<property name="driverClassName" value="${jdbc.driver}" />
</bean>
</beans>
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans-ds.xml");
DruidDataSource druidDataSource = applicationContext.getBean(DruidDataSource.class);
logger.info("数据库对象:{}", druidDataSource);
3.13 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">
<!--
创建对象,并设置作用域范围:
默认为单例singleton,bean在IOC容器中只有一个实例,IOC容器初始化时创建对象;
可通过scope属性设置为多实例对象[scope为prototype],bean在IOC容器中可以有多个实例,getBean()时创建对象
-->
<bean id="apple" class="com.giser.spring6.scope.Apple" scope="prototype" />
</beans>
public class SpringScopeTest {
private final Logger logger = LoggerFactory.getLogger(SpringScopeTest.class);
/**
* scope=singleton时:
* [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'apple'
* [main] INFO spring6.SpringScopeTest - 单例对象:com.giser.spring6.scope.Apple@436390f4
* [main] INFO spring6.SpringScopeTest - 单例对象:com.giser.spring6.scope.Apple@436390f4
*
* scope="prototype"时:
* [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 1 bean definitions from class path resource [beans-scope.xml]
* [main] INFO spring6.SpringScopeTest - 单例对象:com.giser.spring6.scope.Apple@733037
* [main] INFO spring6.SpringScopeTest - 单例对象:com.giser.spring6.scope.Apple@7728643a
*
* 如果是在WebApplicationContext环境下还会有另外几个作用域(但不常用)
* request:在一个请求域内有效
* session:在一个会话范围内有效
*/
@Test
public void testScope(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans-scope.xml");
Apple apple = applicationContext.getBean(Apple.class);
logger.info("单例对象:{}", apple);
Apple apple2 = applicationContext.getBean(Apple.class);
logger.info("单例对象:{}", apple2);
}
}
3.14 bean的生命周期
package com.giser.spring6.lifecycle;
/**
* @author giserDev
* @description
* @date 2023-12-31 21:22:01
*/
public class Phone {
private String phoneName;
public Phone() {
System.out.println("Bean生命周期:[1 bean对象创建(调用无参构造方法)]");
}
public void initMethod(){
System.out.println("Bean生命周期:[4 bean对象初始化(调用bean配置时init-method属性指定的初始化方法)] ");
}
public void destroyMethod(){
System.out.println("Bean生命周期:[7 bean对象销毁(调用bean配置时destroy-method属性指定的销毁方法)] ");
}
public String getPhoneName() {
return phoneName;
}
public void setPhoneName(String phoneName) {
System.out.println("Bean生命周期:[2 为对象属性赋值(调用属性set方法)] ");
this.phoneName = phoneName;
}
}
package com.giser.spring6.lifecycle;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
/**
* @author giserDev
* @description
* @date 2023-12-31 21:26:40
*/
public class MyBeanProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("Bean生命周期:[3 bean后置处理器(在bean初始化之前执行)] ");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("Bean生命周期:[5 bean后置处理器(在bean初始化之后执行)] ");
return 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">
<!--
init-method: 设置对象初始化方法
destroy-method: 设置对象销毁方法
scope: 设置对象作用域
-->
<bean id="phone" class="com.giser.spring6.lifecycle.Phone"
init-method="initMethod"
destroy-method="destroyMethod"
scope="singleton" >
<property name="phoneName" value="小米手机" />
</bean>
<!-- bean的后置处理器要放入IOC容器才能生效 -->
<bean id="myBeanProcessor" class="com.giser.spring6.lifecycle.MyBeanProcessor" />
</beans>
import com.giser.spring6.lifecycle.Phone;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author giserDev
* @description
* @date 2023-12-31 21:30:42
*/
public class SpringBeanLifeCycleTest {
private Logger logger = LoggerFactory.getLogger(SpringBeanLifeCycleTest.class);
/**
* 在scope="singleton"情况下:
* 2023-12-31 22:09:45 404 [main] DEBUG org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@422c3c7a
* 2023-12-31 22:09:45 631 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 2 bean definitions from class path resource [beans-lifecycle.xml]
* 2023-12-31 22:09:45 679 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'myBeanProcessor'
* 2023-12-31 22:09:45 713 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'phone'
* Bean生命周期:[1 bean对象创建(调用无参构造方法)]
* Bean生命周期:[2 为对象属性赋值(调用属性set方法)]
* Bean生命周期:[3 bean后置处理器(在bean初始化之前执行)]
* Bean生命周期:[4 bean对象初始化(调用bean配置时init-method属性指定的初始化方法)]
* Bean生命周期:[5 bean后置处理器(在bean初始化之后执行)]
* 2023-12-31 22:09:45 787 [main] INFO spring6.SpringBeanLifeCycleTest - Bean生命周期:[6 bean对象准备就绪,可以使用了]
* 2023-12-31 22:09:45 789 [main] DEBUG org.springframework.context.support.ClassPathXmlApplicationContext - Closing org.springframework.context.support.ClassPathXmlApplicationContext@422c3c7a, started on Sun Dec 31 22:09:45 CST 2023
* Bean生命周期:[7 bean对象销毁(调用bean配置时destroy-method属性指定的销毁方法)]
* 2023-12-31 22:09:45 793 [main] DEBUG org.springframework.beans.factory.support.DisposableBeanAdapter - Custom destroy method 'destroyMethod' on bean with name 'phone' completed
*/
@Test
public void test01(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans-lifecycle.xml");
Phone phone = applicationContext.getBean(Phone.class);
logger.info("Bean生命周期:[6 bean对象准备就绪,可以使用了]");
applicationContext.close();
}
}
3.15 FactoryBean
public class UserInfo {
}
public class UserFactoryBean implements FactoryBean<UserInfo> {
@Override
public UserInfo getObject() throws Exception {
return new UserInfo();
}
@Override
public Class<?> getObjectType() {
return UserInfo.class;
}
@Override
public boolean isSingleton() {
return FactoryBean.super.isSingleton();
}
}
<?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="userInfo" class="com.giser.spring6.factorybean.UserFactoryBean" />
</beans>
public class SpringFactoryBeanTest {
private Logger logger = LoggerFactory.getLogger(SpringFactoryBeanTest.class);
/**
* FactoryBean是Spring提供的一种整合第三方框架的常用机制。
* 和普通的bean不同,配置一个FactoryBean类型的bean,
* 在获取bean的时候得到的并不是class属性中配置的这个类的对象,
* 而是getObject()方法的返回值。
* 在整合Mybatis时,Spring就是通过FactoryBean机制来帮我们创建SqlSessionFactory对象的。
*
* [main] DEBUG org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@14b030a0
* [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 1 bean definitions from class path resource [beans-factorybean.xml]
* [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'userInfo'
* [main] INFO spring6.SpringFactoryBeanTest - 通过FactoryBean获取的对象为com.giser.spring6.factorybean.UserInfo@255990cc
*/
@Test
public void testFactoryBean(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans-factorybean.xml");
UserInfo userInfo = applicationContext.getBean(UserInfo.class);
logger.info("通过FactoryBean获取的对象为{}", userInfo);
}
}
3.16 自动装配
UserDao
public interface UserDao {
void sayHello();
}
public class UserDaoImpl implements UserDao {
@Override
public void sayHello() {
System.out.println("userDao sayHello");
}
}
UserService
public interface UserService {
void sayHello();
}
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void sayHello() {
userDao.sayHello();
}
}
UserController
public class UserController {
private UserService userService;
public void setUserService(UserService userService) {
this.userService = userService;
}
public void sayHello(){
userService.sayHello();
}
}
自动装配
<?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">
<!--
自动装配:byType 根据类型自动装配bean
-->
<!--
<bean id="userController" class="com.giser.spring6.autowire.controller.UserController" autowire="byType" />
<bean id="userService" class="com.giser.spring6.autowire.service.impl.UserServiceImpl" autowire="byType" />
<bean id="userDao" class="com.giser.spring6.autowire.dao.impl.UserDaoImpl" autowire="byType" />
-->
<!--
自动装配:byName 根据名称自动装配bean
-->
<bean id="userController" class="com.giser.spring6.autowire.controller.UserController" autowire="byName" />
<bean id="userService" class="com.giser.spring6.autowire.service.impl.UserServiceImpl" autowire="byName" />
<bean id="userDao" class="com.giser.spring6.autowire.dao.impl.UserDaoImpl" autowire="byName" />
</beans>
测试
import com.giser.spring6.autowire.controller.UserController;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author giserDev
* @description
* @date 2023-12-31 22:51:15
*/
public class SpringAutowiredTest {
@Test
public void testAutowired(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans-autowire.xml");
UserController userController = applicationContext.getBean(UserController.class);
userController.sayHello();
}
}
4 Spring基于注解管理bean
从 Java 5 开始,Java 增加了对注解(Annotation)的支持,它是代码中的一种特殊标记,可以在编译、类加载和运行时被读取,执行相应的处理。Spring 从 2.5 版本开始提供了对注解技术的全面支持,我们可以使用注解来实现自动装配,简化 Spring 的 XML 配置。
Spring 通过注解实现自动装配的步骤如下:
- 引入依赖
- 开启组件扫描
- 使用注解定义 Bean
- 依赖注入
4.1 基础环境搭建
创建子模块giser-java-spring6-ioc-annotation,并引入配置文件beans.xml、log4j2.xml,添加如下依赖:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<!--log4j2的依赖-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j2-impl</artifactId>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- 数据源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<!--
@Resource注解属于JDK扩展包,不在JDK当中,所以需要额外引入以下依赖:
【如果是JDK8的话不需要额外引入依赖。高于JDK11或低于JDK8需要引入以下依赖。】
-->
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>2.1.1</version>
</dependency>
</dependencies>
4.2 开启组件扫描
Spring 默认不使用注解装配 Bean,因此我们需要在 Spring 的 XML 配置中,通过 context:component-scan 元素开启 Spring Beans的自动扫描功能。开启此功能后,Spring 会自动从扫描指定的包(base-package 属性设置)及其子包下的所有类,如果类上使用了 @Component 注解,就将该类装配到容器中。示例如下:
<?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">
<!--
开启组件扫描:
在Spring中默认不开启组件扫描,因此需要在xml中通过context:component-scan开启组件扫描功能。
开启扫描后,会自动扫描所有子包中的类,如果使用了注解,如@Component、@Controller等,会将该类装配到容器中。
-->
<!-- 配置方式一: 扫描包下所有组件 -->
<context:component-scan base-package="com.giser.spring6.anno" />
</beans>
注意: 在使用 context:component-scan 元素开启自动扫描功能前,首先需要在 XML 配置的一级标签 中添加 context 相关的约束。
有三种情况:
- 扫描包下所有组件
<!--
开启组件扫描:
在Spring中默认不开启组件扫描,因此需要在xml中通过context:component-scan开启组件扫描功能。
开启扫描后,会自动扫描所有子包中的类,如果使用了注解,如@Component、@Controller等,会将该类装配到容器中。
-->
<!-- 配置方式一: 扫描包下所有组件 -->
<context:component-scan base-package="com.giser.spring6.anno" />
- 可指定排除的组件
<!-- 配置方式二: 可指定排除的组件 -->
<context:component-scan base-package="com.giser.spring6.anno" >
<!--
指定排除的组件:
type:组件类型,可取值包括annotation、assignable、aspectj、regex、custom
"annotation" indicates an annotation to be present or meta-present at the type level in target components;
"assignable" indicates a class (or interface) that the target components are assignable to (extend/implement);
"aspectj" indicates an AspectJ type pattern expression to be matched by the target components;
"regex" indicates a regex pattern to be matched by the target components' class names;
"custom" indicates a custom implementation of the org.springframework.core.type.TypeFilter interface.
expression:Indicates the filter expression, the type of which is indicated by "type".
-->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
- 仅扫描指定组件
<!-- 配置方式三: 仅扫描指定组件 -->
<!--
use-default-filters:
Indicates whether automatic detection of classes annotated with @Component, @Repository, @Service,
or @Controller should be enabled. Default is "true".
use-default-filters="false" 取消自动扫描以上几种注解
-->
<context:component-scan base-package="com.giser.spring6.anno" use-default-filters="false">
<!-- context:include-filter标签:指定在原有扫描规则的基础上追加的规则 -->
<!-- use-default-filters属性:取值false表示关闭默认扫描规则 -->
<!-- 此时必须设置use-default-filters="false",因为默认规则即扫描指定包下所有类 -->
<!--
type:设置排除或包含的依据
type="annotation",根据注解扫描,expression中设置要排除的注解的全类名
type="assignable",根据类型排除,expression中设置要排除的类型的全类名
-->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<!--<context:include-filter type="assignable" expression="com.giser.spring6.controller.UserController"/>-->
</context:component-scan>
4.3 使用注解定义bean
4.3.1 @Autowired
此注解可以用在构造方法上、方法上、形参上、属性上、注解上。
- 单独使用@Autowired注解,默认根据类型装配,即默认是byType
@Autowired
private UserService userService;
- 通过set方法注入
private UserService userService;
/**
* 通过set方法注入
* @param userService
*/
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
- 通过构造方法注入
private UserService userService;
/**
* 根据构造方法注入
* Autowiring by type from bean name 'userController' via constructor to bean named 'userServiceImpl'
* @param userService
*/
@Autowired
public UserController(UserService userService) {
this.userService = userService;
}
- 形参注入
private UserService userService;
/**
* 形参上注入
* Autowiring by type from bean name 'userController' via constructor to bean named 'userServiceImpl'
* @param userService
*/
public UserController(@Autowired UserService userService) {
this.userService = userService;
}
- Autowired之只有一个构造函数时,无需注解也可以注入
private UserService userService;
/**
* 只有一个构造函数时,无需注解也可以注入
* Autowiring by type from bean name 'userController' via constructor to bean named 'userServiceImpl'
* @param userService
*/
public UserController(UserService userService) {
this.userService = userService;
}
- Autowired之使用@Qualifier指定bean名称(多实现情况)
/**
* @Autowired默认是根据类型注入的,如果需要根据名称注入,需添加@Qualifier注解
*
*/
@Autowired
@Qualifier("userDaoImpl")
private UserDao userDao;
4.3.2 @Resource
使用@Resource注解需要引入以下坐标:
<!--
@Resource注解属于JDK扩展包,不在JDK当中,所以需要额外引入以下依赖:
【如果是JDK8的话不需要额外引入依赖。高于JDK11或低于JDK8需要引入以下依赖。】
-->
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
</dependency>
/**
* 根据名称注入
* 默认byName注入,没有指定name时把属性名当做name,根据name找不到时,才会byType注入。
* byType注入时,某种类型的Bean只能有一个.
* 当@Resource注解使用时没有指定name的时候,还是根据name进行查找,这个name是属性名
*/
@Resource(name = "myUserService")
private UserService userService;
4.4 Spring全注解开发
创建配置类
@Configuration
@ComponentScan(basePackages = "com.giser.spring6.anno")
public class SpringConfig {
}
@Test
public void testFullAnno(){
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
MyUserController myUserController = applicationContext.getBean("myUserController", MyUserController.class);
myUserController.exec();
}