打印启动信息
转载自:www.javaman.cn
1 spring Bean实例化流程
基本流程:
1、Spring容器在进行初始化时,会将xml或者annotation配置的bean的信息封装成一个BeanDefinition对象(每一个bean标签或者@bean注解都封装成一个BeanDefinition对象),所有的BeanDefinition存储到一个名为beanDefinitionMap的Map集合中去
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {private final Map<String, BeanDefinition> beanDefinitionMap;···this.beanDefinitionMap.put(beanName, beanDefinition);
}
2、Spring框架在对该Map进行遍历,使用反射创建Bean实例对象,创建好的Bean对象存储在一个名为singletonObjects(单例池)的Map集合中,当调用getBean方法时则最终从该Map集合中取出Bean实例对象返回
public class DefaultSingletonBeanRegistry extends ... implements ... {//存储Bean实例的单例池//key:是Bean的beanName,value:是Bean的实例对象private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);//注册beanpublic void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {Assert.notNull(beanName, "Bean name must not be null");Assert.notNull(singletonObject, "Singleton object must not be null");synchronized(this.singletonObjects) {Object oldObject = this.singletonObjects.get(beanName);if (oldObject != null) {throw new IllegalStateException("Could not register object [" + singletonObject + "] under bean name '" + beanName + "': there is already object [" + oldObject + "] bound");} else {this.addSingleton(beanName, singletonObject);}}}}
总体流程如下:
- 加载xml配置文件,解析获取配置中的每个的信息,封装成一个个的BeanDefinition对象;
- 将BeanDefinition存储在一个名为beanDefinitionMap的Map中;
- ApplicationContext底层遍历beanDefinitionMap,反射创建Bean实例对象;
- 创建好的Bean实例对象,被存储到一个名为singletonObjects的Map中;
- 当执行applicationContext.getBean(beanName)时,从singletonObjects去匹配Bean实例返回
2 Spring的后处理器
Spring的后处理器是Spring对外开发的重要扩展点,允许我们介入到Bean的整个实例化流程中来,以达到动态注册BeanDefinition,动态修改BeanDefinition,以及动态修改Bean的作用。Spring主要有两种后处理器:
-
BeanFactoryPostProcessor:Bean工厂后处理器,在BeanDefinitionMap填充完毕,Bean实例化之前执行;
-
BeanPostProcessor:Bean后处理器,一般在Bean实例化之后,填充到单例池singletonObjects之前执行。
3 实现BeanFactoryPostProcessor接口,创建SpringUtils工具类
实现了 BeanFactoryPostProcessor
接口。这意味着它可以在Spring容器加载Bean定义后,在实例化Bean之前对BeanFactory进行自定义的处理
创建SpringUtils工具类,提供了一些静态方法,以便在应用程序中更方便地获取和操作Spring容器中的Bean
package com.ds.core.util;import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;@Component
public final class SpringUtils implements BeanFactoryPostProcessor {//1、存储Spring应用上下文的Bean工厂,在postProcessBeanFactory方法中初始化//2、静态的 ConfigurableListableBeanFactory 类型的变量 beanFactory,用于存储Spring应用上下文的Bean工厂private static ConfigurableListableBeanFactory beanFactory;@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {SpringUtils.beanFactory = configurableListableBeanFactory;}/*** 根据名称获取bean** @param name* @param <T>* @return*/public static <T> T getBean(String name) throws BeansException {return (T) beanFactory.getBean(name);}/*** 根据类型获取bean* @param clz* @param <T>* @return* @throws BeansException*/public static <T> T getBean(Class<T> clz) throws BeansException {return (T) beanFactory.getBean(clz);}/*** 检查是否存在具有指定名称的Bean。如果存在,它返回true;否则,返回false。* @param name* @return*/public static boolean containsBean(String name){return beanFactory.containsBean(name);}/***判断一个Bean是否是单例的(在Spring中只有一个实例)* @param name* @return* @throws NoSuchBeanDefinitionException*/public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException {return beanFactory.isSingleton(name);}/*** 获取指定名称的Bean的类型* @param name* @return* @throws NoSuchBeanDefinitionException*/public static Class<?> getType(String name) throws NoSuchBeanDefinitionException{return beanFactory.getType(name);}/*** 获取aop代理对象** @param invoker* @return*/@SuppressWarnings("unchecked")public static <T> T getAopProxy(T invoker){return (T) AopContext.currentProxy();}}
4 实现ApplicationListener,监听 Web 服务器初始化事件
实现了 ApplicationListener<WebServerInitializedEvent>
接口,意味着它监听应用程序事件,特别是监听 Web 服务器初始化事件。重写的方法 onApplicationEvent(WebServerInitializedEvent webServerInitializedEvent)
。当 Web 服务器初始化事件发生时,这个方法会被触发。在这个方法内部,通过传入的 WebServerInitializedEvent
对象获取到正在运行的服务器的端口号,并将其赋值给 serverPort
变量,从而获取服务器的URL地址。
@Component
public class ServerConfig implements ApplicationListener<WebServerInitializedEvent>{private int serverPort;public String getUrl(){InetAddress address = null;try {address = InetAddress.getLocalHost();} catch (UnknownHostException e) {e.printStackTrace();}return "http://"+address.getHostAddress()+":"+this.serverPort;}@Overridepublic void onApplicationEvent(WebServerInitializedEvent webServerInitializedEvent) {this.serverPort = webServerInitializedEvent.getWebServer().getPort();}}
5 启动添加日志信息
@Slf4j
@SpringBootApplication
@EnableAspectJAutoProxy(exposeProxy = true)
public class BookApplication {public static void main(String[] args) {SpringApplication.run(BookApplication.class,args);printUrl();}private static void printUrl() {//获取ServerConfig.class//因为是私有方法,所以无法通过@Autowired注入,通过ServerConfig serverConfig = SpringUtils.getBean(ServerConfig.class);log.info("\n----------------------------------------------------------\n\t" +"Application is running! Access URLs:\n\t" +"访问网址:"+ serverConfig.getUrl()+ "\n" +"----------------------------------------------------------");}
}
运行如下图: