基於SpringBoot從零構建博客網站 - 整合ehcache和開發註冊登陸功能

對於程序中一些字典信息、配置信息應該在程序啓動時加載到緩存中,用時先到緩存中取,若是沒有命中,再到數據庫中獲取同時放到緩存中,這樣作能夠減輕數據庫層的壓力。目前暫時先整合ehcache緩存,同時預留了集成redis和memcached的接口。java

先開發兩個最基本的功能,就是註冊和登陸,對於頁面幾乎就是直接用bootstrap的風格,目前沒有過多的設計。redis

一、整合ehcache

在spring boot中整合ehcache仍是很方便的,首先添加依賴:spring

<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>

其中設置了兩種緩存類型,一個是臨時緩存,另外一個是永久緩存。bootstrap

此處使用緩存方式不是基於註解的,雖然基於註解的方式也很方便,可是我的以爲仍是本身程序控制緩存好一些。緩存

程序中會將站點的配置信息加載到緩存中,那麼使用方式以下:微信

(1)、首先定義一個緩存接口,本程序中須要用到緩存的,必須實現該接口,即:session

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,實現該接口,即:app

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);
    }
}

二、註冊功能

註冊頁面效果以下:dom

頁面風格很素,這個暫時先這樣。

主要看一下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狀態。

三、登陸功能

登陸頁面效果以下:

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;
}

當用戶登陸時,還有一些相關統計信息,這裏因爲其它功能尚未開發完,因此獲取統計信息的代碼後面再加上。

關注我

以你最方便的方式關注我:
微信公衆號:

相關文章
相關標籤/搜索