SpringBoot進(jìn)階

SpringBoot2

1.Springboot自動(dòng)配置原理分析

  1. SpringApplication.run()啟動(dòng)應(yīng)用

  2. 掃描到@SpringBootApplication注解

  3. 調(diào)用@EnableAutoConfiguration注解

  4. 掃描@Import({AutoConfigurationImportSelector.class})

  5. 進(jìn)入AutoConfigurationImportSelector

  6. 調(diào)用selectImports()方法中的獲取所有配置集合

    List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
    
  7. 調(diào)用SpringFactoriesLoader.loadFactoryNames()讀取了ClassPath下面的META-INF/spring.factories文件

  8. 讀取文件中org.springframework.boot.autoconfigure.EnableAutoConfiguration里面所有的參數(shù)测蹲,開始運(yùn)行預(yù)設(shè)好的配置類

  9. 各個(gè)配置類根據(jù)@Conditional痴昧,決定是否實(shí)例化配置類內(nèi)部定義的bean粱胜,避免在bean初始化過(guò)程中由于條件不足绰垂,導(dǎo)致應(yīng)用啟動(dòng)失敗

1.1 @Conditional

使用

  • Conditional要配和Condition的實(shí)現(xiàn)類(ClassCondition)進(jìn)行使用

  • 實(shí)現(xiàn)一個(gè)Condition接口巷波,并且重寫matchs方法

  • @Conditional注解中指定Condition接口的實(shí)現(xiàn)即可

案例1:需求

? 在spring的IOC容器中有一個(gè)User的Bean沧竟,現(xiàn)要求:
? 導(dǎo)入Jedis坐標(biāo)后前翎,加載該Bean杆融,沒(méi)導(dǎo)入燃乍,則不加載唆樊。

  1. 實(shí)現(xiàn)一個(gè)Condition接口,并且重寫matchs方法
public class ClassCondition implements Condition {
    /**
     * @param conditionContext 上下文對(duì)象刻蟹。用于獲取環(huán)境逗旁,IOC容器,ClassLoader對(duì)象
     * @param annotatedTypeMetadata 注解元對(duì)象舆瘪。 可以用于獲取注解定義的屬性值
     * @return
     */
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        try {
            Class<?> cls = Class.forName("redis.clients.jedis.Jedis");
            return true;
        } catch (ClassNotFoundException e) {
            return false;
        }
    }
}
  1. @Conditional注解中指定Condition接口的實(shí)現(xiàn)
@Configuration
public class UserConfig {
    @Bean
    @Conditional(ClassCondition.class)
    public User user(){
        return new User();
    }
}
  1. 驗(yàn)證
@SpringBootApplication
public class SpringbootTestApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringbootTestApplication.class, args);
        Object user = context.getBean("user");
        System.out.println(user);
    }
}

案例2:需求

? 在spring的IOC容器中有一個(gè)User的Bean片效,現(xiàn)要求:
? 導(dǎo)入Jedis坐標(biāo)后,加載該Bean英古,沒(méi)導(dǎo)入淀衣,則不加載。

? 將類的判斷定義為動(dòng)態(tài)的召调。判斷哪個(gè)字節(jié)碼文件存在可以動(dòng)態(tài)指定膨桥。

  1. 編寫自定義條件注解類
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ClassCondition.class)
public @interface ConditionOnClass {
    String[] value();
}
  1. 修改ClassCondition
public class ClassCondition implements Condition {
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        try {
            Map<String, Object> map = annotatedTypeMetadata.getAnnotationAttributes(ConditionOnClass.class.getName());
            String[] value = (String[]) map.get("value");
            for (String className : value) {
                Class<?> cls = Class.forName(className);
            }
            return true;
        } catch (ClassNotFoundException e) {
            return false;
        }
    }
}
  1. 修改注解類中的UserConfig
@Configuration
public class UserConfig {
    @Bean
    @ConditionOnClass("redis.clients.jedis.Jedis")
    public User user(){
        return new User();
    }
}
  1. 驗(yàn)證
@SpringBootApplication
public class SpringbootTestApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringbootTestApplication.class, args);
        Object user = context.getBean("user");
        System.out.println(user);
    }
}

springboot中的條件注解有很多蛮浑,常用的條件注解

  • ConditionalOnProperty:判斷配置文件中是否有對(duì)應(yīng)屬性和值才初始化Bean

  • ConditionalOnClass:判斷環(huán)境中是否有對(duì)應(yīng)字節(jié)碼文件才初始化Bean

  • ConditionalOnMissingBean:判斷環(huán)境中沒(méi)有對(duì)應(yīng)Bean才初始化Bean


1.2 @EnableAutoConfiguration

@EnableXXXX常用來(lái)開啟某些功能,其實(shí)底層就是使用@Importe來(lái)導(dǎo)入配置類

問(wèn)題:springboot工程是否可以直接獲取jar包中定義的Bean

三種解決方案:

  1. 使用@ComponentScan掃描config包
  2. 可以使用@Import注解只嚣,加載類沮稚。
  3. 自定義注解,對(duì)@Import進(jìn)行封裝

@Import的四種用法

  1. 直接導(dǎo)入bean
@Import(User.class)
public class EnableApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(EnableApplication.class, args);
        Map<String, User> map = context.getBeansOfType(User.class);
        System.out.println(map);

    }
}
  1. 導(dǎo)入配置類
@Import(UserConfig.class)
public class EnableApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(EnableApplication.class, args);
        Map<String, User> map = context.getBeansOfType(User.class);
        System.out.println(map);
    }
}
  1. 導(dǎo)入 ImportSelector 實(shí)現(xiàn)類
/**
* ImportSelector 實(shí)現(xiàn)類
*/
public class UserImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        return new String[]{"com.itheima.springboot_test.domain.User"};
    }
}
@Import(UserImportSelector.class)
public class EnableApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(EnableApplication.class, args);
        Map<String, User> map = context.getBeansOfType(User.class);
        System.out.println(map);
    }
}
  1. 導(dǎo)入 ImportBeanDefinitionRegistrar 實(shí)現(xiàn)類
/**
* ImportBeanDefinitionRegistrar 實(shí)現(xiàn)類
*/
public class UserImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition();
        registry.registerBeanDefinition("user",beanDefinition);
    }
}
@Import(UserImportBeanDefinitionRegistrar.class)
public class EnableApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(EnableApplication.class, args);
        Map<String, User> map = context.getBeansOfType(User.class);
        System.out.println(map);
    }
}

小結(jié)

  1. @EnableAutoConfiguration 注解內(nèi)部使用 @Import(AutoConfigurationImportSelector.class)來(lái)加載配置類
  2. 配置文件位置:META-INF/spring.factories册舞,該配置文件中定義了大量的配置類蕴掏,當(dāng) SpringBoot 應(yīng)用啟動(dòng)時(shí),會(huì)自動(dòng)加載這些配置類环础,初始化Bean
  3. 并不是所有的Bean都會(huì)被初始化囚似,在配置類中使用Condition來(lái)加載滿足條件的Bean

1.3 自定義starter

需求:自定義redis-starter。要求當(dāng)導(dǎo)入redis坐標(biāo)時(shí)线得,SpringBoot自動(dòng)創(chuàng)建Jedis的Bean饶唤。

  1. 創(chuàng)建 springboot_test 模塊

  2. 創(chuàng)建 redis-spring-boot-starter 模塊,依賴 springboot_test的模塊

  3. 在 springboot_test模塊中初始化 Jedis 的 Bean。并定義META-INF/spring.factories 文件

在redis-spring-boot-autoconfigure創(chuàng)建RedisProperties類贯钩,方便從配置文件注入值募狂,創(chuàng)建RedisAutoConfiguration自動(dòng)配置類

@ConfigurationProperties(prefix = "redis")
public class RedisProperties {
    private String host="localhost";
    private int port=6379;
    public String getHost() {
        return host;
    }
    public void setHost(String host) {
        this.host = host;
    }
    public int getPort() {
        return port;
    }
    public void setPort(int port) {
        this.port = port;
    }
}
@Configuration
@EnableConfigurationProperties(RedisProperties.class)
public class RedisAutoConfiguration {
    @Bean
    public Jedis jedis(RedisProperties redisProperties){
        return new Jedis(redisProperties.getHost(),redisProperties.getPort());
    }
}

在resource目錄下創(chuàng)建META-INF文件夾并創(chuàng)建spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.itheima.springboot_test.config.RedisAutoConfiguration
  1. 在測(cè)試模塊中引入自定義的 redis-starter 依賴,測(cè)試獲取 Jedis 的Bean角雷,操作 redis

2. SpringBoot事件監(jiān)聽

springboot的監(jiān)聽機(jī)制就是對(duì)java提供的時(shí)間監(jiān)聽機(jī)制的封裝

SpringBoot 在項(xiàng)目啟動(dòng)時(shí)祸穷,會(huì)對(duì)幾個(gè)監(jiān)聽器進(jìn)行回調(diào),我們可以實(shí)現(xiàn)這些監(jiān)聽器接口勺三,在項(xiàng)目啟動(dòng)時(shí)完成一些操作雷滚。

  1. ApplicationContextInitializer:要在resource文件夾下添加META-INF/spring.factories
  2. SpringApplicationRunListener:使用要添加構(gòu)造器
  3. CommandLineRunner:當(dāng)項(xiàng)目啟動(dòng)后執(zhí)行run方法
  4. ApplicationRunner:當(dāng)項(xiàng)目啟動(dòng)后執(zhí)行run方法

3. springboot部署

SpringBoot 項(xiàng)目開發(fā)完畢后,支持兩種方式部署到服務(wù)器:

  1. 打jar包
java -jar 文件路徑
  1. 打war包

引導(dǎo)類繼承SpringBootServletInitializer

@SpringBootApplication
public class SpringbootDeployApplication extends SpringBootServletInitializer {
   public static void main(String[] args) {
       SpringApplication.run(SpringbootDeployApplication.class, args);
   }
   @Override
   protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
       return builder.sources(SpringbootDeployApplication.class);
   }
}

指定打包的名稱

<build>
       <finalName>springboot</finalName>
       <plugins>
           <plugin>
               <groupId>org.springframework.boot</groupId>
               <artifactId>spring-boot-maven-plugin</artifactId>
           </plugin>
       </plugins>
   </build>
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末吗坚,一起剝皮案震驚了整個(gè)濱河市祈远,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌商源,老刑警劉巖车份,帶你破解...
    沈念sama閱讀 218,525評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異牡彻,居然都是意外死亡扫沼,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門庄吼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)缎除,“玉大人,你說(shuō)我怎么就攤上這事总寻“檎遥” “怎么了?”我有些...
    開封第一講書人閱讀 164,862評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵废菱,是天一觀的道長(zhǎng)技矮。 經(jīng)常有香客問(wèn)我,道長(zhǎng)殊轴,這世上最難降的妖魔是什么衰倦? 我笑而不...
    開封第一講書人閱讀 58,728評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮旁理,結(jié)果婚禮上樊零,老公的妹妹穿的比我還像新娘。我一直安慰自己孽文,他們只是感情好驻襟,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,743評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著芋哭,像睡著了一般沉衣。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上减牺,一...
    開封第一講書人閱讀 51,590評(píng)論 1 305
  • 那天豌习,我揣著相機(jī)與錄音,去河邊找鬼拔疚。 笑死肥隆,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的稚失。 我是一名探鬼主播栋艳,決...
    沈念sama閱讀 40,330評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼句各!你這毒婦竟也來(lái)了吸占?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,244評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤诫钓,失蹤者是張志新(化名)和其女友劉穎旬昭,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體菌湃,經(jīng)...
    沈念sama閱讀 45,693評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡问拘,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,885評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了惧所。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片骤坐。...
    茶點(diǎn)故事閱讀 40,001評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖下愈,靈堂內(nèi)的尸體忽然破棺而出纽绍,到底是詐尸還是另有隱情,我是刑警寧澤势似,帶...
    沈念sama閱讀 35,723評(píng)論 5 346
  • 正文 年R本政府宣布拌夏,位于F島的核電站僧著,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏障簿。R本人自食惡果不足惜盹愚,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,343評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望站故。 院中可真熱鬧皆怕,春花似錦、人聲如沸西篓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)岂津。三九已至虱黄,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間寸爆,已是汗流浹背礁鲁。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留赁豆,地道東北人仅醇。 一個(gè)月前我還...
    沈念sama閱讀 48,191評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像魔种,于是被迫代替她去往敵國(guó)和親析二。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,955評(píng)論 2 355