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" +
                "----------------------------------------------------------");
    }
}

运行如下图:

相关推荐
考虑考虑30 分钟前
JDK9中的dropWhile
java·后端·java ee
想躺平的咸鱼干38 分钟前
Volatile解决指令重排和单例模式
java·开发语言·单例模式·线程·并发编程
hqxstudying1 小时前
java依赖注入方法
java·spring·log4j·ioc·依赖
·云扬·1 小时前
【Java源码阅读系列37】深度解读Java BufferedReader 源码
java·开发语言
春生野草2 小时前
关于SpringMVC的整理
spring
Bug退退退1232 小时前
RabbitMQ 高级特性之重试机制
java·分布式·spring·rabbitmq
小皮侠2 小时前
nginx的使用
java·运维·服务器·前端·git·nginx·github
Zz_waiting.2 小时前
Javaweb - 10.4 ServletConfig 和 ServletContext
java·开发语言·前端·servlet·servletconfig·servletcontext·域对象
全栈凯哥2 小时前
02.SpringBoot常用Utils工具类详解
java·spring boot·后端
兮动人3 小时前
获取终端外网IP地址
java·网络·网络协议·tcp/ip·获取终端外网ip地址