景安网站备案 不去拍照,网站建设可行性的分析,本地写wordpress,陕西省交通建设集团公司西商分公司网站#x1f3e1;浩泽学编程#xff1a;个人主页 #x1f525; 推荐专栏#xff1a;《深入浅出SpringBoot》《java项目分享》 《RabbitMQ》《Spring》《SpringMVC》 #x1f6f8;学无止境#xff0c;不骄不躁#xff0c;知行合一 文章目录 前言一、生命周期二… 浩泽学编程个人主页 推荐专栏《深入浅出SpringBoot》《java项目分享》 《RabbitMQ》《Spring》《SpringMVC》 学无止境不骄不躁知行合一 文章目录 前言一、生命周期二、作用域总结 前言
前面我们讲诉了将Bean正确地装配到IoC容器却未讲诉IoC如何装配和销毁Bean。本篇文章主要讲诉一下Bean的生命周期和作用域。 一、生命周期 Bean 的生命周期的过程 它大致分为Bean定义、Bean 的初始化、 Bean 的生存期和 Bean 的销毁4个部分。 其中 Bean 定义过程大致如下 Spring 通过我们的配置如ComponentScan 定义的扫描路径去找到带有Component 的类 这个过程就是一个资源定位的过程。一旦找到了资源那么它就开始解析并且将定义的信息保存起来。注意此时还没有初始 化Bean也就没有Bean 的实例它有的仅仅是Bean 的定义。然后就会把Bean 定义发布到 Spring IoC 容器中。 此时 IoC 容器也只有Bean 的定义还是 没有Bean 的实例生成。 完成了这3 步只是一个资源定位并将Bean 的定义发布到IoC容器的过程还没有Bean实例的生成更没有完成依赖注入。在默认的情况下 Spring会继续去完成Bean 的实例化和依赖注入这样从IoC 容器中就可以得到一个依赖注入完成的Bean。 但是有些Bean会受到变化因素的影响这时我们倒希望是取出 Bean 的时候完成初始化和依赖注入换句话说就是让那些 Bean 只是将定义发布到IoC 容器而不做实例化和依赖注入 当我们取出来的时候才做初始化和依赖注入等操作。 Spring Bean的初始化过程 ComponentScan 中还有一个配置项 lazyI nit只可以配置 Boolean 值且默认值为 false也就是默认不进行延迟初始化因此在默认的情况下Spring会对Bean进行实例化和依赖注入对应的属性值。 引入例子人类Person有时候利用一些动物(Animal去完成一些事情比方说狗Dog是用来看门的猫Cat是用来抓老鼠的.。 代码如下
//定义人类接口
public interface Person {void service();void setAnimal(Animal animal);
}
//定义动物接口
public interface Animal {void user();
}
//定义狗
Component
public class Dog implements Animal {Overridepublic void user() {System.out.println(狗【 Dog.class.getSimpleName() 】是用来看门的);}
}
//定义年轻人
Component
public class YoungPerson implements Person {Autowiredprivate Animal animal null;Overridepublic void service() {this.animal.user();}Overridepublic void setAnimal(Animal animal) {this.animal animal;}
}
//定义猫
Component
public class Cat implements Animal{Overridepublic void user() {System.out.println(猫【 Cat.class.getSimpleName() 】是抓老鼠的);}
}//定义配置类
Configuration
ComponentScan(com.dragon.restart)//所有的包和类都在restart下
public class AppConfig {
}
此时没有配置lazyInit的情况进行断点测试如下 可以看到在断点处我们并没有获取Bean 的实例而日志就已经打出了可见它是在SpringIoC容器初 始化时就执行了实例化和依赖注入。为了改变这个情况我们在配置类AppConfig的ComponentScan 中加入lazylnit 配置如下面的代码
Configuration
ComponentScan(value com.dragon.restart,lazyInit true)
public class AppConfig {
}就可以发现在断点处“延迟依赖注入”这行并不会出现在日志中只有运行过断点处才会出现这行日志这是因为我们把它修改为了延迟初始化 Spring并不会在发布Bean定义后马上为我们完成实例化和依赖注入。
如果仅仅是实例化和依赖注入还是比较简单的还不能完成进行自定义的要求。 为了完成依赖注入的功能 Spring 在完成依赖注入之后还提供了一系列的接口和配置来完成Bean初始化的过程让我们学习这个过程。 Spring在完成依赖注入后还会进行如下图所示流程来完成它的生命周期
图中描述的是整个IoC容器初始化Bean 的流程作为开发者需要注意这些流程。除此之外还需要注意以下两点
这些接口和方法是针对什么而言的。 对于上图 在没有注释的情况下的流程节点都是针对单个Bean 而言的但是BeanPostProcessor 是针对所有 Bean 而言的这是我们需要注意的地方。即使你定义了 ApplicationContextAware 接口但是有时候并不会调用这要根据你的 IoC 容器来决定。 我们知道 Spring IoC 容器最低的要求是实现 BeanFactory 接口而不是实现ApplicationContext 接口 。 对于那些没有实现 ApplicationContext 接口的容器在生命周期对应的ApplicationContextAware 定义的方法也是不会被调用的只有实现了 ApplicationContext 接口的容器才会在生命周期调用 ApplicationContextAware 所定义的 setApplicationContext方法。
现在改造一下YoungPerson类
Component
public class YoungPerson implements Person, BeanNameAware , BeanFactoryAware, ApplicationContextAware, InitializingBean, DisposableBean {private Animal animal null;Overridepublic void service() {this.animal.user();}AutowiredQualifier(dog)Overridepublic void setAnimal(Animal animal) {System.out.println(延迟依赖注入);this.animal animal;}Overridepublic void setBeanName(String name) {System.out.println (【 this.getClass().getSimpleName() 】调用BeanNameAware的setBeanName);}Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {System.out.println (【 this.getClass().getSimpleName() 】调用BeanFactoryAware的setBeanFactory);}Overridepublic void destroy() throws Exception {System.out.println (【 this.getClass().getSimpleName() 】调用DisposableBean方法);}Overridepublic void afterPropertiesSet() throws Exception {System.out.println (【 this.getClass().getSimpleName() 】调用InitializingBean方法的afterPropertiesSet方法);}Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {System.out.println (【 this.getClass().getSimpleName() 】调用ApplicationContextAware方法的setApplicationContext方法);}PostConstructpublic void init () {System.out.println(【 this.getClass().getSimpleName() 】注解PostConstruct定义的自定义初始化方法);}PreDestroypublic void destroyl () {System.out.println(【 this.getClass().getSimpleName() 】注解PreDestroy定义的自定义销毁方法);}
}这样这个 B巳an 就实现了生命周期中单个 Bean 可以实现的所有接口 并且通过注解PostConstruct 定义了初始化方法通过注解PreDestroy 定义了销毁方法。 为了测试 Bean 的后置处理器 这里创建一个类BeanPostProcessorExampIe如下
/*** Version: 1.0.0* Author: Dragon_王* ClassName: BeanPostProcessorExample* Description: TODO描述* Date: 2024/1/20 23:34*/
public class BeanPostProcessorExample implements BeanPostProcessor {Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println(BeanPostProcessor调用postProcessBeforeinitialization方法参数【bean.getClass().getSimpleName()】【beanName】);return bean;}Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println(BeanPostProcessor调用postProcessAfterinitialization方法参数【bean.getClass().getSimpleName()】【beanName】);return bean;}
}注意这个Bean后置处理器将对所有的Bean有效运行测试如下 测试类
AnnotationConfigApplicationContext ctx new AnnotationConfigApplicationContext(AppConfig.class) ;ctx.close();2024-01-20T23:43:23.13508:00 INFO 748 --- [ main] c.d.restart.RestartApplicationTests : Starting RestartApplicationTests using Java 19 with PID 748 (started by ThundeRobot in E:\IDEA_projects\restart)
2024-01-20T23:43:23.13608:00 INFO 748 --- [ main] c.d.restart.RestartApplicationTests : No active profile set, falling back to 1 default profile: default
BeanPostProcessor调用postProcessBeforeinitialization方法参数【RestartApplication$$SpringCGLIB$$0】【restartApplication】
BeanPostProcessor调用postProcessAfterinitialization方法参数【RestartApplication$$SpringCGLIB$$0】【restartApplication】
BeanPostProcessor调用postProcessBeforeinitialization方法参数【AppConfig$$SpringCGLIB$$0】【appConfig】
BeanPostProcessor调用postProcessAfterinitialization方法参数【AppConfig$$SpringCGLIB$$0】【appConfig】
BeanPostProcessor调用postProcessBeforeinitialization方法参数【Cat】【cat】
BeanPostProcessor调用postProcessAfterinitialization方法参数【Cat】【cat】
BeanPostProcessor调用postProcessBeforeinitialization方法参数【Dog】【dog】
BeanPostProcessor调用postProcessAfterinitialization方法参数【Dog】【dog】
延迟依赖注入
【YoungPerson】调用BeanNameAware的setBeanName
【YoungPerson】调用BeanFactoryAware的setBeanFactory
【YoungPerson】调用ApplicationContextAware方法的setApplicationContext方法
BeanPostProcessor调用postProcessBeforeinitialization方法参数【YoungPerson】【youngPerson】
【YoungPerson】注解PostConstruct定义的自定义初始化方法
【YoungPerson】调用InitializingBean方法的afterPropertiesSet方法
BeanPostProcessor调用postProcessAfterinitialization方法参数【YoungPerson】【youngPerson】
BeanPostProcessor 调用 postProcessBeforeinitialization 方法参数 【Cat】【cat】BeanPostProcessor 调用 postProcessAfterinitialization 方法 参数 【Cat】【cat】2024-01-20T23:43:24.04408:00 INFO 748 --- [main] c.d.restart.RestartApplicationTests : Started RestartApplicationTests in 1.142 seconds (process running for 1.772)
【YoungPerson】注解PreDestroy定义的自定义销毁方法
【YoungPerson】调用DisposableBean方法从日志可以看出对于Bean后置处理器BeanPostProcessor而言 它对所有的 Bean 都起作用而其他的接口则是对于单个Bean起作用。我们还可以注意到BussinessPerson执行的流程是上图所画出的流程。有时候Bean 的定义可能使用的是第三方的类此时可以使用注解Bean来配置自定义初始化和销毁方法如下所示
Bean(InitMethod ”Init” destroyMethod ”destroy” )二、作用域 在介绍IoC 容器最顶级接口 BeanFactory 的时候 可以看到 isSingleton 和 isPrototype 两个方法。其中isSingleton 方法如果返回 true则 Bean 在 loC 容器中以单例存在这也是 Spring IoC 容器的默认值如果 isPrototype 方法返回 true则当我们每次获取 Bean 的时候 IoC 容器都会创建一个新的 Bean这显然存在很大的不同这便是Spring Bean 的作用域的问题。在一般的容器中 Bean都会存在单例Singleton和原型Prototype两种作用域 Java EE 广泛地使用在互联网中而在 Web容器中 则存在页面page、请求request、会话 session和应用application) 4 种作用域。对于页面page是针对 JSP 当前页面的作用域所以 Spring是无法支持的。为了满足各类的作用域在Spring 的作用域中就存在如表所示的几种类型。 作用域类型使用范围作用域描述singleton所有Spring 应用默认值 loC 容器只存在单例prototype所有Spring 应用每当从IoC 容器中取出一个 Bean则创建一个新的BeansessionSpring Web 应用HTTP 会话applicationSpring Web 应用Web 工程生命周期requestSpring Web 应用Web 工程单次请求 request)globalSessionSpring Web 应用在一个全局的HTTPSession 中 一个 Bean 定义对应一个实例。 实践中基本不使用
前四个最常用对于application作用域完全可以使用单例来替代。 下面我们探讨单例 Singleton和原型prototype的区别 首先定义一个类
Component
//Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class ScopeBean { }这是一个简单的类 可以看到这里声明作用域的代码已经被注释掉了 这样就是启用默认的作用域实际就是单例。为了证明作用域的存在我们进行一下测试
AnnotationConfigApplicationContext ctxnew AnnotationConfigApplicationContext (AppConfig.class);ScopeBean scopeBeanl ctx.getBean (ScopeBean.class);ScopeBean scopeBean2 ctx.getBean (ScopeBean .class);System.out.println (scopeBeanl scopeBean2) ;从测试的结果来看显然scopeBeanl 和 scopeBean2 这两个变量都指向了同一的实例所以在IoC容器中 只有一个ScopeBean 的实例。 然后取消代码中作用域代码的注释进行同样的测试 则可以看到scopeBeanl scopeBean2 返回的将是 false而不再是 true 那是因为我们将Bean 的作用域修改为了 prototype这样就能让IoC 容器在每次获取Bean 时都新建一个Bean的实例返回给调用者。 这里的 ConfigurableBeanFactory 只能提供单例 SCOPE_ SINGLETON 和原型 SCOPE_PROTOTYPE两种作用域供选择 如果是在 SpringMVC环境中还可以使用 WebApplicationContext去定义其他作用域 如请求SCOPE REQUEST、 会话 SCOPE_SESSION 和应用 SCOPE_APPLICATION。 例如下面的代码就是定义请求作用域
Component
Scope(WebApplicationContext.SCOPE_REQUEST)
public class ScopeBean { }总结
以上就是Bean生命周期和作用域的讲解。