SpringBoot進階


沒想到把,SpringBoot雖然簡化了開發流程,但要學的東西還有很多

1. Spring Boot簡介

  • 採用約定大於配置,簡化Spring開發步驟與複雜的部署流程
  • 快速創立可獨立運行的Spring項目以及集成主流框架
  • 嵌入式Servlet容器,無需打war包
  • starter自動依賴與版本控制
  • 大量的自動配置,可修改默認值
  • 需要xml,無程式碼生成,開箱即用
  • 准生產環境的運行時應用監控
  • 與其他框架天然集成
  • 整合Spring技術棧的大框架

2. 入門解析

1.創建maven工程jar

2.導入父項目與依賴

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.9.RELEASE</version>
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

3.創建主程式

// 標註主程式類,說明是Springboot應用
@SpringBootApplication
public class HelloWorld {

    public static void main(String[] args) {

        // 讓Spring應用啟動,需要傳入主程式類,和其參數
        SpringApplication.run(HelloWorld.class,args);

    }
}

4.編寫controller

@RestController
public class HelloController {

    @RequestMapping(value = "/hello")
    public String hello(){
        return "hello world";
    }
}

5.測試

來到主程式運行main方法

6.打包部署(記住名字不能有空格)

<!--  將應用打包成可執行jar包的插件,package命令  -->
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

7.分析pom.xml

spring-boot-starter-parent:還有一個父項目spring-boot-dependencies,裡面規定了依賴版本號:

<屬性標籤s>
<properties>
		<!-- Dependency versions -->
		<activemq.version>5.14.5</activemq.version>
		<antlr2.version>2.7.7</antlr2.version>
		<appengine-sdk.version>1.9.59</appengine-sdk.version>
		<artemis.version>1.5.5</artemis.version>
		<aspectj.version>1.8.13</aspectj.version>
		。。。。。
</properties>		

8.導入的依賴

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

spring-boot-starter-web:springBoot的場景啟動器,裡面很多依賴如:spring-web、spring-webmvc、jackson、hibernate-validator、spring-boot-starter-tomcat、spring-boot-starter

場景啟動器:將功能場景抽取出來,做成starters啟動器,只要項目中導入對應的啟動器,那麼相關場景的依賴就會自動導入

9.主程式類,入口類

@SpringBootApplication
public class HelloWorld {

    public static void main(String[] args) {

        // 讓Spring應用啟動,需要傳入主程式類,和其參數
        SpringApplication.run(HelloWorld.class,args);
    }
}

@SpringBootApplication:說明是Springboot的主配置類,那麼就會運行main方法來啟動應用

10.@SpringBootApplication的內部註解

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)

@SpringBootConfiguration:表示配置類,配置類也是容器的一個組件即@Component,(內部是用Spring的@Configuration)

11.@EnableAutoConfiguration:開啟自動配置功能,其內部又有:

@AutoConfigurationPackage
@Import({EnableAutoConfigurationImportSelector.class})

12.@AutoConfigurationPackage:將主配置類@SpringBootApplication標註的類及同級下面所有子包所有子包組件掃描

內部是@Import({Registrar.class}),spring的導入組件註解,Registrar.class內部有個方法

// 註冊bean的定義資訊,即導組件
// metadata註解標註的原資訊
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
            AutoConfigurationPackages.register
            (registry,
            // 這裡可以獲取主配置類的上級包名
            (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
}

13.@Import({EnableAutoConfigurationImportSelector.class}):

導入組件的選擇器EnableAutoConfigurationImportSelector.class,內容是:

// 內部繼承父類,父類中有個方法
// 將所需導入的組件以全類名的方式返回,組件會被導入容器中
// 方法內部會導入非常多的自動配置類xxxAutoConfiguration,就是導入場景所需的全部組件,並配置好
public String[] selectImports(AnnotationMetadata annotationMetadata) {
    XXXX
}

有了自動配置類,就免去我們手動配置的麻煩,SpringBoot啟動時,從類路徑下spring-boot-autofigure包中的META-INF/spring.factories中獲取EnableAutoConfiguration的值(各種配置類的全限定類名),作為自動配置類導入容器中,那麼自動配置類生效幫我們自動配置,其真正配置類也在這個包下

3. 快速創建Spring應用(創建嚮導)

Spring Initializer

  • 主程式生成好了
  • resources中包括
    • static保存所有的靜態資源
    • templates保存模板頁面
    • application.properties:springboot的配置文件,這裡可修改默認的

4. 配置文件(名字固定)

  • application.properties(默認使用,優先順序高)
  • application.yml

4.1 yml格式

server:
  port: 8080
	
	
	
"" : 特殊字元不會轉義
'' : 特殊字元會轉義


對象、Map:
friends:
  lastName: howl
  age: 20
  
friends: {lastName: howl,age: 20}


數組:
pets:
  - cat
  - dog
  
pets: [dog,cat,pig]

4.2 配置文件yml獲取組件的值

// 組件必須的
@Component
@ConfigurationProperties(prefix = "person") // 默認從主配置文件獲取
public class Person{
    private int age;
    xxxxxx
}
person:
  age: 20
  name: Howl
<!--    導入配置文件處理器,配置文件yml綁定屬性時會有提示    -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>

properties也一樣

亂碼:idea使用utf-8,要在編譯器裡面設置,編譯轉換ascii碼

person.age=20
person.name=哈哈哈

4.3 從配置文件中獲取值

@Value(Spring註解)

4.4 讀取外部文件與xml配置文件

@PropertySource(value = "classpath:person.yml")
@ImportResources(locations = {"classpath:bean.xml"}) //讀取是spring的<bean>標籤的文件,放在主程式入口處

4.5 註解實現配置類

@Configuration
public class Myconfig {

    @Bean
    public HelloWorld helloService(){
        return new HelloWorld();
    }
}

4.6 配置文件隨機數與佔位符

${random.value}
person.age=20
person.name=${person.age:默認值}_Howl

4.7 多環境支援

4.7.1 多profile文件

主配置文件編寫時,文件規定可以是 application-{profile}.properties/yml

eg:配置文件命名為:
application-dev.properties
application-prod.properties

主配置文件中加入:
spring.profiles.active=dev激活dev的配置文件

4.7.2 yml文檔塊

server:
  port: 8081
spring:
  profiles:
    active: dev
---

server:
  port: 8082
spring:
  profiles: dev
---
server:
  port: 8083
spring:
  profiles:prod

4.7.3 配置文件載入位置

application.properties
application.yml

優先順序由高到低,優先採用高的,但也會互補
/config
/
classpath:/config
classpath:/

5. 自動配置原理

1)SpringBoot啟動的時候載入主配置類,開啟了自動配置功能:@EnableAutoConfiguration

2)@EnableAutoConfiguration 作用:(去aotuconfigure包的META-INF中獲取全限定類名,再將對應的類載入進來)

  • 利用@Import的 AutoConfigurationImportSelector.class選擇器給容器導入一些組件
  • 這個選擇器中的selectImports()方法負責載入

3)每一個載入的自動配置類進行自動配置功能

4)舉例一個配置類的內部的註解(根據不用條件判斷配置類是否生效)

@Configuration
4.1 @EnableConfigurationProperties({CacheProperties.class})  // .class是個properties映射成bean對象(但頭部沒有加@Component註解),要使其生效即要加入bean容器
@ConditionalOnClass({CacheManager.class}) // 內部是Spring註解@Conditional,滿足條件配置類生效
其還有有參構造器,將對應的XXXproerties注入到內部變數中:

4.1)所有在配置i文件能配置的屬性都是在XXXProperties類中封裝著,配置文件能配置什麼,就看對應的XXXProperties

5)所以我們能在主配置文件中配置什麼,完全是是看xxxproperties的

Debug模式

# 主配置文件中
# 開啟SpringBoot的debug模式,會顯示啟用的自動配置類
debug=true

6. 日誌框架

日誌抽象層(類似於JDBC) 日誌實現
SLF4j、jbossing-logging、JCL(Commons-logging) JUL(Java.util.logging)、Log4j、Log4j2、Logback

Spring框架默認JCL(導包common-logging)

SpringBoot選用SLF4j和logback

應該調用日誌抽象層

使用

導入SLF4j抽象層和Logback實現類

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HelloWorld {
  public static void main(String[] args) {
    Logger logger = LoggerFactory.getLogger(HelloWorld.class);
    logger.info("Hello World");
  }
}
Logger logger = LoggerFactory.getLogger(this.getClass());

logger.trace("這時trace日誌");
logger.debug("這時debug日誌");
//日誌級別
logger.info("這時info日誌");
logger.warn("這時warn日誌");
logger.error("這時error日誌");

查看默認配置

去springboot第一個包中查看properties文件,
去去springboot-autoConfiguration看配置類

高級特性

自己寫的logback.xml中可以加
<springProfile name="dev">

</springProfile name="dev">
表示只在某個環境生效

7. Web開發

eg:資料庫jdbc開發:去Springboot- autoConfigtuation包下看DataSourceAutoConfiguration,頭上有註解@EnableConfigurationProperties({DataSourceProperties.class})開啟properties映射對象生效,DataSourceProperties是個映射properties的bean類

我們需要熟悉自動配置原理,然後才可以很好地書寫配置文件

靜態資源的映射規則

在webMvcAutoConfiguration

public void addResourceHandlers(ResourceHandlerRegistry registry) {
    if (!this.resourceProperties.isAddMappings()) {
        logger.debug("Default resource handling disabled");
    } else {
        Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
        CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
        if (!registry.hasMappingForPattern("/webjars/**")) {
        
        // Webjars/**下的資源請求都去 classpath:/META-INF/resources/webjars/ 找資源
        
            this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{"/webjars/**"}).addResourceLocations(new String[]{"classpath:/META-INF/resources/webjars/"}).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl));
        }

        String staticPathPattern = this.mvcProperties.getStaticPathPattern();
        if (!registry.hasMappingForPattern(staticPathPattern)) {
            this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{staticPathPattern}).addResourceLocations(WebMvcAutoConfiguration.getResourceLocations(this.resourceProperties.getStaticLocations())).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl));
        }

    }
}

webjars:以jar包方式引入靜態資源(juery、BootStrap等打包成jar,用maven導入)

訪問Webjars/**下的資源請求都去 classpath:/META-INF/resources/webjars/ 找資源,

導包後直接寫名字訪問就行eg: /webjars/jquery

1)addResourceHandlers

java哪些編寫程式碼的文件和資源文件夾下的文件編譯後都放在target的classes下,classes才是類路徑。根路徑是個特例,不在資源文件夾下,但編譯後放在classes內

  1. /** 默認去classpath找;Springboot的resourcers是默認的classpath
"classpath:/META-INF/resources/",
"classpath:/resources/",
"classpath:/static/",
"classpath:/public/"
"/": 當前項目的根路徑,

3)歡迎頁配置

歡迎頁;靜態資源文件夾下的所有Index

@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext, FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
    WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(new TemplateAvailabilityProviders(applicationContext), applicationContext, this.getWelcomePage(), this.mvcProperties.getStaticPathPattern());
    welcomePageHandlerMapping.setInterceptors(this.getInterceptors(mvcConversionService, mvcResourceUrlProvider));
    return welcomePageHandlerMapping;
}


// 方法引用在靜態資源下找index.html,那個方法引用不用看了
return Arrays.stream(locations).map(this::getIndexHtml).filter(this::isReadable).findFirst();

所以: localhost:8080/ 默認找資源類路徑下的 index.html文件

圖標也一樣;靜態資源下找 /favicon.ico

8. 模板引擎thymeleaf

1)加入依賴

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

資源放在classpath:/templates/下,就自動渲染html

2)導入thymeleaf的名稱空間、有語法提示

<html lang="en" xmlns:th="//www.thymeleaf.org">

3)語法

th:text
th:each   這個標籤每次遍歷都會生成
${} 獲取值
*{}

9. SpringMVC 自動配置

  • 配置了ViewResolver視圖解析器
  • 配置了webjars解析
  • support 靜態資源文件路徑、webjars
  • support 靜態首頁訪問 index.html
  • support favicon.ico
  • 自動註冊了Converter、Formatter的beans
  • Support th:each

10. 修改SpringBoot的默認配置

模式:

1)自動配置組件時(組合使用,互補),先看容器中有沒有用戶自己配置的@Component,@Bean,如果有就用用戶配置,如果沒有才自己創建自動配置

@ConditionalOnMissingBean({FormContentFilter.class})
public OrderedFormContentFilter formContentFilter() {
return new OrderedFormContentFilter();
}

2)擴展SpringMVC(eg:Interceptors、formatters)

編寫一個配置類加上註解@Configuration,實現WebMvcConfigurer介面要重寫的配置即可(介面有方法體了),這裡不能加@EnableWebMvc(加了就是自己的生效,自動配置失效)

原理

在做其他自動配置時回導入 EnableWebMvcConfiguration.class,其父類是重點

public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
}



public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
    private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();

	// 從容器中獲取所有的WebMvcConfigurer就是自己寫的全託管配置類,然後把其賦值內部的configurers
    @Autowired(
        required = false
    )
    public void setConfigurers(List<WebMvcConfigurer> configurers) {
        if (!CollectionUtils.isEmpty(configurers)) {
            this.configurers.addWebMvcConfigurers(configurers);
        }
    }
    
    // 舉例剛才的視圖解析器,這方法添加視圖映射
    protected void addViewControllers(ViewControllerRegistry registry) {
        this.configurers.addViewControllers(registry);
    }
    
    // 進去configurers.addViewControllers(registry);
    // delegates內部遍歷,注入類WebMvcConfigurer
    // 把自己配的WebMvcConfigurer遍歷裡面的viewController全調用起作用
    // 這裡遍歷就包括了自己覆蓋的,已經沒有覆蓋的
    public void addViewControllers(ViewControllerRegistry registry) {
    	Iterator var2 = this.delegates.iterator();

        while(var2.hasNext()) {
            WebMvcConfigurer delegate = (WebMvcConfigurer)var2.next();
            delegate.addViewControllers(registry);
        }
    }
}

3)全面接管@EnableWebMvc,所有都是我們自己配

原理就是@EnableWebMvc註解裡面導入了WebMvcConfiguration.class類

而我們的總的Mvc自動配置類上要先判斷,否則不生效
ConditionOnMissingBean(WebMvcConfiguration.class)

4)SpringBoot中會有很多很多的xxxCustomizer幫助我們進行訂製配置

11. 攔截器實現登錄攔截

實現 HandlerInterceptor 介面,並在自己的配置類中註冊

public class LoginHandlerInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
        HttpSession session = request.getSession();
        Object user = session.getAttribute("user");
        if(user == null){
            request.getRequestDispatcher("/").forward(request,response);
            return false;
        }
        return true;
    }
}
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 已經boot做好了靜態資源放行
        registry.addInterceptor(new LoginHandlerInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/","/user/login");

    }

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        // 瀏覽器發送/howl請求,也來到success頁面
        registry.addViewController("/howl").setViewName("success");
    }
}

12. Restful

普通 Restful
查詢 /user/get /user –GET
添加 /user/create /user –POST
修改 /user/update /user/{id} –PUT
刪除 /user/delete /user/{id} –DELETE

實際操作

URI: /資源名稱/資源表示 HTTP請求方式區分對資源的CURD

請求url 請求方式
查詢所有用戶 /users GET
查詢某個用戶 /user/{id} GET
添加用戶 /user POST
修改用戶 /user PUT
刪除用戶 /user{id} DELETE
// 查詢所有
@GetMapping(value = "/users")
public String list(){
    return employeeDao.getAll().toString();
}

// 查詢單個
@GetMapping(value = "/user/{id}")
public String listById(@PathVariable(value = "id") Integer id){
    return id + " 單個用戶查詢";
}

// 添加
@PostMapping(value = "/user")
public String addUser(){

    return "這裡是添加員工";
}

@PutMapping(value = "/user")
public String updateUser(){
    return "這裡是put更新用戶";
}

@DeleteMapping(value = "/user/{id}")
public String deleteUser(@PathVariable(value = "id") Integer id){
    return "這裡是刪除用戶" + id;
}

13. 錯誤處理

1)錯誤頁面

錯誤處理的自動配置:ErrorMvcAutoConfiguration

給容器添加了

DefaultErrorAttributes、

BasicErrorController:處理默認的/error請求

ErrorPageCustomizer:系統出現錯誤來到error請求進行處理

步驟:一旦系統出現4xx或5xx的錯誤,ErrorPageCustomizer會生效(訂製錯誤的相應規則)

2)訂製錯誤的json數據

@RestControllerAdvice
public class MyEceptionHandler {

    @ExceptionHandler(UserException.class)
    public String HandleException(Exception e){
        return e.getMessage();
    }
}

14. 嵌入式Servlet容器

SpringBoot默認使用的是嵌入式的Servlet容器(Tomcat)

1)訂製和修改Servlet容器的相關配置

server.port=8081
server.servlet.context-path=/curd

server.tomcat.uri-encoding=UTF-8

# 通用的Servlet容器設置
server.xxx
server.tomcat.xxx

編寫一個WebServerFactoryCustomizer:嵌入式的Servlet容器的訂製器,來修改Servlet容器的配置

@Bean
public WebServerFactoryCustomizer<ConfigurableWebServerFactory> webServerFactoryCustomizer() {
     
     // 其實就是返回本類的實現類給他
	return new WebServerFactoryCustomizer<ConfigurableWebServerFactory>() {
	
        @Override
        // 傳參是一個容器
        public void customize(ConfigurableWebServerFactory factory) {
            //設置tomcat的埠號
            factory.setPort(9091);
        }
     };
}

重點

2)註冊Servlet、Filter、Listener

ServletRegistrationBean

FilterRegistrationBean

SerlvetListenerRegistrationBean

Springboot默認是以jar包方式啟動嵌入式的Servlet容器來啟動應用,沒有web.xml文件

編寫Servlet

public class MySerlvet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().write("Hello World");
    }
}

使用官方提供的註冊

package com.howl.springboot.config;

@Configuration
public class MyServerConfig {

    @Bean
    public ServletRegistrationBean myServlet(){
        return new ServletRegistrationBean(new MySerlvet(),"/myServlet");
    }

    @Bean
    public FilterRegistrationBean myFilter(){
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        filterRegistrationBean.setFilter(new MyFileter());
        filterRegistrationBean.setUrlPatterns(Arrays.asList("/myServlet"));
        return filterRegistrationBean;
    }

    @Bean
    public ServletListenerRegistrationBean myListener(){
        return  new ServletListenerRegistrationBean<MyListener>(new MyListener());
    }
}

重點:使用其他的嵌入式Servlet容器(上面11點有提及怎麼配置)

Jetty(長連接),聊天

Undertow(不支援JSP),非阻塞的,並發性能好

在pom.xml中,spring-boot-starter-web中默認加了spring-boot-starter-tomcat,所以我們要去除他,在加上spring-boot-starter-jetty

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>

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

1) 嵌入式Servlet容器自動配置原理

autoConfigtuar.web.embedded下有個EmbeddedWebServerFactoryCustomizerAutoConfiguration(嵌入式容器工廠的訂製器自動配置),其內部是有各種@ConditionalOnClass({Tomcat.class, UpgradeProtocol.class})條件判斷,看容器中有哪個容器類類型才生效哪個配置返回一個TomcatWebServerFactoryCustomizer(Tomcat容器的訂製器,傳入ServerProperties.class)

注意:EmbeddedWebServerFactoryCustomizerAutoConfiguration(嵌入式容器工廠的訂製器自動配置)

有@EnableConfigurationProperties({ServerProperties.class})註解,即與映射的配置文件綁定,即主配置文件中可修改

自動配置類中以tomcat為例

@Configuration
@ConditionalOnClass({Tomcat.class, UpgradeProtocol.class})
public static class TomcatWebServerFactoryCustomizerConfiguration {
	
	public TomcatWebServerFactoryCustomizerConfiguration() {}

	@Bean
	public TomcatWebServerFactoryCustomizer tomcatWebServerFactoryCustomizer(Environment environment, ServerProperties serverProperties) {
	
			// 將訂製器返回,下面進入訂製器
            return new TomcatWebServerFactoryCustomizer(environment, serverProperties);
        }
    }

2)嵌入式容器啟動原理

@SpringBootApplication
public class SpringbootWebApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootWebApplication.class, args);
    }

}

SpringApplication類run方法中
先: context = this.createApplicationContext();創建IOC容器(有其他類型,舉例WebApplication應用就創建WebApplication容器,下面有說明)
再this.refreshContext(context)刷新容器(這裡就創建嵌入式容器,包括各種bean對象)

refreshContext一直往裡走有這個方法,給子容器來實現

this.onRefresh();

// 而這個方法是給子容器實現的
protected void onRefresh() throws BeansException {
}

子容器重寫該方法:就會創建嵌入式的Servlet容器

子容器的抽象類下面的實現類,Tomcat是使用SerlvetWebServerApplicaitonContext

總得來說:SpringBoot啟動時,根據主程式入口的類類型來創建相應的容器,然後刷新容器refresh()(創建各種bean對象),此時也是創建嵌入式容器的。相關容器的子類實現類中,onRefresh方法實現了(方法中調用this.createWebServer()來創建並返回ServletWebServerFactory,再根據工廠來獲取嵌入式容器)

ServletWebServerFactory factory = this.getWebServerFactory(); //內部其實就是從IOC容器中獲取這個組件

IOC容器啟動的時候會創建嵌入式容器

深入看看創建容器(createApplicationContext),簡化

protected ConfigurableApplicationContext createApplicationContext() {
    
    Class<?> contextClass = this.applicationContextClass;
    
    switch (this.webApplicationType) {
	case SERVLET:
			contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
		break;
		
	case REACTIVE:
             contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
         break;
                
    default:
            contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");
            }
        } 
    return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}

15. 使用外置的Servlet容器

嵌入式簡單便捷、優化訂製比較複雜,使用訂製器或properties等來改變

外置的Serlvlet容器:外面安裝Tomcat伺服器–應用打包war包打包

  1. 必須創建war包
  2. 將嵌入式Tomcat指定為provided
  3. 必須編寫一個ServletInitializer,並調用configure方法
  4. 啟動伺服器

外置Servlet容器的啟動原理,

jar包:執行Spring主類的main方法,啟動IOC容器,過程中創建Servlet容器

war包:啟動伺服器,伺服器啟動Springboot應用(ServletInitializer),接著上面的流程

servlet3.0中有個規範:

規則:

  1. 伺服器啟動會創建當前web應用裡面每一個jar包裡面的ServlerConttaininerInitializer實例
  2. ServlerConttaininerInitializer的實現放在jar包的META-INF/services文件夾下,有個文件內容是指向ServlerConttaininerInitializer的實現類的全限定類名
  3. 使用註解@handlesTypes,在應用啟動的時候載入我們感興趣的類

流程

1)啟動Tomcat

2)按照規則就會去創建jar下的實例,規則1

3)ServlerConttaininerInitializer將註解標註的類創建實例

4)每一個SpringBootServletInitializer就是多出來的ServletInitializer類的父類被創建,然後調用configure方法,其參數是主程式類,內部使用builder創建Spring應用最後run啟動了

16. SpringBoot與數據訪問

導入依賴

整合基本JDBC,在主配置文件中加入即可,默認使用class com.zaxxer.hikari.HikariDataSource數據源

spring.datasource.username=root
spring.datasource.password=
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

自動配置包下jdbc.DataSourceConfiguration中,默認支援:dbcp2、hikari、tomcat。還可以看到一個屬性:spring.datasource.type

17. 整合druid數據源

引入依賴

<!-- //mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.21</version>
</dependency>

<!-- 德魯伊依賴log4j我有什麼辦法 -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

配置數據源具體數值

spring:
  datasource:
    username: root
    password:
    url: jdbc:mysql://127.0.0.1:3306/spring?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useAffectedRows=true
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource

    maxActive: 20
    initialSize: 1
    maxWait: 60000
    minIdle: 1
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 30000
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true
    maxOpenPreparedStatements: 20

    filters: stat,wall,log4j

因為自己加的druid是第三方數據源,所以要自己配置上去

@Configuration
public class DruidConfig {

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

    // 配置監控
    // 1.配置管理後台的Servlet
    // 2.配置一個監控的filter
    @Bean
    public ServletRegistrationBean statViewServlet(){
        ServletRegistrationBean bean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");
        Map<String,String> map = new HashMap<>();
        map.put("loginUsername", "root");
        map.put("loginPassword", "root");
        map.put("allwo","");

        bean.setInitParameters(map);
        return bean;
    }

    @Bean
    public FilterRegistrationBean WebStatFilter(){
        FilterRegistrationBean<Filter> bean = new FilterRegistrationBean<>(new WebStatFilter());
        Map<String,String> map = new HashMap<>();
        map.put("exclusions","*.js,*.css,/druid/*");
        bean.setUrlPatterns(Arrays.asList("/*"));
        bean.setInitParameters(map);
        return bean;
    }
}

18. 整合Mybatis

嚮導開發,自動導入依賴

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.2</version>
</dependency>

搞定數據源Druid,省略

註解版

@Mapper
public interface DepartmentMapper {

    @Select("SELECT * FROM department WHERE id=#{id}")
    public Department getDeptById(Integer id);

    @Delete("DELETE FROM department WHERE id=#{id}")
    public int deleteById(Integer id);

    @Options(useGeneratedKeys = true,keyProperty = "id")
    @Insert("INSERT INTO department (`departmentName`) VALUES (#{departmentName})")
    public int insertDept(Department department);

    public int updateDept(Department department);
}
@RestController
public class DeptController {

    @Autowired
    DepartmentMapper departmentMapper;

    @GetMapping("/dept/{id}")
    public Department getDeptment(@PathVariable("id") Integer id){
        return departmentMapper.getDeptById(id);
    }

    @GetMapping("/dept")
    public Department insertDept(Department department){
        departmentMapper.insertDept(department);
        return department;
    }
}

主程式入口

@MapperScan(value = "com.howl.springboot.mapper")

重點配置文件(要在主配置文件中配置)

mybatis:
  config-location: classpath:mybatis/mybatis-config.xml
  mapper-locations: classpath:mybatis/mapper/*

19. 最後一點SpringBoot啟動原理

  1. 創建SpringApplication對象
保存主配置類
判斷是否web應用
從類路徑下找applicationContextInitializer然後保存起來
從類路徑下找applicationContextListener然後保存起來
從多個配置類中找到main方法的主配置類

  1. 運行run方法
獲取SpringapplicationRunListeners:從類路徑下META-INF下spring.factories
回調所有的獲取獲取SpringapplicationRunListeners.starting()方法
封裝命令行參數
準備環境:IOC環境,eg:profile
	創建環境後回調SpringapplicationRunListeners.environmentprepared():表示環境準備完成
創建IOC容器
準備上下文:
	environment保存到IOC中
	而且要執行applyInitalizers()方法(上面創建應用的時候就拿到了所有的Initializer),回調裡面全部方法
	回調所有的Listeners的contextPrepared();
	回調所有的Listeners的contextLoaded()方法
	刷新容器:IOC容器初始化,載入組件(配置類、@bean),還有嵌入式容器
	從IOC獲取所有的ApplicationRunner和CommandRunner

參考來自

//www.bilibili.com/video/BV1bb411A7bD

Tags: