簡介:
這節聊一聊項目的後端,之前也講過,因爲對前端不熟悉,所以在前端花了太多的時間,導致到後端開發的時候搞的人有點疲,所以很多東西從簡了,很多細節東西沒有考慮,只想着把基本功能做出來就好了。框架選擇的是現在比較流行的Springboot+Mybatis+Tomcat+MySQL,Springboot是在Spring的基礎上做了集成和配置簡化,使用起來超級舒服。
一、搭建Springboot框架
1. 框架簡介
Springboot是現在非常火熱的JavaEE框架,其設計目的是用來簡化新Spring應用的初始搭建以及開發過程,爲什麼這麼說呢,看一下他的幾個特點:
>>> 嵌入的Tomcat,無需部署WAR文件
>>> 簡化Maven配置
>>> 自動配置Spring
>>> 提供生產就緒型功能,如指標,健康檢查和外部配置
>>> 對XML也沒有配置要求,比較靈活
然後我們選擇Maven來管理我們的JAR包,這個沒什麼好說的,以後我們用一個pom文件就可以走天下了,不用在揹着沉重的JAR包到處跑。
2. Springboot項目初始化
快速創建Springboot可以使用 https://start.spring.io/網頁版生成項目然後導入Eclipse中或者使用IDEA創建,這裏我們選擇前者:首先我們訪問 https://start.spring.io/ ,然後按照順序選擇填寫:
1. 項目管理工具,現在一般使用Maven來管理
2. 項目開發語言
3. 這是Springboot版本
4. 項目唯一標識,可以用來確定下面的包名
5. 項目名稱
6. 項目包名、打包方式、java版本
7. 項目依賴:下面勾選對應的依賴,這裏我只選了web可以視自己情況而定
8. 生成項目
這裏直接生成yytf.zip包,我們解壓後用eclipse使用導入maven項目方式導入項目(這裏要注意剛導入後項目目錄不是下圖中這種結構,要等一會jar包下載完成後纔會形成如下圖結構),如下圖目錄就是導入後的目錄結構,而pom文件裏的參數也是我們之前在網頁填寫的對號入座。
然後我們找到項目的啓動文件,這裏是BlogApplication.java,右鍵run as使用java application運行,控制檯出現Started BlogApplication in 2.101 seconds (JVM running for 2.455)則springboot啓動成功。
二、項目配置
1. 配置pom.xml引入需要的JAR包
<dependencies> <!-- springboot啓動依賴 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <!-- 排除Springboot自帶的日誌工具 --> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> <!-- 使用外部TOMCAT方式一:排除Springboot自帶的tomcat --> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <!-- 使用外部TOMCAT方式二:添加provided --> <!-- provided:已提供依賴範圍。使用此依賴範圍的Maven依賴,對於編譯和測試classpath有效,但在運行時候無效 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope> </dependency> <!-- springboot整合log4j --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> </dependency> <!-- springboot整合mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.1</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.1</version> </dependency> <!-- springboot連接mysql驅動依賴 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.11</version> </dependency> <!-- springboot整合jwt的token驗證 --> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>2.2.0</version> </dependency> </dependencies> <!-- generate工具生成dao,mapper,xml --> <build> <finalName>blog</finalName> <resources> <resource> <directory>src/main/java</directory> <!-- 此配置不可缺,否則mybatis的Mapper.xml將會丟失 --> <includes> <include>**/*.xml</include> </includes> </resource> <!-- 指定資源的位置 --> <resource> <directory>src/main/resources</directory> </resource> </resources> </build>
2. 配置外部tomcat部署項目
2.1 maven依賴配置
首先我們把打包方式改成war,在pom.xml把<packaging>jar</packaging>修改成<packaging>war</packaging>,在上面的pom.xml中已經寫清楚兩種使用外部tomcat方式:第一種是去掉springboot內置的tomcat,第二中時在運行時使內部tomcat失效
2.2 修改啓動類
在Application.java中繼承SpringBootServletInitializer並重寫configure方法
package com.yytf; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.web.support.SpringBootServletInitializer; import org.springframework.scheduling.annotation.EnableScheduling; @MapperScan("com.yytf.dao") @SpringBootApplication @EnableScheduling public class Application extends SpringBootServletInitializer{ @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { return application.sources(Application.class); } public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
3.配置generatorConfig.xml自動生成vo,dao,mapper
3.1在pom.xml中修改build標籤,代碼上面已貼出
3.2 首先安裝eclipse插件:Mybatis Generator 1.3.5,Eclipse => Help => Eclipse Marketplace 搜索mybatis安裝最新版本
3.3 生成generatorConfig.xml文件
New一個文件,然後選擇mybatis generator configuration file,然後選擇到generatorConfig.xml文件要保存的位置,例如這裏保存到\blog\src\main\resources下面
3.4 配置generatorConfig.xml文件,這裏注意mysql驅動包要使用絕對路徑,targetProject爲生成文件位置,容易出錯:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"> <generatorConfiguration> <!-- 必須使用絕對路徑 --> <classPathEntry location="D:\Project\Blog\Repository\mysql\mysql-connector-java\5.1.6\mysql-connector-java-5.1.6.jar"/> <context id="blogTable" targetRuntime="MyBatis3"> <commentGenerator> <!-- 是否去除自動生成的註釋 true:是 : false:否 --> <property name="suppressAllComments" value="true" /> </commentGenerator> <!--數據庫連接的信息:驅動類、連接地址、用戶名、密碼 --> <!-- Oracle連接方式 --> <!-- <jdbcConnection driverClass="oracle.jdbc.OracleDriver" connectionURL="jdbc:oracle:thin:@000.000.000.000:1521:orcl" userId="root" password="********"> </jdbcConnection> --> <!-- Mysql連接方式 --> <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://000.000.000.000:3306/blog?useUnicode=true&characterEncoding=UTF-8" userId="root" password="********" /> <!-- 默認false,把JDBC DECIMAL 和 NUMERIC 類型解析爲 Integer,爲 true時把JDBC DECIMAL 和 NUMERIC 類型解析爲java.math.BigDecimal --> <javaTypeResolver> <property name="forceBigDecimals" value="false" /> </javaTypeResolver> <!-- targetProject:生成PO類的位置 --> <javaModelGenerator targetPackage="com.yytf.vo" targetProject="blog/src/main/java"> <!-- enableSubPackages:是否讓schema作爲包的後綴 --> <property name="enableSubPackages" value="false" /> <!-- 從數據庫返回的值被清理前後的空格 --> <property name="trimStrings" value="true" /> </javaModelGenerator> <!-- targetProject:mapper映射文件生成的位置 --> <sqlMapGenerator targetPackage="mapper" targetProject="blog/src/main/resources"> <!-- enableSubPackages:是否讓schema作爲包的後綴 --> <property name="enableSubPackages" value="false" /> </sqlMapGenerator> <!-- targetPackage:mapper接口生成的位置 --> <javaClientGenerator type="XMLMAPPER" targetPackage="com.yytf.dao" targetProject="blog/src/main/java"> <!-- enableSubPackages:是否讓schema作爲包的後綴 --> <property name="enableSubPackages" value="false" /> </javaClientGenerator> <!-- 指定數據庫表 --> <!-- 文章表 --> <table tableName="article" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"> </table> <!-- 分類表 --> <table tableName="category" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"> </table> </context> </generatorConfiguration>
3.5 運行mybatis generator
4.配置application.properties
#springboot整合mybatis #實體類包 mybatis.type-aliases-package=com.yytf.vo #mybatis配置文件和mapper文件位置 mybatis.mapper-locations=classpath*:mapper/*.xml #mysql數據庫連接信息 spring.datasource.driverClassName = com.mysql.cj.jdbc.Driver spring.datasource.url = jdbc:mysql://000.000.000.000:3306/blogs?useUnicode=true&allowPublicKeyRetrieval=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8& spring.datasource.username = root spring.datasource.password = ******** spring.datasource.max-idle=10 spring.datasource.max-wait=10000 spring.datasource.min-idle=5 spring.datasource.initial-size=5 #tomcat端口號 server.port=8080 server.session.timeout=10 server.tomcat.uri-encoding=UTF-8 server.context-path=/ #log4j日誌配置 logging.config=classpath:log4j2.xml #log4j打印mybatis的sql語句 #logging.level.com.yytf.dao=debug
5.springboot整合mybatis
5.1 引入依賴,在pom.xml中引入:
<!-- springboot整合mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.1</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.1</version> </dependency> <!-- springboot連接mysql驅動依賴 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.11</version> </dependency>
5.2 在application.properties裏配置:
#實體類包 mybatis.type-aliases-package=com.yytf.vo #mybatis配置文件和mapper文件位置 mybatis.mapper-locations=classpath*:mapper/*.xml #mysql數據庫連接信息 spring.datasource.driverClassName = com.mysql.cj.jdbc.Driver spring.datasource.url = jdbc:mysql://000.000.000.000:3306/blogs?useUnicode=true&allowPublicKeyRetrieval=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8& spring.datasource.username = root spring.datasource.password = ********
5.3 在啓動類Application.java中配置mapper掃描註解 @MapperScan("com.yytf.dao")
6.springboot整合log4j
6.1 在pom.xml文件中添加log4j的jar包依賴,首先排除內置的日誌工具
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <!-- 排除Springboot自帶的日誌工具 --> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> </dependency> <!-- springboot整合log4j --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> </dependency> </dependencies>
6.2 在application.properties中配置指定log4j2.xml的位置
#log4j日誌配置 logging.config=classpath:log4j2.xml #log4j打印mybatis的sql語句 #logging.level.com.yytf.dao=debug
6.3 配置log4j2.xml
下面貼上代碼:解釋一下
>>> LOG_HOME:這裏的/var/log/blogs路徑如果在linux就是對應的目錄,如果在windows上則在當前盤符下新建一個var文件夾,例如tomcat在D盤,則在D盤新建路徑D:\var\log\blogs
>>> FILE_NAME:臨時生成的日誌文件名稱,下面配置中第二天會被新生成的替代
>>> Console標籤:輸出到控制檯
>>> RollingRandomAccessFile標籤:週期性生成新的日誌文件並存檔
>>> filePattern:存檔的日誌文件的命名規則,例如下面配置生成:D:\var\log\blogs\2018-09\blogs-2018-09-15-1.log,i%代表1開始遞增
>>> TimeBasedTriggeringPolicy標籤:生成新文件週期數,例如下面interval="1"是一天一個日誌文件
>>> SizeBasedTriggeringPolicy標籤和i%是配合使用的,表示當日志文件大小超過20MB就生成新日誌文件,新日誌文件後綴i%就遞:blogs-2018-09-15-2.log
>>> 最後一個Logger name="com.yytf.dao"是在本地調試時把sql語句打印在控制檯
<?xml version="1.0" encoding="UTF-8"?> <Configuration status="WARN"> <properties> <property name="LOG_HOME">/var/log/blogs</property> <property name="FILE_NAME">blogs</property> <property name="log.sql.level">info</property> </properties> <Appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %l - %msg%n" /> </Console> <RollingRandomAccessFile name="RollingRandomAccessFile" fileName="${LOG_HOME}/${FILE_NAME}.log" filePattern="${LOG_HOME}/$${date:yyyy-MM}/${FILE_NAME}-%d{yyyy-MM-dd}-%i.log"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %l - %msg%n"/> <Policies> <TimeBasedTriggeringPolicy interval="1"/> <SizeBasedTriggeringPolicy size="10 MB"/> </Policies> <DefaultRolloverStrategy max="20"/> </RollingRandomAccessFile> </Appenders> <Loggers> <Root level="info"> <AppenderRef ref="Console" /> <AppenderRef ref="RollingRandomAccessFile" /> </Root> <Logger name="com.yytf.dao" level="${log.sql.level}" additivity="false"> <AppenderRef ref="Console" /> </Logger> </Loggers> </Configuration>
7.springboot使用內置定時任務
7.1 啓動類Application.java
添加註解:@EnableScheduling
7.2 定時任務類
package com.yytf.util; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @Component public class Timer { @Autowired EverydayTalkMapper everydayTalkMapper; @Scheduled(cron = "0 59 23 * * ?") public void scheduleUpdateTalkId() { System.out.println("======>" + System.currentTimeMillis()); } }
7.3 使用場景
項目中每日一語需要每天變換,設置cron表達式爲:cron = "0 59 23 * * ?"在每天0點前把項目中的存儲每日一語的靜態變量的信息更換。
8.springboot使用JWT(Java Web Token)做Token驗證
8.1 爲什麼要使用token驗證
>>> 因爲我們的前後端是分離的,所有的請求都是跨域行爲,所以sessionId一直是變化的,不能通過sessionId來獲得用戶信息
>>> 有token的時候我們只用在第一次登錄的時候查詢一下數據庫,然後在token的過期時間內就可以不用再與數據庫用戶表交互,減少IO操作。
>>> 如果有多臺服務器做負載均衡,因爲token是無狀態的,所以服務器之間可以共用token
8.2 pom.xml中引入jwt的jar包依賴
<dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>2.2.0</version> </dependency>
8.3 新建Token簽發與解析驗證類JavaWebToken.java,注意payload裏面不要存放敏感信息,例如不要存放密碼等,因爲該部分是對稱加密,在客戶端可以解密。SECRET是不能暴露出去的,相當於私鑰,加密的方式由它決定。
package com.yytf.util; import java.util.HashMap; import java.util.Map; import com.auth0.jwt.JWTSigner; import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.internal.com.fasterxml.jackson.databind.ObjectMapper; public class JavaWebToken { private static final String SECRET = "********"; private static final String EXP = "exp"; private static final String PAYLOAD = "payload"; public static <T> String sign(T object, long maxAge) { try { final JWTSigner signer = new JWTSigner(SECRET); final Map<String, Object> claims = new HashMap<String, Object>(); ObjectMapper mapper = new ObjectMapper(); String jsonString = mapper.writeValueAsString(object); claims.put(PAYLOAD, jsonString); claims.put(EXP, System.currentTimeMillis() + maxAge); return signer.sign(claims); } catch(Exception e) { return null; } } public static<T> T unsign(String jwt, Class<T> classT) { final JWTVerifier verifier = new JWTVerifier(SECRET); try { final Map<String,Object> claims= verifier.verify(jwt); if (claims.containsKey(EXP) && claims.containsKey(PAYLOAD)) { long exp = (Long)claims.get(EXP); long currentTimeMillis = System.currentTimeMillis(); if (exp > currentTimeMillis) { String json = (String)claims.get(PAYLOAD); ObjectMapper objectMapper = new ObjectMapper(); return objectMapper.readValue(json, classT); } } return null; } catch (Exception e) { return null; } } }
8.4 登錄成功簽發token
在loginController.java中:
import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import com.yytf.util.JavaWebToken; import com.yytf.vo.UserVO; @Controller @RequestMapping(value="/login") public class LoginController { @RequestMapping(value="/login",method=RequestMethod.POST) @ResponseBody public ResponseUtil login(@RequestBody UserVO userVO) { UserVO resultUserVO = new UserVO(); String userName = PropertiesUtil.getProperty("userName"); String password = PropertiesUtil.getProperty("password"); if(userVO.getUserName().equals(userName) && userVO.getPassword().equals(password)) { Map<String, UserVO> data = new HashMap<String, UserVO>(); resultUserVO.setUserName(userName); String token = JavaWebToken.sign(resultUserVO, 1000L * 3600L * 3L); data.put("user", resultUserVO); return ResponseUtil.loginSuccess(data, token); } return ResponseUtil.fail("login fail"); } }
8.5 訪問怎刪改查接口驗證是否登錄
前臺傳入token,然後驗證token是否正確,如果不正確則返回前臺登錄界面
public class CheckIsLogin { /** * 檢查是否有操作權限 * @param token * @return */ public static boolean checkIsLogin(String token) { UserVO userVO = JavaWebToken.unsign(token, UserVO.class); String userName = PropertiesUtil.getProperty("userName"); if(userVO != null && userVO.getUserName().equals(userName)) { return true; } return false; } }
9.springboot使用CORS配置解決跨域問題
package com.yytf.util; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; /** * 全局CORS配置解決跨域問題 */ @Configuration public class CORSConfiguration { /*方式一隻支持一個域名配置*/ // @Bean // public FilterRegistrationBean corsFilter() { // UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); // CorsConfiguration config = new CorsConfiguration(); // config.setAllowCredentials(true); // // 設置你要允許的網站域名,如果全允許則設爲 * // config.addAllowedOrigin("*"); //// config.addAllowedOrigin("http://test.com"); // // 如果要限制 HEADER 或 METHOD 請自行更改 // config.addAllowedHeader("*"); // config.addAllowedMethod("*"); // source.registerCorsConfiguration("/**", config); // FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source)); // // 這個順序很重要哦,爲避免麻煩請設置在最前 // bean.setOrder(0); // return bean; // } /*方式二支持多個域名*/ @Bean public WebMvcConfigurer corsConfigurer() { return new WebMvcConfigurerAdapter() { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("http://test.com","http://www.test.com") .allowedHeaders("*") .allowedMethods("GET", "POST", "DELETE", "PUT", "OPTIONS") .allowCredentials(true).maxAge(3600); } }; } }
三、Debug記錄
1.數據正常返回,前臺卻報404錯誤,因爲後臺方法缺少@ResponseBody註解
2.mybatis中foreach標籤使用參數中間缺少逗號
3.使用mybatis-generator生成dao,mapper時報錯
4.使用mybatis-generator生成dao,mapper時報錯
5.MySQL8以上(包含8)版本注意事項
在前面的配置文件裏代碼已經貼出
6.Mapper和Service實例注入失敗,不能被發現
騰訊雲最新服務器活動--雲服務器免費送。
OJBK大概就這些了,剩下的都基本是苦力活就不說了,整個項目搞下來還是花了不少時間,超出了預期時間很多,不過也學到了不少東西。俗話說:紙上得來終覺淺,絕知此事要躬行,只用多寫,多敲,多練我們才能進步的更快。我的自控力很差,玩起來就什麼不管了,很難堅持一件事到底,所以我有時候想是不是要給自己每天規劃一下要完成什麼任務,要先堅持一個東西,比如寫博客來鍛鍊自己的持久力,能做好一個,其它的慢慢就都能搞好了吧。渴望進步證明我還沒有墮落到當一個鹹魚,哈哈,加油吧!