SpringBoot 文章來源于:雷神:https://www.bilibili.com/video/BV19K4y1L7MT/?spm_id_from=333.337.search-card.all.click&vd_source=a9bff059910348f08db3690eefbeacbe
特點:
-
依賴管理 父專案用來做依賴管理:利用maven的依賴傳遞原則 其他都不用寫明版本號 可以在當前專案里面重寫配置,利用的是 maven的 就近依賴原則
-
自動配置 匯入Tomcat依賴后
-
自動配好Web常見功能,如:字符編碼問題
-
默認的包結構,用于注解掃描
-
-
主程式所在包(啟動類所在包)及其下面的所有子包里面的組件都會被默認掃描進來
-
所以無需以前的包掃描配置
-
如果想要改變掃描路徑,
-
@SpringBootApplication(scanBasePackages="com.atguigu")或者@ComponentScan 指定掃描路徑
-
各種配置擁有默認值
-
-
默認配置最終都是映射到某個類的屬性上,如:MultipartProperties
-
組態檔的值最侄訓系結到對應的類上,這些類會在容器中創建物件,所以用到這些值的時候,SpringBoot底層會拿到這些類的物件,提取出值,之后系結,關于如何系結,要討論
-
-
按需加載所有自動配置項
-
-
非常多的starter
-
引入了哪些場景這個場景的自動配置才會開啟,例如:web場景就只有web
-
SpringBoot所有的自動配置功能都在 spring-boot-autoconfigure 包里面
-
自動配置,spring-boot-autoconfigure,包里原始碼標紅,表示依賴的其它的包未匯入,匯入其依賴的包即可使用
-
-
@SpringBootApplication
標識啟動類,有下三個重要注解標識
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication{}
======================
1.@SpringBootConfiguration
@Configuration,代表當前是一個配置類
2.@ComponentScan
指定掃描哪些,Spring注解;
3.@EnableAutoConfiguration
該注解有兩個注解進行標識
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}
AutoConfigurationPackage是匯入你自己創建的Bean,比如controller啥的,下面的Import是匯入第三方的組件,比如DispatcherServlet啥的,
所以說本專案中的 類的優先級高于自動配置starter中的bean創建
1.@AutoConfigurationPackage
自動配置包?指定了默認的包規則
2.@Import(AutoConfigurationImportSelector.class)
有選擇的匯入
@ConditionalOnBean(Value)
標注在類和方法上,作用,如果ioc容器中有value值的Bean才能將其標注的方法或者類生效
@ConditionalOnMissingBean(value)
標注在類和方法上,作用與上一個相反,如果ioc容器中沒有value值的Bean才能將其標注的方法或者類生效
@ImportResource(value="https://www.cnblogs.com/zzc1102/p/classpath:")
作用在類上,將組態檔匯入,
@EanalbeConfigurationProperties({Car.class})
作用在配置類上,作用1.開啟Car這個類的配置系結功能(可以實作類屬性系結yml自定義值),2.把Car這個組件加入ioc容器
類屬性系結yml中的自定義值
1. @Component + @ConfigurationProperties(prefix="自定義前綴")
只有在容器中的組件,才會擁有SpringBoot提供的強大功能,所以要加上@Component注解
2. @EnableConfigurationProperties(類.class) + @ConfigurationProperties(prefix)
@EnableConfigurationProperties 一定要寫在配置類
定制化mvc
1、使用Servlet API
@ServletComponentScan(basePackages = "com.atguigu.admin") :指定原生Servlet組件都放在那里
請求不發送到DispatchServlet,不經過dispatcher方法,不過攔截器
攔截器是DispatcherServlet類中dodispatch()方法中的,這里DispatcherServlet的沒走,走的是我們定義 的Servlet,所以攔截器就不會生效.
Servlet 是規范之一,只是框架在底層幫我們實作了
原生不經過那一系列框架,效率會更高
原因很簡單,就是springmvc底層實際上就是一個大型的servlet, 而他的urlpatterns為 / ,所以當請求/.my時
會精確匹配到自定義的servlet, 不會經過spring.
@WebServlet(urlPatterns = "/my"):效果:直接回應,沒有經過Spring的攔截器?
@WebFilter(urlPatterns={"/css/*","/images/*"})
@WebFilter(filterName = "loginCheckFilter",urlPatterns = "/*")
public class LoginCheckFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
?
//1、獲取本次請求的URI
String requestURI = request.getRequestURI();// /backend/index.html
//過濾器放行
filterChain.doFilter(request,response);
//直接return則表示攔截
return;
}
@WebListener
東西太多看別個博客吧,
https://blog.csdn.net/weixin_41924879/article/details/101175659
2、使用RegistrationBean
ServletRegistrationBean`, `FilterRegistrationBean`, and `ServletListenerRegistrationBean
//filter用于字符過濾,listener用于初始化作用域資料,interceptor用于攔截請求
@Configuration()
public class MyRegistConfig {
?
@Bean
public ServletRegistrationBean myServlet(){
MyServlet myServlet = new MyServlet();
?
return new ServletRegistrationBean(myServlet,"/my","/my02");
}
?
?
@Bean
public FilterRegistrationBean myFilter(){
?
MyFilter myFilter = new MyFilter();
// return new FilterRegistrationBean(myFilter,myServlet());
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(myFilter);
// /*是原生servlet的寫法,/**是spring的寫法
filterRegistrationBean.setUrlPatterns(Arrays.asList("/my","/css/*"));
return filterRegistrationBean;
}
?
@Bean
public ServletListenerRegistrationBean myListener(){
MySwervletContextListener mySwervletContextListener = new MySwervletContextListener();
return new ServletListenerRegistrationBean(mySwervletContextListener);
}
}
擴展:DispatchServlet 如何注冊進來
-
容器中自動配置了 DispatcherServlet 屬性系結到 WebMvcProperties;對應的組態檔配置項是 spring.mvc,
-
通過 ServletRegistrationBean<DispatcherServlet> 把 DispatcherServlet 配置進來,
-
-
老師這里錯了吧,dispatcherServlet是用@Bean標簽注冊到了容器中
-
這個注冊類應該是將dispatcherservlet封裝注冊到tomcat服務器的Servletcontext全域作用域中
-
Servlet是要被ServletContext管理才能生效的,DispatcherServletRegistrationBean的作用就是把這個servlet交給ServletContext管理
-
-
默認映射的是 / 路徑,
攔截器是在請求發過來的時候交給Spring去攔截的(dispatcherServlet)

3、定制化
-
撰寫自定義的配置類 xxxConfiguration;+ @Bean替換、增加容器中默認組件;視圖決議器
-
最常用:Web應用 撰寫一個配置類實作 WebMvcConfigurer 即可定制化web功能;+ @Bean給容器中再擴展一些組件
@Configuration
public class AdminWebConfig implements WebMvcConfigurer{}
@EnableWebMvc
@ EnableWebMvc+ 實作 WebMvcConfigurer —— @Bean 可以全面接管SpringMVC,所有規則全部自己重新配置; 實作定制和擴展功能
攔截器原理
攔截器(Interceptor)和過濾器(Filter)的執行順序 過濾前-攔截前-Action處理-攔截后-過濾后
攔截器(Interceptor)使用 interceptor 的執行順序大致為:
請求到達 DispatcherServlet DispatcherServlet 發送至 Interceptor ,執行 preHandle 請求達到 Controller 請求結束后,postHandle 執行
過濾器,攔截器這種類似的都是責任鏈模式,
1、根據當前請求,找到HandlerExecutionChain【可以處理請求的handler以及handler的所有 攔截器(攔截器鏈)】
2、先來順序執行 所有攔截器的 preHandle方法
-
1、如果當前攔截器prehandle()回傳為true,則執行下一個攔截器的preHandle()
-
2、如果當前攔截器回傳為false,直接 倒序執行所有已經執行了的攔截器的 afterCompletion()方法;
3、如果任何一個攔截器回傳false,直接跳出不執行目標方法
4、所有攔截器都回傳True,執行目標方法
5、倒序執行所有攔截器的postHandle()方法,
6、前面的步驟有任何例外都會直接倒序觸發 afterCompletion
7、頁面成功渲染完成以后,也會倒序觸發 afterCompletion
preHandle順序執行,postHandle和afterCompletion都是逆序執行,afterCompletion會在preHandle回傳false/任何例外發生時/渲染視圖完成后執行,
圖解攔截器原理:

1、實作HandlerInterceptor 介面
/**
* 登錄檢查
* 1、配置好攔截器要攔截哪些請求
* 2、把這些配置放在容器中
*/
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
?
/**
* 目標方法執行之前
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//@Slf4j提供的日志功能,檢測出靜態資源也被攔截
// String requestURI = request.getRequestURI();
// log.info("preHandle攔截的請求路徑是{}",requestURI);
?
//登錄檢查邏輯
HttpSession session = request.getSession();
?
Object loginUser = session.getAttribute("loginUser");
?
if(loginUser != null){
//放行
return true;
}
?
//攔截住,未登錄,跳轉到登錄頁
//如果重定向,寫保存在session域中,前端要${session.msg},視頻獲取不到,因為login頁中獲取的是request里的msg,所以獲取不到,
//這里使用轉發
request.setAttribute("msg","請先登錄");
// re.sendRedirect("/");
request.getRequestDispatcher("/").forward(request,response);
return false;
}
?
/**
* 目標方法執行完成以后
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("postHandle執行{}",modelAndView);
}
?
/**
* 頁面渲染以后
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("afterCompletion執行例外{}",ex);
}
}
-
preHandle
用來攔截處理器的執行,preHandle方法將在Controller處理之前呼叫的,SpringMVC里可以同時存在多個interceptor,它們基于鏈式方式呼叫,每個interceptor都根據其宣告順序依次執行,這種鏈式結構可以中斷,當方法回傳false時整個請求就結束了,
方法的回傳值是布爾型別,方法如果回傳false,那后面的interceptor和Controller都不會執行(通常都會回應一個自定義的 Http 錯誤碼給客戶端),如果回傳值為true,則接著呼叫下一個interceptor的preHandle方法,如果當前是最后一個interceptor,接下來就會直接呼叫Controller的處理方法,
-
postHandle:
postHandle 會在Controller方法呼叫之后,但是在DispatcherServlet 渲染視圖之前呼叫,因此我們可以在這個階段,對將要回傳給客戶端的ModelAndView進行操作,
-
afterCompletion:
afterCompletion在當前interceptor的preHandle方法回傳true時才執行,該方法會在整個請求處理完成后被呼叫,就是DispatcherServlet渲染視圖完成以后,主要是用來進行資源清理作業,需要注意的是,afterCompletion在interceptor鏈式結構中以相反的順序執行,也就是說先申明的interceptor回傳會后呼叫,
2、配置攔截器
把這些配置放在容器中,
/**
* 1、撰寫一個攔截器實作HandlerInterceptor介面
* 2、攔截器注冊到容器中(實作WebMvcConfigurer的addInterceptors)
* 3、指定攔截規則【如果是攔截所有,靜態資源也會被攔截】
4、/*只代表當前目錄下,/**同時還包含了子檔案夾(類路徑又是什么意思 )
*/
@Configuration
public class AdminWebConfig implements WebMvcConfigurer {
?
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**") //所有請求都被攔截包括靜態資源
.excludePathPatterns("/","/login","/css/**","/fonts/**","/images/**","/js/**"); //放行的請求
}
}
檔案上傳
1、頁面表單
?
<!-- method="post" enctype="multipart/form-data" 檔案上傳表單的固定寫法,
enctype就是encodetype就是編碼型別的意思,multipart/form-data是指表單資料中由多部分構成
既有文本資料,又有檔案等二進制資料 -->
<form method="post" action="/upload" enctype="multipart/form-data">
<input type="file" name="file"><br>
<input type="submit" value=https://www.cnblogs.com/zzc1102/p/"提交">
</form>
2、檔案上傳代碼
/**
* MultipartFile 自動封裝上傳過來的檔案
//各位記得設定上傳最大空間值,放的圖片太大會有例外
*/
@PostMapping("/upload")
public String upload(@RequestParam("email") String email,
@RequestParam("username") String username,
//表單里面的檔案項
@RequestPart("headerImg") MultipartFile headerImg,
@RequestPart("photos") MultipartFile[] photos) throws IOException {
?
log.info("上傳的資訊:email={},username={},headerImg={},photos={}",
email,username,headerImg.getSize(),photos.length);
?
if(!headerImg.isEmpty()){
//保存到檔案服務器,OSS服務器
String originalFilename = headerImg.getOriginalFilename();
//名字不重復,加UUID就可以了,然后后面拼接上后綴名就可以了
headerImg.transferTo(new File("H:\\cache\\"+originalFilename));
}
?
if(photos.length > 0){
for (MultipartFile photo : photos) {
if(!photo.isEmpty()){
String originalFilename = photo.getOriginalFilename();
photo.transferTo(new File("H:\\cache\\"+originalFilename));
}
}
}
return "main";
}
?
3、檔案上傳自動配置類
在組態檔中可設定檔案上傳大小
//檔案上傳大小配置,單個檔案,整個請求
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=100MB
例外
1、默認規則
-
默認情況下,Spring Boot提供
/error處理所有錯誤的映射 -
對于機器客戶端,它將生成JSON回應,其中包含錯誤,HTTP狀態和例外訊息的詳細資訊,
-
對于瀏覽器客戶端,回應一個“ whitelabel”錯誤視圖,以HTML格式呈現相同的資料
-
要對其進行自定義,添加View決議為error
替換調mvc組件error(也就是白頁)
-
要完全替換默認行為,可以實作
ErrorController并注冊該型別的Bean定義,或添加ErrorAttributes型別的組件以使用現有機制但替換其內容, -
error/下的4xx,5xx頁面會被自動決議;
2、定制錯誤處理邏輯
-
1、自定義錯誤頁
-
-
error/404.html error/5xx.html;有精確的錯誤狀態碼頁面就匹配精確,沒有就找 4xx.html;如果都沒有就觸發白頁
-
-
2、@ControllerAdvice+@ExceptionHandler 處理全域例外;底層是 ExceptionHandlerExceptionResolver 支持的,因為標了@ExceptionHandler注解
/**
* 處理整個web controller的例外
*/
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler({ArithmeticException.class,NullPointerException.class}) //處理哪些例外 算數例外,空指標例外
public String handleArithException(Exception e){
?
log.error("例外是:{}",e);
return "login"; //視圖地址
}
}
-
3、@ResponseStatus+自定義例外 ;底層是 ResponseStatusExceptionResolver 決議器處理,因為標了@ResponseStatus注解,把responsestatus注解的資訊,底層呼叫 response.sendError(statusCode, resolvedReason);然后還是下面的流程,沒有決議器可以決議,然后tomcat發送的/error到mvc,找4xx,5xx頁面
//設定狀態碼 和例外資訊 程式執行到這里時主要將注解中的資訊獲得
@ResponseStatus(value= HttpStatus.FORBIDDEN,reason = "用戶數量太多")
public class UserTooManyException extends RuntimeException {
?
public UserTooManyException(){
?
}
public UserTooManyException(String message){
super(message);
}
}
?
-
4、自定義實作 HandlerExceptionResolver 處理例外;把優先級調高(一旦出現例外優先被其處理),可以作為默認的全域例外處理規則
@Order(value= Ordered.HIGHEST_PRECEDENCE) //優先級,數字越小優先級越高
@Component
public class CustomerHandlerExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response,
Object handler, Exception ex) {
?
try {
response.sendError(511,"我喜歡的錯誤");
} catch (IOException e) {
e.printStackTrace();
}
return new ModelAndView();
}
}
?
-
ErrorViewResolver 實作自定義處理例外;
-
-
主動呼叫response.sendError() ,error請求就會轉給controller,如果沒有4叉叉這些頁面,就會呼叫BeanNameViewResolver決議器,回傳error白頁(為mvc的錯誤頁面,組件View)
-
你的例外沒有任何人能處理,tomcat底層呼叫 response.sendError(),error請求就會轉給controller,如果沒有4叉叉這些頁面,就會呼叫Tomcat自己的錯誤頁面
-
以上都是由basicErrorController 控制器處理,要去的頁面地址是 ErrorViewResolver決議器決議 ;
-
@PathVariable(rest風格)、@RequestHeader、@RequestParam、@CookieValue、@RequestBody、@RequestAttribute(用來請求域中取資料,然后請求轉發)、@ModelAttribute、@MatrixVariable(矩陣變數)
整合mybatis
1、純配置模式
-
全域組態檔
-
SqlSessionFactory: 自動配置好了
-
SqlSession:自動配置了 SqlSessionTemplate 組合了SqlSession
-
@Import(AutoConfiguredMapperScannerRegistrar.class);
-
Mapper: 只要我們寫的操作MyBatis的介面標注了 @Mapper 就會被自動掃描
可以修改組態檔中 mybatis 開始的所有配置;
# 第一種方式:純配置模式
# 配置mybatis規則
yml:-------------------------------------------
mybatis:
// config-location: classpath:mybatis/mybatis-config.xml #全域組態檔位置
mapper-locations: classpath:mybatis/mapper/*.xml #sql映射檔案位置
configuration:
map-underscore-to-camel-case: true
全域組態檔和在yml中使用configuration配置,二者只能選其一 推薦在yml中
可以不寫全域組態檔,所有全域組態檔的配置資訊都放在configuration配置項中即可
Mapper介面--->系結Xml
介面----------------------------------------------
@Mapper
public interface AccountMapper {
public Account getAcct(Long id);
}
SQL實作的xml----------------------------------------------------
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.admin.mapper.AccountMapper">
<!-- public Account getAcct(Long id); -->
<select id="getAcct" resultType="com.atguigu.admin.bean.Account">
select * from account_tbl where id=#{id}
</select>
</mapper>
mybatis使用規則:
-
匯入mybatis官方starter
-
撰寫mapper介面,標注@Mapper注解
-
撰寫sql映射檔案并系結mapper介面
-
在application.yaml中指定Mapper組態檔的位置,以及指定全域組態檔的資訊 (建議不寫全域組態檔,直接配置在mybatis.configuration)
2、純注解模式
//這個方式講mybatis的時候就提了,1.不適合復雜查詢 2.寫死了sql陳述句
//主要是因為注解只適合小型的sql陳述句,而公司里的陳述句都非常長,用xml配置會更清晰
@Mapper
public interface CityMapper {
?
@Select("select * from city where id=#{id}")
@Options(可實作xml中配置sql的其他功能,比如自增主鍵回傳)
public City getById(Long id);
?
public void insert(City city);
?
}
3、推薦混合模式
//一般都混合使用,簡單的sql陳述句就通過注解寫在方法上
@Mapper
public interface CityMapper {
?
@Select("select * from city where id=#{id}")
public City getById(Long id);
?
public void insert(City city);
?
}
最佳實戰:
-
引入mybatis-starter依賴
-
配置application.yaml中,指定mapper-location位置即可
-
撰寫Mapper介面并標注@Mapper注解
-
簡單方法直接注解方式
-
復雜方法撰寫mapper.xml進行系結映射
-
在程式啟動類上面標注@MapperScan("com.atguigu.admin.mapper") 簡化,其他的介面就可以不用標注@Mapper注解,
整合 MyBatis-Plus
優點:資料層: mapper的CRUD不用寫了
-
只需要我們的Mapper繼承 BaseMapper 就可以擁有crud能力,因為BaseMapper宣告了許多方法,如果不夠用,那就映射xml檔案,
public interface UserMapper extends BaseMapper<User> {}
優點:業務層: Service 的CRUD也不用寫了
/**
* service的介面繼承頂級service介面---IService
*/
public interface UserService extends IService<User> {
?
}
service的實作類繼承ServiceImpl----它是頂級介面IService的實作類,所以我們就可以直接用方法了,
如果不夠用,那就自己加在介面中UserService,然后再來UserServiceImpl實作,
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper,User> implements UserService {}
3、CRUD功能
//控制器方法
@GetMapping("/user/{id}")
//restful風格: pn是請求引數,是url路徑下?pn=1樣子的值, 而id是在路徑變數中,二者注意區分,給路徑變數賦值(id=${user.id})
public String deleteUser(@PathVariable("id") Long id,
@RequestParam(value = "pn",defaultValue = "1")Integer pn,
RedirectAttributes ra){//
?
userService.removeById(id);
//重定向攜帶引數,自動添加到重定向url后面
ra.addAttribute("pn",pn);
return "redirect:/dynamic_table";
}
?
?
@GetMapping("/dynamic_table")
public String dynamic_table(@RequestParam(value="pn",defaultValue = "1") Integer pn,Model model){
?
//從資料庫中查出user表中的用戶進行展示
//構造分頁引數
//第幾頁為pn,通過引數傳進來,每頁顯示多少條為2
Page<User> page = new Page<>(pn, 2);
//呼叫page進行分頁,Wrapper為條件,沒有條件就寫null
//userPage有很多有關分頁的資訊
Page<User> userPage = userService.page(page, null);
?
?
model.addAttribute("users",userPage);
?
return "table/dynamic_table";
}
//使用分頁還需要的配置,分頁攔截器
@Configuration
public class MyBatisConfig {
/**
* MybatisPlusInterceptor
* 掃一眼原始碼,這個貌似跟SpringBoot的攔截器還不一樣,這個攔截器是發生在服務器和資料庫之間的,不涉及路徑攔截(可能說的不對)
* @return
*/
@Bean
public MybatisPlusInterceptor paginationInterceptor() {
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
// 設定請求的頁面大于最大頁后操作, true調回到首頁,false 繼續請求 默認false
// paginationInterceptor.setOverflow(false);
// 設定最大單頁限制數量,默認 500 條,-1 不受限制
// paginationInterceptor.setLimit(500);
// 開啟 count 的 join 優化,只針對部分 left join
?
//這是分頁攔截器
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
paginationInnerInterceptor.setOverflow(true);
paginationInnerInterceptor.setMaxLimit(500L);
mybatisPlusInterceptor.addInnerInterceptor(paginationInnerInterceptor);
?
return mybatisPlusInterceptor;
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/543249.html
標籤:Java
