IOC 容器
一、 IOC 底层原理
1、什么是IOC
- 控制反转,把对象创建和对象之间的调用过程,交给Spring进行管理
- 目的:为了耦合度降低
2、IOC 底层原理
- xml解析、工厂模式、反射
3、IOC 过程
-
原理
-
通过解析 xml 配置文件,获取 bean 标签中的 class 属性的值,然后通过反射得到对应的类,再获取类对应的对象。
-
如果类的位置发生改变是需要配置文件中 class 属性的值
-
-
IOC 过程
-
第一步 xml 配置文件,配置创建的对象
<bean id="dao" class="classAddress"></bean> <!--address 地址-->
-
第二步 有 service 类和 dao 类后,创建工厂类
class UserFactory(){ public static UserDao getDao(){ // xml解析加反射创建对象 String classValue = class属性值; // 1 xml解析 Class class = Class.forName(classValue); // 2 通过反射创建对象 return (UserDao)class.newInstance(); } }
-
创建 testDome 进行测试
@Test public void test(){ //解析beans.xml文件 , 生成管理相应的Bean对象 //加载 spring 配置文件 ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); //getBean : 参数即为spring配置文件中bean的id . // 获取配置文件中创建的对象 UserDao dao = (UserDao) context.getBean("dao"); dao.add(); }
-
工厂模式 图解:
二、IOC 接口(BeanFactory)
-
IOC 思想基于 IOC 容器实现,IOC 容器底层就是对象工厂
-
Spring 提供 IOC 容器实现两种方法:(两个接口)
-
BeanFactory :IOC 容器基本实现,是Spring内部使用接口,不提供看法人员使用
** 加载配置文件时,不会创建对象,在获取(使用)对象时才去创建
-
ApplicationContext :BeanFactory接口的子接口,提供更多更强大的功能,一般由开发人员进行使用
** 加载配置文件时就会把配置文件中的对象进行创建
-
-
ApplicationContext 接口的主要实现类
- FileSysteXmlApplicationContext ( configLocation:“盘路径” );
- ClassPathXmlApplicationContext( configLocation:“类路径” );
三、IOC 操作 Bean 管理(概念)
1、什么是 Bean 管理
- Bean 管理的两个操作
- Spring 创建对象
- Spring 注入属性
2、Bean 管理操作有两种实现方式
- IOC 操作 Bean 管理 (基于xml)
- IOC 操作 Bean 管理 (基于注解)
四、IOC 操作 Bean 管理 (基于xml)
1、基于 xml 方式创建对象
<bean id="dao" class="classAddress"></bean> <!--address 地址-->
- 在 spring 配置文件中,使用 bean 标签,标签内添加对应属性,就可以实现对象创建
- bean 中的常用属性值
- id —— 唯一标识
- name —— 同 id 一样,但可以使用特殊符号,以舍弃
- class —— 类全路径(包类路径)
- 创建对象的时候,默认执行无参构造方法
2、基于 xml 方式注入属性
-
DI :依赖注入,就是注入属性。DI 是 IOC 的一种具体实现,需要在创建对象的基础上使用
-
第一种注入方式:使用 set 方法进行注入
-
① 创建类,定义属性和对应的 set 方法
public class book{ private String bname; public void setBname(String bname){ this.bname = bname; } }
-
② 在 spring 配置文件配置对象创建,配置属性注入
方法一:如果要将对象注入到某个类中,那么对应类中需要有 set 方法
<!-- 通过 bean标签 创建对象 --> <bean id="book" class="classAddress"> <!--控制器调用 set 方法--> <property name="attributeName" value="attributeValue"></property> <!-- 将容器中的aaa bean作为传入的参数 外部bean 中有详细介绍 <property name="attributeName" ref="aaa"></property> --> </bean> <!--address 地址 attributName 属性-->
方法二:简化:p 名称空间注入,原理:spring 的底层 set 方法 ```xml <!--在 xml 文件头部 beans 标签中插入 p名称空间--> xmlns:p="http://www.springframework.org/schema/p"
<!--进行属性注入,在 bean 标签中进行操作--> <bean id="book" class="classAddress" p:bname="attributeValue"></bean> <!--address 地址 attributName 属性-->
-
property 标签:控制器调用 set 方法
-
name 属性:决定类中的哪个参数
-
value 属性:创建一个新的对象
-
ref 属性:引用一个已经存在的对象
ref:指当前xml文件中叫做aaa的这个bean,把它当作参数传进class中
-
-
第二种注入方式:使用有参构造进行注入
-
① 创建带有 有参构造方法 的类
-
② 在 spring 配置文件配置对象创建,配置属性注入
<bean id="className" class="ClassAddress"> <constructor-arg name="attributeName" value="attributeValue"/> </bean>
<!-- 第一种根据index参数下标设置 --> <bean id="userT" class="com.kuang.pojo.UserT"> <!-- index指构造方法 , 下标从0开始 --> <constructor-arg index="0" value="kuangshen2"/> </bean> <!-- 第二种根据参数名字设置 --> <bean id="userT" class="com.kuang.pojo.UserT"> <!-- name指参数名 --> <constructor-arg name="name" value="kuangshen2"/> </bean> <!-- 第三种根据参数类型设置 --> <bean id="userT" class="com.kuang.pojo.UserT"> <constructor-arg type="java.lang.String" value="kuangshen2"/> </bean>
-
3、IOC 操作 Bean 管理( xml 注入其他类型属性)
-
① null 值 – 使用
<null/>
标签表示空值<property name="attributeName"> <null/> </property>
-
② 属性值包含特殊符号
- 方法一,<>进行转义
< >
(转义符) - 方法二,把带特殊符号的内容写到 CDATA 中
<property name="attributeName"> <!--注入 <<南京>> --> <value><![CDATA[<<南京>>]]></value> </property>
<![CDATA[]]>
标记为 xml 文档标记- (1) 此部分不能再包含
”]]>”
;
(2) 不允许嵌套使用;
(3)”]]>”
这部分不能包含空格或者换行。 <![CDATA[]]>
表示xml解析器忽略解析,所以更快。
- (1) 此部分不能再包含
- 方法一,<>进行转义
4、注入属性 – 外部bean
-
创建两个类 service 类和 dao 类 ,在 service 设置 userdao 的 set 方法
public class UserDao{ private String uname; public void setUname(String uname) { this.uname = uname; } public void add(){ System.out.println("add+++"); } }
public class UserService{ // 创建 UserDao 类的对象 userdao private UserDao userDao; // 设置 userdao 的 set 方法 public void setUserDao(UserDao userDao){ this.userDao = userDao; } public void servAdd(){ System.out.println("servAdd+++"); userDao.add(); } }
-
在 bean.xml 文件中注入属性 – 外部bean,使用ref
<!-- 将 bean 对象 userDao 通过 ref 属性注入 UserService 类的 userDao 属性中--> <bean id="userService" class="UserService"> <property name="userDao" ref="userDao"></property> </bean> <!-- 创建 UserDao 的 bean 对象 userDao --> <bean id="userDao" class="UserDao"></bean>
-
创建 TestBean 测试类,并调用 spring 加载文件
public class TestBean { @Test public void TestBean_01() { // 1 加载 spring 配置文件 ApplicationContext context = new ClassPathXmlApplicationContext("bean_01.xml"); // 2 获取配置文件中创建的对象 UserService userService = context.getBean("userService", UserService.class); userService.servAdd(); } }
-
输出结果:
servAdd+++ add+++
5、注入属性 – 内部 bean
-
创建 service 类和 dao 类,
-
bean.xml注入属性 – 内部bean
<!-- 内部 bean --> <bean id="userService" class="UserService"> <property name="userDao"> <!-- 通过在 property 属性中新建新的 bean 标签来替代 value/ref 来实现内部 bean 注入属性--> <bean id="userDao" class="UserDao"> <property name="uname" value="guxinhu"></property> </bean> </property> </bean>
-
创建 TestBean 测试类,并调用 spring 加载文件
public class TestBean { @Test public void TestBean_01() { // 1 加载 spring 配置文件 ApplicationContext context = new ClassPathXmlApplicationContext("bean_01.xml"); // 2 获取配置文件中创建的对象 UserService userService = context.getBean("userService", UserService.class); userService.servAdd(); } }
-
输出结果:
servAdd+++ add+++
6、注入属性 – 级联赋值
-
创建 service 类和 dao 类
public class UserDao{ private String uname; // 输出的方法 public void setUname(String uname){ this.uname = uname; } public String toString(){ return "uname:"+uname; } }
public class UserService{ private UserDao userDao; public void setUserDao(UserDao userDao){ this.userDao = userDao; } //设置 userdao 对象的 get 方法 public UserDao getUserDao(){ return userDao; } public void servAdd(){ System.out.println(userDao.toString()); } }
-
bean.xml注入属性 – 级联赋值
<!-- 内部 bean --> <bean id="userService" class="UserService"> <!-- 级联赋值 --> <!-- 第一种方法,类似于 外部bean 注入,区别在 级联赋值 存在property标签--> <property name="userDao" ref="userDao"></property> <!-- 第二种方法,需要在对应的类(service)中,写入对应属性的 get 方法 --> <!-- 使用 UserService 对象 userdao 调用 UserDao 的属性 uname --> <property name="userDao.uname" value="guxinhu"></property> </bean> <bean id="userDao" class="UserDao"> <!-- 级联赋值 存在property标签--> <property name="uname" value="guxinhu"></property> </bean>
-
创建 TestBean 测试类,并调用 spring 加载文件
public class TestBean { @Test public void TestBean_01() { //调用spring配置文件 ApplicationContext context = new ClassPathXmlApplicationContext("bean_01.xml"); //获取配置文件中创建的对象 并放入 userSerice 对象中 UserService userService = context.getBean("userService", UserService.class); // 输出 userService.servAdd(); } }
-
输出结果:
uname:guxinhu
6、注入容器(集合)属性(xml)
数组,list集合,map集合,set集合
-
创建容器的类,和相应属性、set方法
public class Container{ // container -- 容器 // 创建容器 // 数组类型属性 private String[] num; // list 集合类型属性 private List<String> list; // map 集合类型属性 private Map<String,String> maps; // set 集合类型属性 private Set<String> sets; // 相应的set方法 public void setNum(String[] num) { this.num = num; } public void setList(List<String> list) { this.list = list; } public void setMaps(Map<String, String> maps) { this.maps = maps; } public void setSets(Set<String> sets) { this.sets = sets; } //输出的方法 public void test(){ // 数组无法直接输出内容,使用 Arrays.toString() System.out.println("num:"+ Arrays.toString(num)); System.out.println("list:"+list); System.out.println("maps:"+maps); System.out.println("set:"+sets); } }
-
bean.xml 注入属性
<bean id="container" class="Container"> <!-- 给数组 num && value标签 注入属性值 --> <property name="num"> <!-- 数组可以使用 array/list标签 进行属性值注入 --> <array> <value>num1</value> <value>num2</value> </array> </property> <!-- 给list集合 list 注入属性值 --> <property name="list"> <!-- 使用 list标签 && value标签 注入属性值 --> <list> <value>list1</value> <value>list2</value> </list> </property> <!-- 给map集合 maps 注入属性值 --> <property name="maps"> <!-- 使用 map标签 && entry标签 注入属性值 --> <map> <entry key="key1" value="maps1"></entry> <entry key="key2" value="maps2"></entry> </map> </property> <!-- 给set集合 sets 注入属性值 --> <property name="sets"> <!-- 使用 set标签 && value标签 注入属性值 --> <set> <value>set1</value> <value>set2</value> </set> </property> </bean>
-
创建 TestBean 测试类,并调用 spring 加载文件
public class TestBean{ @Test public void TestBean_02(){ //调用spring配置文件 ApplicationContext context = new ClassPathXmlApplicationContext("bean_02.xml"); //获取配置文件中创建的对象 并放入 userSerice 对象中 Container container = context.getBean("container", Container.class); // 输出 container.test(); } }
-
输出结果:
num:[num1, num2] list:[list1, list2] maps:{key1=maps1, key2=maps2} set:[set1, set2]
-
集合中注入对象
<!-- 以list 为例子 --> <!-- 给list集合 list 注入属性值(属性值为对象) --> <bean id="container" class="Container"> <property name="list"> <!-- 使用 list标签 && ref标签 注入属性值 --> <list> <!-- 使用级联赋值(外部bean) --> <ref bean="user1"></ref> <ref bean="user2"></ref> </list> </property> </bean> <bean id="user1" class="UserDao"> <property name="uname" value="gu"></property> </bean> <bean id="user2" class="UserDao"> <property name="uname" value="xin"></property> </bean>
-
集合注入部分其他 xml文件 可用
<!-- 使用 名称空间util ,xml文件 头部中插入--> <!-- 创建list集合类型,名为 userDao 的 bean对象 --> <util:list id="userDao"> <value>gu</value> <value>xin</value> <value>hu</value> </util:list> <!-- 使用 ref 进行注入(外部bean 注入) -->
6、FactoryBean
-
spring 有俩种 bean,一种 普通bean (默认类型),一种 工厂bean(FactoryBean)
-
普通bean:在配置文件中定义的 bean类型 就是返回类型
-
工厂bean:在配置文件中定义的 bean类型 可以和返回类型不一样
-
第一步:创建类,让这个类成为 工厂bean ,实现接口 FactoryBean
-
第二步:实现接口里面的方法(3个方法),在实现的方法中定义返回的 bean类型
public class MyBean implements FactoryBean<Xin>{ // 定义返回的 bean类型——“Xin”,创建一个 Xin类,属性有Xname @Override public Xin getObject() throws Exception{ Xin xin = new Xin(); xin.setXname("asd"); return xin; } } // 在 TestBean 测试类中,接收xml配置文件中创建的对象时,使用 Xin类 的实例化对象,而不是MyBean
-
bean.xml 配置文件
<!-- class属性值为 工厂bean 的绝对位置(全限定名) --> <bean id="myBean" class="MyBean"></bean>
-
7、 Bean 作用域
-
spring 中 Bean (scope属性)定义了 5 种作用域,分别为 singleton(单例)、prototype(原型/多列)、request(web请求)、session(web会话) 和 global session,后三种作用域仅在基于web的应用中使用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9kS9D4M3-1650026449412)(C:\Users\30386\Desktop\临时文件\bean 作用域-1619426335562.png)]
-
Spring中的bean默认都是单例的,即singleton(单实例),在加载spring配置文件时就会创建 单实例对象
<bean id="" class="" scope="singleton"></bean> <!-- 在TeseBean类中,创建多个bean对象的接受对象时,它们的地址一样 -->
-
prototype (多实例),在每次调用getBean()时,就会创建一个新的实例
<bean id="" class="" scope="prototype"></bean> <!-- 在TeseBean类中,创建多个bean对象的接受对象时,它们的地址不一样 -->
8、Bean 生命周期
-
图示:
-
第三步,第五步 BeanPostProcessor接口
- 针对所有Spring上下文中所有的bean,可以在配置文档applicationContext.xml中配置一个BeanPostProcessor,然后对所有的bean进行一个初始化之前和之后的代理。
- BeanPostProcessor接口中有两个方法: postProcessBeforeInitialization和postProcessAfterInitialization。
- postProcessBeforeInitialization方法在bean初始化之前执行,
- postProcessAfterInitialization方法在bean初始化之后执行。
-
第四步,bean 的属性 init-method,初始化bean的时候执行,可以针对某个具体的bean进行配置。
<!-- testInit 方法名 --> <bean id="testInitMethod" class="com.TestInitMethod" init-method="testInit"></bean>
-
Spring 容器 Bean 生命周期中,Spring 允许在 Bean 在初始化完成后以及 Bean 销毁前执行特定的操作,常用的设定方式有以下三种:
-
通过实现 InitializingBean/DisposableBean 接口来定制初始化之后/销毁之前的操作方法;
-
通过 元素的 init-method/destroy-method属性指定初始化之后 /销毁之前调用的操作方法;
-
在指定方法上加上@PostConstruct 或@PreDestroy注解来制定该方法是在初始化之后还是销毁之前调用但他们之前并不等价。即使3个方法都用上了,也有先后顺序.
Constructor > @PostConstruct >InitializingBean > init-method
-
8、xml 自动装配
-
自动装配是根据指定装配规则(属性名称或者属性类型),spring 自动将属性值进行注入
-
bean标签 autowire 属性实现自动装配,两个值
- byName 根据属性名称注入,注入值 bean 的 id值 和属性名称一至,可以重复
- byType 根据属性类型注入,无法重复
-
xml
<bean id="" class="" autewire="byNaem"></bean> <!-- 假设 Dept 类中存在名为dept的属性 --> <bean id="dept" class=""></bean>
9、xml 引入外部属性文件
-
创建外部属性文件,properties 格式文件,存放外部数据 test.properties
// 赋值方法 key = value prop.gu=gu prop.xin=xin prop.hu=hu
-
xml
<!-- 使用 context名称空间 --> <!-- 引入外部文件 --> <!-- location="外部属性文件的绝对地址" --> <context:property-placeholder location="Test.properties"/> <!-- 配置外部数据 --> <bean id="userService" class="UserService"> <!-- value="${key}" --> <property name="gu" value="${prop.gu}"></property> <property name="xin" value="${prop.xin}"></property> <property name="hu" value="${prop.hu}"></property> </bean>
五、IOC 操作 Bean 管理 (基于注解)
1、什么是注解
- 注解是代码的特殊格式,格式:@注解名称(属性名称=属性值,属性名称=属性值…)
- 使用注解,注解作用在类上面,方法上面,属性上面
- 使用注解的目的:简化xml的配置
2、组件扫描
-
Spring能够从指定的classpath下自动扫描,侦测和实例化具有特定注解的bean
-
方法一 xml形式
<!-- 1 component 组件 2 scan 扫描 3 base-package = "包名" 如果扫描多个包,可以用逗号隔开 可以扫描 包上层路径 --> <context:component-scan base-package=""></context:component-scan>
-
方法二 config类 —— 以类的形式实现组件扫描 SpringConfig.class
@Configuration @ComponentScan(basePackages = {"com.xin"}) public class SpringConfig { }
class TestBean
public class Test { @org.junit.Test public void TestBean_01() { // AnnotationConfigApplicationContext 寻找config类 —— 以类的形式实现组件扫描 ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class); UserXinImpl userXinImpl = context.getBean("userXinImpl", UserXinImpl.class); System.out.println(userXinImpl.toString()); } }
3、Spring 针对 Bean 管理中创建对象提供注解
4个注解功能一样,都可以来创建 bean实例
- @Component —— 普通的注解
- @Service —— service层
- @Controller —— web层
- @Repository —— dao层
4、Spring 注解注入属性
-
@Autowired —— 根据属性类型进行自动装配
-
@Qualifier —— 根据属性名进行注入,必须同 @Autowired 一起使用
Autowired默认先按byType,如果发现找到多个bean,则,又按照byName方式比对,如果还有多个,则报出异常。 1.可以手动指定按byName方式注入,使用@Qualifier。 //通过此注解完成从spring配置文件中 查找满足Fruit的bean,然后按//@Qualifier指定pean @Autowired @Qualifier(“pean”) public Fruit fruit; 2.如果要允许null 值,可以设置它的required属性为false,如:@Autowired(required=false) public Fruit fruit;
-
@Resource —— 根据属性类型、属性名进行注入,默认属性名
默认按 byName自动注入,如果找不到再按byType找bean,如果还是找不到则抛异常,无论按byName还是byType如果找到多个,则抛异常。 @Resource(name="" tpye="") 可以手动指定bean,它有2个属性分别是name和type,使用name属性,则使用byName的自动注入,而使用type属性时则使用byType自动注入。 这个注解是属于J2EE的,减少了与spring的耦合。
————————以上为 对象属性注入
-
@Value —— 基本类型 属性的注入
// @Value 可以引入 properties 格式文件中的数据 @Value(“${wx_appid}”) public String name;
注意:
- @Autowired,@Qualifier 是Spring的注解
- @Resource是javax.annotation注解,而是来自于JSR-250,J2EE提供
5、其他注解
-
@PostConstruct 和 @PreDestory
实现初始化和销毁bean之前进行的操作
-
@PostConstruct:在构造方法和init方法(如果有的话)之间得到调用,且只会执行一次。
-
@PreDestory:注解的方法在destory()方法调用后得到执行。
public class TestService { @PostConstruct public void init(){ System.out.println("初始化"); } @PreDestroy public void dostory(){ System.out.println("销毁"); } }
-
-
@Lazy(true)
用于指定该Bean是否取消预初始化,用于注解类,延迟初始化。
-
@Scope
设置Spring容器如何新建Bean实例(方法上,得有@Bean)
其设置类型包括:- Singleton (单例,一个Spring容器中只有一个bean实例,默认模式),
- Protetype (每次调用新建一个bean),
- Request (web项目中,给每个http request新建一个bean),
- Session (web项目中,给每个http session新建一个bean),
- GlobalSession(给每一个 global http session新建一个Bean实例)
-
@Bean
注解在方法上,声明当前方法的返回值为一个bean,替代xml中的方式(方法上)
-
@Configuration
声明当前类为配置类,其中内部组合了@Component注解,表明这个类是一个bean(类上)
-
@ComponentScan
用于对Component进行扫描,相当于xml中的(类上)
-
@WishlyConfiguration
为@Configuration与@ComponentScan的组合注解,可以替代这两个注解