百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

你绝对想不到的spring boot中的centre

lipiwang 2024-10-23 13:55 14 浏览 0 评论

猿灯塔【全文略长,大家耐心读完哦!希望对大家有用!】



1,spring boot 是什么

spring boot 从字面上来理解就是spring 引导。是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。

官网描述:Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”.

Spring boot使创建独立的、产品级的基于Spring的应用程序变得很容易。以往基于spring的应用需要配置引入各种依赖、各种配置,解决各种依赖冲突等;而spring boot提供了各种starter,只需要在pom文件引入对应的starter,由对应的starter指向具体的依赖,引入默认配置,大大减少了spring应用配置的复杂度。

当然spring boot也不仅仅是starter,还包括一系列非常良好的支持,比如:内嵌的Tomcat,无需部署WAR文件,直接运行;应用监管程序actuator等。

2,spring boot 核心

spring boot如何简化spring应用的搭建的呢?其实就是通过利用pom文件的继承机制,预定义依赖包版本+根据应用场景,抽取依赖包,并封装,利用pom的传递依赖,完成完整的依赖包引入。我们分三部分来分析spring boot。1,spring boot依赖版本管理;2,spring boot 启动器;3,spring boot 启动过程,组件整合生效过程。

1,spring boot 依赖版本管理

我从一个简单的spring boot pom文件来看一下。

先看看spring boot 的pom配置parent 指向spring-boot-starter-parent;spring-boot-starter-parent的parent又指向spring-boot-dependencies;在其pom文件中引入了几乎所有常用的依赖jar包。这个就是spring boot应用的依赖包版本的统一管理。其后,在spring boot 的应用中,实际引入的依赖包,就不需要再定义版本,避免了依赖版本冲突等问题。

2,spring boot 启动器

查看spring boot应用的pom文件,我们发现,依赖相当简明,就是引入的spring-boot-stater-;

这个starter就是spring boot 的启动器。spring boot预先根据各种应用程序场景,整合需要引入的依赖包,组成一个个满足不同应用场景需要的stater;比如:spring-boot-starter-web、spring-boot-starter-thymeleaf、mybatis-spring-boot-starter等等(spring 官方提供的starter一般为:spring-boot-starter-*;其它提供通过SPI扩展的,如mybatis的,就不一样了)。每个starter会引入spring-boot-starters+自身需要的其他依赖,以满足当前场景启动需要的依赖。

spring boot就是通过这种方式,简化了依赖配置,使得开发spring应用时的配置简单清晰。

3,spring boot 启动过程

我们以最常见的web应用环境来分析spring boot 应用启动。(就是在spring boot应用中依赖spring-boot-starter-web;spring boot 版本为:1.5.14.RELEASE)

1,创建一个新的Spring应用程序实例。

    //springboot启动时,初始化环境
    @SuppressWarnings({ "unchecked", "rawtypes" })
    private void initialize(Object[] sources) {
        //将当前启动类放入资源;
        if (sources != null && sources.length > 0) {
            this.sources.addAll(Arrays.asList(sources));
        }
        //web环境诊断;处理了webEnvironment为true
        this.webEnvironment = deduceWebEnvironment();
        //;
        //设置初始化器;getSpringFactoriesInstances获取spring应用上下文初始化工厂实例;
        //从spring-boot-1.5.14.RELEASE.jar!/META-INF/spring.factories下根据映射加载,不同版本,会有细微差异;
        setInitializers((Collection) getSpringFactoriesInstances(
                ApplicationContextInitializer.class));
        //设置监听器;getSpringFactoriesInstances获取spring应用监听器工厂实例;
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        //诊断当前启动类是不是main函数启动,如是,返回当前启动类的类;通过栈信息,依次往上找,直到找到main函数所在的类;找到后,设置进入mainApplicationClass属性
        this.mainApplicationClass = deduceMainApplicationClass();
    }
    //spring boot 启动 spring应用
    /**
     * Run the Spring application, creating and refreshing a new
     * {@link ApplicationContext}.
     * @param args the application arguments (usually passed from a Java main method)
     * @return a running {@link ApplicationContext}
     */
    public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        //开始一个简单的watch,记录每个指定任务的总运行时间和运行时间。
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        FailureAnalyzers analyzers = null;
        //设置java.awt.headless属性;如果没有在属性文件中配置,则默认为true;
        configureHeadlessProperty();
        //创建并设置进入:listeners;并返回org.springframework.boot.context.event.EventPublishingRunListener
        SpringApplicationRunListeners listeners = getRunListeners(args);
        listeners.starting();
        try {
            //设置默认的应用程序参数;默认命令行参数;
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            //准备环境;装载.properties、.yaml等配置文件等
            ConfigurableEnvironment environment = prepareEnvironment(listeners,
                    applicationArguments);
            //打印spring标识;
            Banner printedBanner = printBanner(environment);
            //创建应用程序上下文;设置了reader、ClassPathBeanDefinitionScanner含web的是AnnotationConfigEmbeddedWebApplicationContext;不含web的是:AnnotationConfigApplicationContext;
            context = createApplicationContext();
            //创建故障分析器,包含一系列spring默认的故障分析器,并给每个分析器设置beanfactory;
            analyzers = new FailureAnalyzers(context);
            //准备ApplicationContext;
            prepareContext(context, environment, listeners, applicationArguments,
                    printedBanner);
            //调用的方法,在下面public void refresh() 详细说明处理;(这里最终调用到spring框架的refresh,这个里面就是spring的启动流程;下面详细描述spring框架的refresh过程;)
            refreshContext(context);
            //如果有扩展ApplicationRunner、CommandLineRunner;则执行。
            afterRefresh(context, applicationArguments);
            //启动监听结束,发布事件;简单的直接启动,这里发布了启动结束事件。
            listeners.finished(context, null);
            //结束watch,打印启动结束事件等;
            stopWatch.stop();
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass)
                        .logStarted(getApplicationLog(), stopWatch);
            }
            return context;
        }
        catch (Throwable ex) {
            handleRunFailure(context, listeners, analyzers, ex);
            throw new IllegalStateException(ex);
        }
    }
//下面是一些细节,可以略过先;
//这里也看看,装载bean的代码:
//这个方法被prepareContext调用;
    /**
     * Load beans into the application context.
     * @param context the context to load beans into
     * @param sources the sources to load
     */
    protected void load(ApplicationContext context, Object[] sources) {
        if (logger.isDebugEnabled()) {
            logger.debug(
                    "Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
        }
        //getBeanDefinitionRegistry(context)检查当前的context是否是:BeanDefinitionRegistry;如果是,则将context转化为BeanDefinitionRegistry返回;如果不是,则检查是否AbstractApplicationContext,转化返回;否则抛异常;
        //createBeanDefinitionLoader()处理创建annotatedReader、xmlReader、groovyReader、scanner(扫描器--ClassPathBeanDefinitionScanner)、scanner中的过滤器等;
        BeanDefinitionLoader loader = createBeanDefinitionLoader(
                getBeanDefinitionRegistry(context), sources);
        if (this.beanNameGenerator != null) {
            loader.setBeanNameGenerator(this.beanNameGenerator);
        }
        if (this.resourceLoader != null) {
            loader.setResourceLoader(this.resourceLoader);
        }
        if (this.environment != null) {
            loader.setEnvironment(this.environment);
        }
        //装载器装载;实际会调用到下面BeanDefinitionLoader的load方法;
        loader.load();
    }

    private int load(Class<?> source) {
        if (isGroovyPresent()) {
            // Any GroovyLoaders added in beans{} DSL can contribute beans here
            if (GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
                GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source,
                        GroovyBeanDefinitionSource.class);
                load(loader);
            }
        }
        if (isComponent(source)) {
            //
            this.annotatedReader.register(source);
            return 1;
        }
        return 0;
    }


    /**
     * Register a bean from the given bean class, deriving its metadata from
     * class-declared annotations.
     * 从给定的bean类注册一个bean,从类声明的注释派生它的元数据。 
     * @param annotatedClass the class of the bean
     * @param name an explicit name for the bean
     * @param qualifiers specific qualifier annotations to consider,
     * in addition to qualifiers at the bean class level
     */
    @SuppressWarnings("unchecked")
    public void registerBean(Class<?> annotatedClass, String name, Class<? extends Annotation>... qualifiers) {
        //AnnotatedGenericBeanDefinition 扩展 org.springframework.beans.factory.support.genericbeandefinition类,添加对通过AnnotatedBeanDefinition接口公开的注释元数据的支持。 
        //abd 包含了bean的class、注解、注解是否嵌套信息;
        AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
        if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
            return;
        }
        //解析适用于提供的bean定义的作用域元数据 
        ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
        abd.setScope(scopeMetadata.getScopeName());
        String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
        AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
        if (qualifiers != null) {
            for (Class<? extends Annotation> qualifier : qualifiers) {
                if (Primary.class == qualifier) {
                    abd.setPrimary(true);
                }
                else if (Lazy.class == qualifier) {
                    abd.setLazyInit(true);
                }
                else {
                    abd.addQualifier(new AutowireCandidateQualifier(qualifier));
                }
            }
        }

        BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
        definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
        BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
    }2,spring boot 启动spring

4,被spring boot 调用的spring框架启动过程

spring boot最终调用了spring的启动refresh处理;这一块儿是spring启动的关键。(ConfigurableApplicationContext run(String… args) 方法中通过refreshContext方法调用到spring框架的刷新方法;)

3,spring boot 整合mybatis、druid

持久层常见使用mybatis、druid;这里一步步描述spring boot整合mybatis、druid。

1,添加依赖的stater到pom文件如下:

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.8</version>
        </dependency>

2,添加yaml配置

spring:
  datasource:
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/mybatis
    type: com.alibaba.druid.pool.DruidDataSource
#   druid连接池配置
    initialSize: 5
    minIdle: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true
    filters: stat,log4j
    maxPoolPreparedStatementPerConnectionSize: 20
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
mybatis:
  config-location: classpath:mybatis/mybatis-config.xml
  mapper-locations: classpath:mybatis/mapper/*.xml
#只配置了druid管理页登录用户名、密码;
druid:
  servlet:
    username: appAdmin
    password: 111111    

3,添加java配置类(druid和myBatis配置)

druid配置:

@Configuration
@PropertySource(value= {"classpath:application.properties"})
public class DruidConfig {

    @ConfigurationProperties(prefix = "spring.datasource")
    @Bean
    public DataSource druid(){
        return  new DruidDataSource();
    }

    @Value("${druid.servlet.username}")
    private String loginUserName;
    @Value("${druid.servlet.password}")
    private String loginPassword;

    //
    //1、配置Druid的管理Servlet
    @Bean
    public ServletRegistrationBean statViewServlet(){
        ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
        final Map<String, String> initParams = new HashMap<String, String>();

        initParams.put("loginUsername",loginUserName);
        initParams.put("loginPassword",loginPassword);

        bean.setInitParameters(initParams);
        return bean;
    }

    //配置Druid的web监控的filter
    @Bean
    public FilterRegistrationBean webStatFilter(){
        FilterRegistrationBean bean = new FilterRegistrationBean();
        bean.setFilter(new WebStatFilter());
        bean.setUrlPatterns(Arrays.asList("/*"));
        return  bean;
    }
}

mybatis配置:

@Configuration
public class MyBatisConfig {

    //通过ConfigurationCustomizer定制mybatis相关规则:
    @Bean
    public ConfigurationCustomizer configurationCustomizer(){
        return new ConfigurationCustomizer(){
            @Override
            public void customize(org.apache.ibatis.session.Configuration configuration) {
                //自定义规则,覆盖默认规则
                //开启驼峰命名映射规则
                configuration.setMapUnderscoreToCamelCase(true);
            }
        };
    }
}

以上三步就一起完成了mybatis、druid整合到spring boot应用。

4,spring boot 整合redis

1.引入spring-boot-starter-data-redis

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

2.配置redis

spring.redis.database=0
# pool settings ...  
spring.redis.pool.max-idle=50
spring.redis.pool.min-idle=10
spring.redis.pool.max-active=200
spring.redis.pool.max-wait=500
# name of Redis server 
spring.redis.sentinel.master=mysentinel
spring.redis.sentinel.nodes=192.168.6.211:26379,192.168.6.211:26380,192.168.6.211:263813,redis配置类

3,redis配置类

@Configuration
public class RedisConfig {
    private static Logger logger = LoggerFactory.getLogger(RedisConfig.class);

    @Bean  
    public RedisTemplate<String, Object> getRedisTemplate(RedisConnectionFactory factory) {  
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();  
        redisTemplate.setConnectionFactory(factory);  
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(redisSerializer);  
        redisTemplate.setHashKeySerializer(redisSerializer);
        //系列化为json
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);  
        ObjectMapper om = new ObjectMapper();  
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);  
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);  
        jackson2JsonRedisSerializer.setObjectMapper(om);  

        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);  
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);  
        redisTemplate.afterPropertiesSet();  

        return redisTemplate;  
    } 
}

5,spring boot 整合RabbitMQ

实际应用程序开发中,由于需要考虑解耦、快速响应和流量削峰,很多时候采用异步机制,这个时候基于消息的架构就是很好的选择,RabbitMQ是高级消息队列协议的一个优秀开源实践,在实际应用中,很多时候选择使用RabbitMQ。

5.1RabbitMQ核心概念简介:


Message:消息由消息头和消息体组成。消息头由一系列可选属性组成,这些属性包括routing-key(用于路由)、priority(相对优先级)、delivery-mode(是否持久存储)等;

Publisher:消息的生产者,向交换器发布消息;

Exchange:交换器,用来接收生产者发送的消息,并根据类型,将消息路由到消息队列;Exchange有4种:direct(默认,点对点)、fanout(广播)、topic(基于路由键匹配队列)和headers(通过消息头信息处理消息发布到队列)。

Queue:消息队列;消息的容器,保存消息直到消费者取走。

Binding:绑定,就是基于Exchange类型和路由键绑定Exchange和Queue的路由规则。交换器和队列间的绑定关系是多对多的。

Virtual Host:虚拟主机;每个虚拟主机就是一个服务器;除了将众多客户逻辑隔离,还能起到命名空间的作用。

Broker:消息队列代理。代理一个消息队列服务器。

Connection:网络连接。

Channel:信道,用于复用一条TCP连接。建立在真实的TCP连接内的虚拟连接,避免TCP连接的建立、销毁的开销。一个TCP连接,开辟多个信道,实际消息的发送、接收等,通过信道完成。

Consumer:消息的消费者,从消息队列中获取消息。

5.2 spring boot 整合RabbitMQ

1,引入 高级消息队列starter:spring-boot-starter-amqp

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
spring:
  rabbitmq:
    host: 192.168.6.211
    username: guest
    password: guest2,添加yaml配置

3,使用AmqpAdmin 管理交换器、队列、绑定;

    //AmqpAdmin 在springboot启动时,已经默认创建完成,直接使用;AmqpAdmin提供了接口,实际参见对应版本接口
    @Autowired
    AmqpAdmin amqpAdmin;
//实际使用代码类似如下:
    //创建交换器:
    amqpAdmin.declareExchange(new DirectExchange("exchangename"));
    //创建队列:
    amqpAdmin.declareQueue(new Queue("queuename",true));
    //创建绑定规则(将指定交换器(参数1)以指定路由键(参数4)绑定到到指定类型(参数2,队列or交换机)、指定目的地(参数1,比如队列名)),最后一个参数为参数头信息;
    amqpAdmin.declareBinding(new Binding("queuename", Binding.DestinationType.QUEUE,"exchangename","plan.*",null));

4,配置消息转换器及开启

@EnableRabbit//启用监听器
@Configuration
public class AMQPConfig {

    //默认是使用:SimpleMessageConverter();这里直接使用本身提供的Jackson2JsonMessageConverter;方便管理工具可读;
    @Bean
    public MessageConverter messageConverter(){
        return new Jackson2JsonMessageConverter();
    }
}

5,发送消息:

    //上面定义了具体的消息转换器,rabbitTemplate中的消息转换器采用了定义的bean;
    @Autowired
    RabbitTemplate rabbitTemplate;
    //具体消息发送rabbitTemplate的接口,类似使用:rabbitTemplate.receiveAndConvert等;6,消息监听:

6,消息监听:

注意在配置类中,通过@EnableRabbit启用了监听监听;实际使用,只需要将监听方法添加@RabbitListener注解,即可监听指定队列。

    @RabbitListener(queues = "myvrdirect")
    public void receive02(Message message){
    }

如果可以的话,大家动动小手帮小编点赞转发一下哦~

你们的支持就是小编持续更文的最大动力!

相关推荐

前端入门——css 网格轨道详细介绍

上篇前端入门——cssGrid网格基础知识整体大概介绍了cssgrid的基本概念及使用方法,本文将介绍创建网格容器时会发生什么?以及在网格容器上使用行、列属性如何定位元素。在本文中,将介绍:...

Islands Architecture(孤岛架构)在携程新版首页的实践

一、项目背景2022,携程PC版首页终于迎来了首次改版,完成了用户体验与技术栈的全面升级。作为与用户连接的重要入口,旧版PC首页已经陪伴携程走过了22年,承担着重要使命的同时,也遇到了很多问题:维护/...

HTML中script标签中的那些属性

HTML中的<script>标签详解在HTML中,<script>标签用于包含或引用JavaScript代码,是前端开发中不可或缺的一部分。通过合理使用<scrip...

CSS 中各种居中你真的玩明白了么

页面布局中最常见的需求就是元素或者文字居中了,但是根据场景的不同,居中也有简单到复杂各种不同的实现方式,本篇就带大家一起了解下,各种场景下,该如何使用CSS实现居中前言页面布局中最常见的需求就是元...

CSS样式更改——列表、表格和轮廓

上篇文章主要介绍了CSS样式更改篇中的字体设置Font&边框Border设置,这篇文章分享列表、表格和轮廓,一起来看看吧。1.列表List1).列表的类型<ulstyle='list-...

一文吃透 CSS Flex 布局

原文链接:一文吃透CSSFlex布局教学游戏这里有两个小游戏,可用来练习flex布局。塔防游戏送小青蛙回家Flexbox概述Flexbox布局也叫Flex布局,弹性盒子布局。它决定了...

css实现多行文本的展开收起

背景在我们写需求时可能会遇到类似于这样的多行文本展开与收起的场景:那么,如何通过纯css实现这样的效果呢?实现的难点(1)位于多行文本右下角的展开收起按钮。(2)展开和收起两种状态的切换。(3)文本...

css 垂直居中的几种实现方式

前言设计是带有主观色彩的,同样网页设计中的css一样让人摸不头脑。网上列举的实现方式一大把,或许在这里你都看到过,但既然来到这里我希望这篇能让你看有所收获,毕竟这也是前端面试的基础。实现方式备注:...

WordPress固定链接设置

WordPress设置里的最后一项就是固定链接设置,固定链接设置是决定WordPress文章及静态页面URL的重要步骤,从站点的SEO角度来讲也是。固定链接设置决定网站URL,当页面数少的时候,可以一...

面试发愁!吃透 20 道 CSS 核心题,大厂 Offer 轻松拿

前端小伙伴们,是不是一想到面试里的CSS布局题就发愁?写代码时布局总是对不齐,面试官追问兼容性就卡壳,想跳槽却总被“多列等高”“响应式布局”这些问题难住——别担心!从今天起,咱们每天拆解一...

3种CSS清除浮动的方法

今天这篇文章给大家介绍3种CSS清除浮动的方法。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。首先,这里就不讲为什么我们要清楚浮动,反正不清除浮动事多多。下面我就讲3种常用清除浮动的...

2025 年 CSS 终于要支持强大的自定义函数了?

大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!1.什么是CSS自定义属性CSS自...

css3属性(transform)的一个css3动画小应用

闲言碎语不多讲,咱们说说css3的transform属性:先上效果:效果说明:当鼠标移到a标签的时候,从右上角滑出二维码。实现方法:HTML代码如下:需要说明的一点是,a链接的跳转需要用javasc...

CSS基础知识(七)CSS背景

一、CSS背景属性1.背景颜色(background-color)属性值:transparent(透明的)或color(颜色)2.背景图片(background-image)属性值:none(没有)...

CSS 水平居中方式二

<divid="parent"><!--定义子级元素--><divid="child">居中布局</div>...

取消回复欢迎 发表评论: