二、IOC 容器
2.1 IOC 底层原理
概念和原理
什么是IOC?
- 控制反转,把对象创建和对象之间的调用过程,交给Spring进行管理
- 使用IOC目的:为了耦合度降低
- 人门案例就是IOC实现
IOC底层原理
- xml解析、工厂模式、放射
图解
IOC过程
### 2.2 IOC接口(BeanFactory)
IOC 思想基于IOC 容器完成,IOC 容器底层就是对象工厂
Spring 提供IOC 容器实现两种方式:(两个接口)
BeanFactory: IOC容器基本实现,是Spring内部使用的,不提供给开发人员进行使用
*加载配置文件时候不会创建对象,在获取对象(使用)才去创建对象
ApplicationContext:BeanFactory接口的子接口,提供更多更强大的功能,一般由开发人员进行使用
*加载配置文件时候就会把在配置文件对象进行创建
ApplicationContext接口有实现类:
CTRL + h: 打开类结构
主要的两个实现类:FileSystemXmlApplicationContext、ClassPathXmlApplicationContext
2.3 IOC 操作Bean管理层(基于xml)
2.3.1 IOC操作Bean 管理(概念)
- 什么是Bean管理
指两个操作(如下)
- Spring创建对象
- Spring注入属性
2.Bean管理操作有两种方式:
- 基于xml配置文件方式实现
- 基于注解方式实现
2.3.2 IOC 操作Bean管理(基于xml方式)
基于xml方式创建对象
1. - 在spring配置文件中,使用bean标签,标签里面添加对应属性,就可以实现对象创建
在bean标签有很多属性,常用属性:
*id属性:唯一标识
*class属性:类全路径(包类路径)
创建对象时候,默认也是执行无参数构造方法完成对象创建
基于xml方式注入属性
DI:依赖注入,就是注入属性
第一种注入方式:使用set方法进行注入
第一步
public class Book1 { private String bname; private String bauthor; public void setBname(String bname) { this.bname = bname; } public void setBauthor(String bauthor) { this.bauthor = bauthor; } }
第二步:在spring配置文件配置对象创建,配置属性注入
<!-- set方法注入属性--> <bean id="book" class="com.hxl.Book1"> <!-- property完成属性注入:name 类里面属性名称;value 向属性注入值--> <property name="bname" value="《悲惨的时间》"></property> <property name="bauthor" value="雨果"></property> </bean>
第二种注入方式:使用有参数构造注入
第一步
private String bname; private String bauthor; //有参构造 public Book1(String bname, String bauthor) { this.bname = bname; this.bauthor = bauthor; }
第二步:
<!-- 有参构造--> <bean id="book" class="com.hxl.Book1"> <constructor-arg name="bauthor" value="XxX"></constructor-arg> <constructor-arg name="bname" value="秘密花园"></constructor-arg> </bean>
第三种注入方式:p名称空间注入(了解)
基于xml方式注入其他类型属性
特殊值
字面量:空值
特殊符号
1. 1. 注入属性-外部bean
第一步:创建两个类service类和dao类
public interface UserDao { public void update(); }
public class UserImpl implements UserDao { @Override public void update() { System.out.println("Dao update ...."); } }
第二步:在service调用dao里面的方法
import com.hxl.dao.UserDao; public class UserService { // 创建UserDao类型属性,生成set方法 private UserDao userDao; public void setUserDao(UserDao userDao) { this.userDao = userDao; } public void add(){ System.out.println("Service add...."); userDao.update(); } }
第三步:在spring配置文件中进行配置
<!-- 1、service和dao对象创建--> <bean id="userService" class="com.hxl.service.UserService"> <!-- 注入userDao对象 name属性:类里面属性名称 ref属性:创建userDao对象bean标签id值--> <property name="userDao" ref="userDaoImpl"></property> </bean> <bean id="userDaoImpl" class="com.hxl.dao.UserImpl"></bean>
注入属性-内部bean
一对多关系:部门与员工
在实体类中表示一对多的关系
public class Dept { private String dname; public void setDname(String dname) { this.dname = dname; } @Override public String toString() { return "Dept{" + "dname='" + dname + '\'' + '}'; } }
public class Emp { private String ename; private String gender; private Dept dept; public void setEname(String ename) { this.ename = ename; } public void setGender(String gender) { this.gender = gender; } public void setDept(Dept dept) { this.dept = dept; } public void add(){ System.out.println(ename+"::"+gender+"::"+dept); } }
在spring配置文件中进行配置
<bean id="Emp" class="com.hxl.Emp"> <property name="ename" value="LJW"></property> <property name="gender" value="男"></property> <property name="dept"> <bean id="dept" class="com.hxl.Dept"> <property name="dname" value="人事部"></property> </bean> </property> </bean>
注入属性-级联赋值
第一种写法:与内部bean的区别是xml配置不太一样
<bean id="Emp" class="com.hxl.Emp"> <property name="ename" value="LJW"></property> <property name="gender" value="男"></property> <property name="dept" ref="dept"></property> </bean> <bean id="dept" class="com.hxl.Dept"> <property name="dname" value="人事部"></property> </bean>
第二种:
//添加get的方法 private Dept dept; public Dept getDept() { return dept; } <bean id="Emp" class="com.hxl.Emp"> <property name="ename" value="LJW"></property> <property name="gender" value="男"></property> <property name="dept" ref="dept"></property> <property name="dept.dname" value="技术部"></property> </bean> <bean id="dept" class="com.hxl.Dept"></bean>
IOC操作Bean管理(xml注入集合属性)
注入数组类型属性、List集合类型属性、Map集合类型属性
注入数组、List、Map、Set等集合类型属性
第一步:创建对象
package com.hxl.collectiontype; import java.util.List; import java.util.Map; import java.util.Set; public class Student { // 1.数组类型属性 private String[] courses; // 2.List集合类型属性 private List<String> lists; // 3.Map集合类型属性 private Map<String, String> maps; // 4.set集合类型属性 private Set<String> sets; public void setCourses(String[] courses) { this.courses = courses; } public void setLists(List[] lists) { this.lists = lists; } public void setMaps(Map<String, String> maps) { this.maps = maps; } public void setSets(Set<String> sets) { this.sets = sets; } }
第二步:Spring的xml配置
<bean id="student" class="com.hxl.collectiontype.Student"> <property name="courses"> <array> <value>JavaWeb</value> <value>数据库</value> <value>Java SE</value> </array> </property> <property name="lists"> <list> <value>Lily</value> <value>Candy</value> <value>Mike</value> </list> </property> <property name="maps"> <map> <entry key="JSE" value="JavaSE"></entry> <entry key="JAVA" value="JavaWeb"></entry> </map> </property> <property name="sets"> <set> <value>MySQL</value> <value>Redis</value> <value>Go</value> </set> </property> </bean>
在集合里面设置对象类型属性
案例代码如下:
//创建课程实体类 public class Course { private String cname; public void setCname(String cname) { this.cname = cname; } @Override public String toString() { return "Course{" + "cname='" + cname + '\'' + '}'; } } // 在学生类中 // 5.学生选了多门课程 private List<Course> courseList; public void setCourseList(List<Course> courseList) { this.courseList = courseList; } // 在xml中 <bean> <property name="courseList"> <list> <ref bean="course1"></ref> <ref bean="course2"></ref> </list> </property> </bean> <bean id="course1" class="com.hxl.collectiontype.Course"> <property name="cname" value="SpringBoot"></property> </bean> <bean id="course2" class="com.hxl.collectiontype.Course"> <property name="cname" value="Spring5"></property> </bean>
- 把集合注入部分提取出来
第一步:在spring配置文件中引入名称空间util
//创建实体类 import java.util.List; public class Book { private List<String> booklist; public void setBooklist(List<String> booklist) { this.booklist = booklist; } public void test(){ System.out.println(booklist); } } //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:p="http://www.springframework.org/schema/p" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">
第二步:使用util标签完成list集合注入提取
<!--1. 提取list集合类型属性注入--> <util:list id="booklist"> <value>SBCD</value> <value>SBCD</value> <value>SBCD</value> </util:list> <!--2.提取list集合类型属性注入使用--> <bean id="book" class="com.hxl.collectiontype.Book"> <property name="booklist" ref="booklist"></property> </bean>
IOC操作Bean管理(FactoryBean)
bean有两种:普通bean;工厂bean(FactoryBean)
普通bean:在配置文件中定义bean类型就是返回类型
工厂bean:在配置文件中定义bean类型可以返回类型不一样
第一步:创建类,让这个类作为工厂bean,实现接口FactoryBean
第二步:实现接口里面的方法,在实现的方法中定义返回的bean类型
//类 import org.springframework.beans.factory.FactoryBean; public class MyBean implements FactoryBean<Course> { // 定义返回bean @Override public Course getObject() throws Exception { Course course = new Course(); course.setCname("abc"); return course; } @Override public Class<?> getObjectType() { return null; } @Override public boolean isSingleton() { return FactoryBean.super.isSingleton(); } } //xml <bean id="myBean" class="com.hxl.collectiontype.MyBean"></bean> //测试 @Test public void test3(){ ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml"); Course course = context.getBean("myBean",Course.class); System.out.println(course); }
IOC操作Bean管理(作用域)
在spring里面,设置创建bean实例是单实例还是多实例
- 在spring里面,默认情况下,bean是单实例对象
Book book = context.getBean("book",Book.class); Book book1 = context.getBean("book",Book.class); System.out.println(book); System.out.println(book1);
地址一样,证明默认单实例对象。
如何设置单实例还是多实例对象
在spring配置文件bean标签里面有属性(scope)用于单实例还是多实例
scope属性值:
第一个值 默认值,singleton,表示单实例对象
第二个值 prototype,表示是多实例对象
<bean id="book" class="com.hxl.collectiontype.Book" scope="prototype"> <property name="booklist" ref="booklist"></property> </bean>
地址不一样,证明为多实例对象
singleton和prototype区别:
第一、singleton单实例,prototype多实例
第二、设置scope为singleton时,加载spring配置文件时会创建单实例对象
设置scope为prototype时,是在调用getBean方法时创建多实例对象,不是在加载spring配置文件时创
request和session,不怎么使用
IOC操作Bean管理(生命周期)
生命周期
- 从对象创建到对象销毁的过程
bean生命周期
- 通过构造器创建bean实例(无参构造)
- 为bean的属性设置值和对其他bean引用(调用set方法)
- 调用bean的初始化的方法(需要进行配置初始化的方法)
- bean可以使用了(对象获取到了)
- 当容器关闭时,调用bean的销毁方法(需要进行配置销毁方法)
演示bean生命周期
public class Order { private String oname; public Order() { System.out.println("第一步,无参构造"); } public void setOname(String oname) { this.oname = oname; System.out.println("第二步: 调用set方法"); } public void initMethod(){ System.out.println("第三步: 初始化方法"); } public void destroyMethod(){ System.out.println("第五步: 调用销毁"); } }
<?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 https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="order" class="com.hxl.beanlife.Order" init-method="initMethod" destroy-method="destroyMethod"> <property name="oname" value="生命周期"></property> </bean> </beans>
@Test public void test4(){ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml"); Order order = context.getBean("order",Order.class); System.out.println("第四步: 获取对象"); System.out.println(order); // 手动让bean实例销毁 context.close(); }
4.bean后置处理器(更完整的生命周期,有7步)
在第三步前后分别把bean实例传递bean后置处理器的方法
演示添加后置处理器效果:
- 创建类,实现接口BeanPostProcessor,创建后置处理器
import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.lang.Nullable; public class MyBeanPost implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("在第三步初始化之前"); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("在第三步初始化之后"); return bean; } }
- xml配置后置处理器
<bean id="myBeanPost" class="com.hxl.beanlife.MyBeanPost"></bean>
IOC操作Bean管理(自动装配)
什么是自动装配
根据指定装配规则(属性名称或者属性类型),Spring自动将配置的属性值进行注入
自动装配演示
<!-- 实现自动装配 bean标签属性autowire,配置自动装配 autowire属性常用两个属性: byName根据属性名称注入,注入值bean的id值和类属性名称一样 byType根据属性类型注入--> <bean id="emp" class="com.hxl.Emp" autowire="byType"> <!-- <property name="dept" ref="dept"></property><!–手动装配–>--> </bean> <bean id="dept" class="com.hxl.Dept"></bean>
IOC操作Bean管理(外部属性文件)
直接配置数据库信息
- 配置德鲁伊连接池
- 引入德鲁伊连接池依赖jar包
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.21</version> </dependency>
<!--直接配置连接池--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/user_db"></property> <property name="username" value="root"></property> <property name="password" value="tl123456"></property> </bean>
引入外部属性文件配置数据库连接池
- 创建外部属性文件,properties格式文件,写入数据库信息
prop.driverClass=com.mysql.jdbc.Driver prop.url=jdbc:mysql://localhost:3306/user_db prop.userName=root prop.password=tl123456
把外部properties属性文件引入到spring配置文件中
引入context名称空间
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
在spring配置文件使用标签引入外部属性文件
<!-- 引入外部属性文件--> <context:property-placeholder location="classpath:jdbc.properties"/> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${prop.driverClass}"></property> <property name="url" value="${prop.url}"></property> <property name="username" value="${prop.userName}"></property> <property name="password" value="${prop.password}"></property> </bean>
2.4 IOC 操作Bean管理(基于注解)
什么是注解
- 注解是代码特殊标记,格式:@注解名称(属性名称=属性值,属性名称=属性值…)
- 使用注解,注解作用在类上面,方法上面,属性上面
- 使用注解的目的:简化xml配置
Spring针对Bean管理中创建对象提供的注解
@Component:普通注解,泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注
@Service:用于标注业务层组件,业务逻辑层,service层
@Controller:用于标注控制层组件(如struts中的action)
@Repository:于标注数据访问组件,即DAO组件.
*上面四个注解功能一样,都可以用来创建bean实例
基于注解方式实现对象创建
引入依赖
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>5.3.19</version> </dependency>
开启组件扫描
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!-- 开启组件扫描 1.如果扫描多个包,使用逗号隔开 2.扫描包上层目录--> <!--示例1 use-default-filters="false" 表示现在不使用默认filter,用自己的filter <context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/> context:include-filter 设置扫描哪些内容--> <!--示例2 <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component"/> context:exclude-filter 设置哪些内容不扫描--> <context:component-scan base-package="com.hxl.zhujie.dao,com.hxl.zhujie.service"></context:component-scan> <context:component-scan base-package="com.hxl.zhujie"></context:component-scan> </beans>
创建类,在类上面添加创建对象注解
import org.springframework.stereotype.Component; //在注解里面value属性值可以忽略不行;默认值是类名称,首写字母小写 @Component(value = "userService")//<bean id="userService" class="..."/> 这里@Controller等等都行 public class UserService { public void add(){ System.out.println("Service add......"); } }
基于注解方式实现属性注入
@Autowired:根据属性类型进行自动装配
@Qualifier:根据属性名称进行注入
@Resource:可以根据类型注入,可以根据名称注入
![在这里插入图片描述](https://img-blog.csdnimg.cn/6f8e2c1c6c8c4b84bc814a1951e1fb4b.png#pic_center
@Value:注入普通类型属性
完全注解开发
创建配置类,替代xml配置文件
编写测试类