微服務(wù)-03-SpringBoot

1. Spring Boot簡(jiǎn)介

Spring Boot的設(shè)計(jì)目的是用來簡(jiǎn)化新Spring應(yīng)用的初始搭建以及開發(fā)過程。SpringBoot所具備的特征有:

  1. 可以創(chuàng)建獨(dú)立的Spring應(yīng)用程序凉馆,并且基于其Maven或Gradle插件已日,可以創(chuàng)建可執(zhí)行的JARs和WARs
  2. 內(nèi)嵌Tomcat或Jetty等Servlet容器
  3. 提供自動(dòng)配置的“starter”項(xiàng)目對(duì)象模型(POMS)以簡(jiǎn)化Maven配置
  4. 盡可能自動(dòng)配置Spring容器
  5. 提供準(zhǔn)備好的特性蒋腮,如指標(biāo)、健康檢查和外部化配置
  6. 絕對(duì)沒有代碼生成腻豌,不需要XML配置

Spring Boot官網(wǎng):https://spring.io/projects/spring-boot/

Spring Boot啟動(dòng)結(jié)構(gòu)圖(圖片太大了引谜,可單獨(dú)查看圖片):

Spring Boot啟動(dòng)過程:

一路追蹤源碼:
SpringApplication.run()
// 先new對(duì)象厦画,然后run
new SpringApplication(primarySources).run(args)

// new對(duì)象的構(gòu)造方法
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    // 配置Source
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    // 判斷是否為web環(huán)境
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    this.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class));
    // 創(chuàng)建初始化構(gòu)造器疮茄,加載spring.factories文件中ApplicationContextInitializer對(duì)應(yīng)的所有類
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    // 創(chuàng)建應(yīng)用監(jiān)聽器,加載spring.factories文件中ApplicationListener對(duì)應(yīng)的所有類
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    // 配置應(yīng)用的主方法所在類
    this.mainApplicationClass = deduceMainApplicationClass();
}

// run方法:運(yùn)行Spring應(yīng)用程序根暑,創(chuàng)建并刷新新的應(yīng)用程序上下文
public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    // 應(yīng)用啟動(dòng)計(jì)時(shí)器開始計(jì)時(shí)
    stopWatch.start();
    DefaultBootstrapContext bootstrapContext = createBootstrapContext();
    ConfigurableApplicationContext context = null;
    configureHeadlessProperty();
    // 獲取spring.factories文件中SpringApplicationRunListener對(duì)應(yīng)的監(jiān)聽器
    SpringApplicationRunListeners listeners = getRunListeners(args);
    // 廣播事件力试,應(yīng)用啟動(dòng)監(jiān)聽器開始監(jiān)聽
    listeners.starting(bootstrapContext, this.mainApplicationClass);
    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        // 準(zhǔn)備環(huán)境
        ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
        configureIgnoreBeanInfo(environment);
        // 應(yīng)用啟動(dòng)時(shí)的提示圖標(biāo),可以在resources下創(chuàng)建一個(gè)banner.txt來修改啟動(dòng)時(shí)的打印信息
        Banner printedBanner = printBanner(environment);
        // 創(chuàng)建新的應(yīng)用程序上下文
        context = createApplicationContext();
        context.setApplicationStartup(this.applicationStartup);
        // 準(zhǔn)備新的應(yīng)用程序上下文
        prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
        // 刷新新的應(yīng)用程序上下文
        refreshContext(context);
        afterRefresh(context, applicationArguments);
        // 應(yīng)用啟動(dòng)計(jì)時(shí)器結(jié)束計(jì)時(shí)
        stopWatch.stop();
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
        }
        // 上下文已經(jīng)刷新排嫌,應(yīng)用程序已經(jīng)啟動(dòng)
        listeners.started(context);
        // 調(diào)用CommandLineRunner和ApplicationRunner
        callRunners(context, applicationArguments);
    } catch (Throwable ex) {
        handleRunFailure(context, ex, listeners);
        throw new IllegalStateException(ex);
    }

    try {
        listeners.running(context);
    } catch (Throwable ex) {
        handleRunFailure(context, ex, null);
        throw new IllegalStateException(ex);
    }
    return context;
}

2. 創(chuàng)建Spring Boot工程

需求:在頁面展示hello, world

2.1 手工創(chuàng)建

  1. 創(chuàng)建Maven工程

  2. 在pom.xml中配置parent和web的起步依賴

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.52lhp</groupId>
        <artifactId>hello</artifactId>
        <version>1.0-SNAPSHOT</version>
    
        <!--1. 繼承spring-boot-starter-parent
        Spring Boot版本控制的原理:
        spring-boot-starter-parent繼承了spring-boot-dependencies畸裳,
        而spring-boot-dependencies中定義了各個(gè)依賴的版本
        -->
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.1.4.RELEASE</version>
        </parent>
        <!--2. 添加web的起步依賴-->
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
        </dependencies>
    </project>
    
  3. 創(chuàng)建啟動(dòng)類

    package com.lhp;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class HelloWorldApplication {
        public static void main(String[] args) {
            SpringApplication.run(HelloWorldApplication.class, args);
        }
    }
    
  4. 編寫業(yè)務(wù)代碼:

    package com.lhp.controller;
    
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class HelloWorldController {
        @RequestMapping("/hello")
        public String hello() {
            return "hello, world";
        }
    }
    
  5. 運(yùn)行啟動(dòng)類的main方法

2.2 通過Spring Initializr創(chuàng)建

在IDEA中創(chuàng)建工程時(shí)使用Spring Initializr

  1. 點(diǎn)擊Next轉(zhuǎn)到Spring Initializr Project Settings頁面-->配置Group、Artifact淳地、Packaging怖糊、Java Version、Package等
  2. 點(diǎn)擊Next轉(zhuǎn)到Dependencies頁面-->選擇Spring Boot的版本和需要的依賴-->Next-->Finish
  3. 右鍵模塊-->Add Framework Support...-->選中Maven

2.3 部署Spring Boot項(xiàng)目

jar包部署:

  1. 在pom.xml中配置spring-boot-maven-plugin插件颇象,將項(xiàng)目打成jar包

  2. 控制臺(tái)執(zhí)行命令

    java -jar xxx.jar
    

war包部署:

  1. 讓啟動(dòng)類繼承SpringBootServletInitializer
  2. 在pom.xml中配置spring-boot-maven-plugin插件伍伤,將項(xiàng)目打成war包
  3. 在Web服務(wù)器(如:Tomcat)中部署war包

3. Spring Boot配置文件

Spring Boot的很多配置都有默認(rèn)值,可以使用application.properties或application.yml(application.yaml)來修改默認(rèn)配置遣钳。SpringBoot默認(rèn)從Resource目錄加載自定義配置文件扰魂。

如果同時(shí)存在properties和yaml/yml,則properties優(yōu)先級(jí)高于yaml/yml蕴茴。

yaml/yml基本語法劝评,參考:https://www.runoob.com/w3cnote/yaml-intro.html

  • 大小寫敏感
  • 使用縮進(jìn)表示層級(jí)關(guān)系
  • 縮進(jìn)不允許使用tab,只允許空格
  • 縮進(jìn)的空格數(shù)不重要荐开,只要相同層級(jí)的元素左對(duì)齊即可
  • #表示注釋
  • 值前面要加一個(gè)空格
  • 對(duì)象鍵值對(duì)使用冒號(hào)結(jié)構(gòu)表示key: value
  • 以-開頭的行表示構(gòu)成一個(gè)數(shù)組
  • 純量包括:字符串付翁、布爾值简肴、整數(shù)晃听、浮點(diǎn)數(shù)、Null砰识、時(shí)間能扒、日期
  • &用來建立錨點(diǎn),*用來引用錨點(diǎn)

3.1 獲取配置文件中的值

  1. application.yml配置文件內(nèi)容:

    person:
      name: lhp
      age: 21
      # 純量數(shù)據(jù)類型
      sex: 男
      # 數(shù)組數(shù)據(jù)類型
      hobbies:
        - study
        - reading
      # 對(duì)象數(shù)據(jù)類型
      pet:
        name: 狗
        food: 骨頭
        # 參數(shù)引用
        age: ${person.age}
    
  2. POJO:

    @Data
    public class Pet implements Serializable {
        private String name;
        private String food;
        private Integer age;
    }
    
    // 1. 通過ConfigurationProperties把POJO的屬性和yml中key的值自動(dòng)建立映射關(guān)系
    // 2. 將POJO交給Spring容器管理(POJO需要有Setter方法)
    // 3. 使用時(shí)直接從Spring容器中獲取該P(yáng)OJO
    // 對(duì)象中的屬性若有自身辫狼,則可能映射失敵醢摺;如Person中含有Person
    @Data
    @Component
    @ConfigurationProperties(prefix = "person")
    public class Person implements Serializable {
        private String name;
        private Integer age;
        private String sex;
        private String[] hobbies;
        private Pet pet;
    }
    
  3. 獲取配置文件中的值:

    import com.lhp.demo.pojo.Person;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.core.env.Environment;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    @RequestMapping("/test")
    public class TestController {
        // @Value("${屬性名}")只能獲取純量
        @Value("${person.hobbies[0]}")
        private String personHobbies0;
        // 對(duì)象和數(shù)組的獲取方式相同
        @Autowired
        private Person person;
        // 通過Environment可以獲取純量
        @Autowired
        private Environment environment;
    
        @GetMapping("/scalars")
        private String scalars() {
            System.out.println(environment.getProperty("person.hobbies[0]"));
            return "person.hobbies[0]=" + personHobbies0;
        }
    
        @GetMapping("/object")
        private String object() {
            return "person=" + person;
        }
    }
    

3.2 使用指定的環(huán)境配置

  1. 方式1:編寫多個(gè)配置文件application-xxx

    1. application.yml:

      # 通過active指定要使用的配置文件
      spring:
        profiles:
          # active的值為application-后面的部分
          active: pro
      
    2. application-dev.yml:

      # 開發(fā)環(huán)境
      server:
        port: 8081
      
    3. application-pro.yml:

      # 生產(chǎn)環(huán)境
      server:
        port: 8082
      
    4. application-test.yml:

      # 測(cè)試環(huán)境
      server:
        port: 8083
      
  2. 方式2:在application.yml中使用---來分割

    spring:
      profiles:
        active: dev
    ---
    # 開發(fā)環(huán)境
    server:
      port: 8081
    spring:
      profiles: dev
    ---
    # 生產(chǎn)環(huán)境
    server:
      port: 8082
    spring:
      profiles: pro
    ---
    # 測(cè)試環(huán)境
    server:
      port: 8083
    spring:
      profiles: test
    
  • 激活profiles的方式:

    1. 方式1:如上膨处,在配置文件中配置spring.profiles.active=環(huán)境

    2. 方式2:運(yùn)行時(shí)指定參數(shù)

      java -jar xxx.jar --spring.profiles.active=test
      
    3. 方式3:配置jvm參數(shù)

      -Dspring.profiles.active=dev
      

4. Spring Boot整合其他框架

4.1 整合MyBatis

  1. 添加依賴:

    <!--mybatis的起步依賴-->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.1.4</version>
    </dependency>
    <!--mysql驅(qū)動(dòng)-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    
  2. 在application.yml中配置:

    spring:
      datasource:
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://localhost:3306/db01?useSSL=false&serverTimezone=UTC&characterEncoding=utf-8
        username: root
        password: 123456
    mybatis:
      # 配置mybatis映射文件的位置
      mapper-locations: classpath:com/lhp/integration/dao/*Dao.xml
    
  3. 在啟動(dòng)類上添加MapperScan注解见秤,掃描Dao接口所在的包:

    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    // MapperScan會(huì)掃描指定包下的所有的接口砂竖,然后將接口的代理對(duì)象交給Spring容器
    @MapperScan(basePackages = "com.lhp.integration.dao")
    public class IntegrationApplication {
        public static void main(String[] args) {
            SpringApplication.run(IntegrationApplication.class, args);
        }
    }
    

4.2 整合JUnit

  1. 添加依賴:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    
  2. 測(cè)試類所在的包最好在啟動(dòng)類所在的包下

  3. 低版本Spring Boot使用:

    package com.lhp.integration;
    
    import com.lhp.integration.dao.UserDao;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.test.context.junit4.SpringRunner;
    
    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class IntegrationApplicationTests {
        @Autowired
        private UserDao userDao;
    
        @Test
        public void contextLoads() {
            System.out.println(userDao.findAll());
        }
    }
    
  4. 高版本Spring Boot使用:

    package com.lhp.integration;
    
    import com.lhp.integration.dao.UserDao;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    
    @SpringBootTest
    public class IntegrationApplicationTests {
        @Autowired
        private UserDao userDao;
    
        @Test
        public void contextLoads() {
            System.out.println(userDao.findAll());
        }
    }
    

4.3 整合Redis

  1. 添加依賴:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    
  2. 在application.yml中配置:

    spring:
      redis:
        host: localhost
        port: 6379
    
  3. 在啟動(dòng)類中配置Redis序列化機(jī)制:

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.annotation.Bean;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
    import org.springframework.data.redis.serializer.StringRedisSerializer;
    
    @SpringBootApplication
    public class IntegrationApplication {
        public static void main(String[] args) {
            SpringApplication.run(IntegrationApplication.class, args);
        }
    
        // 配置Redis序列化機(jī)制
        @Bean
        public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
            RedisTemplate<Object, Object> template = new RedisTemplate();
            template.setConnectionFactory(redisConnectionFactory);
            // 設(shè)置Key的序列化機(jī)制為StringRedisSerializer
            template.setKeySerializer(new StringRedisSerializer());
            // 設(shè)置value的序列化機(jī)制為JdkSerializationRedisSerializer
            template.setValueSerializer(new JdkSerializationRedisSerializer());
            return template;
        }
    }
    
  4. 使用:

    package com.lhp.integration;
    
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.data.redis.core.RedisTemplate;
    
    @SpringBootTest
    public class IntegrationApplicationTests {
        @Autowired
        private RedisTemplate redisTemplate;
    
        @Test
        public void redisTest() {
            redisTemplate.boundValueOps("name").set("lhp");
            String name = String.valueOf(redisTemplate.boundValueOps("name").get());
            System.out.println(name);
        }
    }
    

RedisTemplate支持的Redis序列化機(jī)制(RedisSerializer接口的實(shí)現(xiàn)類):

  1. GenericJackson2JsonRedisSerializer
  2. GenericToStringSerializer
  3. Jackson2JsonRedisSerializer
  4. JdkSerializationRedisSerializer(默認(rèn)):要求key和value都實(shí)現(xiàn)Serializable接口
  5. OxmSerializer
  6. StringRedisSerializer
  7. 自己定義的RedisSerializer接口實(shí)現(xiàn)類

5. Spring Boot自動(dòng)配置的原理

5.1 Condition接口

Condition接口和@Conditional注解是Spring4之后提供的,增加條件判斷功能鹃答,可以選擇性的注入Bean對(duì)象到Spring容器中乎澄。

自定義Condition實(shí)現(xiàn)類,需求:根據(jù)是否導(dǎo)入坐標(biāo)测摔,來選擇是否加載Bean

  1. 是否導(dǎo)入的坐標(biāo):

    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
        <version>3.2.0</version>
    </dependency>
    
  2. POJO:

    public class User {
    }
    
  3. 自定義Condition實(shí)現(xiàn)類:

    package com.lhp.study.condition;
    
    import com.lhp.study.annotation.ConditionalOnClass;
    import org.springframework.context.annotation.Condition;
    import org.springframework.context.annotation.ConditionContext;
    import org.springframework.core.type.AnnotatedTypeMetadata;
    
    import java.util.Map;
    
    public class OnClassCondition implements Condition {
        /**
         * @param context  上下文信息對(duì)象:可以獲取環(huán)境信息置济、容器工程、類加載器對(duì)象
         * @param metadata 注解的元數(shù)據(jù):可以獲取注解的屬性信息
         * @return 是否匹配條件锋八,true匹配浙于,false不匹配
         */
        @Override
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            /*try {
                Class.forName("redis.clients.jedis.Jedis");
                // 如果加載成功,則依賴已經(jīng)導(dǎo)入挟纱,返回true
                return true;
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
                // 如果加載失敗羞酗,則有依賴沒有導(dǎo)入,返回false
                return false;
            }*/
            // 獲取注解的屬性信息
            Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(ConditionalOnClass.class.getName());
            // 獲取注解中name的值
            String[] names = (String[]) annotationAttributes.get("name");
            for (String name : names) {
                try {
                    Class.forName(name);
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                    // 如果加載失敗樊销,則有依賴沒有導(dǎo)入整慎,返回false
                    return false;
                }
            }
            // 如果指定的類都加載成功,則對(duì)應(yīng)的依賴已經(jīng)導(dǎo)入围苫,返回true
            return true;
        }
    }
    
  4. 自定義一個(gè)注解:

    package com.lhp.study.annotation;
    
    import com.lhp.study.condition.OnClassCondition;
    import org.springframework.context.annotation.Conditional;
    
    import java.lang.annotation.*;
    
    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Conditional({OnClassCondition.class})
    public @interface ConditionalOnClass {
        // 類全限定名的字符串?dāng)?shù)組
        String[] name() default {};
    }
    
  5. 配置類:

    package com.lhp.study.config;
    
    import com.lhp.study.annotation.ConditionalOnClass;
    import com.lhp.study.condition.OnClassCondition;
    import com.lhp.study.pojo.User;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Conditional;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class UserConfig {
        @Bean
        // @Conditional注解表示一個(gè)組件只有在value指定的所有條件都匹配時(shí)才有資格注冊(cè)
        // @Conditional(value = OnClassCondition.class)
        @ConditionalOnClass(name = "redis.clients.jedis.Jedis")
        public User user() {
            return new User();
        }
    }
    
  6. 在啟動(dòng)類中測(cè)試:

    package com.lhp.study;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.ConfigurableApplicationContext;
    
    @SpringBootApplication
    public class StudyApplication {
        public static void main(String[] args) {
            ConfigurableApplicationContext context = SpringApplication.run(StudyApplication.class, args);
            // 若導(dǎo)入坐標(biāo)裤园,則打印類信息;若沒有導(dǎo)入坐標(biāo)剂府,則拋出NoSuchBeanDefinitionException
            System.out.println(context.getBean("user"));
        }
    }
    

在org.springframework.boot.autoconfigure.condition包下有很多Spring已經(jīng)定義好的Condition實(shí)現(xiàn)類和注解拧揽,如:

  1. ConditionalOnBean:當(dāng)Spring容器中有某一個(gè)Bean時(shí)使用
  2. ConditionalOnClass:當(dāng)目前類路徑下有某一個(gè)類時(shí)使用
  3. ConditionalOnMissingBean:當(dāng)Spring容器中沒有某一個(gè)Bean時(shí)使用
  4. ConditionalOnMissingClass:當(dāng)目前類路徑下沒有某一個(gè)類時(shí)使用
  5. ConditionalOnProperty:當(dāng)配置文件中有某一個(gè)鍵值對(duì)時(shí)使用

5.2 切換內(nèi)置的Web容器

org.springframework.boot.autoconfigure.web.embedded包下有4種Web容器定制器:

  1. JettyWebServerFactoryCustomizer
  2. NettyWebServerFactoryCustomizer
  3. TomcatWebServerFactoryCustomizer
  4. UndertowWebServerFactoryCustomizer

在spring-boot-starter-web中排除spring-boot-starter-tomcat依賴,然后添加其他的Web容器依賴即可:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <!--在spring-boot-starter-web中排除spring-boot-starter-tomcat依賴-->
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<!--添加其他的Web容器依賴-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

5.3 加載第三方的Bean

@SpringBootApplication上有@SpringBootConfiguration腺占,而@SpringBootConfiguration上有@Configuration淤袜,因此啟動(dòng)類本身也是一個(gè)配置類,該配置類相當(dāng)于Spring中的applicationContext.xml文件衰伯,用于加載配置使用铡羡。

@SpringBootApplication上有@EnableAutoConfiguration,這種@EnableXxx開頭的注解是Spring Boot中定義的一些動(dòng)態(tài)啟用某些功能的注解意鲸,其底層原理實(shí)際上是用@import注解導(dǎo)入一些配置烦周,來自動(dòng)進(jìn)行配置、加載Bean怎顾。

一路追蹤源碼:
@SpringBootApplication
@EnableAutoConfiguration
@Import({AutoConfigurationImportSelector.class})
String[] selectImports(AnnotationMetadata annotationMetadata)
AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata)
List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes)
META-INF/spring.factories

在spring-boot-autoconfigure-x.x.x.jar中的META-INF/spring.factories文件中就有各種自動(dòng)配置類

@SpringBootApplication上有@ComponentScan读慎,而@ComponentScan類似于xml中的包掃描context:component-scan;如果不指定掃描路徑槐雾,則掃描該注解修飾的啟動(dòng)類所在的包及子包夭委。

@Import注解用于導(dǎo)入其他的配置,讓Spring容器進(jìn)行加載和初始化募强;其使用方式有:

  1. 直接導(dǎo)入Bean
  2. 導(dǎo)入配置類
  3. 導(dǎo)入ImportSelector的實(shí)現(xiàn)類
  4. 導(dǎo)入ImportBeanDefinitionRegistrar實(shí)現(xiàn)類

實(shí)現(xiàn)加載第三方的Bean示例:創(chuàng)建兩個(gè)工程enable1和enable2株灸,enable2中有Bean崇摄,enable1依賴enable2,期望在enable1中直接使用enable2中的Bean

  1. 在enable2中創(chuàng)建POJO和配置類:

    // POJO
    package com.lhp.enable2.pojo;
    
    public class User {
    }
    
    // 配置類
    package com.lhp.enable2.config;
    
    import com.lhp.enable2.pojo.User;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class UserConfig {
        @Bean
        public User user() {
            return new User();
        }
    }
    
  2. 在enable1中添加第三方依賴enable2的坐標(biāo)

  3. 在enable1的啟動(dòng)類中直接加載使用第三方依賴enable2中的Bean

  4. 解決NoSuchBeanDefinitionException:

    1. 方式1:在enable1的啟動(dòng)類上使用@ComponentScan("com")注解將包掃描路徑放大

    2. 方式2:在enable1的啟動(dòng)類上使用@Import({UserConfig.class})注解導(dǎo)入配置類

    3. 方式3:自定義一個(gè)注解@EnableUser慌烧,然后在enable1的啟動(dòng)類上使用該注解

      // 自定義注解
      package com.lhp.enable2.config;
      
      import org.springframework.context.annotation.Import;
      
      import java.lang.annotation.*;
      
      @Target(ElementType.TYPE)
      @Retention(RetentionPolicy.RUNTIME)
      @Documented
      @Import({UserConfig.class})
      public @interface EnableUser {
      }
      
    4. 方式4:自定義一個(gè)ImportSelector實(shí)現(xiàn)類配猫,然后在enable1的啟動(dòng)類上使用@Import注解導(dǎo)入該實(shí)現(xiàn)類;此時(shí)不需要配置類

      // ImportSelector實(shí)現(xiàn)類
      package com.lhp.enable2.config;
      
      import org.springframework.context.annotation.ImportSelector;
      import org.springframework.core.type.AnnotationMetadata;
      
      public class MyImportSelector implements ImportSelector {
          /**
           * @return 需要加載的Bean的全限定名數(shù)組
           */
          @Override
          public String[] selectImports(AnnotationMetadata importingClassMetadata) {
              // 全限定名數(shù)組可以從配置文件中讀取
              // 此時(shí)Bean的id為全限定名杏死,可以通過getBean("com.lhp.enable2.pojo.User")或getBean(User.class)獲取
              return new String[]{"com.lhp.enable2.pojo.User"};
          }
      }
      
    5. 方式5:自定義一個(gè)ImportBeanDefinitionRegistrar實(shí)現(xiàn)類泵肄,然后在enable1的啟動(dòng)類上使用@Import注解導(dǎo)入該實(shí)現(xiàn)類;此時(shí)不需要配置類

      // ImportBeanDefinitionRegistrar實(shí)現(xiàn)類
      package com.lhp.enable2.config;
      
      import com.lhp.enable2.pojo.User;
      import org.springframework.beans.factory.support.AbstractBeanDefinition;
      import org.springframework.beans.factory.support.BeanDefinitionBuilder;
      import org.springframework.beans.factory.support.BeanDefinitionRegistry;
      import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
      import org.springframework.core.type.AnnotationMetadata;
      
      public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
          @Override
          public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
              String beanName = "user";
              // 創(chuàng)建beanDefinition
              AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition();
              // 注冊(cè)Bean
              registry.registerBeanDefinition(beanName, beanDefinition);
          }
      }
      

5.4 自定義starter起步依賴

需求:當(dāng)加入jedis的坐標(biāo)時(shí)淑翼,自動(dòng)配置jedis的Bean腐巢,加載到Spring容器中

  1. 創(chuàng)建起步依賴工程starter,添加spring-boot-starter和jedis的坐標(biāo)

  2. 創(chuàng)建配置的POJO:

    package com.lhp.starter.pojo;
    
    import lombok.Data;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    
    @Data
    @ConfigurationProperties(prefix = "redis")
    public class RedisProperties {
        // 給定默認(rèn)值
        private String host = "localhost";
        private Integer port = 6379;
    }
    
  3. 創(chuàng)建自動(dòng)配置類

    // 自動(dòng)配置類
    package com.lhp.starter.autoconfigure;
    
    import com.lhp.starter.pojo.RedisProperties;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import redis.clients.jedis.Jedis;
    
    @Configuration
    // 啟用POJO玄括,交給Spring容器管理冯丙,與配置文件建立映射關(guān)系
    @EnableConfigurationProperties(RedisProperties.class)
    // 當(dāng)類路徑下有Jedis這個(gè)類時(shí)再使用這個(gè)配置類
    @ConditionalOnClass(Jedis.class)
    public class RedisAutoConfiguration {
        @Bean
        // 當(dāng)沒有jedis這個(gè)Bean時(shí)再配置,避免和用戶自己的jedis沖突
        @ConditionalOnMissingBean(name = "jedis")
        public Jedis jedis(RedisProperties redisProperties) {
            System.out.println(redisProperties);
            return new Jedis(redisProperties.getHost(), redisProperties.getPort());
        }
    }
    
  4. 在resources下創(chuàng)建META-INF/spring.factories文件:

    # Auto Configure
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    com.lhp.starter.autoconfigure.RedisAutoConfiguration
    
  5. 在其他工程中加入自定義的starter起步依賴的坐標(biāo)遭京,便可直接使用自定義starter起步依賴中的Bean

6. Spring Boot監(jiān)控

6.1 Spring Boot Actuator

Spring Boot Actuator是Spring Boot自帶的組件胃惜,可以監(jiān)控Spring Boot應(yīng)用,如健康檢查哪雕、審計(jì)船殉、統(tǒng)計(jì)、HTTP追蹤等

使用Spring Boot Actuator:

  1. 添加依賴:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    
  2. 在application.yml中配置:

    management:
      # 配置健康端點(diǎn)開啟所有詳情信息
      endpoint:
        health:
          show-details: always
      # 設(shè)置開放所有web相關(guān)的端點(diǎn)信息
      endpoints:
        web:
          exposure:
            include: "*"
    # 設(shè)置info前綴的信息
    info:
      name: lhp
      age: 21
    
  3. 瀏覽器訪問:http://localhost:8080/actuator

部分監(jiān)控路徑列表:

  1. /beans:描述應(yīng)用程序上下文里全部的Bean及它們的關(guān)系
  2. /env:獲取全部環(huán)境屬性
  3. /env/{name}:根據(jù)名稱獲取特定的環(huán)境屬性值
  4. /health:報(bào)告應(yīng)用程序的健康指標(biāo)斯嚎,這些值由HealthIndicator的實(shí)現(xiàn)類提供
  5. /info:獲取應(yīng)用程序的定制信息利虫,這些信息由info打頭的屬性提供
  6. /mappings:描述全部的URI路徑,以及它們和控制器(包含Actuator端點(diǎn))的映射關(guān)系
  7. /metrics:報(bào)告各種應(yīng)用程序度量信息堡僻,比如內(nèi)存用量和HTTP請(qǐng)求計(jì)數(shù)
  8. /metrics/{name}:報(bào)告指定名稱的應(yīng)用程序度量值
  9. /trace:提供基本的HTTP請(qǐng)求跟蹤信息(時(shí)間戳糠惫、HTTP頭等)

6.2 Spring Boot Admin

Spring Boot Admin是一個(gè)開源社區(qū)項(xiàng)目;它有兩個(gè)角色钉疫,Client和Server硼讽;應(yīng)用程序作為Client向Server注冊(cè);Server可以通過圖形化界面展示Client的監(jiān)控信息

  1. 每一個(gè)Spring Boot工程都是一個(gè)Client
  2. Server可以收集統(tǒng)計(jì)所有相關(guān)Client注冊(cè)過來的信息牲阁,并進(jìn)行匯總展示

使用Spring Boot Admin Server:

  1. 創(chuàng)建server工程固阁,導(dǎo)入坐標(biāo):

    <properties>
        <java.version>1.8</java.version>
        <spring-boot-admin.version>2.3.1</spring-boot-admin.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>de.codecentric</groupId>
            <artifactId>spring-boot-admin-starter-server</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>de.codecentric</groupId>
                <artifactId>spring-boot-admin-dependencies</artifactId>
                <version>${spring-boot-admin.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    
  2. 在啟動(dòng)類上添加@EnableAdminServer注解來啟用Server功能

  3. 在application.yml中配置:

    server:
      port: 8088
    

使用Spring Boot Admin Client:

  1. 創(chuàng)建server工程,導(dǎo)入坐標(biāo):

    <properties>
        <java.version>1.8</java.version>
        <spring-boot-admin.version>2.3.1</spring-boot-admin.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>de.codecentric</groupId>
            <artifactId>spring-boot-admin-starter-client</artifactId>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>de.codecentric</groupId>
                <artifactId>spring-boot-admin-dependencies</artifactId>
                <version>${spring-boot-admin.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    
  2. 在application.yml中配置:

    spring:
      # 配置server的地址
      boot:
        admin:
          client:
            url: http://localhost:8088
      # 配置系統(tǒng)名稱
      application:
        name: client
    management:
      endpoint:
        health:
          # 啟用健康檢查(默認(rèn)就是true)
          enabled: true
          # 配置顯示所有的監(jiān)控詳情
          show-details: always
      endpoints:
        web:
          # 開放所有端點(diǎn)
          exposure:
            include: "*"
    
  3. 瀏覽器訪問服務(wù)端地址:http://localhost:8088/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末咨油,一起剝皮案震驚了整個(gè)濱河市您炉,隨后出現(xiàn)的幾起案子柒爵,更是在濱河造成了極大的恐慌役电,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,907評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件棉胀,死亡現(xiàn)場(chǎng)離奇詭異法瑟,居然都是意外死亡冀膝,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門霎挟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來窝剖,“玉大人,你說我怎么就攤上這事酥夭〈蜕矗” “怎么了?”我有些...
    開封第一講書人閱讀 164,298評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵熬北,是天一觀的道長疙描。 經(jīng)常有香客問我,道長讶隐,這世上最難降的妖魔是什么起胰? 我笑而不...
    開封第一講書人閱讀 58,586評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮巫延,結(jié)果婚禮上效五,老公的妹妹穿的比我還像新娘。我一直安慰自己炉峰,他們只是感情好畏妖,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著疼阔,像睡著了一般瓜客。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,488評(píng)論 1 302
  • 那天报嵌,我揣著相機(jī)與錄音会涎,去河邊找鬼。 笑死疯攒,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的列荔。 我是一名探鬼主播敬尺,決...
    沈念sama閱讀 40,275評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼贴浙!你這毒婦竟也來了砂吞?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,176評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤崎溃,失蹤者是張志新(化名)和其女友劉穎蜻直,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,619評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡概而,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評(píng)論 3 336
  • 正文 我和宋清朗相戀三年呼巷,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片赎瑰。...
    茶點(diǎn)故事閱讀 39,932評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡王悍,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出餐曼,到底是詐尸還是另有隱情压储,我是刑警寧澤,帶...
    沈念sama閱讀 35,655評(píng)論 5 346
  • 正文 年R本政府宣布源譬,位于F島的核電站渠脉,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏瓶佳。R本人自食惡果不足惜芋膘,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望霸饲。 院中可真熱鬧为朋,春花似錦、人聲如沸厚脉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽傻工。三九已至霞溪,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間中捆,已是汗流浹背鸯匹。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留泄伪,地道東北人殴蓬。 一個(gè)月前我還...
    沈念sama閱讀 48,095評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像蟋滴,于是被迫代替她去往敵國和親染厅。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容