框架controller找不到_SpingBoot框架知識詳解

Spring boot框架

1、什么是Spring Boot?

? Spring Boot是Spring開源組織下的子項目,是Spring組件一站式解決方案,主要是簡化了使用Spring的難度,簡省了繁重的配置,提供了各種啟動器,開發者能快速上手。

Spring Boot官方文檔:https://spring.io/projects/spring-boot/

2、Spring Boot的優點與特點

優點:

  • 獨立運行
  • Spring Boot內嵌了各種servlet容器,Tomcat、Jetty等,不再需要打成war包部署到容器中,Spring Boot只要打成一個可執行的jar包就能獨立運行,所有的依賴包都在一個jar包內
  • 簡化配置
  • spring-boot-starter-web啟動器自動依賴其他組件,簡少了maven的配置。
  • 自動配置
  • Spring Boot能根據當前類路徑下的類、jar包來自動配置bean,如添加一個spring-boot-starter-web啟動器就能擁有web的功能,無需其他配置
  • 無代碼生成和XML配置
  • Spring Boot配置過程中無代碼生成,也無需XML配置文件就能完成所有配置工作,這一切都是借助于條件注解完成的,這也是Spring4.x的核心功能之一。
  • 應用監控
  • Spring Boot提供一系列端點可以監控服務及應用,做健康檢測。
  • 上手容易

特點:

  • 為 Spring 開發提供一個更快、更廣泛的入門體驗。
  • 開箱即用,遠離繁瑣的配置。
  • 提供了一系列大型項目通用的非業務性功能,例如:內嵌服務器、安全管理、運行數據監控、運行狀況檢查和外部化配置等。
  • 絕對沒有代碼生成,也不需要XML配置。

3、Spring Boot的配置文件以及之間的區別

.properties 和 .yml,它們的區別主要是書寫格式不同。

1).properties

app.user.name = javastack

2).yml

app:user:name: javastack

注:.yml 格式不支持 @PropertySource 注解導入配置。

4、Spring Boot的常用注解

@SpringBootApplication

申明讓spring boot自動給程序進行必要的配置,這個配置等同于:@Configuration @EnableAutoConfiguration 和 @ComponentScan 三個配置。

@ResponseBody

表示該方法的返回結果直接寫入HTTP response body中,一般在異步獲取數據時使用,用于構建RESTful的api。在使用@RequestMapping后,返回值通常解析為跳轉路徑,加上@esponsebody后返回結果不會被解析為跳轉路徑,而是直接寫入HTTP response body中。比如異步獲取json數據,加上@Responsebody后,會直接返回json數據。該注解一般會配合@RequestMapping一起使用。

@Controller

用于定義控制器類,在spring項目中由控制器負責將用戶發來的URL請求轉發到對應的服務接口(service層),一般這個注解在類中,通常方法需要配合注解@RequestMapping。

@RestController

用于標注控制層組件(如struts中的action),@ResponseBody和@Controller的合集。

@RequestMapping

提供路由信息,負責URL到Controller中的具體函數的映射。

@EnableAutoConfiguration

SpringBoot自動配置(auto-configuration):嘗試根據你添加的jar依賴自動配置你的Spring應用。例如,如果你的classpath下存在HSQLDB,并且你沒有手動配置任何數據庫連接beans,那么我們將自動配置一個內存型(in-memory)數據庫”。你可以將@EnableAutoConfiguration或者@SpringBootApplication :添加到一個@Configuration類上來選擇自動配置。如果發現應用了你不想要的特定自動配置類,你可以使用@EnableAutoConfiguration注解的排除屬性來禁用它們。

@ComponentScan

表示將該類自動發現掃描組件。個人理解相當于,如果掃描到有@Component、@Controller、@Service等這些注解的類,并注冊為Bean,可以自動收集所有的Spring組件,包括@Configuration類。我們經常使用@ComponentScan注解搜索beans,并結合@Autowired注解導入。可以自動收集所有的Spring組件,包括@Configuration類。我們經常使用@ComponentScan注解搜索beans,并結合@Autowired注解導入。如果沒有配置的話,Spring Boot會掃描啟動類所在包下以及子包下的使用了@Service,@Repository等注解的類。

@Configuration

相當于傳統的xml配置文件,如果有些第三方庫需要用到xml文件,建議仍然通過@Configuration類作為項目的配置主類——可以使用@ImportResource注解加載xml配置文件。

@Import:用來導入其他配置類。

@ImportResource:用來加載xml配置文件。

@Service:一般用于修飾service層的組件

@Repository:使用@Repository注解可以確保DAO或者repositories提供異常轉譯,這個注解修飾的DAO或者repositories類會被ComponetScan發現并配置,同時也不需要為它們提供XML配置項。

@Value:注入Spring boot application.properties配置的屬性的值。示例代碼:

@Inject:等價于默認的@Autowired,只是沒有required屬性;

@Component:泛指組件,當組件不好歸類的時候,我們可以使用這個注解進行標注。

@Bean:相當于XML中的,放在方法的上面,而不是類,意思是產生一個bean,并交給spring管理。

@AutoWired

自動導入依賴的bean。byType方式。把配置好的Bean拿來用,完成屬性、方法的組裝,它可以對類成員變量、方法及構造函數進行標注,完成自動裝配的工作。當加上(required=false)時,就算找不到bean也不報錯。

@Qualifier

當有多個同一類型的Bean時,可以用@Qualifier(“name”)來指定。與@Autowired配合使用。@Qualifier限定描述符除了能根據名字進行注入,但能進行更細粒度的控制如何選擇候選者,具體使用方式如下:

@Resource(name=”name”,type=”type”):沒有括號內內容的話,默認byName。與@Autowired干類似的事。

5、spring boot開啟的兩種方式

? ① 繼承spring-boot-starter-parent項目

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

? ② 導入spring-boot-dependencies項目依賴

<dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>1.5.6.RELEASE</version><type>pom</type><scope>import</scope></dependency></dependencyManagement>

Spring Boot依賴注意點

1、屬性覆蓋只對繼承有效

? Spring Boot依賴包里面的組件的版本都是和當前Spring Boot綁定的,如果要修改里面組件的版本,只需要添加如下屬性覆蓋即可,但這種方式只對繼承有效,導入的方式無效。

<properties><slf4j.version>1.7.25<slf4j.version></properties>

如果導入的方式要實現版本的升級,達到上面的效果,這樣也可以做到,把要升級的組件依賴放到Spring Boot之前。

<dependencyManagement><dependencies><!-- Override Spring Data release train provided by Spring Boot --><dependency><groupId>org.springframework.data</groupId><artifactId>spring-data-releasetrain</artifactId><version>Fowler-SR2</version><scope>import</scope><type>pom</type></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>1.5.6.RELEASE</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement>

需要注意,要修改Spring Boot的依賴組件版本可能會造成不兼容的問題。

2、資源文件過濾問題

使用繼承Spring Boot時,如果要使用Maven resource filter過濾資源文件時,資源文件里面的占位符為了使${}和Spring Boot區別開來,此時要用@...@包起來,不然無效。另外,@...@占位符在yaml文件編輯器中編譯報錯,所以使用繼承方式有諸多問題,坑要慢慢趟。

6、Spring Boot的幾種運行方式

  • 打包用命令或者放到容器中運行
  • 用 Maven/ Gradle 插件運行
  • 直接執行 main 方法運行

7、Spring Boot實現熱部署

第一種引用devtools依賴

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><optional>true</optional></dependency>

第二種自定義配置熱部署

# 熱部署開關,false即不啟用熱部署spring.devtools.restart.enabled: true# 指定熱部署的目錄#spring.devtools.restart.additional-paths: src/main/java# 指定目錄不更新spring.devtools.restart.exclude: test/**

第三種Intellij Idea修改

? 1、勾上自動編譯或者手動重新編譯

? File > Settings > Compiler-Build Project automatically

? 2、注冊

? ctrl + shift + alt + / > Registry > 勾選Compiler autoMake allow when app running

注意事項:

1、生產環境devtools將被禁用,如java -jar方式或者自定義的類加載器等都會識別為生產環境。

2、打包應用默認不會包含devtools,除非你禁用SpringBoot Maven插件的 excludeDevtools屬性。

3、Thymeleaf無需配置 spring.thymeleaf.cache:false,devtools默認會自動設置,參考完整屬性。

4、devtools會在windows資源管理器占用java進程,在開發工具里面殺不掉,只能手動kill掉,不然重啟會選成端口重復綁定報錯。

更多詳細用法,參考發官方文檔:https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-devtools.html

8、保護Spring Boot的應用方法

  1. 在生產中使用HTTPS
  2. 使用Snyk檢查你的依賴關系
  3. 升級到最新版本
  4. 啟動CSRF保護
  5. 使用內容安全策略防止XSS攻擊
  6. 使用OpenID Connect進行身份驗證
  7. 管理密碼使用面密碼哈希
  8. 安全地存儲秘密
  9. 使用OWASP的ZAP測試應用程序
  10. 安全團隊進行代碼審查

9、Spring Boot配置加載順序(2.0)

1、properties文件;

2、YAML文件;

3、系統環境變量;

4、命令行參數;

等等……

配置屬性加載的順序如下:

1、開發者工具 `Devtools` 全局配置參數;2、單元測試上的 `@TestPropertySource` 注解指定的參數;3、單元測試上的 `@SpringBootTest` 注解指定的參數;4、命令行指定的參數,如 `java -jar springboot.jar --name="Java技術棧"`;5、命令行中的 `SPRING_APPLICATION_JSONJSON` 指定參數, 如 `java -Dspring.application.json='{"name":"Java技術棧"}' -jar springboot.jar`6、`ServletConfig` 初始化參數;7、`ServletContext` 初始化參數;8、JNDI參數(如 `java:comp/env/spring.application.json`);9、Java系統參數(來源:`System.getProperties()`);10、操作系統環境變量參數;11、`RandomValuePropertySource` 隨機數,僅匹配:`ramdom.*`;12、JAR包外面的配置文件參數(`application-{profile}.properties(YAML)`)13、JAR包里面的配置文件參數(`application-{profile}.properties(YAML)`)14、JAR包外面的配置文件參數(`application.properties(YAML)`)15、JAR包里面的配置文件參數(`application.properties(YAML)`)16、`@Configuration`配置文件上 `@PropertySource` 注解加載的參數;17、默認參數(通過 `SpringApplication.setDefaultProperties` 指定);

10、Spring Boot日志集成

Spring Boot日志框架

Spring Boot支持Java Util Logging,Log4j2,Lockback作為日志框架,如果你使用starters啟動器,Spring Boot將使用Logback作為默認日志框架。無論使用哪種日志框架,Spring Boot都支持配置將日志輸出到控制臺或者文件中。

spring-boot-starter啟動器包含spring-boot-starter-logging啟動器并集成了slf4j日志抽象及Logback日志框架。

11、Spring Boot讀取配置的幾種方式

讀取application文件

在application.yml或者properties文件中添加:

info.address=USA

info.company=Spring

info.degree=high

@Value注解讀物方式

import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Component;@Componentpublic class InfoConfig1 {@Value("${info.address}")private String address;@Value("${info.company}")private String company;@Value("${info.degree}")private String degree;public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}public String getCompany() {return company;}public void setCompany(String company) {this.company = company;}public String getDegree() {return degree;}public void setDegree(String degree) {this.degree = degree;}}

@ConfigurationProperties注解讀取方式

@Component@ConfigurationProperties(prefix = "info")public class InfoConfig2 {private String address;private String company;private String degree;public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}public String getCompany() {return company;}public void setCompany(String company) {this.company = company;}public String getDegree() {return degree;}public void setDegree(String degree) {this.degree = degree;}}

讀取指定文件

資源目錄下建立config/db-config.properties:

db.username=root

db.password=123456

@PropertySource+@Value注解讀取方式

@Component@PropertySource(value = { "config/db-config.properties" })public class DBConfig1 {@Value("${db.username}")private String username;@Value("${db.password}")private String password;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;}}

注意:@PropertySource不支持yml文件讀取。

@PropertySource+@ConfigurationProperties注解讀取方式

@Component@ConfigurationProperties(prefix = "db")@PropertySource(value = { "config/db-config.properties" })public class DBConfig2 {private String username;private String password;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;}}

Environment讀取方式

以上所有加載出來的配置都可以通過Environment注入獲取到。

@Autowiredprivate Environment env;// 獲取參數String getProperty(String key);

總結:從以上示例來看,Spring Boot可以通過@PropertySource,@Value,@Environment,@ConfigurationProperties來綁定變量。

12、Spring Boot自動配置原理

① SpringBoot啟動的時候加載主配置類,開啟了自動配置功能@EnableAutoConfiguration。

② @EnableAutoConfiguration的作用是利用AutoConfigurationImportSelector給容器中導入一些組件。

③ 可以查看public String[] selectImports(AnnotationMetadata annotationMetadata)方法的內容。

④ 通過protected List getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes)獲取候選的配置,這個是掃描所有jar包類路徑下"META-INF/spring.factories";

⑤ 然后把掃描到的這些文件包裝成Properties對象。

⑥ 從properties中獲取到EnableAutoConfiguration.class類名對應的值,然后把他們添加在容器中。

⑦ 整個過程就是將類路徑下"META-INF/spring.factories"里面配置的所有EnableAutoConfiguration的值加入到 容器中。

⑧ 每一個這樣XXAutoConfiguration類都是容器中的一個組件都加入到容器中,用他們來做自動配置。

每一個自動配置類進行自動配置功能,以HttpEncodingAutoConfiguration為例解釋自動配置原理

⑨根據當前不同的條件判斷,決定這個配置是否生效。

13、Spring Boot中的starter

? 首先,這個 Starter 并非什么新的技術點,基本上還是基于 Spring 已有功能來實現的。首先它提供了一個自動化配置類,一般命名為 XXXAutoConfiguration ,在這個配置類中通過條件注解來決定一個配置是否生效(條件注解就是 Spring 中原本就有的),然后它還會提供一系列的默認配置,也允許開發者根據實際情況自定義相關配置,然后通過類型安全的屬性注入將這些配置屬性注入進來,新注入的屬性會代替掉默認屬性。正因為如此,很多第三方框架,我們只需要引入依賴就可以直接使用了。 當然,開發者也可以自定義 Starter,自定義 Starter 可以參考:徒手擼一個 Spring Boot 中的 Starter ,解密自動化配置黑魔法!

14、Spring Boot跨域問題

? 跨域可以在前端通過 JSONP 來解決,但是 JSONP 只可以發送 GET 請求,無法發送其他類型的請求,在 RESTful 風格的應用中,就顯得非常雞肋,因此我們推薦在后端通過 (CORS,Cross-origin resource sharing) 來解決跨域問題。這種解決方案并非 Spring Boot 特有的,在傳統的 SSM 框架中,就可以通過 CORS 來解決跨域問題,只不過之前我們是在 XML 文件中配置 CORS ,現在則是通過 @CrossOrigin 注解來解決跨域問題。關于 CORS ,小伙伴們可以參考:Spring Boot 中通過 CORS 解決跨域問題

15、Spring Boot定時任務

? 使用spring boot創建定時任務主要有以下三種創建方式:

? 一、基于注解@Scheduled

? 默認為單線程,開啟多個任務時,任務的執行時機會受上一個任務執行時 間的影響。

? ① 創建定時器

@Configuration      //1.主要用于標記配置類,兼備Component的效果。
@EnableScheduling   // 2.開啟定時任務
public class SaticScheduleTask {//3.添加定時任務@Scheduled(cron = "0/5 * * * * ?")//或直接指定時間間隔,例如:5秒//@Scheduled(fixedRate=5000)private void configureTasks() {System.err.println("執行靜態定時任務時間: " + LocalDateTime.now());}
}

②啟動測試

d35a442a3d73c4fb0a1cfead58076eef.png

? 顯然,使用@Scheduled 注解很方便,但缺點是當我們調整了執行周期的時候,需要重啟應用才能生效,這多少有些不方便。為了達到實時生效的效果,可以使用接口來完成定時任務。

? 二、基于接口(SchedulingConfigurer)

? 1、導入依賴包:

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId><version>2.0.4.RELEASE</version></parent><dependencies><dependency><!--添加Web依賴 --><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><!--添加MySql依賴 --><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><!--添加Mybatis依賴 配置mybatis的一些初始化的東西--><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>1.3.1</version></dependency><dependency><!-- 添加mybatis依賴 --><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.4.5</version><scope>compile</scope></dependency></dependencies>

? 2、添加數據庫記錄

開啟本地數據庫mysql,隨便打開查詢窗口,然后執行腳本內容,代碼如下:

DROP DATABASE IF EXISTS `socks`;
CREATE DATABASE `socks`;
USE `SOCKS`;
DROP TABLE IF EXISTS `cron`;
CREATE TABLE `cron`  (`cron_id` varchar(30) NOT NULL PRIMARY KEY,`cron` varchar(30) NOT NULL  
);
INSERT INTO `cron` VALUES ('1', '0/5 * * * * ?');

然后在項目中的application.yml添加數據源:

spring:datasource:url: jdbc:mysql://localhost:3306/socksusername: rootpassword: 123456

3、創建定時器

? 數據庫準備好數據之后,我們編寫定時任務,注意這里添加的是TriggerTask,目的是循環讀取我們在數據庫設置好的執行周期,以及執行相關定時任務的內容。

具體代碼如下:

@Configuration      //1.主要用于標記配置類,兼備Component的效果。
@EnableScheduling   // 2.開啟定時任務
public class DynamicScheduleTask implements SchedulingConfigurer {@Mapperpublic interface CronMapper {@Select("select cron from cron limit 1")public String getCron();}@Autowired      //注入mapper@SuppressWarnings("all")CronMapper cronMapper;/*** 執行定時任務.*/@Overridepublic void configureTasks(ScheduledTaskRegistrar taskRegistrar) {taskRegistrar.addTriggerTask(//1.添加任務內容(Runnable)() -> System.out.println("執行動態定時任務: " + LocalDateTime.now().toLocalTime()),//2.設置執行周期(Trigger)triggerContext -> {//2.1 從數據庫獲取執行周期String cron = cronMapper.getCron();//2.2 合法性校驗.if (StringUtils.isEmpty(cron)) {// Omitted Code ..}//2.3 返回執行周期(Date)return new CronTrigger(cron).nextExecutionTime(triggerContext);});}}

4、啟動測試

啟動應用后,查看控制臺,打印時間是我們預期的每10秒一次:

403ed8ecf61ffb144adf90f87837180d.png

然后打開Navicat ,將執行周期修改為每6秒執行一次,如圖:

d04ba80ce21febb30ed91c1a8ee0ded8.png

查看控制臺,發現執行周期已經改變,并且不需要我們重啟應用,十分方便。如圖:

efb9cee0f8e5533e58894dc7e8b5eb05.png

注意: 如果在數據庫修改時格式出現錯誤,則定時任務會停止,即使重新修改正確;此時只能重新啟動項目才能恢復。

三、基于注解設定多線程定時任務

1、創建多線程定時任務

//@Component注解用于對那些比較中立的類進行注釋;
//相對與在持久層、業務層和控制層分別采用 @Repository、@Service 和 @Controller 對分層中的類進行注釋
@Component
@EnableScheduling   // 1.開啟定時任務
@EnableAsync        // 2.開啟多線程
public class MultithreadScheduleTask {@Async@Scheduled(fixedDelay = 1000)  //間隔1秒public void first() throws InterruptedException {System.out.println("第一個定時任務開始 : " + LocalDateTime.now().toLocalTime() + "rn線程 : " + Thread.currentThread().getName());System.out.println();Thread.sleep(1000 * 10);}@Async@Scheduled(fixedDelay = 2000)public void second() {System.out.println("第二個定時任務開始 : " + LocalDateTime.now().toLocalTime() + "rn線程 : " + Thread.currentThread().getName());System.out.println();}}

2、啟動測試查看控制臺

eb5b5913c775e8a5e96ead1bd2be8fbd.png

從控制臺可以看出,第一個定時任務和第二個定時任務互不影響;

并且,由于開啟了多線程,第一個任務的執行時間也不受其本身執行時間的限制,所以需要注意可能會出現重復操作導致數據異常

16、Spring Boot 全局異常處理

1、基于@ControllerAdvice注解的Controller層

創建 MyControllerAdvice,并添加 @ControllerAdvice注解。

package com.shsxt.demo.controller;import org.springframework.ui.Model;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;/*** controller 增強器* @author sam* @since 2017/7/17*/
@ControllerAdvice
public class MyControllerAdvice {/*** 應用到所有@RequestMapping注解方法,在其執行之前初始化數據綁定器* @param binder*/@InitBinderpublic void initBinder(WebDataBinder binder) {}/*** 把值綁定到Model中,使全局@RequestMapping可以獲取到該值* @param model*/@ModelAttributepublic void addAttributes(Model model) {model.addAttribute("author", "Magical Sam");}/*** 全局異常捕捉處理* @param ex* @return*/@ResponseBody@ExceptionHandler(value = Exception.class)public Map errorHandler(Exception ex) {Map map = new HashMap();map.put("code", 100);map.put("msg", ex.getMessage());return map;}}

啟動應用后,被 @ExceptionHandler、@InitBinder、@ModelAttribute 注解的方法,都會作用在 被 @RequestMapping 注解的方法上。

@ModelAttribute:在Model上設置的值,對于所有被 @RequestMapping 注解的方法中,都可以通過 ModelMap 獲取,如下:

@RequestMapping("/home")
public String home(ModelMap modelMap) {System.out.println(modelMap.get("author"));
}//或者 通過@ModelAttribute獲取@RequestMapping("/home")
public String home(@ModelAttribute("author") String author) {System.out.println(author);
}

@ExceptionHandler 攔截了異常,我們可以通過該注解實現自定義異常處理。其中,@ExceptionHandler 配置的 value 指定需要攔截的異常類型,上面攔截了 Exception.class 這種異常。

2、基于Springboot自身的全局異常統一處理,主要是實現ErrorController接口或者繼承AbstractErrorController抽象類或者繼承BasicErrorController類

Controller層代碼:

@Controller
@RequestMapping(value = "error")
@EnableConfigurationProperties({ServerProperties.class})
public class ExceptionController implements ErrorController {private ErrorAttributes errorAttributes;@Autowiredprivate ServerProperties serverProperties;/*** 初始化ExceptionController* @param errorAttributes*/@Autowiredpublic ExceptionController(ErrorAttributes errorAttributes) {Assert.notNull(errorAttributes, "ErrorAttributes must not be null");this.errorAttributes = errorAttributes;}/*** 定義404的ModelAndView* @param request* @param response* @return*/@RequestMapping(produces = "text/html",value = "404")public ModelAndView errorHtml404(HttpServletRequest request,HttpServletResponse response) {response.setStatus(getStatus(request).value());Map<String, Object> model = getErrorAttributes(request,isIncludeStackTrace(request, MediaType.TEXT_HTML));return new ModelAndView("error/404", model);}/*** 定義404的JSON數據* @param request* @return*/@RequestMapping(value = "404")@ResponseBodypublic ResponseEntity<Map<String, Object>> error404(HttpServletRequest request) {Map<String, Object> body = getErrorAttributes(request,isIncludeStackTrace(request, MediaType.TEXT_HTML));HttpStatus status = getStatus(request);return new ResponseEntity<Map<String, Object>>(body, status);}/*** 定義500的ModelAndView* @param request* @param response* @return*/@RequestMapping(produces = "text/html",value = "500")public ModelAndView errorHtml500(HttpServletRequest request,HttpServletResponse response) {response.setStatus(getStatus(request).value());Map<String, Object> model = getErrorAttributes(request,isIncludeStackTrace(request, MediaType.TEXT_HTML));return new ModelAndView("error/500", model);}/*** 定義500的錯誤JSON信息* @param request* @return*/@RequestMapping(value = "500")@ResponseBodypublic ResponseEntity<Map<String, Object>> error500(HttpServletRequest request) {Map<String, Object> body = getErrorAttributes(request,isIncludeStackTrace(request, MediaType.TEXT_HTML));HttpStatus status = getStatus(request);return new ResponseEntity<Map<String, Object>>(body, status);}/*** Determine if the stacktrace attribute should be included.* @param request the source request* @param produces the media type produced (or {@code MediaType.ALL})* @return if the stacktrace attribute should be included*/protected boolean isIncludeStackTrace(HttpServletRequest request,MediaType produces) {ErrorProperties.IncludeStacktrace include = this.serverProperties.getError().getIncludeStacktrace();if (include == ErrorProperties.IncludeStacktrace.ALWAYS) {return true;}if (include == ErrorProperties.IncludeStacktrace.ON_TRACE_PARAM) {return getTraceParameter(request);}return false;}/*** 獲取錯誤的信息* @param request* @param includeStackTrace* @return*/private Map<String, Object> getErrorAttributes(HttpServletRequest request,boolean includeStackTrace) {RequestAttributes requestAttributes = new ServletRequestAttributes(request);return this.errorAttributes.getErrorAttributes(requestAttributes,includeStackTrace);}/*** 是否包含trace* @param request* @return*/private boolean getTraceParameter(HttpServletRequest request) {String parameter = request.getParameter("trace");if (parameter == null) {return false;}return !"false".equals(parameter.toLowerCase());}/*** 獲取錯誤編碼* @param request* @return*/private HttpStatus getStatus(HttpServletRequest request) {Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");if (statusCode == null) {return HttpStatus.INTERNAL_SERVER_ERROR;}try {return HttpStatus.valueOf(statusCode);}catch (Exception ex) {return HttpStatus.INTERNAL_SERVER_ERROR;}}/*** 實現錯誤路徑,暫時無用* @see ExceptionMvcAutoConfiguration#containerCustomizer()* @return*/@Overridepublic String getErrorPath() {return "";}}

3、基于AOP也可以實現異常的全局處理

在執行切點中配置的路徑中的方法有異常時,可以被捕獲到

建議使用該方式:選用AOP方式主要是因為AOP不只可以做全局異常統一處理還可以統一打印接口請求入參和返回結果日志,打印接口訪問性能日志,處理sql注入攻擊以及處理入參特殊字符等問題

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Stopwatch;
import com.google.common.collect.Lists;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;/*** @Author: smy* @Description: 調用接口打印性能日志以及接口報錯之后記錄錯誤日志* @Date: 2018/9/20* @Time: 15:16*/
@Component
@Aspect
public class InterfaceRequestErrrorAndPerformanceLog {public static final Logger logger = LoggerFactory.getLogger(InterfaceRequestErrrorAndPerformanceLog.class);@Value("${dc.log.bad.value:3000}")private int performanceBadValue;@Resourceprivate RabbitMQService rabbitMQService;@Resourceprivate InterfaceErrorService interfaceErrorService;@Pointcut("execution(* test.test.test.test.test.controller.*.*.*(..))")public void pointCut(){}@Around("pointCut()")public APIResponse handleControllerMethod(ProceedingJoinPoint pjp) throws Throwable{Stopwatch stopwatch = Stopwatch.createStarted();APIResponse apiResponse;try {logger.info("執行Controller開始: " + pjp.getSignature() + " 參數:" + Lists.newArrayList(pjp.getArgs()).toString());//處理入參特殊字符和sql注入攻擊checkRequestParam(pjp);//執行訪問接口操作apiResponse = (APIResponse) pjp.proceed(pjp.getArgs());try{logger.info("執行Controller結束: " + pjp.getSignature() + ", 返回值:" + JSONObject.toJSONString(apiResponse));//此處將日志打印放入try-catch是因為項目中有些對象實體bean過于復雜,導致序列化為json的時候報錯,但是此處報錯并不影響主要功能使用,只是返回結果日志沒有打印,所以catch中也不做拋出異常處理}catch (Exception ex){logger.error(pjp.getSignature()+" 接口記錄返回結果失敗!,原因為:{}",ex.getMessage());}Long consumeTime = stopwatch.stop().elapsed(TimeUnit.MILLISECONDS);logger.info("耗時:" + consumeTime + "(毫秒).");//當接口請求時間大于3秒時,標記為異常調用時間,并記錄入庫if(consumeTime > performanceBadValue){DcPerformanceEntity dcPerformanceEntity = new DcPerformanceEntity();dcPerformanceEntity.setInterfaceName(pjp.getSignature().toString());dcPerformanceEntity.setRequestParam(Lists.newArrayList(pjp.getArgs()).toString());dcPerformanceEntity.setConsumeTime(consumeTime + "毫秒");RabbitMQMessageTarget mqTarget = RabbitMQMessageTarget.createFanoutTarget(ProjectConstants.DC_KEY_EXCHANGE_PERFORMANCE, new String[] { ProjectConstants.DC_KEY_QUEUE_PERFORMANCE});rabbitMQService.send(mqTarget, JSON.toJSONString(dcPerformanceEntity));}} catch (Exception throwable) {apiResponse = handlerException(pjp, throwable);}return apiResponse;}/*** @Author: smy* @Description: 處理接口調用異常* @Date: 15:13 2018/10/25*/private APIResponse handlerException(ProceedingJoinPoint pjp, Throwable e) {APIResponse apiResponse;if(e.getClass().isAssignableFrom(ProjectException.class) ){//ProjectException為自定義異常類,項目中Controller層會把所有的異常都catch掉,并手工封裝成ProjectException拋出來,這樣做的目的是ProjectException會記錄拋出異常接口的路徑,名稱以及請求參數等等,有助于錯誤排查ProjectException projectException = (ProjectException)e;logger.error("捕獲到ProjectException異常:",JSONObject.toJSONString(projectException.getDcErrorEntity()));RabbitMQMessageTarget mqTarget = RabbitMQMessageTarget.createFanoutTarget(ProjectConstants.DC_KEY_EXCHANGE_INTERFACE_ERROR, new String[] { ProjectConstants.DC_KEY_QUEUE_INTERFACE_ERROR});rabbitMQService.send(mqTarget, JSON.toJSONString(dataCenterException.getDcErrorEntity()));apiResponse = new APIResponse(APIResponse.FAIL,null,projectException.getDcErrorEntity().getErrorMessage());} else if (e instanceof RuntimeException) {logger.error("RuntimeException{方法:" + pjp.getSignature() + ", 參數:" + pjp.getArgs() + ",異常:" + e.getMessage() + "}", e);apiResponse = new APIResponse(APIResponse.FAIL,null,e.getMessage());} else {logger.error("異常{方法:" + pjp.getSignature() + ", 參數:" + pjp.getArgs() + ",異常:" + e.getMessage() + "}", e);apiResponse = new APIResponse(APIResponse.FAIL,null,e.getMessage());}return apiResponse;}/*** @Author: gmy* @Description: 處理入參特殊字符和sql注入攻擊* @Date: 15:37 2018/10/25*/private void checkRequestParam(ProceedingJoinPoint pjp){String str = String.valueOf(pjp.getArgs());if (!IllegalStrFilterUtil.sqlStrFilter(str)) {logger.info("訪問接口:" + pjp.getSignature() + ",輸入參數存在SQL注入風險!參數為:" + Lists.newArrayList(pjp.getArgs()).toString());DcErrorEntity dcErrorEntity = interfaceErrorService.processDcErrorEntity(pjp.getSignature() + "",Lists.newArrayList(pjp.getArgs()).toString(),"輸入參數存在SQL注入風險!");throw new DataCenterException(dcErrorEntity);}if (!IllegalStrFilterUtil.isIllegalStr(str)) {logger.info("訪問接口:" + pjp.getSignature() + ",輸入參數含有非法字符!,參數為:" + Lists.newArrayList(pjp.getArgs()).toString());DcErrorEntity dcErrorEntity = interfaceErrorService.processDcErrorEntity(pjp.getSignature() + "",Lists.newArrayList(pjp.getArgs()).toString(),"輸入參數含有非法字符!");throw new DataCenterException(dcErrorEntity);}}}

IllegalStrFilterUtil代碼:

import org.slf4j.LoggerFactory;import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** @Author: gmy* @Description: 特殊字符檢測工具(防止傳入非法字符和sql注入攻擊)* @Date: 2018/10/25* @Time: 15:08*/
public class IllegalStrFilterUtil {private static final org.slf4j.Logger Logger = LoggerFactory.getLogger(IllegalStrFilterUtil.class);private static final String REGX = "!|!|@|◎|#|#|($)|¥|%|%|(^)|……|(&)|※|(*)|×|(()|(|())|)|_|——|(+)|+|(|)|§ ";/*** 對常見的sql注入攻擊進行攔截** @param sInput* @return*  true 表示參數不存在SQL注入風險*  false 表示參數存在SQL注入風險*/public static Boolean sqlStrFilter(String sInput) {if (sInput == null || sInput.trim().length() == 0) {return false;}sInput = sInput.toUpperCase();if (sInput.indexOf("DELETE") >= 0 || sInput.indexOf("ASCII") >= 0 || sInput.indexOf("UPDATE") >= 0 || sInput.indexOf("SELECT") >= 0|| sInput.indexOf("'") >= 0 || sInput.indexOf("SUBSTR(") >= 0 || sInput.indexOf("COUNT(") >= 0 || sInput.indexOf(" OR ") >= 0|| sInput.indexOf(" AND ") >= 0 || sInput.indexOf("DROP") >= 0 || sInput.indexOf("EXECUTE") >= 0 || sInput.indexOf("EXEC") >= 0|| sInput.indexOf("TRUNCATE") >= 0 || sInput.indexOf("INTO") >= 0 || sInput.indexOf("DECLARE") >= 0 || sInput.indexOf("MASTER") >= 0) {Logger.error("該參數怎么SQL注入風險:sInput=" + sInput);return false;}Logger.info("通過sql檢測");return true;}/*** 對非法字符進行檢測** @param sInput* @return*  true 表示參數不包含非法字符*  false 表示參數包含非法字符*/public static Boolean isIllegalStr(String sInput) {if (sInput == null || sInput.trim().length() == 0) {return false;}sInput = sInput.trim();Pattern compile = Pattern.compile(REGX, Pattern.CASE_INSENSITIVE);Matcher matcher = compile.matcher(sInput);Logger.info("通過字符串檢測");return matcher.find();}
}

17、spring-boot-starter-parent 的作用

  1. 定義了 Java 編譯版本為 1.8 。
  2. 使用 UTF-8 格式編碼。
  3. 繼承自 spring-boot-dependencies,這個里邊定義了依賴的版本,也正是因為繼承了這個依賴,所以我們在寫依賴時才不需要寫版本號。
  4. 執行打包操作的配置。
  5. 自動化的資源過濾。
  6. 自動化的插件配置。
  7. 針對 application.properties 和 application.yml 的資源過濾,包括通過 profile 定義的不同環境的配置文件,例如 application-dev.properties 和 application-dev.yml。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/383052.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/383052.shtml
英文地址,請注明出處:http://en.pswp.cn/news/383052.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

架構的演變

基本概念 在介紹架構之前&#xff0c;為了避免部分讀者對架構設計中的一些概念不了解&#xff0c;下面對幾個最基礎的概念進行介紹。 1.什么是分布式&#xff1f; 系統中的多個模塊在不同服務器上部署&#xff0c;即可稱為分布式系統&#xff0c;如Tomcat和數據庫分別部署在…

axure8.0導出頁面打不開問題_excel怎么轉pdf?excel打不開?轉換成PDF就行了

excel轉pdf怎么做&#xff1f;年底最后一天了&#xff0c;我都被一堆的Excel文件搞得頭疼&#xff0c;在這些時間里&#xff0c;要讓我對幾個G的文件進行操作&#xff0c;我已經是忙得不可開交&#xff0c;而在最后的最后&#xff0c;我的主管還說他的電腦無法打開我的Excel 了…

質數相關問題

試除法判定質數 題目描述 給定n個正整數ai&#xff0c;判定每個數是否是質數。 輸入格式 第一行包含整數n。 接下來n行&#xff0c;每行包含一個正整數ai。 輸出格式 共n行&#xff0c;其中第 i 行輸出第 i 個正整數ai是否為質數&#xff0c;是則輸出“Yes”&#xff0c…

python怎么爬蟲理數據_Python神技能 | 使用爬蟲獲取汽車之家全車型數據

最近想在工作相關的項目上做技術改進&#xff0c;需要全而準的車型數據&#xff0c;尋尋覓覓而不得&#xff0c;所以就只能自己動手豐衣足食&#xff0c;到網上獲&#xff08;竊&#xff09;得&#xff08;取&#xff09;數據了。汽車之家是大家公認的數據做的比較好的汽車網站…

linux運算_CentOS「linux」學習筆記22:算術運算符、邏輯運算符、關系運算符

?linux基礎操作&#xff1a;主要介紹啦算術運算符、邏輯運算符、關系運算符1.算術運算符[主要用來計算數值]注意使用expr運算時運算符和數值之間需要有空格&#xff0c;其他方式運算時不能有空格。常用算術運算符號&#xff1a;表示相加&#xff0c;&#xff0d;表示相減&…

python實現小型搜索引擎設計_基于JAVA的中小型飯店餐飲管理系統的設計與實現...

好程序設計擅長JAVA(SSM,SSH,SPRINGBOOT)、PYTHON(DJANGO/FLASK)、THINKPHP、C#、安卓、微信小程序、MYSQL、SQLSERVER等&#xff0c;歡迎咨詢今天將為大家分析一個中小型飯店餐飲管理系統(俗話說“民以食為天”,中國的飲食文化有著久遠的歷史。“吃”不僅僅指的是填飽肚子,它早…

評估報告有效期過期了怎么辦_托福成績過期了怎么辦?

托福成績是有期限的&#xff0c;考生申請美國大學的時候也只能在托福成績有效期內。所以考托福的時候一定要關注一下托福成績什么時候過期&#xff0c;以及大學申請的截止日期&#xff0c;提前做好安排。下面我們一起看看關于托福成績有效期的相關問題。托福成績有效期是多久&a…

sql語句的經典練習

表結構 –1.學生表 Student(s_id,s_name,s_birth,s_sex) –學生編號,學生姓名, 出生年月,學生性別 –2.課程表 Course(c_id,c_name,t_id) – –課程編號, 課程名稱, 教師編號 –3.教師表 Teacher(t_id,t_name) –教師編號,教師姓名 –4.成績表 Score(s_id,c_id,s_score…

四階龍格庫塔法的基本思想_數值常微分方程-歐拉法與龍格-庫塔法

大三時候在跳蚤市場閑逛&#xff0c;從一位數學院的學長那里買了一些閑書&#xff0c;最近翻出來剛好有李榮華、劉播老師的《微分方程數值解法》和王仁宏老師的《數值逼近》&#xff0c;結合周善貴老師的《計算物理》課程&#xff0c;整理一下筆記。本文整理常微分方程數值求解…

OC中的類

OC中類 OC中類的定義 在Xcode中創建一個新的類&#xff0c;會自動給你生成兩個文件一個是.h另外一個是.m文件,你新創建的類默認繼承了NSObject類&#xff0c;因為有一些方法都需要基類中的方法。比如alloc分配內存 OC中用來描述類的使用interface 類名&#xff1a;父類來進行…

裝配組件_基于Haption力反饋系統的交互式裝配仿真

在一個新工業產品的設計過程中&#xff0c;裝配規劃是非常重要的任務。如果規劃不好將造成很大的資金浪費&#xff0c;致使組件不能正確地集成。例如典型問題&#xff1a;移動一個組件到指定位置但空間不足&#xff1b;使用工具夠不到螺絲&#xff1b;操作者沒有足夠的視域以保…

OC中的基本容器和基本數據類型

基本數據類型 NSRange 是一個結構體&#xff0c;里面有兩個數據成員數據類型都為NSUInteger 就是c語言中的無符號整形&#xff0c;一個是location表示集合的起始地址&#xff0c;另外一個變量是length表示從起始地址開始算多少個元素。 NSRange的三種創建方式 //1.NSRange r…

python程序開發總結_python開發總結

兩本不錯的書&#xff1a;《Python參考手冊》&#xff1a;對Python各個標準模塊&#xff0c;特性介紹的比較詳細。《Python核心編程》&#xff1a;介紹的比較深入&#xff0c;關鍵是&#xff0c;對Python很多高級特性都有介紹。一個開源代碼&#xff1a;openstack&#xff0c;關…

Centos7通過yum安裝jsoncpp庫

拒絕下載軟件包 一堆網上下載安裝包&#xff0c;為了編譯暗轉包又下載插件&#xff0c;是真麻煩 看看有沒有jsoncpp的相關庫 $ yum list | grep jsoncpp-devel然后執行這兩句&#xff0c;就完了 yum install jsoncpp.x86_64 yum install jsoncpp.devel.x86-64多簡單

作為唯一索引_Mysql什么情況下不走索引?

本文基于Mysql5.7版本和InnoDB存儲引擎。1、InnoDB索引組織表在InnoDB引擎中&#xff0c;表都是按照主鍵順序組織存放的&#xff0c;這種存放方式的表稱為索引組織表。InnoDB存儲引擎中的表&#xff0c;都有主鍵&#xff0c;如果沒有顯式聲明主鍵&#xff0c;則采取以下措施&am…

python捕獲全局異常統一管理_python中如何用sys.excepthook來對全局異常進行捕獲、顯示及輸出到error日志中...

使用sys.excepthook函數進行全局異常的獲取。1. 使用MessageDialog實現異常顯示&#xff1b;2. 使用logger把捕獲的異常信息輸出到日志中&#xff1b;步驟&#xff1a;定義異常處理函數&#xff0c; 并使用該函來替換掉系統的內置處理函數&#xff1b;對于threading.py的異常捕…

r語言系統計算上是奇異的_R語言實現并行計算

Python作為多線程的編程語言在并行方面相對于R語言有很大的優勢&#xff0c;然而作為占據統計分析一席之地的R語言自然不能沒有并行計算的助力。那么我們來看下在R語言中有哪些并行的包&#xff1a;隱式并行&#xff1a;OpenBLAS&#xff0c;Intel MKL&#xff0c;NVIDIA cuBLA…

cansina 目錄_dirmap - 一個高級web目錄、文件掃描工具-華盟網

Dirmap一個高級web目錄掃描工具&#xff0c;功能將會強于DirBuster、Dirsearch、cansina、御劍需求分析經過大量調研&#xff0c;總結一個優秀的web目錄掃描工具至少具備以下功能&#xff1a;并發引擎能使用字典能純爆破能爬取頁面動態生成字典能fuzz掃描自定義請求自定義響應結…

唯有自己變得強大_物競天擇,適者生存,唯有強大自己,方能百毒不侵

物競天擇&#xff0c;適者生存&#xff0c;這是亙古不變的道理。面對生活中的困難&#xff0c;人生路上的挫折&#xff0c;我們只有足夠堅強&#xff0c;足夠勇敢&#xff0c;足夠強大&#xff0c;才能戰勝這一切。人活著要明白&#xff0c;你所有的負面&#xff0c;都源于你的…

樹莓派c語言運行_樹莓派完成簡單的編程(四)

在上一篇文章中&#xff0c;我們學習了Vi文本編輯器&#xff0c;那么用它可以實現什么功能呢&#xff1f;樹莓派python以及c語言編程這里我選擇了最簡單和很流行的兩種編程語言&#xff1a;C語言和Python。實現最簡單的功能&#xff0c;輸出hello world。Python編程簡介Python是…