微服務以前也在用,但是沒有深入去學習,今年面試時候很多公司都在問會不會使用微服務架構,微服務架構面試問題回答的不好,所以現在記錄下springboot框架基礎學習。
一、基礎學習
1,首先建立一個springboot項目,使用intellij idea 來快速創建一個springboot項目
2、點擊下一步
3、在下一步,這裏使用的是springboot 2.1.4版本。點擊next就創建好一個springboot項目了。
4、我這裏創建後項目結構如下,並添加了多個子模塊。
先看看父類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>org.learn</groupId>
<artifactId>learn</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>learn</name>
<modules>
<module>learn_spring_boot_demo</module>
<module>learn_spring_boot_drools</module>
<module>learn_spring_boot_mybatis_demo</module>
<module>learn_spring_boot_mybatis_datasource</module>
</modules>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<jdk.version>1.8</jdk.version>
<spring.version>5.1.6.RELEASE</spring.version>
<mysql.version>8.0.11</mysql.version>
<thymeleaf.layout.version>2.4.1</thymeleaf.layout.version>
<lombok.version>1.18.2</lombok.version>
<drools.version>7.20.0.Final</drools.version>
<kie.version>7.20.0.Final</kie.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--核心模塊,包括自動配置支持、日誌和YAML-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--Web模塊 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--測試模塊,包括JUnit、Hamcrest、Mockito-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!--thymeleaf 框架-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--springBoot 2.0 將佈局單獨提取出來,需要單獨引入依賴-->
<dependency>
<groupId>nz.net.ultraq.thymeleaf</groupId>
<artifactId>thymeleaf-layout-dialect</artifactId>
<version>${thymeleaf.layout.version}</version>
</dependency>
<!--修改html後自動發佈-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<!--自動配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-compiler</artifactId>
<version>${drools.version}</version>
</dependency>
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-spring</artifactId>
<version>${kie.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<!--在添加了該插件之後,當運行“mvn package”進行打包時,
會打包成一個可以直接運行的 JAR 文件,使用“java -jar”命令就可以直接運行。
這在很大程度上簡化了應用的部署,只需要安裝了 JRE 就可以運行。-->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<!-- 指定maven編譯的jdk版本,如果不指定,maven3默認用jdk 1.5 maven2默認用jdk1.3 -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<!-- 一般而言,target與source是保持一致的,但是,
有時候爲了讓程序能在其他版本的jdk中運行(對於低版本目標jdk,
源代碼中不能使用低版本jdk中不支持的語法),會存在target不同於source的情況 -->
<source>1.8</source> <!-- 源代碼使用的JDK版本 -->
<target>1.8</target> <!-- 需要生成的目標class文件的編譯版本 -->
<encoding>UTF-8</encoding> <!-- 字符集編碼 -->
</configuration>
</plugin>
</plugins>
</build>
</project>
5、第一個子模塊leran_spring_boot_demo啓動類
該模塊的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>
<parent>
<groupId>org.learn</groupId>
<artifactId>learn</artifactId>
<version>1.0-SNAPSHOT</version>
<!--這裏是指向父類pom文件-->
<relativePath> <!-- lookup parent from repository -->
../pom.xml
</relativePath>
</parent>
<artifactId>learn_spring_boot_demo</artifactId>
<dependencies>
<!--核心模塊,包括自動配置支持、日誌和YAML-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--Web模塊 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--測試模塊,包括JUnit、Hamcrest、Mockito-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!--自動配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<!--Maven通過Maven Surefire Plugin插件執行單元測試。
(通過Maven Failsafe Plugin插件執行集成測試)
在pom.xml中配置JUnit,TestNG測試框架的依賴,即可自動識別和運行src/test目錄下利用該框架編寫的測試用例。
surefire也能識別和執行符合一定命名約定的普通類中的測試方法(POJO測試)。
生命週期中test階段默認綁定的插件目標就是surefire中的test目標,無需額外配置,直接運行mvn test就可以。-->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
</plugins>
</build>
</project>
6、resources目錄下結構
7、application.properties配置文件
server.port=8081
spring.banner.charset=UTF-8
server.tomcat.uri-encoding=UTF-8
spring.http.encoding.charset=UTF-8
spring.http.encoding.enabled=true
spring.http.encoding.force=true
spring.messages.encoding=UTF-8
spring.jpa.show-sql=true
logging.level.org.springframework.data=debug
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/lin?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&useSSL=true
spring.datasource.username=root
spring.datasource.password=123
# 這個必須依賴了mysql 才行
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
spring.thymeleaf.cache=false
# 定位模板的目錄
spring.mvc.view.prefix=classpath:/templates/
# 給返回的頁面添加後綴名
spring.mvc.view.suffix=.html
org.learn.boot.demo.name="林"
org.learn.boot.demo.wish="你要加油呀"
8、運行後,控制檯打印的日誌,端口爲80819、在瀏覽器中訪問這個http://localhost:8081/地址,便可以看到以下頁面(Spring Boot默認錯誤頁面)
10、現在通過子模塊learn_spring_boot_demo來做一下簡單增刪改查。
創建兩個實體類
11、UserRepository 接口類,這裏通過實現JpaRepository類來進行數據增、刪、改、查操作
package org.learn.boot.demo.model;
import org.learn.boot.demo.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
/**
* ClassName: UserDao
* Description: 數據訪問層
* Date: 2019/3/20 15:25
* History:
* <version> 1.0
* @author lin
*/
public interface UserRepository extends JpaRepository<User,Long> {
/**
* @Description 根據ID查詢
* @param id
* @return com.boot.demo.entity.User
* @author lin
* @Date 15:42 2019/3/20
**/
@Query(value = "select u from User u where u.id=:id")
User findById(@Param("id") int id);
/**
* @Description 根據姓名查詢
* @param name
* @return java.util.List<com.boot.demo.entity.User>
* @author lin
* @Date 15:43 2019/3/20
**/
@Query(value ="select u from User u where u.name=:name")
List<User> findByName(@Param(value = "name") String name);
/**
* @Description 添加用戶
* @param name
* @param age
*
* @return com.boot.demo.entity.User
* @author lin
* @Date 16:40 2019/3/20
**/
@Modifying
@Query(value = "insert into c_user(name,age) values(?1,?2)",nativeQuery = true)
int saveUser(String name, Integer age);
/**
* @Description 修改
* @param id
* @param name
* @param age
* @return com.boot.demo.entity.User
* @exception
* @author lin
* @Date 11:43 2019/3/21
**/
@Modifying
@Query(value = "update User u set u.name=?2 , u.age=?3 where u.id=?1")
int update(Integer id, String name, Integer age);
/**
* @Description 刪除(使用原生sql)
* @param id
* @return void
* @exception
* @author lin
* @Date 11:50 2019/3/21
**/
@Modifying
@Query(value = "delete from c_user WHERE id=?1",nativeQuery = true)
void deleteById(Integer id);
}
12、UserService接口類
package org.learn.boot.demo.service;
import org.learn.boot.demo.entity.User;
import java.util.List;
/**
* ClassName: UserService
* Description: 接口類
* Author: lin
* Date: 2019/3/20 16:23
* History:
* <version> 1.0
*/
public interface UserService {
/**
* @Description 根據ID查詢
* @param id
* @return com.boot.demo.entity.User
* @author lin
* @Date 15:42 2019/3/20
**/
User findById(int id);
/**
* @Description 根據姓名查詢
* @param name
* @return java.util.List<com.boot.demo.entity.User>
* @author lin
* @Date 15:43 2019/3/20
**/
List<User> findByName(String name);
/**
* @Description 添加用戶
* @param name
* @param age
*
* @return int
* @author lin
* @Date 16:40 2019/3/20
**/
int saveUser(String name, Integer age);
/**
* @Description 查詢全部
* @return java.util.List<com.boot.demo.entity.User>
* @author lin
* @Date 18:55 2019/3/20
**/
List<User> finAll();
/**
* @Description 更新
* @param id
* @param name
* @param age
* @return com.boot.demo.entity.User
* @exception
* @author lin
* @Date 11:38 2019/3/21
**/
int update(Integer id, String name, Integer age);
/**
* @Description 刪除
* @param id
* @return void
* @exception
* @author lin
* @Date 11:51 2019/3/21
**/
void delete(Integer id);
/**
* @Description 添加用戶信息
* @param user
* @return User
* @exception
* @author lin
* @Date 12:50 2019/3/21
**/
User addUser(User user);
}
13、UserServiceImpl實現類
package org.learn.boot.demo.service.impl;
import org.learn.boot.demo.entity.User;
import org.learn.boot.demo.model.UserRepository;
import org.learn.boot.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* ClassName: UserServiceImpl
* Description: 實現類
* Date: 2019/3/20 16:41
* History:
* <version> 1.0
* @author lin
*/
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserRepository userRepository;
@Override
public User findById(int id) {
return userRepository.findById(id);
}
@Override
public List<User> findByName(String name) {
return userRepository.findByName(name);
}
@Override
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = {RuntimeException.class})
public int saveUser(String name, Integer age) {
return userRepository.saveUser(name, age);
}
@Override
public List<User> finAll() {
return userRepository.findAll();
}
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = {RuntimeException.class})
@Override
public int update(Integer id, String name, Integer age) {
return userRepository.update(id,name,age);
}
/**
* @param id
* @return void
* @throws
* @Description 刪除
* @author lin
* @Date 11:51 2019/3/21
**/
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = {RuntimeException.class})
@Override
public void delete(Integer id) {
userRepository.deleteById(id);
}
/**
* @param user
* @return int
* @throws
* @Description 添加用戶信息
* @author lin
* @Date 12:50 2019/3/21
**/
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = {RuntimeException.class})
@Override
public User addUser(User user) {
return userRepository.save(user);
}
}
14、UserRedirectController 類
package org.learn.boot.demo.controller;
import org.learn.boot.demo.entity.User;
import org.learn.boot.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.List;
/**
* ClassName: UserRedirectController
* Description: 重定向
* Date: 2019/3/21 0:18
* History:
* <version> 1.0
* @author lin
*/
@Controller
public class UserRedirectController {
@Autowired
private UserService userService;
@RequestMapping("/indexRedirect")
public String index() {
return "redirect:/list";
}
@RequestMapping("/list")
public String list(Model model){
List<User> users = userService.finAll();
model.addAttribute("users",users);
return "user/list";
}
//return "user/userEdit"; 代表會直接去 resources 目錄下找相關的文件。
//return "redirect:/list"; 代表轉發到對應的 Controller,這個示例就相當於刪除內容之後自動調整到 list 請求,然後再輸出到頁面。
@RequestMapping("/toAdd")
public String toAdd(){
return "user/userAdd";
}
@RequestMapping("/add")
public String add(User user) {
userService.addUser(user);
return "redirect:/list";
}
@RequestMapping("/toEdit")
public String toEdit(Model model, Integer id) {
User user=userService.findById(id);
model.addAttribute("user", user);
return "user/userEdit";
}
@RequestMapping("/edit")
public String edit(Integer id, String userName, Integer age) {
userService.update(id, userName, age);
return "redirect:/list";
}
@RequestMapping("/delete")
public String delete(Integer id) {
userService.delete(id);
return "redirect:/list";
}
}
15、訪問http://localhost:8081/indexRedirect 地址會重新定向到http://localhost:8081/list 地址,然後就是list.html頁面,在這裏就可以進行簡單的增刪查改操作。
二、springboot 源碼分析
1、在程序的入口類中有個註解@SpringBootApplication ,該註解的作用是什麼呢?
2、進入springBootApplication註解類中,可以看到有@springBootConfiguration 註解,@EnableAutoConfiguration註解。
@springBootConfiguration:表示的是spring Boot的配置類。進入該類就可以知道這個類是有註解@Configuration 註解,這就是我們在使用spring 的一個 配置時的一種方式(spring 配置方式 有 xml方式、基於註解的方式、基於java 的配置方式)。
https://blog.csdn.net/icarus_wang/article/details/51649635(spring Bean配置的三種方式)
3、在進入配置類裏面,我們可以看到@Component 註解,這就表示這個類也是容器中的一個組件。
4、@EnableAutoConfiguration註解
該註解就是開啓自動裝配功能,在springBoot 應用中我們沒有做任何配置,那springmvc 也啓動起來了整個應用也能用了,包掃面也掃進去了。這些功能是怎麼做的呢? 就是通過@EnableAutoConfiguration註解 來實現。
以前spring 需要配置的東西,springBoot幫我們自動配置,@EnableAutoConfiguration 告訴springBoot 開啓自動配置功能。這樣自動配置功能才能生效。
在進入 @EnableAutoConfiguration 註解類中,我們可以看到@AutoConfigurationPakage 註解 ,表示自動配置包
5、進入@AutoConfigurationPakage 註解類中,它是通過@Import 註解來實現,這個註解是屬於spring 的底層註解。它的作用就是給容器導入一個組件;導入的組件由 AutoConfigurationPakages.Registras.class 這個class類
6、我們進入registar 這個內部類。這個類有個方法,就是註冊一些bean定義信息,這是給容器導組件
7、組件主要是 New PackageImport(metadata).getPackageName(); 這個metadata 就是這個註解標註的原信息。然後同getPackageName() 去拿去包名。通過debug的方式可以拿到 這個包名。所以這個@AutoConfigurationPakage 註解 將主配置類(@springbootApplication 標註的類)的所在包及下面所有子包裏面的所有組件掃描到spring 容器; 所有我們能掃面到controller ,因爲他是在主配置類的子包下。
8、按照上面的想法,一旦換包了或者在目錄java 下再建立一個包並創建一個類用來驗證。
9、這個時候再啓動,並訪問 localhost:8080/index 就會報404。
所以在創建項目包的時候要將包和有@SpringBootApplication註解的類放在 的子包下或者同一級下。
10、在@EnableAutoConfiguration註解類中 還有 @Import(AutoConfigurationImportSelector.class) 註解,這個註解是給容器導入註解,該import導入的是AutoConfigurationImportSelector組件。進入該組件類中 一個方法是selectImports
11、selectImports 方法 中會去調用getAutoConfigurationEntry()方法,進入該方法
autoConfigurationMetadata主要是自動配置的元數據,
annotationMetadata 是註解的元數據。
12、在 方法中 list configurations 這個list 中包含很多的自動配置類(xxxAutoConfiguration). 就是給容器導入這個場景所需要的所有組件,並配置好這些組件。
如果要Aop的功能,那麼呢Aop的自動配置類就幫我配置好了,如果我們要做批處理功能,那麼批處理Batch 就會幫我們配置好。如果要做Mongodb功能 ,mongodb配置類就會幫我配置好。
13、有了自動配置類,免去了我們手動編寫配置注入功能組件等的工作。通過自動配置類幫我處理。
那麼自動配置類怎麼掃描到這些呢,從哪兒得到的呢。 它是通過 getCandidateConfigurations 候選配置文件。主要調用了SpringFactoriesLoader.loadFactoryNames()。他有兩個參數一個是 EnableAutoConfiguration.class, 另一個是getBeanClassLoader() 類加載器機制。
14、在loadFactoryNames方法中調用loadSpringFactories方法, 並通過該方法中參數classloader 類加載器來獲取資源,獲取資源後會把這個資源當成一個 properties配置文件,從這個配置文件中獲取這個工廠的name。
15、獲取的地方就是這個META-INF/spring.factories中獲取 EnableAutoConfiguration指定的值
在autoConfigure中 spring.factories中 有EnableAutoConfiguration的信息,這裏配置信息就是我們導入自動配置類。
總結下:springboot啓動時候會去類路徑下的META-INF/spring.factories中獲取 EnableAutoConfiguration指定的值,並將這些值作爲自動配置類導入到容器中去。這些自動配置類就生效了 就能幫我們進行自動配置工作。 相比我們在使用spring時需要進行xml的配置或者註解的配置。springboot中它把spring需要進行xml形式的配置簡化了。所有的配置都由springboot來幫我們完成。