写点什么

Java 中高级核心知识全面解析 (1),Java 开发实战

发布于: 1 小时前

配置文件:


<bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor" />
复制代码

2.实现*Aware 接口 在 Bean 中使用 Spring 框架的一些对象

有些时候我们需要在 Bean 的初始化中使用 Spring 框架自身的一些对象来执行一些操作,比如获取ServletContext的一些参数,获取 ApplicaitionContext 中的 BeanDefinition的名字,获取 Bean 在容器中的名字等等。为了让 Bean 可以获取到框架自身的一些对象,Spring 提供了一组名为*Aware的接口。


这些接口均继承于 org.springframework.beans.factory.Aware 标记接口,并提供一个将由 Bean 实现的 set*方法,Spring 通过基于 setter 的依赖注入方式使相应的对象可以被 Bean 使用。


网上说,这些接口是利用观察者模式实现的,类似于servlet listeners,目前还不明白,不过这也不在本文的讨论范围内。


介绍一些重要的 Aware 接口:


  • ApplicationContextAware: 获得ApplicationContext对象,可以用来获取所有Bean definition的名字。

  • BeanFactoryAware:获得BeanFactory对象,可以用来检测 Bean 的作用域。

  • BeanNameAware:获得 Bean 在配置文件中定义的名字。

  • ResourceLoaderAware:获得ResourceLoader对象,可以获得 classpath 中某个文件。

  • ServletContextAware:在一个 MVC 应用中可以获取ServletContext对象,可以读取 context 中的参数。

  • ServletConfigAware: 在一个 MVC 应用中可以获取ServletConfig对象,可以读取 config 中的参数。


public class GiraffeService implements ApplicationContextAware,     ApplicationEventPublisherAware, BeanClassLoaderAware, BeanFactoryAware,     BeanNameAware, EnvironmentAware, ImportAware, ResourceLoaderAware{     @Override   public void setBeanClassLoader(ClassLoader classLoader) {     System.out.println("执行setBeanClassLoader,ClassLoader Name = " + classLoader.getClass().getName());   }  @Override   public void setBeanFactory(BeanFactory beanFactory) throws BeansException {     System.out.println("执行setBeanFactory,setBeanFactory:: giraffe bean singleton=" + beanFactory.isSingleton("giraffeService"));   }  @Override   public void setBeanName(String s) {     System.out.println("执行setBeanName:: Bean Name defined in context=" + s);   }  @Override   public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {     System.out.println("执行setApplicationContext:: Bean Definition Names="     + Arrays.toString(applicationContext.getBeanDefinitionNames()));   }  @Override   public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {     System.out.println("执行setApplicationEventPublisher");   }  @Override   public void setEnvironment(Environment environment) {     System.out.println("执行setEnvironment");   }  @Override   public void setResourceLoader(ResourceLoader resourceLoader) {     Resource resource = resourceLoader.getResource("classpath:spring- beans.xml");     System.out.println("执行setResourceLoader:: Resource File Name=" + resource.getFilename());   }  @Override   public void setImportMetadata(AnnotationMetadata annotationMetadata) {     System.out.println("执行setImportMetadata");   } }
复制代码

3.BeanPostProcessor

上面的*Aware 接口是针对某个实现这些接口的 Bean 定制初始化的过程,Spring 同样可以针对容器中的所有 Bean,或者某些 Bean 定制初始化过程,只需提供一个实现 BeanPostProcessor接口的类即可。 该接口中包含两个方法,postProcessBeforeInitializationpostProcessAfterInitializationpostProcessBeforeInitialization方法会在容器中的 Bean 初始化之前执行, postProcessAfterInitialization方法在容器中的 Bean 初始化之后执行。


例子如下:


public class CustomerBeanPostProcessor implements BeanPostProcessor {   @Override   public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {     System.out.println("执行BeanPostProcessor的 postProcessBeforeInitialization方法,beanName=" + beanName);     return bean;   }  @Override   public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {     System.out.println("执行BeanPostProcessor的postProcessAfterInitialization 方法,beanName=" + beanName);     return bean;   } }
复制代码


要将BeanPostProcessor的 Bean 像其他 Bean 一样定义在配置文件中


<bean class="com.giraffe.spring.service.CustomerBeanPostProcessor"/>
复制代码

4.总结

所以结合第一节控制台输出的内容,Spring Bean 的生命周期是这样纸的:


  • Bean 容器找到配置文件中 Spring Bean 的定义。

  • Bean 容器利用 Java Reflection API 创建一个 Bean 的实例。

  • 如果涉及到一些属性值 利用 set 方法设置一些属性值。

  • 如果 Bean 实现了BeanNameAware接口,调用setBeanName()方法,传入 Bean 的名字。

  • 如果 Bean 实现了BeanClassLoaderAware接口,调用setBeanClassLoader()方法,传入 ClassLoader 对象的实例。

  • 如果 Bean 实现了BeanFactoryAware接口,调用setBeanFactory()方法,传入 BeanFactory 对象的实例。

  • 与上面的类似,如果实现了其他*Aware接口,就调用相应的方法。

  • 如果有和加载这个 Bean 的 Spring 容器相关的BeanPostProcessor对象,执行postProcessBeforeInitialization()方法

  • 如果 Bean 实现了InitializingBean接口,执行afterPropertiesSet()方法。

  • 如果 Bean 在配置文件中的定义包含init-method属性,执行指定的方法。

  • 如果有和加载这个 Bean 的 Spring 容器相关的BeanPostProcessor对象,执行postProcessAfterInitialization()方法

  • 当要销毁 Bean 的时候,如果 Bean 实现了DisposableBean接口,执destroy()方法。

  • 当要销毁 Bean 的时候,如果 Bean 在配置文件中的定义包含destroy-method属性,执行指定的方法。



与之比较类似的中文版本:



其实很多时候我们并不会真的去实现上面说描述的那些接口,那么下面我们就除去那些接口,针对 bean 的单例和非单例来描述下 bean 的生命周期:

5.单例管理的对象

scope=”singleton”,即默认情况下,会在启动容器时(即实例化容器时)时实例化。但我们可以指定 Bean 节点的lazy-init=”true”来延迟初始化 bean,这时候,只有在第一次获取 bean 时才会初始化 bean,即第一次请求该 bean 时才初始化。如下配置:


<bean id="ServiceImpl" class="cn.csdn.service.ServiceImpl" lazy-init="true"/>
复制代码


如果想对所有的默认单例 bean 都应用延迟初始化,可以在根节点 beans 设置default-lazy-init属性为 true,如下所示:


<beans default-lazy-init="true" …>
复制代码


默认情况下,Spring 在读取 xml 文件的时候,就会创建对象。在创建对象的时候先调用构造器,然后调用 init-method 属性值中所指定的方法。对象在被销毁的时候,会调用 destroy-method 属性值中所指定的方法(例如调用Container.destroy()方法的时候)。写一个测试类,代码如下:


public class LifeBean {   private String name;     public LifeBean(){     System.out.println("LifeBean()构造函数");   }    public String getName() {     return name;   }    public void setName(String name) {     System.out.println("setName()");     this.name = name;   }    public void init(){     System.out.println("this is init of lifeBean");   }    public void destory(){     System.out.println("this is destory of lifeBean " + this);   } }
复制代码


life.xml 配置如下:


<bean id="life_singleton" class="com.bean.LifeBean" scope="singleton" init-method="init" destroy-method="destory" lazy-init="true"/>
复制代码


测试代码:


LifeBean()构造函数 this is init of lifeBean com.bean.LifeBean@573f2bb1 ……this is destory of lifeBean com.bean.LifeBean@573f2bb1
复制代码

6.非单例管理的对象

scope=”prototype” 时,容器也会延迟初始化 bean,Spring 读取 xml 文件的时候,并不会立刻创建对象,而是在第一次请求该 bean 时才初始化(如调用 getBean 方法时)。在第一次请求每一个 prototype 的 bean 时,Spring 容器都会调用其构造器创建这个对象,然后调用 init-method 属性值中所指定的方法。对象销毁的时候,Spring 容器不会帮我们调用任何方法,因为是非单例,这个类型的对象有很多个,Spring 容器一旦把这个对象交给你之后,就不再管理这个对象了。


为了测试prototype bean的生命周期 life.xml 配置如下:


<bean id="life_prototype" class="com.bean.LifeBean" scope="prototype" init- method="init" destroy-method="destory"/>
复制代码


测试程序:


public class LifeTest {   @Test   public void test() {     AbstractApplicationContext container = new ClassPathXmlApplicationContext("life.xml");     LifeBean life1 = (LifeBean)container.getBean("life_singleton");     System.out.println(life1);         LifeBean life3 = (LifeBean)container.getBean("life_prototype");     System.out.println(life3);     container.close();   } }
复制代码


运行结果:


LifeBean()构造函数 this is init of lifeBean com.bean.LifeBean@573f2bb1 LifeBean()构造函数 this is init of lifeBean com.bean.LifeBean@5ae9a829 ……

# 本次面试答案,以及收集到的大厂必问面试题分享:
![字节跳动超高难度三面java程序员面经,大厂的面试都这么变态吗?](https://static001.geekbang.org/infoq/1f/1ffb14002e1ff2c719722161e68c94c5.jpeg)
**[资料领取方式:戳这里即可免费下载](https://gitee.com/vip204888/java-p7)**
复制代码


用户头像

VX:Lzzzzzz63 领取资料 2021.07.07 加入

还未添加个人简介

评论

发布
暂无评论
Java中高级核心知识全面解析(1),Java开发实战