springboot打印启动信息

打印启动信息

转载自:www.javaman.cn

1 spring Bean实例化流程

基本流程:

1、Spring容器在进行初始化时,会将xml或者annotation配置的bean的信息封装成一个BeanDefinition对象(每一个bean标签或者@bean注解都封装成一个BeanDefinition对象),所有的BeanDefinition存储到一个名为beanDefinitionMap的Map集合中去

java 复制代码
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实例对象返回

java 复制代码
public class DefaultSingletonBeanRegistry extends ... implements ... {
	//存储Bean实例的单例池
	//key:是Bean的beanName,value:是Bean的实例对象
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
    
    //注册bean
        public 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);
                }
            }
        }
    }

总体流程如下:

  1. 加载xml配置文件,解析获取配置中的每个的信息,封装成一个个的BeanDefinition对象;
  2. 将BeanDefinition存储在一个名为beanDefinitionMap的Map中;
  3. ApplicationContext底层遍历beanDefinitionMap,反射创建Bean实例对象;
  4. 创建好的Bean实例对象,被存储到一个名为singletonObjects的Map中;
  5. 当执行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

java 复制代码
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;


    @Override
    public 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地址。

java 复制代码
@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;
    }

        @Override
        public void onApplicationEvent(WebServerInitializedEvent webServerInitializedEvent) {
            this.serverPort = webServerInitializedEvent.getWebServer().getPort();
        }

}
5 启动添加日志信息
java 复制代码
@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" +
                "----------------------------------------------------------");
    }
}

运行如下图:

相关推荐
刃神太酷啦12 小时前
力扣校招算法通关:双指针技巧全场景拆解 —— 从数组操作到环检测的高效解题范式
java·c语言·数据结构·c++·算法·leetcode·职场和发展
Mos_x12 小时前
计算机组成原理核心知识点梳理
java·后端
墨寒博客栈12 小时前
Linux基础常用命令
java·linux·运维·服务器·前端
回忆是昨天里的海13 小时前
k8s-部署springboot容器化应用
java·容器·kubernetes
INFINI Labs13 小时前
使用 Docker Compose 轻松实现 INFINI Console 离线部署与持久化管理
java·docker·eureka·devops·docker compose·console·easyserach
Cosolar13 小时前
国产麒麟系统 aarch64 架构 PostgreSQL 15 源码编译安装完整教程
java·后端
GalaxyPokemon13 小时前
PlayerFeedback 插件开发日志
java·服务器·前端
天天摸鱼的java工程师13 小时前
别再写那些重复代码了!8年Java老兵教你用 Hutool 提升开发效率
java·后端