對於程序中一些字典信息、配置信息應該在程序啟動時加載到緩存中,用時先到緩存中取,如果沒有命中,再到數據庫中獲取同時放到緩存中,這樣做可以減輕數據庫層的壓力。目前暫時先整合ehcache緩存,同時預留了集成redis和memcached的接口。
先開發兩個最基本的功能,就是注冊和登錄,對於頁面幾乎就是直接用bootstrap的風格,目前沒有過多的設計。
1、整合ehcache
在spring boot中整合ehcache還是很方便的,首先添加依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.10.2</version>
</dependency>
新增ehcache的配置文件,ehcache.xml,即:
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd"
updateCheck="false"
monitoring="autodetect"
dynamicConfig="true">
<diskStore path="java.io.tmpdir" />
<!-- 系統臨時緩存(十分鍾) -->
<cache
name="SystemTempCache"
maxEntriesLocalHeap="0"
maxEntriesLocalDisk="10000000"
eternal="false"
timeToIdleSeconds="0"
timeToLiveSeconds="600"
overflowToDisk="false"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
diskSpoolBufferSizeMB="30"
memoryStoreEvictionPolicy="LRU">
</cache>
<!-- 系統永久緩存 -->
<cache
name="SystemEternalCache"
maxEntriesLocalHeap="0"
maxEntriesLocalDisk="10000000"
eternal="true"
overflowToDisk="false"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
diskSpoolBufferSizeMB="30"
memoryStoreEvictionPolicy="LRU">
</cache>
</ehcache>
其中設置了兩種緩存類型,一個是臨時緩存,另一個是永久緩存。
此處使用緩存方式不是基於注解的,雖然基於注解的方式也很方便,但是個人覺得還是自己程序控制緩存好一些。
程序中會將站點的配置信息加載到緩存中,那么使用方式如下:
(1)、首先定義一個緩存接口,本程序中需要用到緩存的,必須實現該接口,即:
package com.swnote.common.cache;
/**
* 緩存接口
*
* @author lzj
* @since 1.0
* @date [2019-04-27]
*/
public interface ICache<T> {
/**
* 根據key獲取緩存數據
*
* @param key
* @return
*/
public T get(Object key);
/**
* 存放緩存數據
*
* @param key
* @param value
*/
public void put(Object key, T value);
/**
* 根據key移除內容
*
* @param key
*/
public void remove(Object key);
}
(2)、站點配置信息緩存ConfigCache,實現該接口,即:
package com.swnote.common.cache;
import com.swnote.common.domain.Config;
import com.swnote.common.service.IConfigService;
import com.swnote.common.util.Const;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.List;
/**
* 緩存配置信息
* 配置信息放到系統永久緩存中,存放形式為:"_CONFIG" + configId為key,value為配置信息對象
*
* @author lzj
* @since 1.0
* @date [2019-04-27]
*/
@Slf4j
@DependsOn("configService")
@Component("configCache")
public class ConfigCache implements ICache<Config> {
/**
* 注入基於Spring提供的Cache接口實例,默認由Ehcache實現
* TODO 以后也可以是Redis、Memcached提供實現
*/
@Autowired
private CacheManager cacheManager;
@Autowired
private IConfigService configService;
/**
* 系統臨時緩存實例
*/
private Cache cache;
/**
* key的前綴
*/
private String keyPrefix = "_CONFIG";
@PostConstruct
public void init() {
// 獲取系統永久緩存實例
cache = cacheManager.getCache(Const.CACHE_SYSTEM_ETERNAL);
log.info("獲取系統永久緩存實例");
log.info("開始加載所有配置信息");
List<Config> configs = configService.list();
if (configs != null && !configs.isEmpty()) {
configs.stream().forEach(config -> cache.put(keyPrefix + config.getConfigId(), config));
}
log.info("加載完畢所有配置信息");
}
@Override
public Config get(Object key) {
Cache.ValueWrapper valueWrapper = cache.get(keyPrefix + key);
if (valueWrapper == null) {
// 此時從數據庫重新加載一次
Config config = configService.getById((String) key);
if (config == null) {
return null;
}
// 再次放到緩存中
cache.put(keyPrefix + config.getConfigId(), config);
return config;
}
return (Config) valueWrapper.get();
}
@Override
public void put(Object key, Config value) {
cache.put(keyPrefix + key, value);
}
@Override
public void remove(Object key) {
cache.evict(keyPrefix + key);
}
}
2、注冊功能
注冊頁面效果如下:
頁面風格很素,這個暫時先這樣。
主要看一下UserController中處理注冊信息的關鍵代碼,即:
/**
* 保存注冊信息
*
* @param model
* @param request
* @return
*/
@RequestMapping(value = "/auth/signup", method = RequestMethod.POST)
@ResponseBody
public Result signup(Model model, HttpServletRequest request) {
Result result = new Result();
try {
// 接收參數
String name = request.getParameter("name");
String email = request.getParameter("email");
String password = request.getParameter("password");
// 簡單校驗
if (StringUtils.isEmpty(name) || StringUtils.isEmpty(email) || StringUtils.isEmpty(password)) {
throw new TipException("缺少必要請求參數");
}
if (!StringUtil.isEmail(email)) {
throw new TipException("郵箱不符全規范");
}
// 校驗用戶名
User tempUser = userService.getByName(name);
if (tempUser != null && !StringUtils.isEmpty(tempUser.getUserId())) {
throw new TipException("該用戶已經注冊了");
}
// 校驗郵箱
tempUser = userService.getByEmail(email);
if (tempUser != null && !StringUtils.isEmpty(tempUser.getUserId())) {
throw new TipException("該郵箱已經注冊了");
}
// 獲取用戶ip
String ip = HttpUtil.getIpAddr(request);
// 構建用戶信息
User user = new User();
user.setLoginName(name);
user.setEmail(email);
user.setPassword(StringUtil.md5(password));
user.setCreateIp(ip);
// 保存用戶信息
boolean flag = userService.create(user);
if (!flag) {
throw new TipException("用戶創建失敗");
}
result.setCode(Result.CODE_SUCCESS);
result.setMsg("用戶創建成功");
} catch (Exception e) {
log.error("用戶創建失敗", e);
result.setCode(Result.CODE_EXCEPTION);
result.setMsg("用戶創建失敗");
}
return result;
}
在UserService中有一個create方法,即:
@Override
public boolean create(User user) {
// 獲取當前時間
Date now = new Date();
// 設置主鍵
user.setUserId(IdGenarator.guid());
// 設置未實名認證
user.setRealStatus(User.REAL_STATUS_NO);
// 用戶是否需要激活
Config config = configCache.get(Const.CONFIG_USER_ACTIVE);
if (config != null && "1".equals(config.getConfigValue())) {
// TODO 發送激活郵件信息
// 說明需要激活
user.setIsActive(User.ACTIVE_NO);
} else {
// 說明不需要激活,默認激活
user.setIsActive(User.ACTIVE_YES);
}
// 設置啟用賬號狀態
user.setStatus(User.STATUS_YES);
// 設置創建時間
user.setCreateTime(now);
// 設置關注數為0
user.setFollows(0);
// 設置粉絲數為0
user.setFans(0);
return save(user);
}
此處有一個還沒有實現的功能,就是發送激活郵件信息,這個功能后面會補上,這里先處於TODO狀態。
3、登錄功能
登錄頁面效果如下:
UserController中關鍵的代碼如下:
/**
* 處理登錄信息
*
* @param request
* @return
*/
@RequestMapping(value = "/auth/login", method = RequestMethod.POST)
@ResponseBody
public Result login(HttpServletRequest request, HttpSession session) {
Result result = new Result();
try {
// 接收參數
String name = request.getParameter("name");
String password = request.getParameter("password");
if (StringUtils.isEmpty(name) || StringUtils.isEmpty(password)) {
throw new TipException("缺少必要請求參數");
}
// 獲取用戶ip
String ip = HttpUtil.getIpAddr(request);
User user = userService.verifyUser(name, password, ip);
if (user == null) {
throw new TipException("用戶名或密碼錯誤");
}
// 放置session信息
session.setAttribute(Const.SESSION_USER, user);
// TODO 還有一些相關統計信息,后面再加上
result.setCode(Result.CODE_SUCCESS);
result.setMsg("登錄成功");
} catch (TipException e) {
result.setCode(Result.CODE_EXCEPTION);
result.setMsg(e.getMessage());
} catch (Exception e) {
log.error("登錄失敗", e);
result.setCode(Result.CODE_EXCEPTION);
result.setMsg("登錄失敗");
}
return result;
}
當用戶登錄時,還有一些相關統計信息,這里由於其它功能還沒有開發完,所以獲取統計信息的代碼后面再加上。
關注我
以你最方便的方式關注我:
微信公眾號: