該文檔是在慕課網實戰課程《Spring Boot企業微信點餐系統》基礎上總結而成,旨在記錄Spring Boot一些相關知識,文章中涉及的代碼都通過驗證,能夠直接使用。 該文檔做爲我的參考資料,會長期更新。php
慕課網課程地址:Spring Boot企業微信點餐系統 html
微信點餐數據庫 - SQL.mdjava
-- 類目
create table `product_category` (
`category_id` int not null auto_increment,
`category_name` varchar(64) not null comment '類目名字',
`category_type` int not null comment '類目編號',
`create_time` timestamp not null default current_timestamp comment '建立時間',
`update_time` timestamp not null default current_timestamp on update current_timestamp comment '修改時間',
primary key (`category_id`)
);
-- 商品
create table `product_info` (
`product_id` varchar(32) not null,
`product_name` varchar(64) not null comment '商品名稱',
`product_price` decimal(8,2) not null comment '單價',
`product_stock` int not null comment '庫存',
`product_description` varchar(64) comment '描述',
`product_icon` varchar(512) comment '小圖',
`product_status` tinyint(3) DEFAULT '0' COMMENT '商品狀態,0正常1下架',
`category_type` int not null comment '類目編號',
`create_time` timestamp not null default current_timestamp comment '建立時間',
`update_time` timestamp not null default current_timestamp on update current_timestamp comment '修改時間',
primary key (`product_id`)
);
-- 訂單
create table `order_master` (
`order_id` varchar(32) not null,
`buyer_name` varchar(32) not null comment '買家名字',
`buyer_phone` varchar(32) not null comment '買家電話',
`buyer_address` varchar(128) not null comment '買家地址',
`buyer_openid` varchar(64) not null comment '買家微信openid',
`order_amount` decimal(8,2) not null comment '訂單總金額',
`order_status` tinyint(3) not null default '0' comment '訂單狀態, 默認爲新下單',
`pay_status` tinyint(3) not null default '0' comment '支付狀態, 默認未支付',
`create_time` timestamp not null default current_timestamp comment '建立時間',
`update_time` timestamp not null default current_timestamp on update current_timestamp comment '修改時間',
primary key (`order_id`),
key `idx_buyer_openid` (`buyer_openid`)
);
-- 訂單商品
create table `order_detail` (
`detail_id` varchar(32) not null,
`order_id` varchar(32) not null,
`product_id` varchar(32) not null,
`product_name` varchar(64) not null comment '商品名稱',
`product_price` decimal(8,2) not null comment '當前價格,單位分',
`product_quantity` int not null comment '數量',
`product_icon` varchar(512) comment '小圖',
`create_time` timestamp not null default current_timestamp comment '建立時間',
`update_time` timestamp not null default current_timestamp on update current_timestamp comment '修改時間',
primary key (`detail_id`),
key `idx_order_id` (`order_id`)
);
-- 賣家(登陸後臺使用, 賣家登陸以後可能直接採用微信掃碼登陸,不使用帳號密碼)
create table `seller_info` (
`id` varchar(32) not null,
`username` varchar(32) not null,
`password` varchar(32) not null,
`openid` varchar(64) not null comment '微信openid',
`create_time` timestamp not null default current_timestamp comment '建立時間',
`update_time` timestamp not null default current_timestamp on update current_timestamp comment '修改時間',
primary key (`id`)
) comment '賣家信息表';
複製代碼
pom.xmlmysql
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.imooc</groupId>
<artifactId>sell</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>sell</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-mp</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>cn.springboot</groupId>
<artifactId>best-pay-sdk</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.2.0</version>
</dependency>
<!--<dependency>-->
<!--<groupId>org.springframework.boot</groupId>-->
<!--<artifactId>spring-boot-starter-cache</artifactId>-->
<!--</dependency>-->
</dependencies>
<build>
<finalName>sell</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
複製代碼
application.ymlgit
spring:
profiles:
active: dev
複製代碼
application-dev.ymlgithub
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
username: root
password: 123456
url: jdbc:mysql://192.168.30.113/sell?characterEncoding=utf-8&useSSL=false
jpa:
show-sql: true
jackson:
default-property-inclusion: non_null
redis:
host: 192.168.30.113
port: 6379
server:
context-path: /sell
#logging:
# pattern:
# console: "%d - %msg%n"
## path: /var/log/tomcat/
# file: /var/log/tomcat/sell.log
# level:
# com.imooc.LoggerTest: debug
wechat:
mpAppId: wxd898fcb01713c658
mpAppSecret: 47ccc303338cee6e62894fxxxxxxxxxxx
openAppId: wx6ad144e54af67d87
openAppSecret: 91a2ff6d38a2bbccfb7e9xxxxxx
mchId: 1483469312
mchKey: 06C56A89949D617xxxxxxxxxxx
keyPath: /var/weixin_cert/h5.p12
notifyUrl: http://sell.natapp4.cc/sell/pay/notify
templateId:
orderStatus: e-Cqq67QxD6YNI41iRiqawEYdFavW_7pc7LyEMb-yeQ
projectUrl:
wechatMpAuthorize: http://sell.natapp4.cc
wechatOpenAuthorize: http://sell.natapp4.cc
sell: http://sell.natapp4.cc
logging:
level:
com.imooc.dataobject.mapper: trace
mybatis:
mapper-locations: classpath:mapper/*.xml
複製代碼
application-prod.ymlweb
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
username: root
password: 123456
url: jdbc:mysql://192.168.30.113/sell?characterEncoding=utf-8&useSSL=false
jackson:
default-property-inclusion: non_null
redis:
host: 192.168.30.113
port: 6379
server:
context-path: /sell
#logging:
# pattern:
# console: "%d - %msg%n"
## path: /var/log/tomcat/
# file: /var/log/tomcat/sell.log
# level:
# com.imooc.LoggerTest: debug
wechat:
mpAppId: wxd898fcb01713c658
mpAppSecret: 47ccc303338cee6e62894fxxxxxxxxxxx
openAppId: wx6ad144e54af67d87
openAppSecret: 91a2ff6d38a2bbccfb7e9xxxxxx
mchId: 1483469312
mchKey: 06C56A89949D617xxxxxxxxxxx
keyPath: /var/weixin_cert/h5.p12
notifyUrl: http://sell.natapp4.cc/sell/pay/notify
templateId:
orderStatus: e-Cqq67QxD6YNI41iRiqawEYdFavW_7pc7LyEMb-yeQ
projectUrl:
wechatMpAuthorize: http://sell.natapp4.cc
wechatOpenAuthorize: http://sell.natapp4.cc
sell: http://sell.natapp4.cc
logging:
level:
com.imooc.dataobject.mapper: trace
mybatis:
mapper-locations: classpath:mapper/*.xml
複製代碼
BlogProperties.javaredis
package com.mindex.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@ConfigurationProperties(prefix = "com.mindex.blog")
@Component
public class BlogProperties {
private String name;
private String desc;
}
複製代碼
BlogPropertiesTest.javaspring
package com.mindex.config;
import lombok.extern.slf4j.Slf4j;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class BlogPropertiesTest {
@Autowired
private BlogProperties blogProperties;
@Test
public void getProperties() throws Exception {
Assert.assertEquals("輪子王", blogProperties.getName());
Assert.assertEquals("用行動改變世界", blogProperties.getDesc());
}
}
複製代碼
WechatAccountConfig.javasql
package com.imooc.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.Map;
@Data
@Component
@ConfigurationProperties(prefix = "wechat")
public class WechatAccountConfig {
private String mpAppId;
private String mpAppSecret;
private String openAppId;
private String openAppSecret;
private String mchId;
private String mchKey;
private String keyPath;
private String notifyUrl;
private Map<String, String> templateId;
}
複製代碼
WechatMpConfig.java
package com.imooc.config;
import me.chanjar.weixin.mp.api.WxMpConfigStorage;
import me.chanjar.weixin.mp.api.WxMpInMemoryConfigStorage;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
@Component
public class WechatMpConfig {
@Autowired
private WechatAccountConfig accountConfig;
@Bean
public WxMpService wxMpService() {
WxMpService wxMpService = new WxMpServiceImpl();
wxMpService.setWxMpConfigStorage(wxMpConfigStorage());
return wxMpService;
}
@Bean
public WxMpConfigStorage wxMpConfigStorage() {
WxMpInMemoryConfigStorage wxMpConfigStorage = new WxMpInMemoryConfigStorage();
wxMpConfigStorage.setAppId(accountConfig.getMpAppId());
wxMpConfigStorage.setSecret(accountConfig.getMpAppSecret());
return wxMpConfigStorage;
}
}
複製代碼
logback-spring.xml
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<appender name="consoleLog" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>
%d - %msg%n
</pattern>
</layout>
</appender>
<appender name="fileInfoLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>DENY</onMatch>
<onMismatch>ACCEPT</onMismatch>
</filter>
<encoder>
<pattern>
%msg%n
</pattern>
</encoder>
<!--滾動策略-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--路徑-->
<fileNamePattern>/Users/kwang/imooc/sell/log/info.%d.log</fileNamePattern>
</rollingPolicy>
</appender>
<appender name="fileErrorLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
<encoder>
<pattern>
%msg%n
</pattern>
</encoder>
<!--滾動策略-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--路徑-->
<fileNamePattern>/Users/kwang/imooc/sell/log/error.%d.log</fileNamePattern>
</rollingPolicy>
</appender>
<root level="info">
<appender-ref ref="consoleLog" />
<appender-ref ref="fileInfoLog" />
<appender-ref ref="fileErrorLog" />
</root>
</configuration>
複製代碼
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.8.0</version>
</dependency>
複製代碼
package com.mindex.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
public class Swagger2Configuration {
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
// 自行修改成本身的包路徑
.apis(RequestHandlerSelectors.basePackage("com.mindex.controller"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("api文檔")
.description("Restful 風格接口")
.version("1.0")
.build();
}
}
複製代碼
package com.mindex.controller;
import com.mindex.entities.User;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import java.util.*;
@RestController
@RequestMapping(value = "/users")
@Api(value = "/users", tags = "測試接口模塊")
public class UserController {
//建立線程安全的Map
static Map<Long, User> users = Collections.synchronizedMap(new HashMap<Long, User>());
@ApiOperation(value = "獲取用戶列表", notes = "")
@RequestMapping(value = "/", method = RequestMethod.GET)
public List<User> getUserList() {
// 處理"/users/"的GET請求,用來獲取用戶列表
// 還能夠經過@RequestParam從頁面中傳遞參數來進行查詢條件或者翻頁信息的傳遞
List<User> userList = new ArrayList<User>(users.values());
return userList;
}
@ApiOperation(value = "建立用戶", notes = "根據User對象建立用戶")
@ApiImplicitParam(name = "user", value = "用戶詳細實體user", required = true, dataType = "User")
@RequestMapping(value = "/", method = RequestMethod.POST)
public String postUser(@ModelAttribute User user) {
// 處理"/users/"的POST請求,用來建立User
// 除了@ModelAttribute綁定參數以外,還能夠經過@RequestParam從頁面中傳遞參數
users.put(user.getId(), user);
return "success";
}
@ApiOperation(value = "獲取用戶詳細信息", notes = "根據url的id來獲取用戶詳細信息")
@ApiImplicitParam(name = "id", value = "用戶ID", required = true, dataType = "Long")
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public User getUser(@PathVariable Long id) {
// 處理"/users/{id}"的GET請求,用來獲取url中id值的User信息
// url中的id可經過@PathVariable綁定到函數的參數中
return users.get(id);
}
@ApiOperation(value = "更新用戶詳細信息", notes = "根據url的id來指定更新對象,並根據傳過來的user信息來更新用戶詳細信息")
@ApiImplicitParams({
@ApiImplicitParam(name = "id", value = "用戶ID", required = true, dataType = "Long"),
@ApiImplicitParam(name = "user", value = "用戶詳細實體user", required = true, dataType = "User")
})
@RequestMapping(value = "/{id}", method = RequestMethod.POST)
public String putUser(@PathVariable Long id, @ModelAttribute User user) {
// 處理"/users/{id}"的PUT請求,用來更新User信息
User u = users.get(id);
u.setName(user.getName());
u.setAge(user.getAge());
users.put(id, u);
return "success";
}
@ApiOperation(value = "刪除用戶", notes = "根據url的id來指定刪除對象")
@ApiImplicitParam(name = "id", value = "用戶ID", required = true, dataType = "Long")
@RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
public String deleteUser(@PathVariable Long id) {
// 處理"/users/{id}"的DELETE請求,用來刪除User
users.remove(id);
return "success";
}
}
複製代碼
http://localhost:8080/swagger-ui.html
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>1.5.1.RELEASE</version>
<scope>provided</scope>
</dependency>
複製代碼
@SpringBootApplication
@ComponentScan(basePackages = "com.mindex")
public class Application extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(Application.class);
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
複製代碼
選中Lifecycle-clean及compile
在IDEA裏新建一個部署配置項。
好處:安裝了Lombok插件和pom引用依賴後,能夠簡化代碼,例如:無需再寫get/set/toString方法,打印日誌時直接使用log關鍵字等。
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
複製代碼
好處1:只要使用**@Data**、@Getter、@Setter、@ToString等註解,無需再寫繁瑣的get/set/toString方法,Lombok會在編譯時自動加入代碼。
package com.imooc.dataobject;
import lombok.Data;
import org.hibernate.annotations.DynamicUpdate;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import java.util.Date;
@Entity
@DynamicUpdate
@Data
public class ProductCategory {
@Id
@GeneratedValue
private Integer categoryId;
private String categoryName;
private Integer categoryType;
private Date createTime;
private Date updateTime;
public ProductCategory(String categoryName, Integer categoryType) {
this.categoryName = categoryName;
this.categoryType = categoryType;
}
public ProductCategory() {
}
}
複製代碼
好處2:輸出日誌時,能夠直接使用log關鍵字輸出,支持參數引用。
package com.imooc;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = LoggerTest.class)
@Slf4j
public class LoggerTest {
// 無需再寫LoggerFactory
// private final Logger logger = LoggerFactory.getLogger(LoggerTest.class);
@Test
public void test1() {
String name = "imooc";
String password = "12345";
log.debug("debug...");
log.info("name: {}, password: {}", name, password);
log.error("error...");
log.warn("warning...");
}
}
複製代碼
SellApplication.java
package com.imooc;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SellApplication {
public static void main(String[] args) {
SpringApplication.run(SellApplication.class, args);
}
}
複製代碼
ResultEnum.java
package com.imooc.enums;
import lombok.Getter;
@Getter
public enum ResultEnum {
PARAM_ERROR(1,"參數不正確"),
PRODUCT_NOT_EXIST(10, "商品不存在"),
PRODUCT_STOCK_ERROR(11, "庫存不正確"),
ORDER_NOT_EXIST(12, "訂單不存在"),
;
private Integer code;
private String message;
ResultEnum(Integer code, String message) {
this.code = code;
this.message = message;
}
}
複製代碼
能夠把經常使用的方法放在util包裏,好比拼接vo視圖、生成惟一編碼等;
ResultVOUtil.java
package com.imooc.utils;
import com.imooc.VO.ResultVO;
public class ResultVOUtil {
public static ResultVO success(Object object) {
ResultVO resultVO = new ResultVO();
resultVO.setData(object);
resultVO.setCode(0);
resultVO.setMsg("成功");
return resultVO;
}
public static ResultVO success() {
return success(null);
}
public static ResultVO error(Integer code, String msg) {
ResultVO resultVO = new ResultVO();
resultVO.setCode(code);
resultVO.setMsg(msg);
return resultVO;
}
}
複製代碼
KeyUtil.java
package com.imooc.utils;
import java.util.Random;
public class KeyUtil {
/** * 生成惟一的主鍵 * 格式: 時間+隨機數 * * @return */
public static synchronized String genUniqueKey() {
Random random = new Random();
Integer number = random.nextInt(900000) + 100000;
return System.currentTimeMillis() + String.valueOf(number);
}
}
複製代碼
JsonUtil.java
package com.imooc.utils;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class JsonUtil {
public static String toJson(Object object) {
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setPrettyPrinting();
Gson gson = gsonBuilder.create();
return gson.toJson(object);
}
}
複製代碼
CookieUtil.java
package com.imooc.utils;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
public class CookieUtil {
/** * 設置 * @param response * @param name * @param value * @param maxAge */
public static void set(HttpServletResponse response, String name, String value, int maxAge) {
Cookie cookie = new Cookie(name, value);
cookie.setPath("/");
cookie.setMaxAge(maxAge);
response.addCookie(cookie);
}
/** * 獲取cookie * @param request * @param name * @return */
public static Cookie get(HttpServletRequest request, String name) {
Map<String, Cookie> cookieMap = readCookieMap(request);
if (cookieMap.containsKey(name)) {
return cookieMap.get(name);
}else {
return null;
}
}
/** * 將cookie封裝成Map * @param request * @return */
private static Map<String, Cookie> readCookieMap(HttpServletRequest request) {
Map<String, Cookie> cookieMap = new HashMap<>();
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie: cookies) {
cookieMap.put(cookie.getName(), cookie);
}
}
return cookieMap;
}
}
複製代碼
MathUtil.java
package com.imooc.utils;
public class MathUtil {
private static final Double MONEY_RANGE = 0.01;
/** * 比較2個金額是否相等 * @param d1 * @param d2 * @return */
public static Boolean equals(Double d1, Double d2) {
Double result = Math.abs(d1 - d2);
if (result < MONEY_RANGE) {
return true;
}else {
return false;
}
}
}
複製代碼
要返回的數據格式以下:
ResultVO.java
package com.imooc.VO;
import lombok.Data;
/** * http請求返回的最外層對象 */
@Data
public class ResultVO<T> {
/* 狀態碼 */
private Integer code;
/* 提示信息 */
private String msg;
/* 具體內容 */
private T data;
}
複製代碼
ProductVO.java
package com.imooc.VO;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.List;
/** * 商品(包含類目) */
@Data
public class ProductVO {
@JsonProperty("name")
private String categoryName;
@JsonProperty("type")
private Integer categoryType;
@JsonProperty("foods")
private List<ProductInfoVO> productInfoVOList;
}
複製代碼
ProductInfoVO.java
package com.imooc.VO;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.math.BigDecimal;
/** * 商品詳情 */
@Data
public class ProductInfoVO {
@JsonProperty("id")
private String productId;
@JsonProperty("name")
private String productName;
@JsonProperty("price")
private BigDecimal productPrice;
@JsonProperty("description")
private String productDescription;
@JsonProperty("icon")
private String productIcon;
}
複製代碼
能夠把DTO理解成數據庫視圖。
OrderDTO.java
package com.imooc.dto;
import com.imooc.dataobject.OrderDetail;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
@Data
public class OrderDTO {
List<OrderDetail> orderDetailList;
private String orderId;
private String buyerName;
private String buyerPhone;
private String buyerAddress;
private String buyerOpenid;
private BigDecimal orderAmount;
private Integer orderStatus;
private Integer payStatus;
private Date createTime;
private Date updateTime;
}
複製代碼
CartDTO.java
package com.imooc.dto;
import lombok.Data;
@Data
public class CartDTO {
private String productId;
private Integer productQuantity;
public CartDTO(String productId, Integer productQuantity) {
this.productId = productId;
this.productQuantity = productQuantity;
}
}
複製代碼
SellException.java
package com.imooc.exception;
import com.imooc.enums.ResultEnum;
public class SellException extends RuntimeException {
private Integer code;
public SellException(ResultEnum resultEnum) {
super(resultEnum.getMessage());
this.code = resultEnum.getCode();
}
public SellException(Integer code, String message) {
super(message);
this.code = code;
}
}
複製代碼
ResponseBankException.java
package com.imooc.exception;
public class ResponseBankException extends RuntimeException {
}
複製代碼
SellerAuthorizeException.java
package com.imooc.exception;
public class SellerAuthorizeException extends RuntimeException {
}
複製代碼
SellExceptionHandler.java
package com.imooc.handler;
import com.imooc.VO.ResultVO;
import com.imooc.config.ProjectUrlConfig;
import com.imooc.exception.ResponseBankException;
import com.imooc.exception.SellException;
import com.imooc.exception.SellerAuthorizeException;
import com.imooc.utils.ResultVOUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.servlet.ModelAndView;
@ControllerAdvice
public class SellExceptionHandler {
@Autowired
private ProjectUrlConfig projectUrlConfig;
//攔截登陸異常
//http://sell.natapp4.cc/sell/wechat/qrAuthorize?returnUrl=http://sell.natapp4.cc/sell/seller/login
@ExceptionHandler(value = SellerAuthorizeException.class)
@ResponseStatus(HttpStatus.FORBIDDEN)
public ModelAndView handlerAuthorizeException() {
return new ModelAndView("redirect:"
.concat(projectUrlConfig.getWechatOpenAuthorize())
.concat("/sell/wechat/qrAuthorize")
.concat("?returnUrl=")
.concat(projectUrlConfig.getSell())
.concat("/sell/seller/login"));
}
@ExceptionHandler(value = SellException.class)
@ResponseBody
public ResultVO handlerSellerException(SellException e) {
return ResultVOUtil.error(e.getCode(), e.getMessage());
}
@ExceptionHandler(value = ResponseBankException.class)
@ResponseStatus(HttpStatus.FORBIDDEN)
public void handleResponseBankException() {
}
}
複製代碼
假設訪問一個不存在的頁面,拋出異常
package com.mindex.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class ThymeleafTest {
@ResponseBody
@RequestMapping("/hello")
public String hello() throws Exception {
throw new Exception("發生錯誤");
}
}
複製代碼
經過使用@ControllerAdvice
定義統一的異常處理類,而不是在每一個Controller中逐個定義。@ExceptionHandler
用來定義函數針對的異常類型,最後將Exception對象和請求URL映射到error.html
中。
package com.mindex.exception;
import com.mindex.entities.ErrorInfo;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
@ControllerAdvice
public class GlobalExceptionHandler {
public static final String DEFAULT_ERROR_VIEW = "error";
@ExceptionHandler(value = Exception.class)
public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
ModelAndView mav = new ModelAndView();
mav.addObject("exception", e);
mav.addObject("url", req.getRequestURL());
mav.setViewName(DEFAULT_ERROR_VIEW);
return mav;
}
}
複製代碼
實現error.html
頁面展現:在templates
目錄下建立error.html
,將請求的URL和Exception對象的message輸出。
<!DOCTYPE html> <html xmlns:th="http://www.w3.org/1999/xhtml"> <head lang="en"> <meta charset="UTF-8"/> <title>統一異常處理</title> </head> <body> <h1>Error Handler</h1> <div th:text="${url}"></div> <div th:text="${exception.message}"></div> </body> </html> 複製代碼
啓動該應用,訪問:http://localhost:8080/hello
,能夠看到以下錯誤提示頁面。
經過實現上述內容以後,咱們只須要在Controller中拋出Exception,固然咱們可能會有多種不一樣的Exception。而後在@ControllerAdvice
類中,根據拋出的具體Exception類型匹配@ExceptionHandler
中配置的異常類型來匹配錯誤映射和處理。
建立統一的JSON返回對象,code:消息類型,message:消息內容,url:請求的url,data:請求返回的數據。
package com.mindex.entities;
import lombok.Data;
@Data
public class ErrorInfo<T> {
public static final Integer OK = 0;
public static final Integer ERROR = 100;
private Integer code;
private String message;
private String url;
private T data;
}
複製代碼
建立一個自定義異常,用來實驗捕獲該異常,並返回json。
package com.mindex.exception;
public class MyException extends Exception {
public MyException(String message) {
super(message);
}
}
複製代碼
Controller
中增長json映射,拋出MyException
異常。
package com.mindex.controller;
import com.mindex.exception.MyException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloController {
@RequestMapping("/json")
public String json() throws MyException {
throw new MyException("發生錯誤2");
}
}
複製代碼
爲MyException
異常建立對應的處理。
@ExceptionHandler(value = MyException.class)
@ResponseBody
public ErrorInfo<String> jsonErrorHandler(HttpServletRequest req, MyException e) throws Exception {
ErrorInfo<String> r = new ErrorInfo<>();
r.setMessage(e.getMessage());
r.setCode(ErrorInfo.ERROR);
r.setData("Some Data");
r.setUrl(req.getRequestURL().toString());
return r;
}
複製代碼
啓動應用,訪問:http://localhost:8080/json
,能夠獲得以下返回內容:
SellerAuthorizeAspect.java
package com.imooc.aspect;
import com.imooc.constant.CookieConstant;
import com.imooc.constant.RedisConstant;
import com.imooc.exception.SellerAuthorizeException;
import com.imooc.utils.CookieUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
@Aspect
@Component
@Slf4j
public class SellerAuthorizeAspect {
@Autowired
private StringRedisTemplate redisTemplate;
@Pointcut("execution(public * com.imooc.controller.Seller*.*(..))" +
"&& !execution(public * com.imooc.controller.SellerUserController.*(..))")
public void verify() {
}
@Before("verify()")
public void doVerify() {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
//查詢cookie
Cookie cookie = CookieUtil.get(request, CookieConstant.TOKEN);
if (cookie == null) {
log.warn("【登陸校驗】Cookie中查不到token");
throw new SellerAuthorizeException();
}
//去redis裏查詢
String tokenValue = redisTemplate.opsForValue().get(String.format(RedisConstant.TOKEN_PREFIX, cookie.getValue()));
if (StringUtils.isEmpty(tokenValue)) {
log.warn("【登陸校驗】Redis中查不到token");
throw new SellerAuthorizeException();
}
}
}
複製代碼
主要用來映射數據庫表及字段關係。
ProductCategory.java
package com.imooc.dataobject;
import lombok.Data;
import org.hibernate.annotations.DynamicUpdate;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import java.util.Date;
@Entity
@DynamicUpdate
@Data
public class ProductCategory {
@Id
@GeneratedValue
private Integer categoryId;
private String categoryName;
private Integer categoryType;
private Date createTime;
private Date updateTime;
public ProductCategory(String categoryName, Integer categoryType) {
this.categoryName = categoryName;
this.categoryType = categoryType;
}
public ProductCategory() {
}
}
複製代碼
JpaRepository對數據庫經常使用操做進行了封裝,經過繼承JpaRepository能夠快速實現數據庫操做。 JpaRepository第一個參數是data object,第二個是該data object的主鍵。
ProductCategoryRepository.java
package com.imooc.repository;
import com.imooc.dataobject.ProductCategory;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface ProductCategoryRepository extends JpaRepository<ProductCategory, Integer> {
List<ProductCategory> findByCategoryTypeIn(List<Integer> categoryTypeList);
}
複製代碼
ProductCategoryRepositoryTest.java
package com.imooc.repository;
import com.imooc.dataobject.ProductCategory;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest
public class ProductCategoryRepositoryTest {
@Autowired
private ProductCategoryRepository repository;
@Test
public void findOneTest() {
ProductCategory productCategory = repository.findOne(1);
System.out.println(productCategory.toString());
}
@Test
@Transactional
public void saveTest() {
ProductCategory productCategory = new ProductCategory("男生最愛", 4);
ProductCategory result = repository.save(productCategory);
Assert.assertNotNull(result);
// Assert.assertNotEquals(null, result);
}
@Test
public void findByCategoryTypeInTest() {
List<Integer> list = Arrays.asList(2,3,4);
List<ProductCategory> result = repository.findByCategoryTypeIn(list);
Assert.assertNotEquals(0, result.size());
}
@Test
public void updateTest() {
// ProductCategory productCategory = repository.findOne(4);
// productCategory.setCategoryName("男生最愛1");
ProductCategory productCategory = new ProductCategory("男生最愛", 4);
ProductCategory result = repository.save(productCategory);
Assert.assertEquals(productCategory, result);
}
}
複製代碼
CategoryService.java
package com.imooc.service;
import com.imooc.dataobject.ProductCategory;
import java.util.List;
public interface CategoryService {
ProductCategory findOne(Integer categoryId);
List<ProductCategory> findAll();
List<ProductCategory> findByCategoryTypeIn(List<Integer> categoryTypeList);
ProductCategory save(ProductCategory productCategory);
}
複製代碼
須要使用**@Service**註解
CategoryServiceImpl.java
package com.imooc.service.impl;
import com.imooc.dataobject.ProductCategory;
import com.imooc.repository.ProductCategoryRepository;
import com.imooc.service.CategoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class CategoryServiceImpl implements CategoryService {
@Autowired
private ProductCategoryRepository repository;
@Override
public ProductCategory findOne(Integer categoryId) {
return repository.findOne(categoryId);
}
@Override
public List<ProductCategory> findAll() {
return repository.findAll();
}
@Override
public List<ProductCategory> findByCategoryTypeIn(List<Integer> categoryTypeList) {
return repository.findByCategoryTypeIn(categoryTypeList);
}
@Override
public ProductCategory save(ProductCategory productCategory) {
return repository.save(productCategory);
}
}
複製代碼
CategoryServiceImplTest.java
package com.imooc.service.impl;
import com.imooc.dataobject.ProductCategory;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.Arrays;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest
public class CategoryServiceImplTest {
@Autowired
private CategoryServiceImpl categoryService;
@Test
public void findOne() throws Exception {
ProductCategory productCategory = categoryService.findOne(1);
Assert.assertEquals(new Integer(1), productCategory.getCategoryId());
}
@Test
public void findAll() throws Exception {
List<ProductCategory> productCategoryList = categoryService.findAll();
Assert.assertNotEquals(0, productCategoryList.size());
}
@Test
public void findByCategoryTypeIn() throws Exception {
List<ProductCategory> productCategoryList = categoryService.findByCategoryTypeIn(Arrays.asList(1,2,3,4));
Assert.assertNotEquals(0, productCategoryList.size());
}
@Test
public void save() throws Exception {
ProductCategory productCategory = new ProductCategory("男生專享", 10);
ProductCategory result = categoryService.save(productCategory);
Assert.assertNotNull(result);
}
}
複製代碼
@RestController 註解,直接返回json格式; @RequestMapping("buyer/product") 註解聲明服務的url前綴;
BuyerProductController.java
package com.imooc.controller;
import com.imooc.VO.ProductInfoVO;
import com.imooc.VO.ProductVO;
import com.imooc.VO.ResultVO;
import com.imooc.dataobject.ProductCategory;
import com.imooc.dataobject.ProductInfo;
import com.imooc.service.CategoryService;
import com.imooc.service.ProductService;
import com.imooc.utils.ResultVOUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/buyer/product")
public class BuyerProductController {
@Autowired
private ProductService productService;
@Autowired
private CategoryService categoryService;
@GetMapping("/list")
public ResultVO list() {
//1. 查詢全部上架商品
List<ProductInfo> productInfoList = productService.findUpAll();
//2. 查詢類目(一次性查詢)
//傳統方法
List<Integer> categoryTypeList = new ArrayList<>();
// for (ProductInfo productInfo : productInfoList) {
// categoryTypeList.add(productInfo.getCategoryType());
// }
//精簡方法(java8, lambda)
categoryTypeList = productInfoList.stream().map(e -> e.getCategoryType()).collect(Collectors.toList());
List<ProductCategory> productCategoryList = categoryService.findByCategoryTypeIn(categoryTypeList);
//3. 數據拼裝
List<ProductVO> productVOList = new ArrayList<>();
for (ProductCategory productCategory : productCategoryList) {
ProductVO productVO = new ProductVO();
productVO.setCategoryName(productCategory.getCategoryName());
productVO.setCategoryType(productCategory.getCategoryType());
List<ProductInfoVO> productInfoVOList = new ArrayList<>();
for (ProductInfo productInfo : productInfoList) {
if (productInfo.getCategoryType().equals(productCategory.getCategoryType())) {
ProductInfoVO productInfoVO = new ProductInfoVO();
BeanUtils.copyProperties(productInfo, productInfoVO);
productInfoVOList.add(productInfoVO);
}
}
productVO.setProductInfoVOList(productInfoVOList);
productVOList.add(productVO);
}
return ResultVOUtil.success(productVOList);
}
}
複製代碼
SellerProductController.java
package com.imooc.controller;
import com.imooc.dataobject.ProductCategory;
import com.imooc.dataobject.ProductInfo;
import com.imooc.exception.SellException;
import com.imooc.form.ProductForm;
import com.imooc.service.CategoryService;
import com.imooc.service.ProductService;
import com.imooc.utils.KeyUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import javax.validation.Valid;
import java.util.List;
import java.util.Map;
@Controller
@RequestMapping("/seller/product")
public class SellerProductController {
@Autowired
private ProductService productService;
@Autowired
private CategoryService categoryService;
/** * 列表 * @param page * @param size * @param map * @return */
@GetMapping("/list")
public ModelAndView list(@RequestParam(value = "page", defaultValue = "1") Integer page, @RequestParam(value = "size", defaultValue = "10") Integer size, Map<String, Object> map) {
PageRequest request = new PageRequest(page - 1, size);
Page<ProductInfo> productInfoPage = productService.findAll(request);
map.put("productInfoPage", productInfoPage);
map.put("currentPage", page);
map.put("size", size);
return new ModelAndView("product/list", map);
}
/** * 商品上架 * @param productId * @param map * @return */
@RequestMapping("/on_sale")
public ModelAndView onSale(@RequestParam("productId") String productId, Map<String, Object> map) {
try {
productService.onSale(productId);
} catch (SellException e) {
map.put("msg", e.getMessage());
map.put("url", "/sell/seller/product/list");
return new ModelAndView("common/error", map);
}
map.put("url", "/sell/seller/product/list");
return new ModelAndView("common/success", map);
}
/** * 商品下架 * @param productId * @param map * @return */
@RequestMapping("/off_sale")
public ModelAndView offSale(@RequestParam("productId") String productId, Map<String, Object> map) {
try {
productService.offSale(productId);
} catch (SellException e) {
map.put("msg", e.getMessage());
map.put("url", "/sell/seller/product/list");
return new ModelAndView("common/error", map);
}
map.put("url", "/sell/seller/product/list");
return new ModelAndView("common/success", map);
}
@GetMapping("/index")
public ModelAndView index(@RequestParam(value = "productId", required = false) String productId, Map<String, Object> map) {
if (!StringUtils.isEmpty(productId)) {
ProductInfo productInfo = productService.findOne(productId);
map.put("productInfo", productInfo);
}
//查詢全部的類目
List<ProductCategory> categoryList = categoryService.findAll();
map.put("categoryList", categoryList);
return new ModelAndView("product/index", map);
}
/** * 保存/更新 * @param form * @param bindingResult * @param map * @return */
@PostMapping("/save")
// @Cacheable(cacheNames = "product", key = "123")
// @Cacheable(cacheNames = "product", key = "456")
// @CachePut(cacheNames = "product", key = "123")
@CacheEvict(cacheNames = "product", allEntries = true, beforeInvocation = true)
public ModelAndView save(@Valid ProductForm form, BindingResult bindingResult, Map<String, Object> map) {
if (bindingResult.hasErrors()) {
map.put("msg", bindingResult.getFieldError().getDefaultMessage());
map.put("url", "/sell/seller/product/index");
return new ModelAndView("common/error", map);
}
ProductInfo productInfo = new ProductInfo();
try {
//若是productId爲空, 說明是新增
if (!StringUtils.isEmpty(form.getProductId())) {
productInfo = productService.findOne(form.getProductId());
} else {
form.setProductId(KeyUtil.genUniqueKey());
}
BeanUtils.copyProperties(form, productInfo);
productService.save(productInfo);
} catch (SellException e) {
map.put("msg", e.getMessage());
map.put("url", "/sell/seller/product/index");
return new ModelAndView("common/error", map);
}
map.put("url", "/sell/seller/product/list");
return new ModelAndView("common/success", map);
}
}
複製代碼
ProductCategoryMapper.java
package com.imooc.dataobject.mapper;
import com.imooc.dataobject.ProductCategory;
import org.apache.ibatis.annotations.*;
import java.util.List;
import java.util.Map;
public interface ProductCategoryMapper {
@Insert("insert into product_category(category_name, category_type) values (#{categoryName, jdbcType=VARCHAR}, #{category_type, jdbcType=INTEGER})")
int insertByMap(Map<String, Object> map);
@Insert("insert into product_category(category_name, category_type) values (#{categoryName, jdbcType=VARCHAR}, #{categoryType, jdbcType=INTEGER})")
int insertByObject(ProductCategory productCategory);
@Select("select * from product_category where category_type = #{categoryType}")
@Results({
@Result(column = "category_id", property = "categoryId"),
@Result(column = "category_name", property = "categoryName"),
@Result(column = "category_type", property = "categoryType")
})
ProductCategory findByCategoryType(Integer categoryType);
@Select("select * from product_category where category_name = #{categoryName}")
@Results({
@Result(column = "category_id", property = "categoryId"),
@Result(column = "category_name", property = "categoryName"),
@Result(column = "category_type", property = "categoryType")
})
List<ProductCategory> findByCategoryName(String categoryName);
@Update("update product_category set category_name = #{categoryName} where category_type = #{categoryType}")
int updateByCategoryType(@Param("categoryName") String categoryName, @Param("categoryType") Integer categoryType);
@Update("update product_category set category_name = #{categoryName} where category_type = #{categoryType}")
int updateByObject(ProductCategory productCategory);
@Delete("delete from product_category where category_type = #{categoryType}")
int deleteByCategoryType(Integer categoryType);
ProductCategory selectByCategoryType(Integer categoryType);
}
複製代碼
ProductCategoryMapperTest.java
package com.imooc.dataobject.mapper;
import com.imooc.dataobject.ProductCategory;
import lombok.extern.slf4j.Slf4j;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class ProductCategoryMapperTest {
@Autowired
private ProductCategoryMapper mapper;
@Test
public void insertByMap() throws Exception {
Map<String, Object> map = new HashMap<>();
map.put("categoryName", "師兄最不愛");
map.put("category_type", 101);
int result = mapper.insertByMap(map);
Assert.assertEquals(1, result);
}
@Test
public void insertByObject() {
ProductCategory productCategory = new ProductCategory();
productCategory.setCategoryName("師兄最不愛");
productCategory.setCategoryType(102);
int result = mapper.insertByObject(productCategory);
Assert.assertEquals(1, result);
}
@Test
public void findByCategoryType() {
ProductCategory result = mapper.findByCategoryType(102);
Assert.assertNotNull(result);
}
@Test
public void findByCategoryName() {
List<ProductCategory> result = mapper.findByCategoryName("師兄最不愛");
Assert.assertNotEquals(0, result.size());
}
@Test
public void updateByCategoryType() {
int result = mapper.updateByCategoryType("師兄最不愛的分類", 102);
Assert.assertEquals(1, result);
}
@Test
public void updateByObject() {
ProductCategory productCategory = new ProductCategory();
productCategory.setCategoryName("師兄最不愛");
productCategory.setCategoryType(102);
int result = mapper.updateByObject(productCategory);
Assert.assertEquals(1, result);
}
@Test
public void deleteByCategoryType() {
int result = mapper.deleteByCategoryType(102);
Assert.assertEquals(1, result);
}
@Test
public void selectByCategoryType() {
ProductCategory productCategory = mapper.selectByCategoryType(101);
Assert.assertNotNull(productCategory);
}
}
複製代碼
ProductCategoryDao.java
package com.imooc.dataobject.dao;
import com.imooc.dataobject.mapper.ProductCategoryMapper;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Map;
public class ProductCategoryDao {
@Autowired
ProductCategoryMapper mapper;
public int insertByMap(Map<String, Object> map) {
return mapper.insertByMap(map);
}
}
複製代碼
OrderForm2OrderDTOConverter.java
package com.imooc.converter;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.imooc.dataobject.OrderDetail;
import com.imooc.dto.OrderDTO;
import com.imooc.enums.ResultEnum;
import com.imooc.exception.SellException;
import com.imooc.form.OrderForm;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.List;
@Slf4j
public class OrderForm2OrderDTOConverter {
public static OrderDTO convert(OrderForm orderForm) {
Gson gson = new Gson();
OrderDTO orderDTO = new OrderDTO();
orderDTO.setBuyerName(orderForm.getName());
orderDTO.setBuyerPhone(orderForm.getPhone());
orderDTO.setBuyerAddress(orderForm.getAddress());
orderDTO.setBuyerOpenid(orderForm.getOpenid());
List<OrderDetail> orderDetailList = new ArrayList<>();
try {
orderDetailList = gson.fromJson(orderForm.getItems(),
new TypeToken<List<OrderDetail>>() {
}.getType());
} catch (Exception e) {
log.error("【對象轉換】錯誤, string={}", orderForm.getItems());
throw new SellException(ResultEnum.PARAM_ERROR);
}
orderDTO.setOrderDetailList(orderDetailList);
return orderDTO;
}
}
複製代碼
OrderMaster2OrderDTOConverter.java
package com.imooc.converter;
import com.imooc.dataobject.OrderMaster;
import com.imooc.dto.OrderDTO;
import org.springframework.beans.BeanUtils;
import java.util.List;
import java.util.stream.Collectors;
public class OrderMaster2OrderDTOConverter {
public static OrderDTO convert(OrderMaster orderMaster) {
OrderDTO orderDTO = new OrderDTO();
BeanUtils.copyProperties(orderMaster, orderDTO);
return orderDTO;
}
public static List<OrderDTO> convert(List<OrderMaster> orderMasterList) {
return orderMasterList.stream().map(e ->
convert(e)
).collect(Collectors.toList());
}
}
複製代碼
spring.http.encoding.force=true
spring.http.encoding.charset=UTF-8
spring.http.encoding.enabled=true
server.tomcat.uri-encoding=UTF-8
複製代碼
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
...
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
複製代碼
index.html
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head lang="en"> <meta charset="UTF-8"/> <title></title> </head> <body> <h1 th:text="${host}">Hello World</h1> </body> </html> 複製代碼
注意:模板的位置能夠直接放在src/main/resources/templates/目錄下。
application.properties
spring.http.encoding.force=true
spring.http.encoding.charset=UTF-8
spring.http.encoding.enabled=true
server.tomcat.uri-encoding=UTF-8
# Enable template caching.
spring.thymeleaf.cache=true
# Check that the templates location exists.
spring.thymeleaf.check-template-location=true
# Content-Type value.
spring.thymeleaf.content-type=text/html
# Enable MVC Thymeleaf view resolution.
spring.thymeleaf.enabled=true
# Template encoding.
spring.thymeleaf.encoding=UTF-8
# Comma-separated list of view names that should be excluded from resolution.
spring.thymeleaf.excluded-view-names=
# Template mode to be applied to templates. See also StandardTemplateModeHandlers.
spring.thymeleaf.mode=HTML5
# Prefix that gets prepended to view names when building a URL.
spring.thymeleaf.prefix=classpath:/templates/
# Suffix that gets appended to view names when building a URL.
spring.thymeleaf.suffix=.html
複製代碼
ThymeleafTest.java
package com.mindex.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class ThymeleafTest {
@RequestMapping("/")
public String index(ModelMap map) {
map.addAttribute("host", "http://www.mindex.com");
return "index";
}
}
複製代碼
運行結果以下:
引入POM依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.21</version>
</dependency>
複製代碼
修改配置文件application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=welcome
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
複製代碼
Service接口:UserService.java
package com.mindex.service;
public interface UserService {
void create(String name, Integer age);
void deleteByName(String name);
Integer getAllUsers();
void deleteAllUsers();
}
複製代碼
Service實現:UserServiceImpl.java
package com.mindex.service.impl;
import com.mindex.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void create(String name, Integer age) {
jdbcTemplate.update("INSERT INTO user (name, age) VALUES (?,?);", name, age);
}
@Override
public void deleteByName(String name) {
jdbcTemplate.update("delete from USER where NAME = ?", name);
}
@Override
public Integer getAllUsers() {
return jdbcTemplate.queryForObject("select count(1) from USER", Integer.class);
}
@Override
public void deleteAllUsers() {
jdbcTemplate.update("delete from USER");
}
}
複製代碼
單元測試:UserServiceImplTest.java
package com.mindex.service.impl;
import com.mindex.service.UserService;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class UserServiceImplTest {
@Autowired
private UserService userService;
@Test
public void create() throws Exception {
// 插入5個用戶
userService.create("a", 1);
userService.create("b", 2);
userService.create("c", 3);
userService.create("d", 4);
userService.create("e", 5);
Assert.assertEquals(5, userService.getAllUsers().intValue());
}
@Test
public void deleteByName() throws Exception {
userService.deleteByName("b");
userService.deleteByName("c");
Assert.assertEquals(3, userService.getAllUsers().intValue());
}
@Test
public void getAllUsers() throws Exception {
}
@Test
public void deleteAllUsers() throws Exception {
}
}
複製代碼
引入pom依賴:pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
複製代碼
在application.properties
建立數據庫鏈接信息。
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=123qweasd
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.properties.hibernate.hbm2ddl.auto=create-drop
複製代碼
spring.jpa.properties.hibernate.hbm2ddl.auto
是hibernate的配置屬性,其主要做用是:自動建立、更新、驗證數據庫表結構。該參數的幾種配置以下:
create
:每次加載hibernate時都會刪除上一次的生成的表,而後根據你的model類再從新來生成新表,哪怕兩次沒有任何改變也要這樣執行,這就是致使數據庫表數據丟失的一個重要緣由。create-drop
:每次加載hibernate時根據model類生成表,可是sessionFactory一關閉,表就自動刪除。update
:最經常使用的屬性,第一次加載hibernate時根據model類會自動創建起表的結構(前提是先創建好數據庫),之後加載hibernate時根據model類自動更新表結構,即便表結構改變了但表中的行仍然存在不會刪除之前的行。要注意的是當部署到服務器後,表結構是不會被立刻創建起來的,是要等應用第一次運行起來後纔會。validate
:每次加載hibernate時,驗證建立數據庫表結構,只會和數據庫中的表進行比較,不會建立新表,可是會插入新值。建立實體 建立一個User實體,包含id(主鍵)、name(姓名)、age(年齡)屬性,經過ORM框架其會被映射到數據庫表中,因爲配置了hibernate.hbm2ddl.auto
,在應用啓動的時候框架會自動去數據庫中建立對應的表。
User.java
package com.mindex.entities;
import lombok.Data;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Data
@Entity
public class User {
@Id
@GeneratedValue
private Long id;
@Column(nullable = false)
private String name;
@Column(nullable = false)
private Integer age;
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
public User() {
}
}
複製代碼
建立數據訪問接口 下面針對User實體建立對應的Repository接口實現對該實體的數據訪問,以下代碼:UserRepository.java
package com.mindex.repository;
import com.mindex.entities.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
public interface UserRepository extends JpaRepository<User, Long> {
User findByName(String name);
User findByNameAndAge(String name, Integer age);
@Query("from User u where u.name=:name")
User findUser(@Param("name") String name);
}
複製代碼
在Spring-data-jpa中,只須要編寫相似上面這樣的接口就可實現數據訪問。再也不像咱們以往編寫了接口時候還須要本身編寫接口實現類,直接減小了咱們的文件清單。
下面對上面的UserRepository
作一些解釋,該接口繼承自JpaRepository
,經過查看JpaRepository
接口的API文檔,能夠看到該接口自己已經實現了建立(save)、更新(save)、刪除(delete)、查詢(findAll、findOne)等基本操做的函數,所以對於這些基礎操做的數據訪問就不須要開發者再本身定義。
在咱們實際開發中,JpaRepository
接口定義的接口每每還不夠或者性能不夠優化,咱們須要進一步實現更復雜一些的查詢或操做。因爲本文重點在spring boot中整合spring-data-jpa,在這裏先拋磚引玉簡單介紹一下spring-data-jpa中讓咱們興奮的功能,後續再單獨開篇講一下spring-data-jpa中的常見使用。
在上例中,咱們能夠看到下面兩個函數:
User findByName(String name)
User findByNameAndAge(String name, Integer age)
它們分別實現了按name查詢User實體和按name和age查詢User實體,能夠看到咱們這裏沒有任何類SQL語句就完成了兩個條件查詢方法。這就是Spring-data-jpa的一大特性:經過解析方法名建立查詢。
除了經過解析方法名來建立查詢外,它也提供經過使用@Query 註解來建立查詢,您只須要編寫JPQL語句,並經過相似「:name」來映射@Param指定的參數,就像例子中的第三個findUser函數同樣。
Spring-data-jpa的能力遠不止本文提到的這些,因爲本文主要以整合介紹爲主,對於Spring-data-jpa的使用只是介紹了常見的使用方式。諸如@Modifying操做、分頁排序、原生SQL支持以及與Spring MVC的結合使用等等內容就不在本文中詳細展開,這裏先挖個坑,後續再補文章填坑,如您對這些感興趣能夠關注我博客或簡書,一樣歡迎你們留言交流想法。
單元測試 在完成了上面的數據訪問接口以後,按照慣例就是編寫對應的單元測試來驗證編寫的內容是否正確。這裏就很少作介紹,主要經過數據操做和查詢來反覆驗證操做的正確性。
package com.mindex.repository;
import com.mindex.entities.User;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@SpringBootTest
@RunWith(SpringJUnit4ClassRunner.class)
public class UserRepositoryTest {
@Autowired
private UserRepository userRepository;
@Test
public void test() throws Exception {
// 建立10條記錄
userRepository.save(new User("AAA", 10));
userRepository.save(new User("BBB", 20));
userRepository.save(new User("CCC", 30));
userRepository.save(new User("DDD", 40));
userRepository.save(new User("EEE", 50));
userRepository.save(new User("FFF", 60));
userRepository.save(new User("GGG", 70));
userRepository.save(new User("HHH", 80));
userRepository.save(new User("III", 90));
userRepository.save(new User("JJJ", 100));
// 測試findAll, 查詢全部記錄
Assert.assertEquals(10, userRepository.findAll().size());
// 測試findByName, 查詢姓名爲FFF的User
Assert.assertEquals(60, userRepository.findByName("FFF").getAge().longValue());
// 測試findUser, 查詢姓名爲FFF的User
Assert.assertEquals(60, userRepository.findUser("FFF").getAge().longValue());
// 測試findByNameAndAge, 查詢姓名爲FFF而且年齡爲60的User
Assert.assertEquals("FFF", userRepository.findByNameAndAge("FFF", 60).getName());
// 測試刪除姓名爲AAA的User
userRepository.delete(userRepository.findByName("AAA"));
// 測試findAll, 查詢全部記錄, 驗證上面的刪除是否成功
Assert.assertEquals(9, userRepository.findAll().size());
}
}
複製代碼
引入POM依賴:pom.xml
<!--此處並非直接使用spring提供的redis-starter-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
複製代碼
外部配置文件:application.yaml
jedis:
host: 127.0.0.1
port: 6379
pool:
max-idle: 300
min-idle: 10
max-total: 600
max-wait: 1000
block-when-exhausted: true
複製代碼
Java配置類(替代傳統xml):RedisConfig.java
package com.mindex.config;
import lombok.Data;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
@Data
@Component
public class RedisConfig {
@Bean("jedis.config")
public JedisPoolConfig jedisPoolConfig(@Value("${jedis.pool.min-idle}") int minIdle, @Value("${jedis.pool.max-idle}") int maxIdle, @Value("${jedis.pool.max-wait}") int maxWaitMillis, @Value("${jedis.pool.block-when-exhausted}") boolean blockWhenExhausted, @Value("${jedis.pool.max-total}") int maxTotal) {
JedisPoolConfig config = new JedisPoolConfig();
config.setMinIdle(minIdle);
config.setMaxIdle(maxIdle);
config.setMaxWaitMillis(maxWaitMillis);
config.setMaxTotal(maxTotal);
// 鏈接耗盡時是否阻塞, false報異常,ture阻塞直到超時, 默認true
config.setBlockWhenExhausted(blockWhenExhausted);
// 是否啓用pool的jmx管理功能, 默認true
config.setJmxEnabled(true);
return config;
}
@Bean
public JedisPool jedisPool(@Qualifier("jedis.config") JedisPoolConfig config, @Value("${jedis.host}") String host, @Value("${jedis.port}") int port) {
return new JedisPool(config, host, port);
}
}
複製代碼
Service接口定義:RedisService.java
package com.mindex.service;
public interface RedisService {
String get(String key);
boolean set(String key, String val);
}
複製代碼
Service接口實現類:RedisServiceImpl.java
package com.mindex.service.impl;
import com.mindex.service.RedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
@Service
public class RedisServiceImpl implements RedisService {
// 此處直接注入便可
@Autowired
private JedisPool jedisPool;
@Override
public String get(String key) {
Jedis jedis = this.jedisPool.getResource();
String ret;
try {
ret = jedis.get(key);
} finally {
if (jedis != null)
jedis.close();
}
return ret;
}
@Override
public boolean set(String key, String val) {
Jedis jedis = this.jedisPool.getResource();
try {
return "OK".equals(jedis.set(key, val));
} finally {
if (jedis != null)
jedis.close();
}
}
}
複製代碼
測試:RedisServiceImplTest.java
package com.mindex.service.impl;
import com.mindex.service.RedisService;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class RedisServiceImplTest {
@Autowired
private RedisService redisService;
@Test
public void testGet() {
// test set
boolean status = this.redisService.set("foo", "bar");
Assert.assertTrue(status);
// test get
String str = this.redisService.get("foo");
Assert.assertEquals("bar", str);
}
}
複製代碼
在Redis中檢查結果
外部配置文件:application.yaml
jedis:
host: 127.0.0.1
port: 6379
pool:
max-idle: 300
min-idle: 10
max-total: 600
max-wait: 1000
block-when-exhausted: true
複製代碼
配置類:RedisConfig1.java
package com.mindex.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig1 {
@Autowired
private RedisConnectionFactory redisConnectionFactory;
/** * 實例化 RedisTemplate 對象 * */
@Bean
public RedisTemplate<String, Object> functionDomainRedisTemplate() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
this.initRedisTemplate(redisTemplate, redisConnectionFactory);
return redisTemplate;
}
/** * 序列化設置 */
private void initRedisTemplate(RedisTemplate<String, Object> redisTemplate, RedisConnectionFactory factory) {
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer());
redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
redisTemplate.setConnectionFactory(factory);
}
@Bean
public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForHash();
}
@Bean
public ValueOperations<String, Object> valueOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForValue();
}
@Bean
public ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForList();
}
@Bean
public SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForSet();
}
@Bean
public ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForZSet();
}
}
複製代碼
簡單測試:RedisAnotherConfigTest.java
package com.mindex;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.test.context.junit4.SpringRunner;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@RunWith(SpringRunner.class)
@SpringBootTest
public class RedisAnotherConfigTest {
@Autowired
private ValueOperations<String, Object> valueOperations;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Test
public void contextLoads() {
}
@Test
public void testStringOps() {
this.valueOperations.set("k1", "spring-redis");
Boolean hasKey = this.redisTemplate.hasKey("k1");
assertEquals(true, hasKey);
Object str = this.valueOperations.get("k1");
assertNotNull(str);
assertEquals("spring-redis", str.toString());
}
}
複製代碼
引入POM依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
複製代碼
增長配置:application.properties
注意:這裏的
password
是郵箱受權碼,不是郵箱密碼。
spring.mail.host=smtp.qq.com
spring.mail.username=85648606@qq.com
spring.mail.password=clqpsraiifwqbidg
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true
複製代碼
單元測試:MailTest.java
package com.mindex;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class MailTest {
@Autowired
private JavaMailSender mailSender;
@Test
public void sendSimpleMail() throws Exception {
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom("85648606@qq.com");
message.setTo("wfgdlut@msn.com");
message.setSubject("主題:簡單郵件");
message.setText("測試郵件內容");
mailSender.send(message);
}
}
複製代碼
@Test
public void sendAttachmentsMail() throws Exception {
MimeMessage mimeMessage = mailSender.createMimeMessage();
MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage);
mimeMessageHelper.setFrom("85648606@qq.com");
mimeMessageHelper.setTo("wfgdlut@msn.com");
mimeMessageHelper.setSubject("Spring Boot Mail 郵件測試【HTML】");
StringBuilder sb = new StringBuilder();
sb.append("<html><head></head>");
sb.append("<body><h1>spring 郵件測試</h1><p>hello!this is spring mail test。</p></body>");
sb.append("</html>");
// 啓用html
mimeMessageHelper.setText(sb.toString(), true);
// 發送郵件
mailSender.send(mimeMessage);
System.out.println("郵件已發送");
}
複製代碼
/** * 發送包含內嵌圖片的郵件 * * @throws Exception */
@Test
public void sendAttachedImageMail() throws Exception {
MimeMessage mimeMessage = mailSender.createMimeMessage();
// multipart模式
MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, true);
mimeMessageHelper.setFrom("85648606@qq.com");
mimeMessageHelper.setTo("wfgdlut@msn.com");
mimeMessageHelper.setSubject("Spring Boot Mail 郵件測試【圖片】");
StringBuilder sb = new StringBuilder();
sb.append("<html><head></head>");
sb.append("<body><h1>spring 郵件測試</h1><p>hello!this is spring mail test。</p>");
// cid爲固定寫法,imageId指定一個標識
sb.append("<img src=\"cid:imageId\"/></body>");
sb.append("</html>");
// 啓用html
mimeMessageHelper.setText(sb.toString(), true);
// 設置imageId
FileSystemResource img = new FileSystemResource(new File("/Users/kwang/IdeaProjects/SpringBootDemoProject/src/main/resources/static/test.png"));
mimeMessageHelper.addInline("imageId", img);
// 發送郵件
mailSender.send(mimeMessage);
System.out.println("郵件已發送");
}
複製代碼
/** * 發送包含附件的郵件 * @throws Exception */
@Test
public void sendAttendedFileMail() throws Exception {
MimeMessage mimeMessage = mailSender.createMimeMessage();
// multipart模式
MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, true, "utf-8");
mimeMessageHelper.setFrom("85648606@qq.com");
mimeMessageHelper.setTo("wfgdlut@msn.com");
mimeMessageHelper.setSubject("Spring Boot Mail 郵件測試【附件】");
StringBuilder sb = new StringBuilder();
sb.append("<html><head></head>");
sb.append("<body><h1>spring 郵件測試</h1><p>hello!this is spring mail test。</p></body>");
sb.append("</html>");
// 啓用html
mimeMessageHelper.setText(sb.toString(), true);
// 設置附件
FileSystemResource img = new FileSystemResource(new File("/Users/kwang/IdeaProjects/SpringBootDemoProject/src/main/resources/static/test.png"));
mimeMessageHelper.addAttachment("test.png", img);
// 發送郵件
mailSender.send(mimeMessage);
System.out.println("郵件已發送");
}
複製代碼