打印启动信息

转载自: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); //注册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

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地址。

@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 启动添加日志信息

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

运行如下图:

springboot打印启动信息的更多相关文章

  1. Springboot项目启动不了。也不打印任何日志信息。

    Springboot项目启动不了.也不打印任何日志信息. <!-- 在创建Spring Boot工程时,我们引入了spring-boot-starter,其中包含了spring-boot-sta ...

  2. springboot之启动原理解析

    前言 SpringBoot为我们做的自动配置,确实方便快捷,但是对于新手来说,如果不大懂SpringBoot内部启动原理,以后难免会吃亏.所以这次博主就跟你们一起一步步揭开SpringBoot的神秘面 ...

  3. SpringBoot一站式启动流程源码分析

    一.前言 由上篇文章我们得知,SpringBoot启动时,就是有很简单的一行代码.那我们可以很清楚的看到这行代码的主角便是SpringApplication了,本文我们就来聊一聊这货,来探寻Sprin ...

  4. springboot之启动原理解析及源码阅读

    前言 SpringBoot为我们做的自动配置,确实方便快捷,但是对于新手来说,如果不大懂SpringBoot内部启动原理,以后难免会吃亏.所以这次博主就跟你们一起一步步揭开SpringBoot的神秘面 ...

  5. springboot项目启动成功后执行一段代码的两种方式

    springboot项目启动成功后执行一段代码的两种方式 实现ApplicationRunner接口 package com.lnjecit.lifecycle; import org.springf ...

  6. SpringBoot的启动简述

    一.注解和启动类SpringBootApplication 它是一个复式注解. @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME ...

  7. Springboot项目启动后自动创建多表关联的数据库与表的方案

    文/朱季谦 在一些项目开发当中,存在这样一种需求,即开发完成的项目,在第一次部署启动时,需能自行构建系统需要的数据库及其对应的数据库表. 若要解决这类需求,其实现在已有不少开源框架都能实现自动生成数据 ...

  8. 深入理解SpringBoot之启动探究

    SpringApplication是SpringBoot的启动程序,我们通过它的run方法可以快速启动一个SpringBoot应用.可是这里面到底发生了什么?它是处于什么样的机制简化我们程序启动的?接 ...

  9. java框架之SpringBoot(10)-启动流程及自定义starter

    启动流程 直接从 SpringBoot 程序入口的 run 方法看起: public static ConfigurableApplicationContext run(Object source, ...

  10. SpringBoot源码分析之SpringBoot的启动过程

    SpringBoot源码分析之SpringBoot的启动过程 发表于 2017-04-30   |   分类于 springboot  |   0 Comments  |   阅读次数 SpringB ...

随机推荐

  1. webrtc终极版(二)搭建自己的iceserver服务,并用到RTCMultiConnection的demo中

    webrtc终极版(二)搭建自己的iceserver服务,并用到RTCMultiConnection的demo中 目录 webrtc终极版(二)搭建自己的iceserver服务,并用到RTCMulti ...

  2. Sea-Search03总结&&un finish

    使用到的设计模式 Facade门面模式 为何使用? 在搜索项目中,由于使用Mvc架构且数据库中各种不同类型的数据源并没有放在同一张表,于是我们不可避免的在Controller中需要注入多个servic ...

  3. JS Leetcode 80. 删除有序数组中的重复项 II题解,常规解法与快慢双指针做法

    壹 ❀ 引 今天的题目来自LeetCode80. 删除有序数组中的重复项 II,是一道难度中等,但实际挺简单的一道题,题目描述如下: 给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使每 ...

  4. NC24608 [USACO 2011 Ope S]Learning Languages

    题目链接 题目 题目描述 Farmer John's N (2 <= N <= 10,000) cows, conveniently numbered 1..N, are fluent i ...

  5. Django实战之文件上传下载

    项目介绍 最近学习django,通过文件上传下载这个小项目,总结下常用的知识点. 做这个案例我有以下需求: 1.要支持一次上传多个文件 2.支持上传后记录上传的数据以及列表展示 3.支持下载和删除文件 ...

  6. base::AtExitManager 和 base::Singleton 的结合使用

    单例模式(Singleton)也称为单件模式,其意图是保证一个类仅有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享.有很多地方需要这样的功能模块,如系统的日志输出,GUI 应用必须是 ...

  7. CDN 加速原理

    => CDN 加速原理 HTTP 请求流程说明: 用户在浏览器输入要访问的网站域名,向本地 DNS 发起域名解析请求. 域名解析的请求被发往网站授权 DNS 服务器. 网站 DNS 服务器解析发 ...

  8. FFmpeg开发笔记(八):ffmpeg解码音频并使用SDL同步音频播放

    若该文为原创文章,未经允许不得转载原博主博客地址:https://blog.csdn.net/qq21497936原博主博客导航:https://blog.csdn.net/qq21497936/ar ...

  9. 默认形参和关键字实参,收集参数,命名关键字参数,return自定义返回,全局变量和局部变量,函数名的使用---day10

    1.函数定义处(默认形参在函数的定义) 1.1.函数的调用处(关键字实参在函数的调用处) 2.收集参数 (1)收集参数: (1) 普通收集参数 在参数的前面加一个*,代表的是普通收集参数 作用:收集多 ...

  10. linux下安装django2.2

    安装 pip3 install django==2.2 创建项目 django-admin startproject pyweb 创建应用 django-admin startapp app01 修改 ...