日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

框架controller找不到_SpingBoot框架知识详解

發(fā)布時間:2023/11/30 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 框架controller找不到_SpingBoot框架知识详解 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

Spring boot框架

1、什么是Spring Boot?

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

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

2、Spring Boot的優(yōu)點(diǎn)與特點(diǎn)

優(yōu)點(diǎn):

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

特點(diǎn):

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

3、Spring Boot的配置文件以及之間的區(qū)別

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

1).properties

app.user.name = javastack

2).yml

app:user:name: javastack

注:.yml 格式不支持 @PropertySource 注解導(dǎo)入配置。

4、Spring Boot的常用注解

@SpringBootApplication:

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

@ResponseBody:

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

@Controller:

用于定義控制器類,在spring項(xiàng)目中由控制器負(fù)責(zé)將用戶發(fā)來的URL請求轉(zhuǎn)發(fā)到對應(yīng)的服務(wù)接口(service層),一般這個注解在類中,通常方法需要配合注解@RequestMapping。

@RestController:

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

@RequestMapping:

提供路由信息,負(fù)責(zé)URL到Controller中的具體函數(shù)的映射。

@EnableAutoConfiguration:

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

@ComponentScan:

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

@Configuration:

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

@Import:用來導(dǎo)入其他配置類。

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

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

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

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

@Inject:等價于默認(rèn)的@Autowired,只是沒有required屬性;

@Component:泛指組件,當(dāng)組件不好歸類的時候,我們可以使用這個注解進(jìn)行標(biāo)注。

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

@AutoWired:

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

@Qualifier:

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

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

5、spring boot開啟的兩種方式

? ① 繼承spring-boot-starter-parent項(xiàng)目

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

? ② 導(dǎo)入spring-boot-dependencies項(xiàng)目依賴

<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依賴注意點(diǎn)

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

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

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

如果導(dǎo)入的方式要實(shí)現(xiàn)版本的升級,達(dá)到上面的效果,這樣也可以做到,把要升級的組件依賴放到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區(qū)別開來,此時要用@...@包起來,不然無效。另外,@...@占位符在yaml文件編輯器中編譯報錯,所以使用繼承方式有諸多問題,坑要慢慢趟。

6、Spring Boot的幾種運(yùn)行方式

  • 打包用命令或者放到容器中運(yùn)行
  • 用 Maven/ Gradle 插件運(yùn)行
  • 直接執(zhí)行 main 方法運(yùn)行

7、Spring Boot實(shí)現(xiàn)熱部署

第一種引用devtools依賴

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

第二種自定義配置熱部署

# 熱部署開關(guān),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

注意事項(xiàng):

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

2、打包應(yīng)用默認(rèn)不會包含devtools,除非你禁用SpringBoot Maven插件的 excludeDevtools屬性。

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

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

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

8、保護(hù)Spring Boot的應(yīng)用方法

  • 在生產(chǎn)中使用HTTPS
  • 使用Snyk檢查你的依賴關(guān)系
  • 升級到最新版本
  • 啟動CSRF保護(hù)
  • 使用內(nèi)容安全策略防止XSS攻擊
  • 使用OpenID Connect進(jìn)行身份驗(yàn)證
  • 管理密碼使用面密碼哈希
  • 安全地存儲秘密
  • 使用OWASP的ZAP測試應(yīng)用程序
  • 安全團(tuán)隊進(jìn)行代碼審查
  • 9、Spring Boot配置加載順序(2.0)

    1、properties文件;

    2、YAML文件;

    3、系統(tǒng)環(huán)境變量;

    4、命令行參數(shù);

    等等……

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

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

    10、Spring Boot日志集成

    Spring Boot日志框架

    Spring Boot支持Java Util Logging,Log4j2,Lockback作為日志框架,如果你使用starters啟動器,Spring Boot將使用Logback作為默認(rèn)日志框架。無論使用哪種日志框架,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;// 獲取參數(shù)String getProperty(String key);

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

    12、Spring Boot自動配置原理

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

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

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

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

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

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

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

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

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

    ⑨根據(jù)當(dāng)前不同的條件判斷,決定這個配置是否生效。

    13、Spring Boot中的starter

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

    14、Spring Boot跨域問題

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

    15、Spring Boot定時任務(wù)

    ? 使用spring boot創(chuàng)建定時任務(wù)主要有以下三種創(chuàng)建方式:

    ? 一、基于注解@Scheduled

    ? 默認(rèn)為單線程,開啟多個任務(wù)時,任務(wù)的執(zhí)行時機(jī)會受上一個任務(wù)執(zhí)行時 間的影響。

    ? ① 創(chuàng)建定時器

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

    ②啟動測試

    ? 顯然,使用@Scheduled 注解很方便,但缺點(diǎn)是當(dāng)我們調(diào)整了執(zhí)行周期的時候,需要重啟應(yīng)用才能生效,這多少有些不方便。為了達(dá)到實(shí)時生效的效果,可以使用接口來完成定時任務(wù)。

    ? 二、基于接口(SchedulingConfigurer)

    ? 1、導(dǎo)入依賴包:

    <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、添加數(shù)據(jù)庫記錄

    開啟本地數(shù)據(jù)庫mysql,隨便打開查詢窗口,然后執(zhí)行腳本內(nèi)容,代碼如下:

    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 * * * * ?');

    然后在項(xiàng)目中的application.yml添加數(shù)據(jù)源:

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

    3、創(chuàng)建定時器

    ? 數(shù)據(jù)庫準(zhǔn)備好數(shù)據(jù)之后,我們編寫定時任務(wù),注意這里添加的是TriggerTask,目的是循環(huán)讀取我們在數(shù)據(jù)庫設(shè)置好的執(zhí)行周期,以及執(zhí)行相關(guān)定時任務(wù)的內(nèi)容。

    具體代碼如下:

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

    4、啟動測試

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

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

    查看控制臺,發(fā)現(xiàn)執(zhí)行周期已經(jīng)改變,并且不需要我們重啟應(yīng)用,十分方便。如圖:

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

    三、基于注解設(shè)定多線程定時任務(wù)

    1、創(chuàng)建多線程定時任務(wù)

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

    2、啟動測試查看控制臺

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

    并且,由于開啟了多線程,第一個任務(wù)的執(zhí)行時間也不受其本身執(zhí)行時間的限制,所以需要注意可能會出現(xiàn)重復(fù)操作導(dǎo)致數(shù)據(jù)異常

    16、Spring Boot 全局異常處理

    1、基于@ControllerAdvice注解的Controller層

    創(chuàng)建 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 增強(qiáng)器* @author sam* @since 2017/7/17*/ @ControllerAdvice public class MyControllerAdvice {/*** 應(yīng)用到所有@RequestMapping注解方法,在其執(zhí)行之前初始化數(shù)據(jù)綁定器* @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;}}

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

    @ModelAttribute:在Model上設(shè)置的值,對于所有被 @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 攔截了異常,我們可以通過該注解實(shí)現(xiàn)自定義異常處理。其中,@ExceptionHandler 配置的 value 指定需要攔截的異常類型,上面攔截了 Exception.class 這種異常。

    2、基于Springboot自身的全局異常統(tǒng)一處理,主要是實(shí)現(xiàn)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數(shù)據(jù)* @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;}}/*** 實(shí)現(xiàn)錯誤路徑,暫時無用* @see ExceptionMvcAutoConfiguration#containerCustomizer()* @return*/@Overridepublic String getErrorPath() {return "";}}

    3、基于AOP也可以實(shí)現(xiàn)異常的全局處理

    在執(zhí)行切點(diǎn)中配置的路徑中的方法有異常時,可以被捕獲到

    建議使用該方式:選用AOP方式主要是因?yàn)锳OP不只可以做全局異常統(tǒng)一處理還可以統(tǒng)一打印接口請求入?yún)⒑头祷亟Y(jié)果日志,打印接口訪問性能日志,處理sql注入攻擊以及處理入?yún)⑻厥庾址葐栴}

    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: 調(diào)用接口打印性能日志以及接口報錯之后記錄錯誤日志* @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("執(zhí)行Controller開始: " + pjp.getSignature() + " 參數(shù):" + Lists.newArrayList(pjp.getArgs()).toString());//處理入?yún)⑻厥庾址蛃ql注入攻擊checkRequestParam(pjp);//執(zhí)行訪問接口操作apiResponse = (APIResponse) pjp.proceed(pjp.getArgs());try{logger.info("執(zhí)行Controller結(jié)束: " + pjp.getSignature() + ", 返回值:" + JSONObject.toJSONString(apiResponse));//此處將日志打印放入try-catch是因?yàn)轫?xiàng)目中有些對象實(shí)體bean過于復(fù)雜,導(dǎo)致序列化為json的時候報錯,但是此處報錯并不影響主要功能使用,只是返回結(jié)果日志沒有打印,所以catch中也不做拋出異常處理}catch (Exception ex){logger.error(pjp.getSignature()+" 接口記錄返回結(jié)果失敗!,原因?yàn)?#xff1a;{}",ex.getMessage());}Long consumeTime = stopwatch.stop().elapsed(TimeUnit.MILLISECONDS);logger.info("耗時:" + consumeTime + "(毫秒).");//當(dāng)接口請求時間大于3秒時,標(biāo)記為異常調(diào)用時間,并記錄入庫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: 處理接口調(diào)用異常* @Date: 15:13 2018/10/25*/private APIResponse handlerException(ProceedingJoinPoint pjp, Throwable e) {APIResponse apiResponse;if(e.getClass().isAssignableFrom(ProjectException.class) ){//ProjectException為自定義異常類,項(xiàng)目中Controller層會把所有的異常都catch掉,并手工封裝成ProjectException拋出來,這樣做的目的是ProjectException會記錄拋出異常接口的路徑,名稱以及請求參數(shù)等等,有助于錯誤排查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() + ", 參數(shù):" + pjp.getArgs() + ",異常:" + e.getMessage() + "}", e);apiResponse = new APIResponse(APIResponse.FAIL,null,e.getMessage());} else {logger.error("異常{方法:" + pjp.getSignature() + ", 參數(shù):" + pjp.getArgs() + ",異常:" + e.getMessage() + "}", e);apiResponse = new APIResponse(APIResponse.FAIL,null,e.getMessage());}return apiResponse;}/*** @Author: gmy* @Description: 處理入?yún)⑻厥庾址蛃ql注入攻擊* @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() + ",輸入?yún)?shù)存在SQL注入風(fēng)險!參數(shù)為:" + Lists.newArrayList(pjp.getArgs()).toString());DcErrorEntity dcErrorEntity = interfaceErrorService.processDcErrorEntity(pjp.getSignature() + "",Lists.newArrayList(pjp.getArgs()).toString(),"輸入?yún)?shù)存在SQL注入風(fēng)險!");throw new DataCenterException(dcErrorEntity);}if (!IllegalStrFilterUtil.isIllegalStr(str)) {logger.info("訪問接口:" + pjp.getSignature() + ",輸入?yún)?shù)含有非法字符!,參數(shù)為:" + Lists.newArrayList(pjp.getArgs()).toString());DcErrorEntity dcErrorEntity = interfaceErrorService.processDcErrorEntity(pjp.getSignature() + "",Lists.newArrayList(pjp.getArgs()).toString(),"輸入?yún)?shù)含有非法字符!");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注入攻擊進(jìn)行攔截** @param sInput* @return* true 表示參數(shù)不存在SQL注入風(fēng)險* false 表示參數(shù)存在SQL注入風(fēng)險*/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("該參數(shù)怎么SQL注入風(fēng)險:sInput=" + sInput);return false;}Logger.info("通過sql檢測");return true;}/*** 對非法字符進(jìn)行檢測** @param sInput* @return* true 表示參數(shù)不包含非法字符* false 表示參數(shù)包含非法字符*/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 的作用

  • 定義了 Java 編譯版本為 1.8 。
  • 使用 UTF-8 格式編碼。
  • 繼承自 spring-boot-dependencies,這個里邊定義了依賴的版本,也正是因?yàn)槔^承了這個依賴,所以我們在寫依賴時才不需要寫版本號。
  • 執(zhí)行打包操作的配置。
  • 自動化的資源過濾。
  • 自動化的插件配置。
  • 針對 application.properties 和 application.yml 的資源過濾,包括通過 profile 定義的不同環(huán)境的配置文件,例如 application-dev.properties 和 application-dev.yml。
  • 總結(jié)

    以上是生活随笔為你收集整理的框架controller找不到_SpingBoot框架知识详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。