springboot教程(一)
擼了今年阿里、頭條和美團(tuán)的面試,我有一個(gè)重要發(fā)現(xiàn).......>>>
使用jdk:1.8、maven:3.3.3
spring獲取Bean的方式
pom.xml文件內(nèi)容:
<?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>cn.ac.iie</groupId><artifactId>spring-course</artifactId><version>1.0-SNAPSHOT</version><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target></properties><dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.0.0.RELEASE</version></dependency></dependencies> </project>配置類MyConfig.java:
package com.edu.spring;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;/*** 配置類*/@Configuration public class MyConfig {// 配置一個(gè)bean@Beanpublic MyBean createMyBean(){return new MyBean();}}MyBean.java
package com.edu.spring;public class MyBean { }主函數(shù):App.java
package com.edu.spring;import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class App {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);// 從容器中獲取bean---從類型獲取System.out.println(context.getBean(MyBean.class));// 從容器中獲取bean---從名字獲取,默認(rèn)名字是方法名System.out.println(context.getBean("createMyBean"));context.close();} }輸出結(jié)果如下:
com.edu.spring.MyBean@1445d7f
com.edu.spring.MyBean@1445d7f
如果需要指定bean名字,需要修改MyConfig.java:
@Configuration public class MyConfig {// 配置一個(gè)bean@Bean(name = "myBean")public MyBean createMyBean(){return new MyBean();}}然后在App.java指定Bean的名字:這樣就無法根據(jù)方法名獲取Bean了。
public class App {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);// 從容器中獲取bean---從類型獲取System.out.println(context.getBean(MyBean.class));// 從容器中獲取bean---從名字獲取,默認(rèn)名字是方法名// System.out.println(context.getBean("createMyBean"));// 從容器中獲取bean---從指定名字獲取System.out.println(context.getBean("myBean"));context.close();} }Bean默認(rèn)是單例
我們可以看到,Bean是單例的,兩次打印出的對(duì)象是一樣的。
如果我們想修改Bean的單例為多例,修改MyConfig.java如下(添加scope(prototype)):
@Configuration public class MyConfig {// 配置一個(gè)bean@Bean(name = "myBean")@Scope("prototype")public MyBean createMyBean(){return new MyBean();}}打印如下:
com.edu.spring.MyBean@10b48321
com.edu.spring.MyBean@6b67034
FactoryBean
新建方法JeepFactoryBean.java
package com.edu.spring;import org.springframework.beans.factory.FactoryBean;public class JeepFactoryBean implements FactoryBean<Jeep> {/*** 創(chuàng)建的實(shí)例對(duì)象* @return* @throws Exception*/@Overridepublic Jeep getObject() throws Exception {return new Jeep();}/**** @return*/@Overridepublic Class<?> getObjectType() {return Jeep.class;}@Overridepublic boolean isSingleton() {return true;} }在MyConfig.java中添加配置:
@Beanpublic JeepFactoryBean createJeepFactoryBean(){return new JeepFactoryBean();}可以在App.java中得到Jeep.class。
System.out.println(context.getBean(Jeep.class)); System.out.println(context.getBean("createJeepFactoryBean"));如果要獲取JeepFactoryBean本身,而不是工廠生產(chǎn)出的類,可以通過下面的兩種方式獲取:
System.out.println(context.getBean(JeepFactoryBean.class)); System.out.println(context.getBean("&createJeepFactoryBean"));目前有兩種方式進(jìn)行Bean的裝配,一種是使用FactoryBean,一種是原始方式
使用第三種裝配
新建CarFactory.java
public class CarFactory {public Car create(){return new Car();} }新建Car.java
配置MyConfig.java
@Beanpublic Car createJeep(CarFactory carFactory){return carFactory.create();}@Beanpublic CarFactory createCarFactory(){return new CarFactory();}獲取對(duì)象:
System.out.println(context.getBean(Car.class));因?yàn)?#xff0c;在Bean的裝配過程中,需要參數(shù)的時(shí)候,spring會(huì)默認(rèn)從當(dāng)前容器中獲取到對(duì)應(yīng)的參數(shù),然后注入。
Bean初始化,在Bean初始化時(shí),進(jìn)行一些操作。
方式一:
創(chuàng)建Cat.java
package com.edu.spring;import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean;public class Cat implements InitializingBean, DisposableBean {@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("====afterPropertiesSet====");}@Overridepublic void destroy() throws Exception {System.out.println("====destroy====");} }配置MyConfig.java
@Beanpublic Cat createCat(){return new Cat();}獲得cat
System.out.println(context.getBean(Cat.class));控制臺(tái)打印:
====afterPropertiesSet==== com.edu.spring.MyBean@7fe8ea47 com.edu.spring.MyBean@226a82c4 com.edu.spring.JeepFactoryBean@731f8236 com.edu.spring.JeepFactoryBean@731f8236 com.edu.spring.Jeep@255b53dc com.edu.spring.Jeep@255b53dc com.edu.spring.Car@1dd92fe2 com.edu.spring.Cat@6b53e23f ====destroy====方法二:
創(chuàng)建Dog.java
package com.edu.spring;public class Dog {public void myInit(){System.out.println("init=====");}public void myDestory(){System.out.println("destory===");}}在配置MyConfig.java時(shí)指定初始化時(shí)執(zhí)行和銷毀時(shí)執(zhí)行的方法
@Bean(initMethod = "myInit", destroyMethod = "myDestory")public Dog createDog(){return new Dog();}方式三:
新建Fish.java
package com.edu.spring;import javax.annotation.PostConstruct; import javax.annotation.PreDestroy;public class Fish {@PostConstructpublic void initial(){System.out.println("fish init");}@PreDestroypublic void close(){System.out.println("fish close");}}配置MyConfig.java
@Beanpublic Fish createFish(){return new Fish();}Bean裝配
新建User.java
package com.edu.spring;import org.springframework.stereotype.Component;@Component public class User { }修改App.java,將User.class添加到容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class, User.class); System.out.println(context.getBean(User.class));但是使用@Component,則無法使用@Bean(initMethod = "myInit", destroyMethod = "myDestory")設(shè)置初始化和銷毀的方法
此外,默認(rèn)的名字是類名。可以指定名字:
@Component("myUser")如果此時(shí)同樣在MyConfig.java中,配置一個(gè)Bean,
@Beanpublic User createUser(){return new User();}則會(huì)報(bào)錯(cuò):
Exception in thread "main" org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.edu.spring.User' available: expected single matching bean but found 2: myUser,createUserat org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1034)at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:340)at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:335)at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1101)at com.edu.spring.App.main(App.java:24)也就是說通過類型獲取User時(shí)找到了兩個(gè) 一個(gè)是myUser一個(gè)是createUser。如果我們通過名字來獲取就沒有問題了。
System.out.println(context.getBean("myUser"));如果想獲取,User類型的所有對(duì)象,可以使用getBeansOfType方法。
除了使用Component,還可以使用Repository來裝配類,一般用在數(shù)據(jù)訪問層,而Component一般用于沒有明確角色的時(shí)候。
新建UserDao.java
@Repository public class UserDao { }添加到AnnotationConfigApplicationContext中
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class, User.class, UserDao.class); System.out.println(context.getBean(UserDao.class));還可以使用Service來裝配,一般用在業(yè)務(wù)邏輯層
新建UserService.java,
// 業(yè)務(wù)邏輯層 @Service public class UserService { }添加到AnnotationConfigApplicationContext:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class, User.class, UserDao.class, UserService.class); System.out.println(context.getBean(UserService.class));使用Controller來裝配,一般用于展示層,
package com.edu.spring;import org.springframework.stereotype.Controller;// 用在展示層 @Controller public class UserController { }依賴注入
在User中,依賴UserService.java
User.java
package com.edu.spring;import org.springframework.stereotype.Component;@Component("myUser") public class User {private UserService userService;public UserService getUserService() {return userService;}public void setUserService(UserService userService) {this.userService = userService;}@Overridepublic String toString() {return "User{" +"userService=" + userService +'}';} }在App.java中調(diào)用show方法
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class, User.class, UserDao.class, UserService.class, UserController.class); User user = context.getBean("myUser", User.class); user.show(); context.close();輸出打印:
null說明UserService沒有注入進(jìn)去,使用@AutoWired修飾UserService,便可以注入進(jìn)去了,同時(shí)也不需要set get方法了。
package com.edu.spring;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component;@Component("myUser") public class User {@Autowiredprivate UserService userService;@Overridepublic String toString() {return "UserService{" +"userDao=" + userDao +'}';}}打印輸出:
User{userService=UserService{userDao=com.edu.spring.UserDao@3bbc39f8}}說明成功注入進(jìn)去了。
如果繼續(xù)在MyConfig.java中裝配一個(gè)Bean:
@Beanpublic UserDao createUserDao(){return new UserDao();}那么這時(shí),就會(huì)有兩個(gè)UserDao存在,就不知道用哪個(gè)對(duì)象,出現(xiàn)報(bào)錯(cuò):
Exception in thread "main" org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.edu.spring.UserDao' available: expected single matching bean but found 2: userDao,createUserDaoat org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1034)at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:340)at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:335)at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1101)at com.edu.spring.App.main(App.java:26)這時(shí)可以用兩個(gè)方法解決問題。
方法一:使用Qualifier注釋
@Service public class UserService {@Autowired@Qualifier("createUserDao")private UserDao userDao;@Overridepublic String toString() {return "UserService{" +"userDao=" + userDao +'}';} }這樣就知道用哪一個(gè)對(duì)象了。(注意要將其他所有用到UserDao的地方,都需要使用Qualifier指明到底用哪一個(gè)userdao)
方法二:使用Primary注釋Bean
在MyConfig.java中:
@Bean@Primarypublic UserDao createUserDao(){return new UserDao();}Primary用于有多個(gè)對(duì)象存在時(shí),首先尋找標(biāo)有Primary的對(duì)象,進(jìn)行注入。
如果要注入其他類,比如Car.java,需要使用Resource注釋來注入。
package com.edu.spring;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component;import javax.annotation.Resource;@Component("myUser") public class User {@Autowiredprivate UserService userService;// 使用JSR 250的注解@Resourceprivate Car car;@Overridepublic String toString() {return "User{" +"userService=" + userService +", car=" + car +'}';} }輸出:User{userService=UserService{userDao=com.edu.spring.UserDao@1b083826}, car=com.edu.spring.Car@55a1c291}
說明Car.java 也注入進(jìn)來了。
此外還可以使用 JSR 330 的注解方式進(jìn)行注入,但是這種方式需要添加依賴:
<dependency><groupId>javax.inject</groupId><artifactId>javax.inject</artifactId><version>1</version></dependency>嘗試將Cat.java注入進(jìn)去User。使用Inject注釋。
package com.edu.spring;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component;import javax.annotation.Resource; import javax.inject.Inject;@Component("myUser") public class User {// Spring提供的注解@Autowiredprivate UserService userService;// 使用JSR-250的注解@Resourceprivate Car car;// 使用JSR-330的注解@Injectprivate Cat cat;@Overridepublic String toString() {return "User{" +"userService=" + userService +", car=" + car +", cat=" + cat +'}';} }控制臺(tái)顯示輸出:信息: JSR-330 'javax.inject.Inject' annotation found and supported for autowiring 說明JSR330 已經(jīng)起作用了。
User{userService=UserService{userDao=com.edu.spring.UserDao@55ca8de8}, car=com.edu.spring.Car@5d740a0f, cat=com.edu.spring.Cat@214b199c}
說明cat也成功注入進(jìn)去了。
我們發(fā)現(xiàn)AnnotationConfigApplicationContext很麻煩,每次都需要手動(dòng)去添加新的類。
我們可以使用包掃描的方式進(jìn)行類裝載,更加方便。
package com.edu.spring;import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class AnnotationClient {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.edu.spring");System.out.println(context.getBean(Jeep.class));System.out.println(context.getBean("createJeepFactoryBean"));System.out.println(context.getBean(Car.class));System.out.println(context.getBean(Cat.class));System.out.println(context.getBean(Dog.class));System.out.println(context.getBean(Fish.class));System.out.println(context.getBean("myUser"));context.close();} }如果我們想排除某一些類,這些類不想被掃描到。方法如下:
新建AnnotationScan.java
package com.edu.spring;import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration;@ComponentScan("com.edu.spring") @Configuration public class AnnotationScan { }新建AnnotationClient2.java
package com.edu.spring;import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class AnnotationClient2 {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AnnotationScan.class);context.close();} }這種方法同樣可以達(dá)到掃描包的效果。如果我們想排除Dog類,需要將MyConfig.java中的配置Dog的地方刪除,新建DogConfig.java
package com.edu.spring;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;@Configuration public class DogConfig {@Bean(initMethod = "myInit", destroyMethod = "myDestory")public Dog createDog(){return new Dog();}}修改AnnotationScan.java
package com.edu.spring;import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType;@ComponentScan(basePackages = "com.edu.spring", excludeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = DogConfig.class)) @Configuration public class AnnotationScan { }這樣就將Dog類排除了,然后執(zhí)行AnnotationClient2.java 就會(huì)報(bào)錯(cuò):
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.edu.spring.Dog' availableat org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:348)at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:335)at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1101)at com.edu.spring.AnnotationClient2.main(AnnotationClient2.java:13)說明Dog已經(jīng)排除成功了。同樣可以排除UserController.java
因?yàn)閁serController上有注釋,可以直接排除。
package com.edu.spring;import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType;@ComponentScan(basePackages = "com.edu.spring", excludeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {DogConfig.class, UserController.class})) @Configuration public class AnnotationScan { }第二課
Spring有兩個(gè)核心接口:BeanFactory和ApplicationContext,其中ApplicationContext是BeanFactory的子接口。他們都可代表Spring容器,Spring容器是生成Bean實(shí)例的工廠,并且管理容器中的Bean。
BeanFactory負(fù)責(zé)配置、創(chuàng)建、管理Bean,他有一個(gè)子接口:ApplicationContext,因此也稱之為Spring上下文。Spring容器負(fù)責(zé)管理Bean與Bean之間的依賴關(guān)系。
BeanFactory接口包含以下幾個(gè)基本方法:
- Boolean containBean(String name):判斷Spring容器是否包含id為name的Bean實(shí)例。
- <T> getBean(Class<T> requiredTypr):獲取Spring容器中屬于requiredType類型的唯一的Bean實(shí)例。
- Object getBean(String name):返回Sprin容器中id為name的Bean實(shí)例。
- <T> T getBean(String name,Class requiredType):返回容器中id為name,并且類型為requiredType的Bean
- Class <?> getType(String name):返回容器中指定Bean實(shí)例的類型。?
?調(diào)用者只需使用getBean()方法即可獲得指定Bean的引用,無須關(guān)心Bean的實(shí)例化過程。即Bean實(shí)例的創(chuàng)建過程完全透明。
讓Bean獲取Spring容器
????在Spring中我們可以使用Spring容器中g(shù)etBean()方法來獲取Spring容器中的Bean實(shí)例。在這樣的訪問模式下,程序中總是持有Spring容器的引用。但是在實(shí)際的應(yīng)用中,Spring容器通常是采用聲明式方式配置產(chǎn)生:即開發(fā)者只要在web.xml文件中配置一個(gè)Listener,該Listener將會(huì)負(fù)責(zé)初始化Spring容器。在這種情況下,容器中Bean處于容器管理下,無須主動(dòng)訪問容器,只需要接受容器的注入管理即可。同時(shí)Bean實(shí)例的依賴關(guān)系通常也是由容器自動(dòng)注入,無須Bean實(shí)例主動(dòng)請(qǐng)求。
如何注入ApplicationContext?
新建myConfig.java
package com.edu.spring;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;@Configuration public class MyConfig {@Beanpublic User createUser(){return new User();}}新建User.java
package com.edu.spring;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext;public class User {@Autowiredprivate ApplicationContext applicationContext;public void show(){System.out.println("user:" + applicationContext.getClass());}}新建App.java
package com.edu.spring;import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class App {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.edu.spring");System.out.println(context.getBean(User.class));System.out.println(context.getBean("createUser"));context.getBean(User.class).show();context.close();} }方法一:
在Use.java中使用AutoWired成功注入ApplicationContext。也可以使用JSR 250 和JSR330 方式注入
輸出如下:
com.edu.spring.User@6ee12bac com.edu.spring.User@6ee12bac user:class org.springframework.context.annotation.AnnotationConfigApplicationContext方法二:
新建Book.java
package com.edu.spring;import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component;@Component public class Book implements ApplicationContextAware {private ApplicationContext applicationContext;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}public void show(){System.out.println("book:" + applicationContext.getClass());}}方法三:
新鍵Bank.java
package com.edu.spring;import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component;@Component public class Bank {private ApplicationContext applicationContext;public Bank (ApplicationContext applicationContext){this.applicationContext = applicationContext;}public void show(){System.out.println("bank:" + applicationContext.getClass());}}這種方法同樣可以注入ApplicationContext。但是這個(gè)方法,構(gòu)造函數(shù)只能有一個(gè),如果有多個(gè)的話,就必須有一個(gè)無參的構(gòu)造函數(shù),此時(shí),spring會(huì)調(diào)用無參的構(gòu)造函數(shù)。構(gòu)造函數(shù)的參數(shù),必須都要在Spring容器中。
BeanPostProcessor
BeanPostProcessor會(huì)在每個(gè)bean初始化的時(shí)候,調(diào)用一次
新建EchoBeanPostProcessor.java
package com.edu.spring;import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.lang.Nullable; import org.springframework.stereotype.Component;/*** BeanPostProcessor會(huì)在每個(gè)bean初始化的時(shí)候,調(diào)用一次*/ @Component public class EchoBeanPostProcessor implements BeanPostProcessor {// 在bean依賴裝配(屬性設(shè)置完)完成之后觸發(fā)@Nullable@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("====postProcessBeforeInitialization======" + bean.getClass());return bean;}// 在bean init方法執(zhí)行之后觸發(fā)@Nullable@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("====postProcessAfterInitialization======" + bean.getClass());return bean;} }打印輸出如下:
====postProcessBeforeInitialization======class org.springframework.context.event.EventListenerMethodProcessor ====postProcessAfterInitialization======class org.springframework.context.event.EventListenerMethodProcessor ====postProcessBeforeInitialization======class org.springframework.context.event.DefaultEventListenerFactory ====postProcessAfterInitialization======class org.springframework.context.event.DefaultEventListenerFactory ====postProcessBeforeInitialization======class com.edu.spring.Bank ====postProcessAfterInitialization======class com.edu.spring.Bank ====postProcessBeforeInitialization======class com.edu.spring.Book ====postProcessAfterInitialization======class com.edu.spring.Book ====postProcessBeforeInitialization======class com.edu.spring.MyConfig$$EnhancerBySpringCGLIB$$3a1c64d1 ====postProcessAfterInitialization======class com.edu.spring.MyConfig$$EnhancerBySpringCGLIB$$3a1c64d1 ====postProcessBeforeInitialization======class com.edu.spring.User ====postProcessAfterInitialization======class com.edu.spring.User com.edu.spring.User@482cd91f com.edu.spring.User@482cd91f user:class org.springframework.context.annotation.AnnotationConfigApplicationContext book:class org.springframework.context.annotation.AnnotationConfigApplicationContext bank:class org.springframework.context.annotation.AnnotationConfigApplicationContext在每個(gè)bean初始化之前和之后執(zhí)行的方法。????
如果我們給User添加一個(gè)初始化方法init
user.java
package com.edu.spring;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext;public class User {@Autowiredprivate ApplicationContext applicationContext;public void init(){System.out.println("user init");}public void show(){System.out.println("user:" + applicationContext.getClass());}}在MyConfig.java中指定初始化方法:
package com.edu.spring;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;@Configuration public class MyConfig {@Bean(initMethod = "init")public User createUser(){return new User();}}則輸出結(jié)果如下:
====postProcessBeforeInitialization======class org.springframework.context.event.EventListenerMethodProcessor ====postProcessAfterInitialization======class org.springframework.context.event.EventListenerMethodProcessor ====postProcessBeforeInitialization======class org.springframework.context.event.DefaultEventListenerFactory ====postProcessAfterInitialization======class org.springframework.context.event.DefaultEventListenerFactory ====postProcessBeforeInitialization======class com.edu.spring.Bank ====postProcessAfterInitialization======class com.edu.spring.Bank ====postProcessBeforeInitialization======class com.edu.spring.Book ====postProcessAfterInitialization======class com.edu.spring.Book ====postProcessBeforeInitialization======class com.edu.spring.MyConfig$$EnhancerBySpringCGLIB$$3a1c64d1 ====postProcessAfterInitialization======class com.edu.spring.MyConfig$$EnhancerBySpringCGLIB$$3a1c64d1 ====postProcessBeforeInitialization======class com.edu.spring.User user init ====postProcessAfterInitialization======class com.edu.spring.User com.edu.spring.User@482cd91f com.edu.spring.User@482cd91f user:class org.springframework.context.annotation.AnnotationConfigApplicationContext book:class org.springframework.context.annotation.AnnotationConfigApplicationContext bank:class org.springframework.context.annotation.AnnotationConfigApplicationContext說明postProcessBeforeInitialization方法是在bean init方法之前執(zhí)行,postProcessAfterInitialization方法是在bean init方法之后執(zhí)行。
修改User.java
package com.edu.spring;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext;public class User {private ApplicationContext applicationContext;public void init(){System.out.println("user init");}public void show(){System.out.println("user:" + applicationContext.getClass());}@Autowiredpublic void setApplicationContext(ApplicationContext applicationContext) {System.out.println("applicationContext set");this.applicationContext = applicationContext;} }Autowired既可以用在變量上,也可以用在方法上。
輸出結(jié)果如下:
====postProcessBeforeInitialization======class org.springframework.context.event.EventListenerMethodProcessor ====postProcessAfterInitialization======class org.springframework.context.event.EventListenerMethodProcessor ====postProcessBeforeInitialization======class org.springframework.context.event.DefaultEventListenerFactory ====postProcessAfterInitialization======class org.springframework.context.event.DefaultEventListenerFactory ====postProcessBeforeInitialization======class com.edu.spring.Bank ====postProcessAfterInitialization======class com.edu.spring.Bank ====postProcessBeforeInitialization======class com.edu.spring.Book ====postProcessAfterInitialization======class com.edu.spring.Book ====postProcessBeforeInitialization======class com.edu.spring.MyConfig$$EnhancerBySpringCGLIB$$50df4f2b ====postProcessAfterInitialization======class com.edu.spring.MyConfig$$EnhancerBySpringCGLIB$$50df4f2b applicationContext set ====postProcessBeforeInitialization======class com.edu.spring.User user init ====postProcessAfterInitialization======class com.edu.spring.User com.edu.spring.User@5b0abc94 com.edu.spring.User@5b0abc94 user:class org.springframework.context.annotation.AnnotationConfigApplicationContext book:class org.springframework.context.annotation.AnnotationConfigApplicationContext bank:class org.springframework.context.annotation.AnnotationConfigApplicationContext說明依賴都裝配完成之后出發(fā)postProcessBeforeInitialization
代理對(duì)象
新建LogUser.java
package com.edu.spring;public class LogUser extends User {@Overridepublic void show() {System.out.println("log start ...");super.show();System.out.println("log end ...");} }修改Use.java
public void show(){System.out.println("user:" + applicationContext);}修改EchoBeanPostProcessor.java
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("====postProcessBeforeInitialization======" + bean.getClass());if(bean instanceof User){return new LogUser();}return bean;}輸出結(jié)果如下:
====postProcessBeforeInitialization======class org.springframework.context.event.EventListenerMethodProcessor ====postProcessAfterInitialization======class org.springframework.context.event.EventListenerMethodProcessor ====postProcessBeforeInitialization======class org.springframework.context.event.DefaultEventListenerFactory ====postProcessAfterInitialization======class org.springframework.context.event.DefaultEventListenerFactory ====postProcessBeforeInitialization======class com.edu.spring.Bank ====postProcessAfterInitialization======class com.edu.spring.Bank ====postProcessBeforeInitialization======class com.edu.spring.Book ====postProcessAfterInitialization======class com.edu.spring.Book ====postProcessBeforeInitialization======class com.edu.spring.MyConfig$$EnhancerBySpringCGLIB$$50df4f2b ====postProcessAfterInitialization======class com.edu.spring.MyConfig$$EnhancerBySpringCGLIB$$50df4f2b applicationContext set ====postProcessBeforeInitialization======class com.edu.spring.User user init ====postProcessAfterInitialization======class com.edu.spring.LogUser com.edu.spring.LogUser@75c072cb com.edu.spring.LogUser@75c072cb log start ... user:null log end ... book:class org.springframework.context.annotation.AnnotationConfigApplicationContext bank:class org.springframework.context.annotation.AnnotationConfigApplicationContext可以對(duì)指定的Bean做一些處理,比如返回對(duì)象的代理對(duì)象。
spring擴(kuò)展二
目前我們知道BeanPostProcessor是在某個(gè)bean初始化的時(shí)候,進(jìn)行回調(diào)的,我們可以控制bean的初始化和其他的操作。如果我們想要對(duì)某個(gè)容器進(jìn)行初始化回調(diào),如何做?spring同樣留了接口, BeanFactoryPostProcessor,是在spring容器初始化之后進(jìn)行的
新建App.java
package com.edu.spring;import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class App {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.edu.spring");context.close();} }新建MyBeanFactoryPostProcessor.java
package com.edu.spring;import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.stereotype.Component;/*** BeanFactoryPostProcessor在spring容器的初始化之后出發(fā),而且只會(huì)觸發(fā)一次* 觸發(fā)的時(shí)機(jī)比BeanPostProcessor早*/ @Component public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {System.out.println("============" + configurableListableBeanFactory.getBeanDefinitionCount());} }運(yùn)行App.java 輸出結(jié)果如下:
============7說明即使我們什么都不做,spring容器還是有很多個(gè)bean的。
新建User.java
public class User {public void init(){System.out.println("user init");} }新建MyConfig.java
package com.edu.spring;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;@Configuration public class MyConfig {@Bean(initMethod = "init")public User createUser(){return new User();}@Beanpublic User createUser2(){return new User();}}運(yùn)行App.java,輸出結(jié)果如下:
============10 user init新建MyBeanPostProcessor.java,對(duì)比BeanPostProcessor和BeanFactoryPostProcessor執(zhí)行順序是什么
package com.edu.spring;import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.stereotype.Component;@Component public class MyBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("====postProcessBeforeInitialization=====" + bean.getClass());return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("====postProcessAfterInitialization=====" + bean.getClass());return bean;} }運(yùn)行輸出結(jié)果:
============11 四月 22, 2019 3:47:16 下午 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor <init> 信息: JSR-330 'javax.inject.Inject' annotation found and supported for autowiring ====postProcessBeforeInitialization=====class org.springframework.context.event.EventListenerMethodProcessor ====postProcessAfterInitialization=====class org.springframework.context.event.EventListenerMethodProcessor ====postProcessBeforeInitialization=====class org.springframework.context.event.DefaultEventListenerFactory ====postProcessAfterInitialization=====class org.springframework.context.event.DefaultEventListenerFactory ====postProcessBeforeInitialization=====class com.edu.spring.MyConfig$$EnhancerBySpringCGLIB$$a601222d ====postProcessAfterInitialization=====class com.edu.spring.MyConfig$$EnhancerBySpringCGLIB$$a601222d ====postProcessBeforeInitialization=====class com.edu.spring.User user init ====postProcessAfterInitialization=====class com.edu.spring.User ====postProcessBeforeInitialization=====class com.edu.spring.User ====postProcessAfterInitialization=====class com.edu.spring.User說明BeanFactoryPostProcessor最先執(zhí)行,但只會(huì)執(zhí)行一次,因?yàn)槿萜鲌?zhí)行完成一次。
BeanDefinitionRegistry
我們使用Component注釋來注冊(cè)一個(gè)bean,但這是靜態(tài)的注冊(cè),我們可以通過BeanDefinitionRegistry來動(dòng)態(tài)注冊(cè)一個(gè)Bean。
新建一個(gè)要注入的類Person.java
package com.edu.spring;public class Person {private String name;@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +'}';}public String getName() {return name;}public void setName(String name) {this.name = name;} }新建MyBeanDefinitionRegistryPostProcessor.java,注入十個(gè)Person類,并給他們的屬性賦值。
package com.edu.spring;import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.stereotype.Component;/*** BeanDefinitionRegistryPostProcessor可以拿到BeanDefinitionRegistry,ConfigurableListableBeanFactory兩個(gè)對(duì)象* BeanDefinitionRegistry可以動(dòng)態(tài)注入bean*/ @Component public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {for( int i = 0; i < 10; i++){BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(Person.class);// 往person內(nèi)注入屬性,可以注入引用beanDefinitionBuilder.addPropertyValue("name", "admin" + i);beanDefinitionRegistry.registerBeanDefinition("person" + i, beanDefinitionBuilder.getBeanDefinition());}}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {} }修改App.java
package com.edu.spring;import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class App {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.edu.spring");context.getBeansOfType(Person.class).values().forEach(person -> {System.out.println(person);});context.close();} }輸出結(jié)果:
============22 四月 22, 2019 4:38:56 下午 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor <init> 信息: JSR-330 'javax.inject.Inject' annotation found and supported for autowiring ====postProcessBeforeInitialization=====class org.springframework.context.event.EventListenerMethodProcessor ====postProcessAfterInitialization=====class org.springframework.context.event.EventListenerMethodProcessor ====postProcessBeforeInitialization=====class org.springframework.context.event.DefaultEventListenerFactory ====postProcessAfterInitialization=====class org.springframework.context.event.DefaultEventListenerFactory ====postProcessBeforeInitialization=====class com.edu.spring.MyConfig$$EnhancerBySpringCGLIB$$bc5fcd78 ====postProcessAfterInitialization=====class com.edu.spring.MyConfig$$EnhancerBySpringCGLIB$$bc5fcd78 ====postProcessBeforeInitialization=====class com.edu.spring.User user init ====postProcessAfterInitialization=====class com.edu.spring.User ====postProcessBeforeInitialization=====class com.edu.spring.User ====postProcessAfterInitialization=====class com.edu.spring.User ====postProcessBeforeInitialization=====class com.edu.spring.Person ====postProcessAfterInitialization=====class com.edu.spring.Person ====postProcessBeforeInitialization=====class com.edu.spring.Person ====postProcessAfterInitialization=====class com.edu.spring.Person ====postProcessBeforeInitialization=====class com.edu.spring.Person ====postProcessAfterInitialization=====class com.edu.spring.Person ====postProcessBeforeInitialization=====class com.edu.spring.Person ====postProcessAfterInitialization=====class com.edu.spring.Person ====postProcessBeforeInitialization=====class com.edu.spring.Person ====postProcessAfterInitialization=====class com.edu.spring.Person ====postProcessBeforeInitialization=====class com.edu.spring.Person ====postProcessAfterInitialization=====class com.edu.spring.Person ====postProcessBeforeInitialization=====class com.edu.spring.Person ====postProcessAfterInitialization=====class com.edu.spring.Person ====postProcessBeforeInitialization=====class com.edu.spring.Person ====postProcessAfterInitialization=====class com.edu.spring.Person ====postProcessBeforeInitialization=====class com.edu.spring.Person ====postProcessAfterInitialization=====class com.edu.spring.Person ====postProcessBeforeInitialization=====class com.edu.spring.Person ====postProcessAfterInitialization=====class com.edu.spring.Person Person{name='admin0'} Person{name='admin1'} Person{name='admin2'} Person{name='admin3'} Person{name='admin4'} Person{name='admin5'} Person{name='admin6'} Person{name='admin7'} Person{name='admin8'} Person{name='admin9'}說明Person自動(dòng)注入進(jìn)去了。
除了這個(gè)方法,也可以使用
AnnotationConfigApplicationContext context.registerBeanDefinition(beanName, beanDefinition);springboot-1
新建pom.xml
<?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>cn.ac.iie</groupId><artifactId>spring-course</artifactId><version>1.0-SNAPSHOT</version><packaging>jar</packaging><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target></properties><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.4.RELEASE</version></parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency></dependencies> </project>新建App.java
package com.edu.spring;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean;@SpringBootApplication public class App {@Beanpublic Runnable createRunable(){return () -> {System.out.println("spring boot is running");};}public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(App.class, args);context.getBean(Runnable.class).run();} }運(yùn)行App.java 可以得到輸出結(jié)果如下:
. ____ _ __ _ _/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \\\/ ___)| |_)| | | | | || (_| | ) ) ) )' |____| .__|_| |_|_| |_\__, | / / / /=========|_|==============|___/=/_/_/_/:: Spring Boot :: (v2.1.4.RELEASE)2019-04-22 22:52:03.868 INFO 11372 --- [ main] com.edu.spring.App : Starting App on duandingyangdeMacBook-Pro.local with PID 11372 (/Users/duandingyang/git-project/springcourse/target/classes started by duandingyang in /Users/duandingyang/git-project/springcourse) 2019-04-22 22:52:03.876 INFO 11372 --- [ main] com.edu.spring.App : No active profile set, falling back to default profiles: default 2019-04-22 22:52:04.651 INFO 11372 --- [ main] com.edu.spring.App : Started App in 1.432 seconds (JVM running for 2.166) spring boot is running這是一個(gè)最簡單的SpringBoot應(yīng)用。
跟之前的spring使用方法差不多,只是main的入口變了。
在pom.xml文件中,如果我們不想使用parent依賴,應(yīng)該怎么做?
新的pom.xml如下
<?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>cn.ac.iie</groupId><artifactId>spring-course</artifactId><version>1.0-SNAPSHOT</version><packaging>jar</packaging><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target></properties><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>2.1.4.RELEASE</version><scope>import</scope><type>pom</type></dependency></dependencies></dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency></dependencies> </project>在App.java中,點(diǎn)擊查看@SpringBootApplication注釋,有三個(gè)重要的注釋,分別是@SpringBootConfiguration @EnableAutoConfiguration @ComponentScan
目前的情況,我們可以直接使用@ComponentScan注釋就行
App.java如下:
package com.edu.spring;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan;@ComponentScan public class App {@Beanpublic Runnable createRunable(){return () -> {System.out.println("spring boot is running");};}public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(App.class, args);context.getBean(Runnable.class).run();} }運(yùn)行結(jié)果跟之前是一樣的。
其實(shí)SpringApplication.run(App.class, args)中App.class一般就是入口,也就是配置類,里面可以配置各種Bean。
刪掉@ComponentScan 修改入口配置
package com.edu.spring;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan;public class App {@Beanpublic Runnable createRunable(){return () -> {System.out.println("spring boot is running");};}public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(App2.class, args);context.getBean(Runnable.class).run();} }新建App2.java
package com.edu.spring;import org.springframework.context.annotation.ComponentScan;@ComponentScan public class App2 {}這樣輸出結(jié)果如下:
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'java.lang.Runnable' availableat org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:343)at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:335)at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1123)at com.edu.spring.App.main(App.java:21)因?yàn)檎也坏絙ean,如果將App.java中的生命bean 的代碼剪切到App2.java中,那么程序正常運(yùn)行。
也可以通過這個(gè)方式加載配置類:
package com.edu.spring;import org.springframework.boot.SpringApplication; import org.springframework.context.ConfigurableApplicationContext;import java.util.HashSet; import java.util.Set;public class App {public static void main(String[] args) {SpringApplication application = new SpringApplication(App2.class);ConfigurableApplicationContext context = application.run(args);context.getBean(Runnable.class).run();}}新建MyConfig.java
package com.edu.spring;import org.springframework.boot.SpringBootConfiguration; import org.springframework.context.annotation.Bean;import java.util.ArrayList; import java.util.List;@SpringBootConfiguration public class MyConfig {@Beanpublic List<String> createList(){ArrayList arrayList = new ArrayList();arrayList.add("a");return arrayList;}}使用SpringBootConfiguration同樣可以裝配Bean
App.java
package com.edu.spring;import org.springframework.boot.SpringApplication; import org.springframework.context.ConfigurableApplicationContext;import java.util.List;public class App {public static void main(String[] args) {SpringApplication application = new SpringApplication(App2.class);ConfigurableApplicationContext context = application.run(args);context.getBean(Runnable.class).run();System.out.println(context.getBean(List.class));}}Springboot配置文件
springboot默認(rèn)的配置文件是application.properties.
新建application.properties,內(nèi)容如下:
local.ip=192.168.1.1 local.port=8080name=springboot app.name=this is ${name}方法一 使用context.getEnvironment().getProperty("local.ip")
新建App.java,
package com.edu.spring;import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringBootConfiguration; import org.springframework.context.ConfigurableApplicationContext;@SpringBootConfiguration public class App {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(App.class, args);System.out.println(context.getEnvironment().getProperty("local.ip"));context.close();}}輸出:
192.168.1.1方法二:
新建UserConfig.java
package com.edu.spring;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.stereotype.Component;@Component public class UserConfig {@Autowiredprivate Environment environment;public void show(){System.out.println("local.ip=" + environment.getProperty("local.ip"));}}修改App.java
package com.edu.spring;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext;@SpringBootApplication public class App {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(App.class, args);context.getBean(UserConfig.class).show();context.close();}}同樣可以輸出local.ip=192.168.1.1
方法三:
修改UserConfig.java, 使用@Value("${local.port}")注釋,來獲取
package com.edu.spring;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.env.Environment; import org.springframework.stereotype.Component;@Component public class UserConfig {@Value("${local.port}")private String localPort;/*** @Value 默認(rèn)要有配置項(xiàng),配置項(xiàng)可以為空,如果沒有配置項(xiàng),則可以給默認(rèn)值*/@Value("${tomcat.port:9090}")private String tomcatPort;@Autowiredprivate Environment environment;public void show(){System.out.println("local.ip=" + environment.getProperty("local.ip"));System.out.println("local.port=" + localPort);System.out.println("name=" + environment.getProperty("name"));System.out.println("app.name=" + environment.getProperty("app.name"));System.out.println("tomcat.port=" + tomcatPort);}}使用配置文件引用其他變量
public void show(){System.out.println("local.ip=" + environment.getProperty("local.ip"));System.out.println("local.port=" + localPort);System.out.println("name=" + environment.getProperty("name"));System.out.println("app.name=" + environment.getProperty("app.name"));}application.properties的位置是resources目錄下(即classpath根目錄)或者是resources/config目錄(即classpath:/config目錄)下。
如果要改application.properties的名字和目錄如何做?
可以在intellij工具 Program argument添加參數(shù) --spring.config.name=文件名 可以省略文件擴(kuò)展名
如果修改目錄 添加參數(shù) --spring.config.location=classpath:conf/app.properties,? 必須指定擴(kuò)展名。還可以指定多個(gè)路徑,用逗號(hào)隔開。之間的指定方式有兩種1.classpath: 2.file:
如果要加載其他配置文件,如何做?
新建jdbc.properties
url=jdbc:mysql:///springboot driverClassName=com.mysql.jdbc.Driver新建FileConfig.java
package com.edu.spring;import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource;@Configuration @PropertySource("classpath:jdbc.properties") public class FileConfig { }新建JdbcConfig.java
package com.edu.spring;import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component;@Component public class JdbcConfig {@Value("${url}")private String url;@Value("${driverClassName}")private String driverClassName;public void show(){System.out.println("url=" + url);System.out.println("driverClassName=" + driverClassName);}}在App.java中
context.getBean(JdbcConfig.class).show();可以正常輸出。
如果是多個(gè)路徑下的文件,修改FileConfig.java
package com.edu.spring;import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource;@Configuration @PropertySource("classpath:jdbc.properties") @PropertySource("file:/e/tmp/jdbc.properties") public class FileConfig { }或者使用
package com.edu.spring;import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import org.springframework.context.annotation.PropertySources;@Configuration @PropertySources({@PropertySource("classpath:jdbc.properties"),@PropertySource("file:/e/tmp/jdbc.properties")}) public class FileConfig { }新建DataSourceProperties.java
package com.edu.spring;import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.PropertySource; import org.springframework.stereotype.Component;@Component @ConfigurationProperties(prefix = "ds") @PropertySource("classpath:/ds.properties") public class DataSourceProperties {private String url;private String driverClassName;private String username;private String password;public void show(){System.out.println("url:" + url);System.out.println("driverClassName:" + driverClassName);System.out.println("username:" + username);System.out.println("password:" + password);}public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public String getDriverClassName() {return driverClassName;}public void setDriverClassName(String driverClassName) {this.driverClassName = driverClassName;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;} }新建ds.properties
ds.url=jdbc:mysql:///springboot ds.driverClassName=com.mysql.jdbc.Driver ds.username=root ds.password=123456springboot 也可以使用application.yml作為配置文件,是使用縮進(jìn)方式書寫。
從配置文件中,注入集合與數(shù)組
新建TomcatProperties.java
package com.edu.spring;import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component;import java.util.ArrayList; import java.util.Arrays; import java.util.List;@Component @ConfigurationProperties("ds") public class TomcatProperties {private List<String> hosts = new ArrayList<>();private String[] ports;@Overridepublic String toString() {return "TomcatProperties{" +"hosts=" + hosts +", ports=" + Arrays.toString(ports) +'}';}public String[] getPorts() {return ports;}public void setPorts(String[] ports) {this.ports = ports;}public List<String> getHosts() {return hosts;}public void setHosts(List<String> hosts) {this.hosts = hosts;} }applicatioin.properties
ds.hosts[0]=192.168.1.100 ds.hosts[1]=192.168.1.101 ds.hosts[2]=192.168.1.102 ds.ports[0]=8080 ds.ports[1]=8081 ds.ports[2]=8082App.java
package com.edu.spring;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext;@SpringBootApplication public class App {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(App.class, args);System.out.println(context.getBean(TomcatProperties.class));context.close();}}注入正常。輸出結(jié)果:
TomcatProperties{hosts=[192.168.1.100, 192.168.1.101, 192.168.1.102], ports=[8080, 8081, 8082]}動(dòng)態(tài)引入配置文件,使用EnvironmentPostProcessor接口
新建MyEnvironmentPostProcessor.java
package com.edu.spring;import org.springframework.boot.SpringApplication; import org.springframework.boot.env.EnvironmentPostProcessor; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.PropertiesPropertySource; import org.springframework.stereotype.Component;import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.Properties;@Component public class MyEnvironmentPostProcessor implements EnvironmentPostProcessor {@Overridepublic void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {try(InputStream inputStream = new FileInputStream("F:/test/springboot.properties")){Properties properties = new Properties();properties.load(inputStream);PropertiesPropertySource propertySource = new PropertiesPropertySource("my", properties);environment.getPropertySources().addLast(propertySource);} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}} }測試輸出
System.out.println(context.getEnvironment().getProperty("springboot.name"));輸出結(jié)果為Null
說明沒有正確注入,在resources目錄下新建META-INF/spring.factories,內(nèi)容如下:
org.springframework.boot.env.EnvironmentPostProcessor=com.edu.spring.MyEnvironmentPostProcessor執(zhí)行輸出結(jié)果正常。原因是EnvironmentPostProcessor接口不屬于spring的,而是屬于Springboot的,因此需要配置到spring.factories。
有了這一功能我們就可以隨意的增加一些配置了,任意讀取配置,即配置文件中心化。
profile,開發(fā)階段和上線測試階段是不同的,如何在不同階段載入不同配置
新建application-dev.properties,內(nèi)容如下
jdbc.url=mysql:jdbc://127.0.0.1/db_springboot_dev新建application-test.properties,內(nèi)容如下:
jdbc.url=mysql:jdbc://127.0.0.1/db_springboot_testApp.java
package com.edu.spring;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext;@SpringBootApplication public class App {public static void main(String[] args) {SpringApplication application = new SpringApplication(App.class);//application.setAdditionalProfiles("dev");application.setAdditionalProfiles("test");ConfigurableApplicationContext context = application.run(args);System.out.println(context.getBean(TomcatProperties.class));System.out.println(context.getEnvironment().getProperty("springboot.name"));System.out.println(context.getEnvironment().getProperty("jdbc.url"));context.close();}}這樣就可以實(shí)現(xiàn)切換。默認(rèn)的application.properties也會(huì)加進(jìn)去。
通過啟動(dòng)參數(shù)來控制生效的profile,--spring.profiles.active=test,dev,這樣test和dev便同時(shí)啟用了。
新建MyConfig.java
package com.edu.spring;import org.springframework.boot.SpringBootConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Profile;@SpringBootConfiguration public class MyConfig {@Beanpublic Runnable createRunable(){System.out.println("===1==");return () -> {};}/*** 當(dāng)application-test.properties激活之后才裝配這個(gè)Bean* @return*/@Bean@Profile("test")public Runnable createRunable2(){System.out.println("===2==");return () -> {};}/*** 當(dāng)application-dev.properties激活之后才裝配這個(gè)Bean* @return*/@Bean@Profile("dev")public Runnable createRunable3(){System.out.println("===3==");return () -> {};}}可以當(dāng)具體properties激活時(shí),裝配Bean。profile注釋也可以用在類上,表示當(dāng)某個(gè)profile生效時(shí),使用這個(gè)類。
spring boot自動(dòng)配置
新建接口EncodingConvert.java
package com.edu.spring;public interface EncodingConvert {}新建實(shí)現(xiàn)類GBKEncodingConvert.java
public class GBKEncodingConvert implements EncodingConvert { }新建實(shí)現(xiàn)類UTF8EncodingConvert.java
package com.edu.spring;public class UTF8EncodingConvert implements EncodingConvert { }新建EncodingConvertConfig.java
package com.edu.spring;import org.springframework.boot.SpringBootConfiguration; import org.springframework.context.annotation.Bean;@SpringBootConfiguration public class EncodingConvertConfig {@Beanpublic EncodingConvert createUTF8EncodingConvert(){return new UTF8EncodingConvert();}@Beanpublic EncodingConvert createGBKEncodingConvert(){return new GBKEncodingConvert();}}新建App.java
package com.edu.spring;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext;@SpringBootApplication public class App {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(App.class, args);System.out.println(System.getProperty("file.encoding"));System.out.println(context.getBeansOfType(EncodingConvert.class));context.close();}}運(yùn)行App.java,打印輸出結(jié)果如下:
{createUTF8EncodingConvert=com.edu.spring.UTF8EncodingConvert@c9d0d6, createGBKEncodingConvert=com.edu.spring.GBKEncodingConvert@6ccdb29f}兩個(gè)Bean都裝配成功。
新建GBKCondition.java
package com.edu.spring;import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata;public class GBKCondition implements Condition {@Overridepublic boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {String encoding = System.getProperty("file.encoding");if(encoding != null){return "gbk".equals(encoding.toLowerCase());}return false;} }新建UTF8Condition.java
package com.edu.spring;import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata;public class UTF8Condition implements Condition {@Overridepublic boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {String encoding = System.getProperty("file.encoding");if(encoding != null){return "utf-8".equals(encoding.toLowerCase());}return false;} }在intellij 的VMoption參數(shù)添加 -Dfile.encoding=GBK
打印輸出:
{createGBKEncodingConvert=com.edu.spring.GBKEncodingConvert@512baff6}@Conditional 是基于條件的自動(dòng)配置,一般配合Condition接口一起使用,只有接口(一個(gè)或多個(gè))實(shí)現(xiàn)類都返回true才裝配,否則不裝配。
它可以用在方法上,則只對(duì)該方法起作用,還可以用在類上,則對(duì)該類的所有方法起作用。
此外,@Conditional({UTF8Condition.class, GBKCondition.class}),表示兩個(gè)條件都返回true才生效。
@ConditionalOnProperty,表示某個(gè)屬性等于某個(gè)值時(shí)。
新建UserConfiguration.java
package com.edu.spring;import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean;@SpringBootConfiguration public class UserConfiguration {@Bean@ConditionalOnProperty(name = "runnable.enabled", havingValue = "true")public Runnable createRunnable(){return ()->{};}}當(dāng)application.properties中的屬性runnable.enabled=true時(shí),這個(gè)Bean才能夠裝配。否則裝配不成功。matchIfMissing表示當(dāng)這個(gè)配置不存在的時(shí)候,也為true。
修改UserConfiguration.java
package com.edu.spring;import com.google.gson.Gson; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean;@SpringBootConfiguration public class UserConfiguration {@Bean@ConditionalOnProperty(name = "runnable.enabled", havingValue = "true", matchIfMissing = true)public Runnable createRunnable() {return () -> {};}@Bean@ConditionalOnClass(name = "com.google.gson.Gson")public Runnable createGsonRunnable() {return () -> {};}}修改pom.xml,添加gson依賴
<dependency><groupId>com.google.code.gson</groupId><artifactId>gson</artifactId><version>2.8.1</version></dependency>@ConditionalOnClass表示當(dāng)存在這個(gè)“com.google.gson.Gson”類的時(shí)候,就裝配這個(gè)Bean。
@ConditionalOnMissingClass表示當(dāng)不存在這個(gè)“com.google.gson.Gson”類的時(shí)候,就裝配這個(gè)Bean。
打印輸出:
{createRunnable=com.edu.spring.UserConfiguration$$Lambda$137/800735172@335b5620, createGsonRunnable=com.edu.spring.UserConfiguration$$Lambda$138/478489615@29a0cdb}去掉pom.xml中的依賴是,就不會(huì)裝配這個(gè)bean。
@ConditionalOnBean??根據(jù)容器中存在某個(gè)bean來進(jìn)行裝配
修改UserConfiguration.java
@Bean@ConditionalOnBean(name="user")public Runnable createBeanRunnable() {return () -> {};}運(yùn)行時(shí)輸出:
{createRunnable=com.edu.spring.UserConfiguration$$Lambda$137/698741991@22356acd, createGsonRunnable=com.edu.spring.UserConfiguration$$Lambda$138/669284403@386f0da3}沒有輸出createBeanRunnable這個(gè)Bean,添加User.java
package com.edu.spring;import org.springframework.stereotype.Component;@Component public class User { }然后運(yùn)行輸出:
{createRunnable=com.edu.spring.UserConfiguration$$Lambda$138/1934932165@27d4a09, createGsonRunnable=com.edu.spring.UserConfiguration$$Lambda$139/1508038883@7e4204e2, createBeanRunnable=com.edu.spring.UserConfiguration$$Lambda$140/728943498@b7c4869}說明成功配置了createBeanRunnable這個(gè)bean了。
@ConditionalOnMissiongBean??根據(jù)容器中不存在某個(gè)bean來進(jìn)行裝配
Spring Boot @Enable*注解工作原理
新建TomcatProperties.java
package com.edu.spring;import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Component;@Component @ConfigurationProperties(prefix = "tomcat") public class TomcatProperties {private String host;private String port;@Overridepublic String toString() {return "TomcatProperties{" +"host='" + host + '\'' +", port='" + port + '\'' +'}';}public String getHost() {return host;}public void setHost(String host) {this.host = host;}public String getPort() {return port;}public void setPort(String port) {this.port = port;} }application.properties
tomcat.host=192.168.1.100 tomcat.port=8080新建App.java
package com.edu.spring;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext;@SpringBootApplication public class App {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(App.class, args);System.out.println(context.getBean(TomcatProperties.class));context.close();}}運(yùn)行輸出結(jié)果:
TomcatProperties{host='192.168.1.100', port='8080'}當(dāng)我們修改App.java的注解時(shí),
package com.edu.spring;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.ComponentScan;/*** @EnableConfiguratinProperties是用來啟用一個(gè)特性的,這個(gè)特性可以把配置文件的屬性注入到bean里面去*/ @EnableConfigurationProperties @ComponentScan public class App {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(App.class, args);System.out.println(context.getBean(TomcatProperties.class));context.close();}}程序也可以正常運(yùn)行,但是當(dāng)我們將注解@EnableConfiguratinProperties刪掉后,程序就獲取不到properties的內(nèi)容了。說明是@EnableConfiguratinProperties起了作用。
如何在Springboot中啟動(dòng)異步?
新建Jeep.java
package com.edu.spring;import org.springframework.stereotype.Component;import java.util.concurrent.TimeUnit;@Component public class Jeep implements Runnable {@Overridepublic void run() {try {for(int i = 1; i<= 10; i++){System.out.println("============" + i);TimeUnit.SECONDS.sleep(1);}}catch (Exception e){e.printStackTrace();}} }修改App.java
context.getBean(Runnable.class).run(); System.out.println("----end-----");輸出結(jié)果如下:
TomcatProperties{host='192.168.1.100', port='8080'} ============1 ============2 ============3 ============4 ============5 ============6 ============7 ============8 ============9 ============10 ----end-----說明當(dāng)run方法執(zhí)行完之后,才輸出end。如何實(shí)現(xiàn)異步?
在App.java上啟動(dòng)異步,添加注釋:
package com.edu.spring;import org.springframework.boot.SpringApplication; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.ComponentScan; import org.springframework.scheduling.annotation.EnableAsync;/*** @EnableConfiguratinProperties是用來啟用一個(gè)特性的,這個(gè)特性可以把配置文件的屬性注入到bean里面去,一般是和@ConfigurationProperties一起使用* @EnableAsync 啟用異步,一般是和@Async一起使用*/ @EnableConfigurationProperties @EnableAsync @ComponentScan public class App {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(App.class, args);System.out.println(context.getBean(TomcatProperties.class));context.getBean(Runnable.class).run();System.out.println("----end-----");context.close();}}在Jeep.java的run方法上添加@Async注釋
package com.edu.spring;import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component;import java.util.concurrent.TimeUnit;@Component public class Jeep implements Runnable {@Async@Overridepublic void run() {try {for(int i = 1; i<= 10; i++){System.out.println("============" + i);TimeUnit.SECONDS.sleep(1);}}catch (Exception e){e.printStackTrace();}} }運(yùn)行輸出結(jié)果如下:
----end----- ============1 ============2 ============3 ============4 ============5 ============6 ============7 ============8 ============9 ============10說明已經(jīng)成功異步了。
新建User.java
package com.edu.spring;public class User { }新建Role.java
package com.edu.spring;public class Role { }新建App2.java
package com.edu.spring;import org.springframework.boot.SpringApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.ComponentScan;@ComponentScan public class App2 {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(App2.class, args);context.close();}}此時(shí)App2.java中無法獲取User的Bean和Role的Bean,想獲取的方式是:有很多,之前有講過,例如:@Component。今天講另一種方式:
修改App2.java
package com.edu.spring;import org.springframework.boot.SpringApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Import;@ComponentScan @Import(User.class) public class App2 {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(App2.class, args);System.out.println(context.getBean(User.class));context.close();}}添加Import注釋,這樣就可以使用User的Bean了。
@Import注釋也可以導(dǎo)入一個(gè)數(shù)組,例如:@Import({User.class, Role.class})
此外,@Import還可以到如配置類
新建MyConfiguration.java
package com.edu.spring;import org.springframework.context.annotation.Bean;public class MyConfiguration {@Beanpublic Runnable createRunnable(){return () -> {};}@Beanpublic Runnable createRunnable2(){return () -> {};}}修改App2.java
package com.edu.spring;import org.springframework.boot.SpringApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Import;/*** @Import 用來導(dǎo)入一個(gè)或多個(gè)類(Bean會(huì)被Spring容器托管),或者配置類(配置類里面的bean會(huì)被Spring容器托管)*/ @ComponentScan @Import({User.class, Role.class, MyConfiguration.class}) public class App2 {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(App2.class, args);System.out.println(context.getBean(User.class));System.out.println(context.getBeansOfType(Runnable.class));context.close();} }使用Import導(dǎo)入MyConfiguration,輸出結(jié)果:
com.edu.spring.User@6f27a732 {jeep=com.edu.spring.Jeep@6c779568, createRunnable=com.edu.spring.MyConfiguration$$Lambda$93/1730704097@f381794, createRunnable2=com.edu.spring.MyConfiguration$$Lambda$94/726379593@2cdd0d4b}說明成功導(dǎo)入了配置類。
ImportSelector是什么?
新建MyImportSelector.java
package com.edu.spring;import org.springframework.context.annotation.ImportSelector; import org.springframework.core.type.AnnotationMetadata;/*** ImportSelector的方法的返回值,必須是一個(gè)class(全稱),該class會(huì)被spring容器托管起來。*/ public class MyImportSelector implements ImportSelector {@Overridepublic String[] selectImports(AnnotationMetadata importingClassMetadata) {return new String[]{"com.edu.spring.User",Role.class.getName(), MyConfiguration.class.getName()};} }修改App2.java
package com.edu.spring;import org.springframework.boot.SpringApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Import;/*** @Import 用來導(dǎo)入一個(gè)或多個(gè)類(Bean會(huì)被Spring容器托管),或者配置類(配置類里面的bean會(huì)被Spring容器托管)*/ @ComponentScan //@Import({User.class, Role.class, MyConfiguration.class}) @Import(MyImportSelector.class) public class App2 {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(App2.class, args);System.out.println(context.getBean(User.class));System.out.println(context.getBean(Role.class));System.out.println(context.getBeansOfType(Runnable.class));context.close();}}同樣可以輸出User的Bean,Role的Bean以及Runnable的Bean,說明裝配成功。
新建EnableLog.java
package com.edu.spring;import org.springframework.context.annotation.Import;import java.lang.annotation.*;@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import(MyImportSelector.class) public @interface EnableLog {String name(); }在MyImportSelector.java中修改:
package com.edu.spring;import org.springframework.context.annotation.ImportSelector; import org.springframework.core.type.AnnotationMetadata;/*** ImportSelector的方法的返回值,必須是一個(gè)class(全稱),該class會(huì)被spring容器托管起來。*/ public class MyImportSelector implements ImportSelector {@Overridepublic String[] selectImports(AnnotationMetadata importingClassMetadata) {System.out.println(importingClassMetadata.getAllAnnotationAttributes(EnableLog.class.getName()));/*** 這里可以獲取到注解的詳細(xì)信息。然后根據(jù)信息去動(dòng)態(tài)的返回需要被spring容器管理的bean*/return new String[]{"com.edu.spring.User",Role.class.getName(), MyConfiguration.class.getName()};} }可以輸出注釋的信息。
修改APP2.java
package com.edu.spring;import org.springframework.boot.SpringApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Import;/*** @Import 用來導(dǎo)入一個(gè)或多個(gè)類(Bean會(huì)被Spring容器托管),或者配置類(配置類里面的bean會(huì)被Spring容器托管)*/ @ComponentScan //@Import({User.class, Role.class, MyConfiguration.class}) @Import(MyImportSelector.class) @EnableLog(name="my springboot") public class App2 {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(App2.class, args);System.out.println(context.getBean(User.class));System.out.println(context.getBean(Role.class));System.out.println(context.getBeansOfType(Runnable.class));context.close();}}輸出結(jié)果可以輸出{name=[my springboot]},可以獲取到注解的詳細(xì)信息
@EnableAutoConfiguration深入分析
新建App.java
package com.edu.spring;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext;@SpringBootApplication public class App {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(App.class, args);System.out.println(context.getBean(Runnable.class));context.close();} }運(yùn)行報(bào)錯(cuò),因?yàn)闆]有Runnable這個(gè)bean。
新建項(xiàng)目:corebean
pom.xml文件:
<?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.edu.core</groupId><artifactId>core-bean</artifactId><version>1.0.0</version><packaging>jar</packaging><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target></properties><dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.1.6.RELEASE</version></dependency></dependencies></project>新建RunnableConfiguration.java
package com.edu.core.bean;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;@Configuration public class RunnableConfiguration {@Beanpublic Runnable createRunnable(){return () -> {};}}在springcourse項(xiàng)目的pom.xml中添加corebean項(xiàng)目依賴:
<?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>cn.ac.iie</groupId><artifactId>spring-course</artifactId><version>1.0-SNAPSHOT</version><packaging>jar</packaging><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target></properties><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>2.1.4.RELEASE</version><scope>import</scope><type>pom</type></dependency></dependencies></dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency><dependency><groupId>com.edu.core</groupId><artifactId>core-bean</artifactId><version>1.0.0</version></dependency></dependencies> </project>在次運(yùn)行App.java
同樣顯示沒有找到Runnable的bean類。
如果spring只支持當(dāng)前項(xiàng)目中加載配置,那么他的擴(kuò)展性太不好了,如何解決這個(gè)第三方j(luò)ar的配置?
使用EnableAutoConfiguration注解
修改App.java,我們只用@EnableAutoConfiguration和@ComponentScan注釋
package com.edu.spring;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.ComponentScan;/*** EnableAutoConfiguration 作用:從classpath中搜索所有META-INF/spring.factories配置文件,* 然后,將其中org.springframework.boot.autoconfigure.EnableAutoConfiguration key對(duì)應(yīng)的配置項(xiàng)加載到spring容器中*/ @EnableAutoConfiguration @ComponentScan public class App {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(App.class, args);System.out.println(context.getBean(Runnable.class));context.close();} }在resources目錄下創(chuàng)建META-INF/spring.factories,內(nèi)容如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.edu.core.bean.RunnableConfiguration運(yùn)行App.java,輸出如下:
com.edu.core.bean.RunnableConfiguration$$Lambda$134/549293029@398dada8成功注入進(jìn)去了Runnable的Bean。
如何配置多個(gè)?
在core-bean中新建User.java和Role.java以及一個(gè)配置類UserConfiguration.java內(nèi)容分別如下:
package com.edu.core.bean;public class User { } package com.edu.core.bean;public class Role { } package com.edu.core.bean;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;@Configuration public class UserConfiguration {@Beanpublic User createUser(){return new User();} }然后在springcourse的spring.factories中內(nèi)容如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.edu.core.bean.RunnableConfiguration,com.edu.core.bean.UserConfiguration,com.edu.core.bean.Role同樣成功注入進(jìn)去了。這樣就實(shí)現(xiàn)了添加多個(gè)配置類。
只有在application.properties 文件中,spring.boot.enableautoconfiguration為true(默認(rèn)為true)時(shí),才啟用自動(dòng)配置。默認(rèn)不用配置時(shí)為true
其內(nèi)部實(shí)現(xiàn)的關(guān)鍵點(diǎn)有:
?1. ImportSelector 該接口的方法的額返回值都會(huì)被納入到spring容器管理中
?2. SpringFactoriesLoader 該類可以從classpath中搜索所有META-INF/spring.factories配置文件,并讀取配置
?如果想要排除某些類,應(yīng)該如何做?
方式一:通過class來排除
使用exclude
使用@EnableAutoConfiguration(exclude = UserConfiguration.class) 就將User的bean排除在外了。
方式二:通過類名來排除
使用@EnableAutoConfiguration(excludeName = "com.edu.core.bean.Role")
Springboot 事件監(jiān)聽
事件流程:
? ? 1. 自定義事件,一般是繼承ApplicationEvent抽象類
? ? 2. 定義時(shí)間監(jiān)聽器,一般是實(shí)現(xiàn)ApplicationListener接口
? ? 3. 啟動(dòng)的時(shí)候,需要把監(jiān)聽器加入到spring容器中
? ? 4. 發(fā)布事件,使用ApplicationContext的publishEvent發(fā)布事件
? ? 5. 配置監(jiān)聽器
????? ? a. SpringApplication.addListeners 添加監(jiān)聽器
????? ? b. 把監(jiān)聽器納入到spring容器中管理 @Component
????? ? c. 可以使用配置項(xiàng),在application.properties中配置context.listener.classes=com.edu.spring.MyApplicationListener? ?詳細(xì)內(nèi)容參數(shù):DelegatingApplicationListener
定義事件,新建MyApplicationEvent.java
package com.edu.spring;import org.springframework.context.ApplicationEvent;/*** 定義事件*/ public class MyApplicationEvent extends ApplicationEvent {public MyApplicationEvent(Object source) {super(source);} }定義監(jiān)聽器,MyApplicationListener.java
package com.edu.spring;import org.springframework.context.ApplicationListener;public class MyApplicationListener implements ApplicationListener<MyApplicationEvent> {@Overridepublic void onApplicationEvent(MyApplicationEvent event) {System.out.println("接收到事件:" + event.getClass());} }新建App.java
package com.edu.spring;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.ComponentScan;@EnableAutoConfiguration(excludeName = "com.edu.core.bean.Role") @ComponentScan public class App {public static void main(String[] args) {SpringApplication application = new SpringApplication(App.class);application.addListeners(new MyApplicationListener());ConfigurableApplicationContext context = application.run(args);//發(fā)布事件context.publishEvent(new MyApplicationEvent(new Object()));context.close();} }運(yùn)行結(jié)果:
接收到事件:class com.edu.spring.MyApplicationEvent說明監(jiān)聽成功。
除了使用application.addListeners(new MyApplicationListener());這種方式添加監(jiān)聽器,還有什么方式?
可以使用@Component注釋給MyApplicationListener.java。這樣也可以。
另一種方式配置監(jiān)聽器:新建MyEventHandler.java
package com.edu.spring;import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component;@Component public class MyEventHandle {/*** 參數(shù)一定要是ApplicationEvent,或者其子類* @param event*/@EventListenerpublic void event(MyApplicationEvent event){System.out.println("接受到事件" + event.getClass());}}使用注解@EventListener也可以配置監(jiān)聽器,且該類需要納入到spring容器重管理(詳細(xì)內(nèi)容參照EventListenerFactory和EventListenerMethodProcessor),不用其他的配置了。如果參數(shù)設(shè)置成public void event(Object event) 那么所有的事件都能夠接收到。?
spring或者Springboot內(nèi)部有哪些已經(jīng)定義好的事件?
修改MyEventHandle.java
package com.edu.spring;import org.springframework.context.event.ContextStoppedEvent; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component;@Component public class MyEventHandle {/*** 參數(shù)一定要是ApplicationEvent,或者其子類* @param event*/@EventListenerpublic void event(MyApplicationEvent event){System.out.println("接受到事件" + event.getClass());}@EventListenerpublic void event2(ContextStoppedEvent event){System.out.println("應(yīng)用停止事件:" + event);}}在App.java中修改:
context.publishEvent(new MyApplicationEvent(new Object()));輸出結(jié)果:
接受到事件class com.edu.spring.MyApplicationEvent 應(yīng)用停止事件:org.springframework.context.event.ContextStoppedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@314c508a, started on Thu Apr 25 17:07:00 CST 2019]監(jiān)聽器起作用了。
spring boot擴(kuò)展分析
新建MyApplicationContextInitializer.java
package com.edu.spring;import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext;public class MyApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {@Overridepublic void initialize(ConfigurableApplicationContext applicationContext) {System.out.println("bean count: " + applicationContext.getBeanDefinitionCount());} }新建MyApplicationContextInitializer2.java
package com.edu.spring;import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext;public class MyApplicationContextInitializer2 implements ApplicationContextInitializer<ConfigurableApplicationContext> {@Overridepublic void initialize(ConfigurableApplicationContext applicationContext) {System.out.println("app name: " + applicationContext.getDisplayName());} }新建App.java
package com.edu.spring;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext;@SpringBootApplication public class App {public static void main(String[] args) {SpringApplication application = new SpringApplication(App.class);application.addInitializers(new MyApplicationContextInitializer());ConfigurableApplicationContext context = application.run(args);context.stop();context.close();} }在application.properties中內(nèi)容如下:
context.initializer.classes=com.edu.spring.MyApplicationContextInitializer, com.edu.spring.MyApplicationContextInitializer2運(yùn)行App.java
輸出結(jié)果如下:
bean count: 5 app name: org.springframework.context.annotation.AnnotationConfigApplicationContext@1a052a00ApplicationContextInitializer 接口是在spring容器執(zhí)行refreshed之前的一個(gè)回調(diào)
?使用步驟:
? ? ?1. 寫一個(gè)類,實(shí)現(xiàn)ApplicationContextInitializer接口
? ? ?2. 注冊(cè)ApplicationContextInitializer
?注冊(cè)方法:
? ? ?1. SpringApplication.addInitializers()
? ? ?2. 通過application.properties 配置context.initializer.classes=com.edu.spring.MyApplicationContextInitializer來進(jìn)行,可以指定多個(gè),多個(gè)用逗號(hào)隔開
? ? ?3. 通過spring.factories機(jī)制,(注冊(cè)listener監(jiān)聽器也可以使用這種方式)
下面詳細(xì)說明如何通過spring.factories實(shí)現(xiàn)。
新建項(xiàng)目initializer。
其中pom.xml內(nèi)容如下:
<?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.edu.spring</groupId><artifactId>initializer</artifactId><version>1.0-SNAPSHOT</version><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target></properties><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>2.1.4.RELEASE</version><scope>import</scope><type>pom</type></dependency></dependencies></dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency></dependencies></project>新建EchoApplicationContextInitializer.java
package com.edu.spring.initializer;import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext;public class EchoApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {@Overridepublic void initialize(ConfigurableApplicationContext configurableApplicationContext) {System.out.println("===EchoApplicationContextInitializer====");} }在resources下新建META-INF/spring.factories,內(nèi)容如下:
org.springframework.context.ApplicationContextInitializer=com.edu.spring.initializer.EchoApplicationContextInitializer然后在springcourse項(xiàng)目的pom中導(dǎo)入Initializer項(xiàng)目
<dependency><groupId>com.edu.spring</groupId><artifactId>initializer</artifactId><version>1.0-SNAPSHOT</version></dependency>運(yùn)行App.java,執(zhí)行結(jié)果如下:
bean count: 5 app name: org.springframework.context.annotation.AnnotationConfigApplicationContext@ca263c2 ===EchoApplicationContextInitializer====說明成功注入。
CommandLineRunner接口回調(diào)
在springboot項(xiàng)目中,新建ServerSuccessReport.java
package com.edu.spring;import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component;@Component public class ServerSuccessReport implements CommandLineRunner {@Overridepublic void run(String... args) throws Exception {System.out.println("=====應(yīng)用已經(jīng)成功啟動(dòng)=====");} }運(yùn)行結(jié)果如下:
bean count: 5 app name: org.springframework.context.annotation.AnnotationConfigApplicationContext@27c6e487 ===EchoApplicationContextInitializer==== 2019-04-26 15:33:13.441 INFO 1396 --- [ main] com.edu.spring.App : Starting App on DESKTOP-AQM2529 with PID 1396 (E:\git\springcourse\target\classes started by vincent in E:\git\springcourse) 2019-04-26 15:33:13.443 INFO 1396 --- [ main] com.edu.spring.App : No active profile set, falling back to default profiles: default 2019-04-26 15:33:14.150 INFO 1396 --- [ main] com.edu.spring.App : Started App in 1.013 seconds (JVM running for 1.577) =====應(yīng)用已經(jīng)成功啟動(dòng)=====從輸出結(jié)果中可以看到,在Started App啟動(dòng)之后,輸出CommandLineRunner接口方法。
CommandLineRunner ApplicationRunner接口是在容器啟動(dòng)成功后的最后一步的回調(diào)。類似開機(jī)自啟動(dòng)
使用步驟:
? ? 1. 寫一個(gè)類,實(shí)現(xiàn)CommandLineRunner接口
? ? 2. 把該類納入到Spring容器中
? ? 3. 可以通過@Order注解或者Ordered接口來控制執(zhí)行順序
如果有多個(gè)類實(shí)現(xiàn)了CommandLineRunner接口,如何保證執(zhí)行順序?
新建ServerStartedReport.java
package com.edu.spring;import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component;import java.time.LocalDateTime;@Component public class ServerStartedReport implements CommandLineRunner {@Overridepublic void run(String... args) throws Exception {System.out.println("=========應(yīng)用啟動(dòng)后的時(shí)間是:" + LocalDateTime.now().toString());} }運(yùn)行App.java,結(jié)果如下:
bean count: 5 app name: org.springframework.context.annotation.AnnotationConfigApplicationContext@7ff2a664 ===EchoApplicationContextInitializer==== 2019-04-26 15:44:03.081 INFO 2656 --- [ main] com.edu.spring.App : Starting App on DESKTOP-AQM2529 with PID 2656 (E:\git\springcourse\target\classes started by vincent in E:\git\springcourse) 2019-04-26 15:44:03.084 INFO 2656 --- [ main] com.edu.spring.App : No active profile set, falling back to default profiles: default 2019-04-26 15:44:03.775 INFO 2656 --- [ main] com.edu.spring.App : Started App in 1.0 seconds (JVM running for 1.426) =========應(yīng)用啟動(dòng)后的時(shí)間是:2019-04-26T15:44:03.784 =====應(yīng)用已經(jīng)成功啟動(dòng)=====如果我們想要ServerSuccessReport類先執(zhí)行,可以使用@Order注釋。
@Order(3) @Component public class ServerStartedReport implements CommandLineRunner {@Overridepublic void run(String... args) throws Exception {System.out.println("=========應(yīng)用啟動(dòng)后的時(shí)間是:" + LocalDateTime.now().toString());} } @Order(2) @Component public class ServerSuccessReport implements CommandLineRunner {@Overridepublic void run(String... args) throws Exception {System.out.println("=====應(yīng)用已經(jīng)成功啟動(dòng)=====");} }只要Order括號(hào)內(nèi)的數(shù)字越小,則越先執(zhí)行。
新建StartedApplicationRunner.java
package com.edu.spring;import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.stereotype.Component;import java.util.Arrays;@Component public class StartedApplicationRunner implements ApplicationRunner {@Overridepublic void run(ApplicationArguments args) throws Exception {System.out.println("應(yīng)用已經(jīng)啟動(dòng),參數(shù)為:" + Arrays.deepHashCode(args.getSourceArgs()));} }修改ServerSuccessReport.java的輸出
@Overridepublic void run(String... args) throws Exception {System.out.println("=====應(yīng)用已經(jīng)成功啟動(dòng)=====" + Arrays.asList(args));}App.java添加參數(shù):
@SpringBootApplication public class App {public static void main(String[] args) {SpringApplication application = new SpringApplication(App.class);//application.addInitializers(new MyApplicationContextInitializer());ConfigurableApplicationContext context = application.run("aa", "bb");context.stop();context.close();} }運(yùn)行App.java,結(jié)果如下:
bean count: 5 app name: org.springframework.context.annotation.AnnotationConfigApplicationContext@78a2da20 ===EchoApplicationContextInitializer==== 2019-04-26 16:04:07.136 INFO 1684 --- [ main] com.edu.spring.App : Starting App on DESKTOP-AQM2529 with PID 1684 (E:\git\springcourse\target\classes started by vincent in E:\git\springcourse) 2019-04-26 16:04:07.138 INFO 1684 --- [ main] com.edu.spring.App : No active profile set, falling back to default profiles: default 2019-04-26 16:04:07.832 INFO 1684 --- [ main] com.edu.spring.App : Started App in 0.994 seconds (JVM running for 1.458) =====應(yīng)用已經(jīng)成功啟動(dòng)=====[aa, bb] =========應(yīng)用啟動(dòng)后的時(shí)間是:2019-04-26T16:04:07.844 應(yīng)用已經(jīng)啟動(dòng),參數(shù)為:[aa, bb]CommandLineRunner ApplicationRunner區(qū)別:
區(qū)別在于方法的參數(shù)不一樣,CommandLineRunner的參數(shù)為最原始參數(shù),沒有做任何處理,ApplicationRunner的參數(shù)是ApplicationArguments,是對(duì)原始參數(shù)做了進(jìn)一步的封裝。
進(jìn)一步說明ApplicationArguments的作用。
修改App.java
@SpringBootApplication public class App {public static void main(String[] args) {SpringApplication application = new SpringApplication(App.class);//application.addInitializers(new MyApplicationContextInitializer());ConfigurableApplicationContext context = application.run(args);ApplicationArguments arguments = context.getBean(ApplicationArguments.class);System.out.println(arguments.getSourceArgs().length);System.out.println(arguments.getOptionNames());System.out.println(arguments.getOptionValues("myname"));context.stop();context.close();} }修改Intellij 的運(yùn)行參數(shù),Program arguments 為 --myname admin,運(yùn)行輸出結(jié)果如下:
bean count: 5 app name: org.springframework.context.annotation.AnnotationConfigApplicationContext@6a79c292 ===EchoApplicationContextInitializer==== 2019-04-26 16:19:12.343 INFO 8216 --- [ main] com.edu.spring.App : Starting App on DESKTOP-AQM2529 with PID 8216 (E:\git\springcourse\target\classes started by vincent in E:\git\springcourse) 2019-04-26 16:19:12.346 INFO 8216 --- [ main] com.edu.spring.App : No active profile set, falling back to default profiles: default 2019-04-26 16:19:13.001 INFO 8216 --- [ main] com.edu.spring.App : Started App in 0.959 seconds (JVM running for 1.401) =====應(yīng)用已經(jīng)成功啟動(dòng)=====[--myname, admin] =========應(yīng)用啟動(dòng)后的時(shí)間是:2019-04-26T16:19:13.010 應(yīng)用已經(jīng)啟動(dòng),參數(shù)為:[--myname, admin] 2 [myname] []修改Intellij 的運(yùn)行參數(shù),Program arguments 為 --myname=admin,運(yùn)行輸出結(jié)果如下:
bean count: 5 app name: org.springframework.context.annotation.AnnotationConfigApplicationContext@78a2da20 ===EchoApplicationContextInitializer==== 2019-04-26 16:20:08.312 INFO 11652 --- [ main] com.edu.spring.App : Starting App on DESKTOP-AQM2529 with PID 11652 (E:\git\springcourse\target\classes started by vincent in E:\git\springcourse) 2019-04-26 16:20:08.315 INFO 11652 --- [ main] com.edu.spring.App : No active profile set, falling back to default profiles: default 2019-04-26 16:20:08.956 INFO 11652 --- [ main] com.edu.spring.App : Started App in 0.939 seconds (JVM running for 1.469) =====應(yīng)用已經(jīng)成功啟動(dòng)=====[--myname=admin] =========應(yīng)用啟動(dòng)后的時(shí)間是:2019-04-26T16:20:08.961 應(yīng)用已經(jīng)啟動(dòng),參數(shù)為:[--myname=admin] 1 [myname] [admin]ApplicationArguments是對(duì)參數(shù)(main方法),做了進(jìn)一步處理
可以解析 --name=value的,我們就可以通過name來獲取value。如果我們用原始的方法進(jìn)行獲取參數(shù)時(shí),還得需要對(duì)參數(shù)進(jìn)行分割。
Springboot補(bǔ)充講解
我們知道@SpringBootApplication注解有三個(gè)注解所組成的,分別是:@SpringBootConfiguration @EnableAutoConfiguration @ComponentScan
在com.edu.spring.springboot包下面新建App.java
package com.edu.spring.springboot;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext;@SpringBootApplication public class App {public static void main(String[] args) {SpringApplication application = new SpringApplication(App.class);ConfigurableApplicationContext context = application.run(args);System.out.println(context.getBean(Runnable.class));context.close();} }在com.edu.spring.bean包下新建RunnableConfiguration.java
package com.edu.spring.bean;import org.springframework.boot.SpringBootConfiguration; import org.springframework.context.annotation.Bean;@SpringBootConfiguration public class RunnableConfiguration {@Beanpublic Runnable createRunnable(){return ()->{};}}運(yùn)行App.java輸出結(jié)果如下:
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'java.lang.Runnable' availableat org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:343)at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:335)at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1123)at com.edu.spring.springboot.App.main(App.java:13)說明沒有成功注入。原因是@SpringBootApplication掃描的是當(dāng)前包和子包下面的所有類。但是同一級(jí)的包是無法掃描到的。可以通過basePackage來指定。修改App.java如下:
package com.edu.spring.springboot;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext;@SpringBootApplication(scanBasePackages = "com.edu.spring") public class App {public static void main(String[] args) {SpringApplication application = new SpringApplication(App.class);ConfigurableApplicationContext context = application.run(args);System.out.println(context.getBean(Runnable.class));context.close();} }這樣就成功注入了。
排除某些類,方法如下:
修改pom.xml,添加gson依賴。
<dependency><groupId>com.google.code.gson</groupId><artifactId>gson</artifactId></dependency>因?yàn)間son依賴,已經(jīng)在springboot父類中定義,所以不需要指定version。
修改RunnableConfiguration.java
@Beanpublic Gson createGson(){return new Gson();}修改App.java
System.out.println(context.getBean(Gson.class));輸出結(jié)果正常。說明成功注入進(jìn)去。
排除Gson的bean。修改App.java
@SpringBootApplication(scanBasePackages = "com.edu.spring", exclude = GsonAutoConfiguration.class)沒有排除掉,目前這有問題。
排除指定類、配置類,exclude:根據(jù)class來排除,excludeName:根據(jù)class name來排除。
自定義Banner方法
我們每次輸出的時(shí)候都會(huì)輸出spring的Banner:
. ____ _ __ _ _/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \\\/ ___)| |_)| | | | | || (_| | ) ) ) )' |____| .__|_| |_|_| |_\__, | / / / /=========|_|==============|___/=/_/_/_/如何控制不輸出呢?
修改App.java如下:
SpringApplication application = new SpringApplication(App.class); application.setBannerMode(Banner.Mode.OFF);這樣就不輸出Spring的Banner了。
還可以自定義Banner,在resources目錄下新建banner.txt
然后就可以執(zhí)行成功了。注意不要把Banner.Mode.OFF開啟。
第二種方式自定義Banner的方法是,在application.properties文件中寫文件路徑。支持圖片的Banner,圖片的格式支持jpg,png,gif
spring.banner.location=banner2.txt spring.banner.image.location=banner2.jpg讀取application.properties時(shí)如果沒有key則獲取默認(rèn)的value,方法如下:
方法一:在@value注釋上使用默認(rèn)的value
@Value(("${server.host:localhost}"))private String serverHosts;方法二:使用getProperty方法:
System.out.println(context.getEnvironment().getProperty("server.host2","localhost2"));spring boot 運(yùn)行流程分析
spingboot的執(zhí)行入口有兩個(gè)分別是:
// 實(shí)例化SpringApplication對(duì)象,然后調(diào)用run方法 SpringApplication application = new SpringApplication(App.class); ConfigurableApplicationContext context = application.run(args); // 直接調(diào)用靜態(tài)run方法(內(nèi)部轉(zhuǎn)換成第一種調(diào)用方式) ConfigurableApplicationContext context = SpringApplication.run(App.class, args);? 運(yùn)行流程
? ?1. 判斷是否是web環(huán)境
? ?2. 加載所有classpath下面的META-INF/spring.factories, ?ApplicationContextInitializerr
? ?3. 加載所有classpath下面的META-INF/spring.factories, ApplicationListener
? ?4. 推斷main方法所在的類
? ?5. 開始執(zhí)行run方法
? ?6. 設(shè)置java.awt.headless系統(tǒng)變量
? ?7. 加載所有classpath下面的META-INF/spring.factories SpringApplicationRunListener
? ?8. 執(zhí)行所有SpringApplicationRunListener的started方法
? ?9. 實(shí)例化ApplicationArguments對(duì)象
? ?10. 創(chuàng)建Environment
? ?11. 配置Environment,主要是把run方法的參數(shù)配置到Environment
? ?12. 執(zhí)行所有SpringApplicationRunListener的environment.prepared方法
? ?13. 如果不是web環(huán)境,但是是web的Environment,則把web的Environment轉(zhuǎn)換成標(biāo)準(zhǔn)的Environment
? ?14. 輸出Banner
? ?15. 初始化applicationContext,如果是web環(huán)境,則實(shí)例化AnnotationConfigEmbeddedWebApplicationContext對(duì)象,否則實(shí)例化AnnotationConfigApplicationContext對(duì)象
? ?16. 如果beanNameGenerator不為空,就把beanNameGenerator對(duì)象注入Context里面去
? ?17. 回調(diào)所有的ApplicationContextInitializer方法
? ?18. 執(zhí)行所有SpringApplicationRunListener的contextPrepared方法
? ?19. 依次往Spring容器中注入:ApplicationArguments, Banner
? ?20. 加載所有的源到context里面去。
? ?21. 執(zhí)行所有SpringApplicationRunListener的contextLoaded方法
? ?22. 執(zhí)行context的refresh方法,并且調(diào)用context的registerShutdownHook方法
? ?23. 回調(diào),獲取容器中所有的ApplicationRunner, CommandLineRunner接口,然后排序,依次調(diào)用
? ?24. 執(zhí)行所有SpringApplicationRunListener的finished的finished方法
總結(jié)
以上是生活随笔為你收集整理的springboot教程(一)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: docker下部署Tomcat运行war
- 下一篇: springboot教程-web(二)