【Spring系列】 Ioc 实现原理,Spring获取bean的方式,创建对象的方式和懒加载
文章目录
- IOC(DI)
- 入门案例
- IOC的实现原理
- 获取bean的方式
- 通过id获取
- 通过class获取
- 通过id+class获取
- 别名标签
- Spring创建对象的方式
- 无参构造创建对象
- 静态工厂创建对象
- 实例工厂创建对象
- Spring工厂创建对象
- 单例和多例
- 单例对象
- bean在单例模式下的生命周期
- 配置单/多例对象(属性`scope`)
- bean在多例模式下的生命周期:
- 懒加载
- Spring提供的懒加载机制
- 配置初始化和销毁的方法
- Spring配置文件配置初始化和销毁方法
- Spring中关键方法的执行顺序:
IOC(DI)
IOC(DI):控制反转(依赖注入)
所谓的IOC称之为控制反转,简单来说就是将对象的创建的权利及对象的生命周期的管理过程交由Spring框架来处理,从此在开发过程中不再需要关注对象的创建和生命周期的管理,而是在需要时由Spring框架提供,这个由spring框架管理对象创建和生命周期的机制称之为控制反转。而在创建对象的过程中Spring可以依据配置对对象的属性进行设置,这个过称之为依赖注入,也即DI。
- IOC+接口可以实现软件分层时的每个层之间的彻底解耦,用接口接收实现类(多态)
入门案例
创建一个java项目
- Spring并不是非要在javaweb环境下才可以使用,一个普通的java程序中也可以使用Spring
将需要用到的jar包导入Spring项目下的lib文件夹
创建Spring配置文件:applicationContext.xml
Spring采用xml文件作为配置文件,xml文件名任意,但通常取名为applicationContext.xml。
- 通常将该文件放置在类加载的目录下(src目录),方便后续使用
- 右键src—New—XML Configuration File—Spring Config
- (如果没有此选项,检查Spring所需要的jar包是否导入正确)
<?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"></beans>
创建bean类,并在Spring中进行配置,交由Spring容器管理
<?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"><!--id不能重复--><bean id="person" class="cn.tedu.domain.Person"></bean><bean id="personx" class="cn.tedu.domain.Person"></bean> </beans>
在程序中通过Spring容器获取对象并使用
/*** SpringIOC方式创建并管理*/ @Test public void test01(){//1.初始化容器ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");//2.通过Spring容器获取beanPerson p = (Person) context.getBean("person");p.eat();p.say();//3.关闭Spring容器((ClassPathXmlApplicationContext)context).close();}
IOC的实现原理
在初始化一个Spring容器时,Spring会去解析指定的xml文件,当解析到其中的
<bean>
标签时,会根据该标签中的class属性指定的类的全路径名,通过反射创建该类的对象,并将该对象存入内置的Map中管理。注意:底层通过反射创建该类的对象:
Person p = Class.forName("cn.tedu.domain.Person")
- 默认是调用了该类的无参构造,如果没有无参构造,Spring的配置文件applicationContext.xml中的
<bean>
标签就会报错 - javaBean中必须要有无参构造
- 默认是调用了该类的无参构造,如果没有无参构造,Spring的配置文件applicationContext.xml中的
其中键就是该标签的id值,值就是该对象。之后,当通过
getBean()
方法来从容器中获取对象时,其实就是根据传入的条件在内置的Map中寻找是否有匹配的键值,如果有则将该键值对中保存的对象返回,如果没有匹配到则抛出异常。
由此可以推测而知:
默认情况下,多次获取同一个id的bean,得到的将是同一个对象。
不可以配置多个id相同的bean
可以配置多个id不同但class相同的bean
/*** 1.id不能相同* 2.在id不同时,class可以相同*/
@Test
public void test03(){//1.初始化容器ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");//2.通过Spring容器获取beanPerson p = (Person) context.getBean("person");Person p1 = (Person) context.getBean("personx");System.out.println(p);System.out.println(p1);System.out.println(pp1);//false:id不同获取的对象也不相同((ClassPathXmlApplicationContext)context).close();}
获取bean的方式
三种方式:
- 通过id获取
- 通过class获取
- 通过id+class获取
通过id获取
返回结果:
- 找到对应的唯一对象:返回该对象
- 找不到对应的对象:抛出异常:NoSuchBeanDefinitionException
- 不可能找到多个对象:因为id不能重复,不可能出现此情况
@Test
public void test04(){ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");//方式一:通过id获取Person p = (Person) context.getBean("person11");System.out.println(p);((ClassPathXmlApplicationContext) context).close();
}
通过class获取
返回结果:
- 找到对应的唯一对象:返回该对象
- 找不到对应的对象:抛出异常:NoSuchBeanDefinitionException
- 找到多个对象:抛出异常:NoUniqueBeanDefinitionException
特点:
- 如果Spring IOC在通过class获取bean时,找不到该类型的bean时,会去检查是否存在该类型的子孙类型的bean
- 有:返回该之孙类型的bean
- 没有/找到多个:抛出异常
- 符合java面向对象思想中的多态的特性
@Test
public void test04(){ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");//方式二:通过class获取Person p1 = context.getBean(Person.class);p1.say();System.out.println(p1);((ClassPathXmlApplicationContext) context).close();
}
通过id+class获取
返回结果:
- 找到对应的唯一对象:返回该对象
- 找不到对应的对象:抛出异常:NoSuchBeanDefinitionException
- 找到多个对象:id+class能唯一定位一个bean,不可能出现查到多个bean的情况
@Test
public void test04(){ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");//方式三:通过id+class获取Person p = context.getBean("person1", Person.class);System.out.println(p);((ClassPathXmlApplicationContext) context).close();
}
别名标签
在 Spring中提供了别名标签
<alias>
可以为配置的<bean>
起一个别名,要注意的是这仅仅是对指定的<bean>
起的一个额外的名字,并不会额外的创建对象存入map。【可以为bean指定别名,之后可以通过这个别名取代id操作bean】
Spring配置文件
<alias name="要起别名的bean的id" alias="要指定的别名"/>
<?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="person" class="cn.tedu.domain.Person"></bean><alias name="person" alias="pers"></alias><!--别名--> </beans>
测试
/*** 别名标签* 可以通过别名标签为bean的id起一个别名,可以通过id获取bean,也可以通过别名取代id获取bean*/ @Test public void test05(){ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");Person p = (Person) context.getBean("person");System.out.println("p:"+p);//p:cn.tedu.domain.Person@57536d79Person p1 = (Person) context.getBean("pers");System.out.println("p1:"+p);//p1:cn.tedu.domain.Person@57536d79//p 和 p1 其实是同一个对象((ClassPathXmlApplicationContext) context).close();}
Spring创建对象的方式
无参构造创建对象
javaBean中有无参构造,通过id、class、id+class创建bean时底层默认使用反射,调用无参构造创建bean对象
javaBean类
public class Person {public Person() {}public Person(String name) {System.out.println("我被创建了");} }
Spring配置文件
<?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="person" class="cn.tedu.domain.Person"></bean></beans>
测试
/*** 无参构造创建对象,必须有无参构造*/ @Test public void test(){ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");Person p = (Person) context.getBean("person");System.out.println(p);((ClassPathXmlApplicationContext) context).close(); }
静态工厂创建对象
静态工厂类
public class PersonStaticFactory {private PersonStaticFactory(){}public static Person getInstance(){return new Person("zzz");} }
Spring配置文件
<?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="person" class="cn.tedu.factory.PersonStaticFactory" factory-method="getInstance"></bean></beans>
测试
/*** 无参构造创建对象,必须有无参构造*/ @Test public void test(){ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");Person p = (Person) context.getBean("person");System.out.println(p);((ClassPathXmlApplicationContext) context).close(); }
实例工厂创建对象
实例工厂类
public class PersonInstanceFactory {public Person getInstance(){return new Person("zzss");} }
Spring配置文件
<?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="personInstanceFactory" class="cn.tedu.factory.PersonInstanceFactory"></bean><bean id="person" factory-bean="personInstanceFactory" factory-method="getInstance"></bean></beans>
测试
/*** 无参构造创建对象,必须有无参构造*/ @Test public void test(){ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");Person p = (Person) context.getBean("person");System.out.println(p);((ClassPathXmlApplicationContext) context).close(); }
Spring工厂创建对象
Spring工厂类
public class PersonSpringFactory implements FactoryBean<Person> {//生产bean对象的方法@Overridepublic Person getObject() throws Exception {return new Person("ww");}//获取bean类型的方法@Overridepublic Class<?> getObjectType() {return Person.class;}//告知当前bean是否采用单例模式@Overridepublic boolean isSingleton() {return true;//采用单例模式} }
Spring配置文件
<?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--><bean id="person" class="cn.tedu.factory.PersonSpringFactory"></bean></beans>
测试
/*** 无参构造创建对象,必须有无参构造*/ @Test public void test(){ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");Person p = (Person) context.getBean("person");System.out.println(p);((ClassPathXmlApplicationContext) context).close(); }
单例和多例
单例:一个bean只有一个对象,之后无论获取多少次都是获取的同一个对象
多例:每次获取时都重新创建一个新的对象
单例对象
Spring容器管理的bean在默认情况下是单例的。
- 即:一个bean只会创建一个对象,存在内置 map中,之后无论获取多少次该bean,都返回同一个对象。
Spring默认采用单例方式,减少了对象的创建,从而减少了内存的消耗。
bean在单例模式下的生命周期
- 容器初始化时创建对象,保存在Spring内部的map中,获取对象直接返回map内保存的对象
- 直到容器销毁时保存在Map中的对象跟着被销毁
配置单/多例对象(属性scope
)
实际开发中是存在多例的需求的。
- Spring也提供了选项可以将bean设置为多例模式。
Spring配置文件
<bean>
标签属性:scope- singleton:单例(大部分情况都是单例)
- prototype:多例
- request:作用于web应用的请求范围
- session:作用于web应用的会话范围
- global-session:作用于集群环境的会话范围(全局会话范围),当不是集群环境时,他就是session
<?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="cart" class="cn.tedu.domain.Cart" scope="prototype"></bean> </beans>
bean在多例模式下的生命周期:
- spring容器启动时解析xml发现该bean标签后,只是将该bean进行管理,并不会创建对象。
- 此后每次使用
getBean()
获取该bean时,spring都会重新创建该对象返回,每次都是一个新的对象。这个对象spring容器并不会持有 - 什么销毁取决于使用该对象的用户自己什么时候销毁该对象。通过JVM的垃圾回收器进行回收
懒加载
Spring默认会在容器初始化的过程中,解析xml,并将单例的bean创建并保存到map中
- 这样的机制在bean比较少时问题不大,但一旦bean非常多时,spring需要在启动的过程中花费大量的时间来创建bean 花费大量的空间存储bean
- 但这些bean可能很久都用不上,这种在启动时在时间和空间上的浪费显得非常的不值得。
Spring提供的懒加载机制
规定指定的bean不在启动时立即创建,而是在后续第一次用到时才创建,从而减轻在启动过程中对时间和内存的消耗。
懒加载机制只对单例bean有作用,对于多例bean设置懒加载没有意义。
为指定bean配置懒加载
<bean>
标签的lazy-init
属性:default: 关闭懒加载
false: 关闭懒加载
true: 开启懒加载
<?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="person" class="cn.tedu.domain.Person" lazy-init="true"></bean> </beans>
为全局配置懒加载
<beans>
标签default-lazy-init
属性:- false: 关闭懒加载
- true: 开启懒加载
<?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"default-lazy-init="true"><!--全局配置懒加载--><bean id="person" class="cn.tedu.domain.Person"></bean> </beans>
配置初始化和销毁的方法
在Spring中如果某个bean在初始化之后 或 销毁之前要做一些 额外操作可以为该bean配置初始化和销毁的方法 ,在这些方法中完成要功能。
Spring配置文件配置初始化和销毁方法
<bean>
标签中的属性:
- 执行初始化方法的属性:
init-method="初始化方法"
- 执行销毁方法的属性:
destroy-method="销毁方法"
<?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="jdbcUtils" class="cn.tedu.domain.JDBCUtils" init-method="init" destroy-method="destory"></bean>
</beans>
Spring中关键方法的执行顺序:
- 在Spring创建bean对象时,先创建对象(通过无参构造或工厂),
- 之后立即调用init方法来执行初始化操作,
- 之后此bean就可以拿来调用其它普通方法,
- 而在对象销毁之前,spring容器调用其destory方法来执行销毁操作。
【Spring系列】 Ioc 实现原理,Spring获取bean的方式,创建对象的方式和懒加载相关推荐
- JPA/hibernate懒加载原理分析及JSON格式API反序列化时连环触发懒加载问题的解决
什么是懒加载 JPA是java持久层的API,也就是java官方提供的一个ORM框架,Spring data jpa是spring基于hibernate开发的一个JPA框架.Spring data j ...
- Spring 天天用,bean 懒加载原理你懂吗?
来源:小小木的博客 www.cnblogs.com/wyc1994666/p/10569091.html 普通的bean的初始化是在容器启动初始化阶段执行的,而被lazy-init修饰的bean 则是 ...
- java懒加载的原理_每天使用 Spring 框架,那你知道 lazy-init 懒加载原理吗?
普通的bean的初始化是在容器启动初始化阶段执行的,而被lazy-init修饰的bean 则是在从容器里第一次进行context.getBean("")时进行触发. Spring ...
- spring系列-注解驱动原理及源码-bean组件注册
目录 一.环境初始化 1.环境准备 二.bean的手动注入 1.xml方式注入bean 2.使用@Configuration&@Bean方式注入bean 三.自动扫描注册组件及bean 1.使 ...
- spring系列-注解驱动原理及源码-自动装配
目录 一.spring规范的bean自动注入 1.使用@Autowired自动注入 2.使用@Qualifier指定需要装配的组件 3.使用@Autowired装配null对象 4.使用@Primar ...
- 使用 spring 的 IOC 解决程序耦合——获取spring的Ioc核心容器,并根据id获取对象、核心容器的两个接口(ApplicationContext、BeanFactory)引发出的问题
IOC概念和spring中的IOC 明确 ioc 的作用: 削减计算机程序的耦合(解除我们代码中的依赖关系). 使用 spring 的 IOC 解决程序耦合 获取spring的Ioc核心容器,并根据i ...
- spring系列-注解驱动原理及源码-spring容器创建流程
目录 一.spring容器的refresh()[创建刷新] 二.BeanFactory 的创建及预准备工作 1.prepareRefresh()刷新前预处理工作. 2.obtainFreshBeanF ...
- Spring源码之getBean(获取 bean)方法(二)解读
目录 前言 `spring` 初始化 `bean` 过程 进入类 `ClassPathXmlApplicationContext` 的构造器 `AbstractApplicationContext` ...
- spring bean的懒加载原理
spring bean的懒加载原理 1 普通的bean的 初始化是在初始化阶段开始执行的,而被lazy-init修饰的bean则是从容器第一次进行context.getbean("" ...
- 天天用 Spring ,Bean 懒加载原理你还不懂吗?
普通的bean的初始化是在容器启动初始化阶段执行的,而被lazy-init修饰的bean 则是在从容器里第一次进行context.getBean("")时进行触发. Spring ...
最新文章
- 【TensorFlow篇】--DNN初始和应用
- 软件测试技术学什么条件,软件测试从业者需要具备哪些技能
- SVN冲突出现原因及解决方法浅谈
- 基于VB和EXCEL的报表设计及打印
- Linux学习之系统编程篇:exec 函数族
- 如何开启需要的垃圾收集器
- AT2363-[AGC012C]Tautonym Puzzle【构造】
- AUTOSAR从入门到精通100讲(三十八)-AUTOSAR 通信服务 - NM概念详解
- python神奇的小海龟_Python笔记_第一篇_面向过程_第一部分_8.画图工具(小海龟turtle)...
- Python+django网页设计入门(4):用户登录与登录验证
- 2021 互联网公司时薪排行榜出炉!微软、美团很不错
- edwardcmh@cnblogs
- 使用js调用摄像头拍照
- 医疗新基建,更需新安全
- android 手机格式验证,android – 使用国家/地区代码验证手机号码
- git提交代码的时候未获取最新代码
- 项目邮件[置顶] 失业的程序员(十二):潜意识的智商
- cv::HOGDescriptor compute()错误的参数设置导致free(): corrupted unsorted chunks错误
- 必须重启计算机才能关闭用户账户控制,Win10系统怎么彻底关闭用户帐户控制?...
- 计算机桌面备份在哪里,电脑备份文件在哪里