個人博客網站(四) - 后端項目


簡介:

這節聊一聊項目的后端,之前也講過,因為對前端不熟悉,所以在前端花了太多的時間,導致到后端開發的時候搞的人有點疲,所以很多東西從簡了,很多細節東西沒有考慮,只想着把基本功能做出來就好了。框架選擇的是現在比較流行的Springboot+Mybatis+Tomcat+MySQLSpringboot是在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.1pom.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&amp;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&amp
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&amp
spring.datasource.username = root
spring.datasource.password = ********

5.3 在啟動類Application.java中配置mapper掃描注解 @MapperScan("com.yytf.dao")

6.springboot整合log4j

6.1 pom.xml文件中添加log4jjar包依賴,首先排除內置的日志工具

<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文件夾,例如tomcatD盤,則在D盤新建路徑D:\var\log\blogs

>>> FILE_NAME:臨時生成的日志文件名稱,下面配置中第二天會被新生成的替代

>>> Console標簽:輸出到控制台

>>> RollingRandomAccessFile標簽:周期性生成新的日志文件並存檔

>>> filePattern:存檔的日志文件的命名規則,例如下面配置生成:D:\var\log\blogs\2018-09\blogs-2018-09-15-1.logi%代表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使用JWTJava Web Token)做Token驗證

8.1 為什么要使用token驗證

>>> 因為我們的前后端是分離的,所有的請求都是跨域行為,所以sessionId一直是變化的,不能通過sessionId來獲得用戶信息

>>> token的時候我們只用在第一次登錄的時候查詢一下數據庫,然后在token的過期時間內就可以不用再與數據庫用戶表交互,減少IO操作。

>>> 如果有多台服務器做負載均衡,因為token是無狀態的,所以服務器之間可以共用token

8.2 pom.xml中引入jwtjar包依賴

<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.mybatisforeach標簽使用參數中間缺少逗號

3.使用mybatis-generator生成dao,mapper時報錯

4.使用mybatis-generator生成dao,mapper時報錯

5.MySQL8以上(包含8)版本注意事項

在前面的配置文件里代碼已經貼出

6.MapperService實例注入失敗,不能被發現

OJBK大概就這些了,剩下的都基本是苦力活就不說了,整個項目搞下來還是花了不少時間,超出了預期時間很多,不過也學到了不少東西。俗話說:紙上得來終覺淺,絕知此事要躬行,只用多寫,多敲,多練我們才能進步的更快。我的自控力很差,玩起來就什么不管了,很難堅持一件事到底,所以我有時候想是不是要給自己每天規划一下要完成什么任務,要先堅持一個東西,比如寫博客來鍛煉自己的持久力,能做好一個,其它的慢慢就都能搞好了吧。渴望進步證明我還沒有墮落到當一個咸魚,哈哈,加油吧!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM