文章目录
容器:IoC
Inversion of Control,作为一种重要的面向对象编程设计思想,指导设计出低耦合、更优良的程序;
Spring通过IoC容器来管理**所有Java对象的实例化和初始化,控制对象与对象间的依赖关系;
由IoC容器管理的对象称为SPring Bean,但实质上与new
创建的Java对象没有任何区别;
IoC容器
控制反转
- 作为一种重要的面向对象编程的设计思想;
- 目的是为了降低程序耦合度,提高程序扩展力;
- 反转的内容是:
将对象的创建权利移交给第三方容器负责;
将对象与对象间的关系维护权移交给第三方容器负责;
- 由DI(Dependency Injection)依赖注入实现该思想;
依赖注入
- 定义:Spring创建对象的过程中,将对象依赖属性通过配置进行注入,有两种方式:
set注入
构造注入
- Bean管理:Bean对象的创建,以及Bean对象中属性的赋值(或称为Bean对象间关系的维护);
IoC容器在Spring中的实现
- BeanFactory:作为IoC容器的基本实现形式,是Spring内部接口,不提供给使用者;
- ApplicationContext:BeanFactory的子接口,面向使用者,几乎所有场合都使用该接口;
- AppliicationContext的主要实现类:
基于XML管理Bean
搭建子模块spring_ioc_xml
- 将原子项目中的依赖移至父工程的依赖中,让整个工程都能够使用;
- 创建子模块
spring_ioc_xml
; - 创建
User
类和bean.xml
配置文件;
获取Bean
根据
id
获取,返回object
类型,需要强制类型转换;
根据类型获取,返回对应类型,要求指定类型Bean只能有一个,多个则选择第三个方法;
根据id
和类型获取,返回对应类型;
同理,当接口实现类的Bean唯一时,可以通过接口类型直接获取Bean。反之多个实现类,则会出现Bean不唯一的错误;
<bean id="userDaoImpl" class="com.jobs.spring6.iocxml.bean.UserDaoImpl"></bean>
依赖注入
- 类属性在创建对象过程中有两种方式设定属性值:
基于
set
方法:对象.set(xxx);
基于构造器:类 对象 = new 类("xxx");
set
方法注入
创建类,生产对应setter
方法
<bean id="car" class="com.jobs.spring6.iocxml.di.Car">
<!--
property:通过组件类的setXxx()方法给组件对象设置属性
name:指定属性名(其通过getXxx、setXxx方法定义的,与成员变量无关)
value:指定属性值
-->
<property name="name" value="雷军"></property>
<property name="brand" value="小米汽车"></property>
</bean>
- 构造器注入
创建类,生成有参构造方法
<bean id="car2" class="com.jobs.spring6.iocxml.di.Car">
<!--
constructor-arg;通过组件类的有参构造方法对组件对象设置属性
name:指定属性名
index:指定属性索引
value:指定属性值
-->
<constructor-arg name="name" value="马克思"></constructor-arg>
<constructor-arg index="1" value="Tesla"></constructor-arg>
</bean>
特殊值处理
- 字面量赋值
使用value
属性赋值时,spring会将该属性的值视为字面量; null
值
value
会将"null"
视为字面量并赋值给对象,若要留空值需:
<property name="name">
<null/>
</property>
- xml实体
在XML文档中存在特殊标签,无法直接作为值使用,需要使用转义字符代替:
eg.
< = <
> = >
...value="≶>"... ===> "<>"
CDATA
节
CDATA代表纯文本数据,使得XML解析器不会对节中内容当作XML标签或属性解析:
eg.
<property name="name">
<value><![CDATA[a < b]]></value>
</property>
对象类型赋值
- 外部Bean注入
- 注入的部门类
public class Department {
private String dept;
public String getDept() {
return dept;
}
public void setDept(String dept) {
this.dept = dept;
}
public String info(){
return dept;
}
}
- 被注入的面试者类
package com.jobs.spring6.iocxml.di;
public class Employee {
public Employee(String name, int age, Department dept) {
this.name = name;
this.age = age;
this.dept = dept;
}
public Employee() {}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setDept(Department dept) {
this.dept = dept;
}
private String name;
private int age;
private Department dept;
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", age=" + age +
", dept=" + dept.info() +
'}';
}
}
- 注入过程
<bean id="dept" class="com.jobs.spring6.iocxml.di.Department">
<property name="dept" value="保安"></property>
</bean>
<bean id="employee" class="com.jobs.spring6.iocxml.di.Employee">
<property name="name" value="Lux"></property>
<property name="age" value="19"></property>
<!--
ref:指定其它Bean的id,以此完成外部Bean注入
-->
<property name="dept" ref="dept"></property>
</bean>
- 内部Bean注入
- 注入过程
<bean id="employee2" class="com.jobs.spring6.iocxml.di.Employee">
<property name="name" value="Jinx"></property>
<property name="age" value="17"></property>
<property name="dept">
<!--
在value位置用需要注入的Bean代替
-->
<bean id="dept2" class="com.jobs.spring6.iocxml.di.Department">
<property name="dept" value="保安"></property>
</bean>
</property>
</bean>
但好像内部注入的Bean不会创建?
3. 级联属性赋值
<bean id="employee3" class="com.jobs.spring6.iocxml.di.Employee">
<property name="name" value="Jax"></property>
<property name="age" value="35"></property>
<!--
用ref标签外部注入后,
在name属性中指定注入对象的成员,
可以在此处重新注入对象值,
或许意味着可以多重注入(内部或外部)
-->
<property name="dept" ref="dept3"></property>
<property name="dept.dept" value="大厅保洁"></property>
</bean>
数组类型赋值
- 数组类
public class Person {
private String name;
private String[] habit;
public Person(String name, String[] habit) {
this.name = name;
this.habit = habit;
}
public Person() {}
public void setName(String name) {
this.name = name;
}
public void setHabit(String[] habit) {
this.habit = habit;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", habit=" + Arrays.toString(habit) +
'}';
}
}
- 注入过程
<bean id="person" class="com.jobs.spring6.iocxml.di_assemble.Person">
<property name="name" value="于谦"></property>
<property name="habit">
<!--
数组用array标签,
在后续用value依次存储单个元素
-->
<array>
<value>抽烟</value>
<value>喝酒</value>
<value>烫头</value>
</array>
</property>
</bean>
- 结果
集合类型赋值
- List集合
先在Department
中添加List集合,与对应方法
private List<Employee> employeeList;
- 注入过程与数组类似,使用
list
标签,同时用ref
引用外部Bean
<bean id="employee1" class="com.jobs.spring6.iocxml.di.Employee">
<property name="name" value="Ash"></property>
<property name="age" value="28"></property>
</bean>
<bean id="employee2" class="com.jobs.spring6.iocxml.di.Employee">
<property name="name" value="Ajax"></property>
<property name="age" value="24"></property>
</bean>
<bean id="dept" class="com.jobs.spring6.iocxml.di.Department">
<property name="dept" value="地勤"></property>
<property name="employeeList">
<list>
<ref bean="employee1"></ref>
<ref bean="employee2"></ref>
</list>
</property>
</bean>
- Map集合
student
类创建集合,及对应方法
private Map<String, Teacher> teacherMap;
- 注入过程类似,
<bean id="teacherone" class="com.jobs.spring6.iocxml.di_assemble.Teacher">
<property name="name" value="Walter"></property>
<property name="number" value="100"></property>
</bean>
<bean id="teachertwo" class="com.jobs.spring6.iocxml.di_assemble.Teacher">
<property name="name" value="Jason"></property>
<property name="number" value="101"></property>
</bean>
<bean id="student" class="com.jobs.spring6.iocxml.di_assemble.Student">
<property name="name" value="Lihua"></property>
<!--
类似的,
使用map标签对集合进行赋值,
单个元素用entry标签(List集合直接value或ref),
key标签对Map.key赋值,
value或ref标签对Map.value赋值
-->
<property name="teacherMap">
<map>
<entry>
<key>
<value>100111</value>
</key>
<ref bean="teacherone"></ref>
</entry>
<entry>
<key>
<value>101000</value>
</key>
<ref bean="teachertwo"></ref>
</entry>
</map>
</property>
</bean>
- 引用集合的Bean
在bean.xml
开始的beans
标签内添加util
约束(共3行),添加后才可以使用
- 为
Student
添加List
,及对应方法
private List<Lesson> lessonList;
- 注入过程中,先创建元素Bean,再使用util标签,最后向目标处赋值
<bean id="teacherone" class="com.jobs.spring6.iocxml.di_assemble.Teacher">
<property name="name" value="Walter"></property>
</bean>
<bean id="teachertwo" class="com.jobs.spring6.iocxml.di_assemble.Teacher">
<property name="name" value="Jason"></property>
</bean>
<bean id="lessonone" class="com.jobs.spring6.iocxml.di_assemble.Lesson">
<property name="name" value="cook"></property>
</bean>
<bean id="lessontwo" class="com.jobs.spring6.iocxml.di_assemble.Lesson">
<property name="name" value="drug"></property>
</bean>
<!--
加载约束后使用util标签单独建立集合的Bean,
在使用场景直接引用对应util的id即可
-->
<util:map id="teacherMap">
<entry>
<key>
<value>100111</value>
</key>
<ref bean="teacherone"></ref>
</entry>
<entry>
<key>
<value>101000</value>
</key>
<ref bean="teachertwo"></ref>
</entry>
</util:map>
<util:list id="lessonList">
<ref bean="lessonone"></ref>
<ref bean="lessontwo"></ref>
</util:list>
<bean id="student" class="com.jobs.spring6.iocxml.di_assemble.Student">
<property name="name" value="Lihua"></property>
<property name="teacherMap" ref="teacherMap"></property>
<property name="lessonList" ref="lessonList"></property>
</bean>
p名称空间注入
在beans
标签内添加p空间和注入的Bean,完成注入
### 引入外部属性文件
pom.xml
中加入依赖
<!--MySQL驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
<!--数据源-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.31</version>
</dependency>
- 创建
jdbc.properties
jdbc.user=root
jdbc.password=root
jdbc.url=jdbc:mysql://localhost:3306/ssm
jdbc.driver=com.mysql.cj.jdbc.Driver
- 在
bean.xml
中引入context
约束后,用context
标签引入外部属性文件jdbc.properties
- 注入过程
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<!--
在存在外部属性文件时,
可以在value标签中用${}引入外部属性
-->
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="driverClassName" value="${jdbc.driver}"></property>
</bean>
- 试验与对比
@Test
public void demo2(){
ApplicationContext context =
new ClassPathXmlApplicationContext("bean_jdbc.xml");
DruidDataSource dataSource = context.getBean(DruidDataSource.class);
System.out.println(dataSource.getUrl()/*getUsername、getPassword、getDriverClassName*/);
}
Bean的作用域
取值 | 含义 | 创建时机 |
---|---|---|
singleton(默认) | 在IoC容器中,该类Bean对象始终为单实例,即多次获取相同目标 | IoC容器初始化时 |
prototype | 该类Bean对象在IoC容器中存在多个实例,即每次获取新的实例 | 获取Bean时 |
注入时声明
<!--不使用scope指明时默认为singleton,单独指明为prototype才为多实例Bean-->
<bean id="user1" class="com.jobs.spring6.iocxml.User"
scope="singleton">
<property name="name" value="Max"></property>
</bean>
<bean id="user2" class="com.jobs.spring6.iocxml.User"
scope="prototype">
<property name="name" value="Wax"></property>
</bean>
对两种类型的Bean进行实验
@Test
public void userTest(){
ApplicationContext context =
new ClassPathXmlApplicationContext("bean_jdbc.xml");
User user1 = context.getBean("user1", User.class);
System.out.println(user1);
User user2 = context.getBean("user1", User.class);
System.out.println(user2);
User user3 = context.getBean("user2", User.class);
System.out.println(user3);
User user4 = context.getBean("user2", User.class);
System.out.println(user4);
}
由输出可知,单实例Bean对象唯一,多实例每次获取新对象;
同时在IoC容器初始化时完成单实例对象的创建,多实例只有在获取时才创建(所以不在输出中)
2024-04-03 14:21:17 689 [main] DEBUG org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@5c08c46a
2024-04-03 14:21:17 856 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 2 bean definitions from class path resource [bean_jdbc.xml]
2024-04-03 14:21:17 888 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'user1'
com.jobs.spring6.iocxml.User@63f34b70
com.jobs.spring6.iocxml.User@63f34b70
com.jobs.spring6.iocxml.User@56b78e55
com.jobs.spring6.iocxml.User@76318a7d
Bean的生命周期
- Bean对象的创建(默认调用类的无参构造)
- 向Bean对象注入相关属性
- Bean后置处理器(初始化前)
- Bean对象初始化(调用指定初始化方法,用init-method指定)
- Bean后置处理器(初始化后)
- Bean对象的使用
- Bean对象的销毁(调用指定销毁方法,用destroy-method指定)
<bean id="user" class="com.jobs.spring6.iocxml.User"
init-method="initMethod" destroy-method="destroyMethod">
<property name="name" value="Tax"></property>
</bean>
- 手动使用后置处理器需要类实现特定接口,同时后置处理并不针对特定某个Bean,而是对IoC容器中所有Bean对象生效
public class MyBeanPost implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
<bean id="myBeanProcessor" class="com.jobs.spring6.iocxml.MyBeanPost"></bean>
FactoryBean
不同于BeanFactory是spring内部的接口,用以达成IoC容器的实现,FactoryBean是spring提供的一种整合第三方框架的常用机制。配置该类型获取Bean时,得到的并不是class
属性中的类对象,而是getObject()
方法的返回值。
基于xml自动装配
- 自动装配:根据制定策略,在IoC容器中匹配某个Bean,自动为其中所依赖的类类型或接口类型属性赋值;
- 根据业务顺序,
controller
层需要注入service
层,service
层需要注入dao
层,原生完成方式是在上层中写入对下层接口的直接调用; - 创建
controller
/service
/dao
//controller
public class Controller {
public void setUserService(UserService userService) {
this.userService = userService;
}
private UserService userService;
public void controller(){
System.out.println("controller");
userService.service();
}
}
//serviceinterface
public interface UserService {
public void service();
}
//serviceimplement
public class UserServiceImpl implements UserService{
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void service() {
System.out.println("service");
userDao.dao();
}
}
//daointerface
public interface UserDao {
public void dao();
}
//daoimplement
public class UserDaoImpl implements UserDao{
@Override
public void dao() {
System.out.println("dao");
}
}
- 自动装配
<!--
autowire作用为自动装配:
byType时按照类型进行自动选择,
若没有对应类型===》装配为NULL,
若只有一个对应类型===》正确装配,
若有多个对应类型===》抛出异常;
byName时按照名称进行自动装配,
选择id与对应注入属性的属性名相同者进行赋值;
-->
<bean id="controller" class="com.jobs.spring6.iocxml.auto.controller.Controller"
autowire="byType"></bean>
<bean id="userService" class="com.jobs.spring6.iocxml.auto.service.UserServiceImpl"
autowire="byName"></bean>
<bean id="userDao" class="com.jobs.spring6.iocxml.auto.dao.UserDaoImpl"
autowire="byType"></bean>
- 测试
public class UserTest {
@Test
public void userTest(){
ApplicationContext context =
new ClassPathXmlApplicationContext("bean_auto.xml");
Controller controller = context.getBean("controller", Controller.class);
controller.controller();
}
}
2024-04-03 15:44:23 435 [main] DEBUG org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@61c9c3fd
2024-04-03 15:44:23 591 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 3 bean definitions from class path resource [bean_auto.xml]
2024-04-03 15:44:23 620 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'controller'
2024-04-03 15:44:23 657 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'userService'
2024-04-03 15:44:23 658 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'userDao'
controller
service
dao