Springboot+redis+freemarker 自定义注解实现页面缓存

pom.xml 

 

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <!--freemarker-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>

        <dependency>
            <groupId>org.jsoup</groupId>
            <artifactId>jsoup</artifactId>
            <version>1.10.2</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.61</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>

        <!--redis-->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>

        <!--自定义注解-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <dependencies>
                    <!-- spring热部署 -->
                    <dependency>
                        <groupId>org.springframework</groupId>
                        <artifactId>springloaded</artifactId>
                        <version>1.2.6.RELEASE</version>
                    </dependency>
                </dependencies>
                <configuration><fork>true</fork></configuration>
            </plugin>
        </plugins>
    </build>

 

application.properties

 

server.port=1234
server.servlet.context-path=/redis_freemarker

# 开启热部署
spring.devtools.restart.enabled=true

spring.mvc.view.suffix=.ftl
spring.mvc.static-path-pattern: /**

#    freemarker静态资源配置
#       设定ftl文件路径
spring.freemarker.tempalte-loader-path=classpath:/templates
#        关闭缓存,及时刷新,上线生产环境需要修改为true
spring.freemarker.cache=false
spring.freemarker.charset=UTF-8
spring.freemarker.check-template-location=true
spring.freemarker.content-type=text/html
spring.freemarker.expose-request-attributes=true
spring.freemarker.expose-session-attributes=true
spring.freemarker.request-context-attribute=request
spring.freemarker.suffix=.ftl
# 空值不报错
spring.freemarker.settings.classic_compatible=true

spring.redis.host=localhost
spring.redis.port=6379
spring.redis.timeout=3
spring.redis.password=123456
spring.redis.jedis.pool.max-wait=3
spring.redis.jedis.pool.max-idle=10
spring.redis.jedis.pool.max-total=10

 

redis 注册类  

RedisConfig
package com.example.redis_freemarker;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;


@Component
@PropertySource(value = {"classpath:application.properties"})
public class RedisConfig {

    @Value("${spring.redis.host}")
    private String host;
    @Value("${spring.redis.port}")
    private int port;
    @Value("${spring.redis.timeout}")
    private int timeout;//秒
    @Value("${spring.redis.password}")
    private String password;
    @Value("${spring.redis.jedis.pool.max-total}")
    private int poolMaxTotal;
    @Value("${spring.redis.jedis.pool.max-idle}")
    private int poolMaxIdle;
    @Value("${spring.redis.jedis.pool.max-wait}")
    private int poolMaxWait;//秒

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    public int getTimeout() {
        return timeout;
    }

    public void setTimeout(int timeout) {
        this.timeout = timeout;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public int getPoolMaxTotal() {
        return poolMaxTotal;
    }

    public void setPoolMaxTotal(int poolMaxTotal) {
        this.poolMaxTotal = poolMaxTotal;
    }

    public int getPoolMaxIdle() {
        return poolMaxIdle;
    }

    public void setPoolMaxIdle(int poolMaxIdle) {
        this.poolMaxIdle = poolMaxIdle;
    }

    public int getPoolMaxWait() {
        return poolMaxWait;
    }

    public void setPoolMaxWait(int poolMaxWait) {
        this.poolMaxWait = poolMaxWait;
    }

    @Bean
    public JedisPool jedisPoolFactory() {
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxIdle(poolMaxIdle);
        jedisPoolConfig.setMaxTotal(poolMaxTotal);
        jedisPoolConfig.setMaxWaitMillis(poolMaxWait * 1000);
        JedisPool jedisPool = new JedisPool(jedisPoolConfig,
                host,
                port,
                timeout * 1000,
                password);
        return jedisPool;
    }
}

 

redis 工具类 

RedisService
package com.example.redis_freemarker;

import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

@Service
public class RedisService {

    @Autowired
    private JedisPool jedisPool;

    /**
     * 获取存储对象
     * @param key
     * @param clazz
     * @param <T>
     * @return
     */
    public <T> T get(String key, Class<T> clazz) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            String str = jedis.get(key);
            T t = stringToBean(str, clazz);
            return t;
        } finally {
            returnToPool(jedis);
        }
    }

    /**
     * 设置对象
     * @param key
     * @param expireSeconds
     * @param value
     * @param <T>
     * @return
     */
    public <T> boolean set(String key, int expireSeconds, T value) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            String str = beanToString(value);
            if (null == str) {
                return false;
            }
            if (expireSeconds <= 0) {
                jedis.set(key, str);
            } else {
                jedis.setex(key, expireSeconds, str);
            }
            return true;
        } finally {
            returnToPool(jedis);
        }
    }

    /**
     * 判断key是否存在
     * */
    public <T> boolean exists(String key) {
        Jedis jedis = null;
        try {
            jedis =  jedisPool.getResource();
            return  jedis.exists(key);
        }finally {
            returnToPool(jedis);
        }
    }

    /**
     * 增加值
     * */
    public <T> Long incr(String key) {
        Jedis jedis = null;
        try {
            jedis =  jedisPool.getResource();
            return  jedis.incr(key);
        }finally {
            returnToPool(jedis);
        }
    }

    /**
     * 减少值
     * */
    public <T> Long decr(String key) {
        Jedis jedis = null;
        try {
            jedis =  jedisPool.getResource();
            return  jedis.decr(key);
        }finally {
            returnToPool(jedis);
        }
    }

    private <T> String beanToString(T value) {
        if (null == value) {
            return null;
        }
        if (value instanceof Integer || value instanceof Long) {
            return "" + value;
        } else if (value instanceof String) {
            return (String) value;
        } else {
            return JSON.toJSONString(value);
        }
    }

    private <T> T stringToBean(String str, Class<T> clazz) {
        if (StringUtils.isEmpty(str) || clazz==null) {
            return null;
        }
        if (clazz==int.class || clazz==Integer.class) {
            return (T) Integer.valueOf(str);
        } else if (clazz == String.class) {
            return (T) str;
        } else if (clazz==long.class || clazz==Long.class) {
            return (T)Long.valueOf(str);
        } else {
            return JSON.toJavaObject(JSON.parseObject(str), clazz);
        }
    }

    private void returnToPool(Jedis jedis) {
        if (null != jedis) {
            jedis.close();
        }
    }
}

 

freemaker 工具类

import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateExceptionHandler;

import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.util.Map;

public class FreemarkerUtils {
    public static String getTemplate(String template, Map<String,Object> map) throws IOException, TemplateException {
        Configuration cfg = new Configuration(Configuration.VERSION_2_3_28);
        String templatePath = FreemarkerUtils.class.getResource("/").getPath()+"/templates";
        cfg.setDirectoryForTemplateLoading(new File(templatePath));
        cfg.setDefaultEncoding("UTF-8");
        cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
        cfg.setLogTemplateExceptions(false);
        cfg.setWrapUncheckedExceptions(true);
        cfg.setClassicCompatible(true);// 空值不报错
        Template temp = cfg.getTemplate(template);
        StringWriter stringWriter = new StringWriter();
        temp.process(map, stringWriter);
        return stringWriter.toString();
    }
}

 

新建一个自定义注解   AutoRedis 

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface AutoRedis {

    String key(); // redis key 值

    String returnFtl();  // 返回的ftl页面路径

    int expireSeconds() default 86400; // 缓存过期时间,默认24小时

    boolean soutIfTrue() default true;  // 是否打印信息

}

 

新建一个 AOP 

RedisAspect
package com.example.redis_freemarker;

import com.alibaba.fastjson.JSON;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.util.Map;



@Aspect
@Configuration
public class RedisAspect {

    @Autowired
    private RedisService redisService;

    //定义切点方法
    @Pointcut("@annotation(com.example.redis_freemarker.AutoRedis)")
    public void pointCut(){}

    //环绕通知
    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable,Exception {

        //1.获取到所有的参数值的数组
        Object[] args = joinPoint.getArgs();
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        //2.获取到方法的所有参数名称的字符串数组
        String[] parameterNames = methodSignature.getParameterNames();
        Method method = methodSignature.getMethod();
        Class<?> returnType = method.getReturnType(); // 返回类型
        Class<?> classTarget = joinPoint.getTarget().getClass();

        AutoRedis redis=(AutoRedis)method.getAnnotation(AutoRedis.class);
        boolean soutIfTrue=redis.soutIfTrue();
        soutIfTrue(soutIfTrue,"■■■■■■■■■■■■■■■■■■■■■-AutoRedis注解开始-■■■■■■■■■■■■■■■■■■■■■");
        String key=redis.key();
        String returnFtl=redis.returnFtl();
        int expireSeconds=redis.expireSeconds();
        soutIfTrue(soutIfTrue,"自定义注解 key:" + key);
        soutIfTrue(soutIfTrue,"自定义注解 returnFtl:" + returnFtl);
        soutIfTrue(soutIfTrue,"自定义注解 expireSeconds:" + expireSeconds);
        soutIfTrue(soutIfTrue,"---------------参数列表开始-------------------------");
        for (int i =0 ,len=parameterNames.length;i < len ;i++){
            soutIfTrue(soutIfTrue,"参数名:"+ parameterNames[i] + " = " +args[i]);
            key=redis.key().replaceAll("【"+parameterNames[i]+"】",args[i].toString());
        }
        soutIfTrue(soutIfTrue,"---------------参数列表结束-------------------------");
        soutIfTrue(soutIfTrue,"自定义注解解析后 key:" + key);

        // String html = redisService.get(key, String.class);
        Map<String,Object> map = redisService.get(key, Map.class);

        /*
         1.这里有两种方式存储,可以存储整个HTML代码放进redis,这种数据库压力较大
         2.也可以把 Object result 放进redis,这样redis库不会浪费空间,然后读缓存的时候用freemarker 转换一下,这种服务器压力较大。
        */

        // 1.
        /*
        if (!StringUtils.isEmpty(html)) {
            soutIfTrue(soutIfTrue,"有缓存,返回缓存数据");
            soutIfTrue(soutIfTrue,"■■■■■■■■■■■■■■■■■■■■■-AutoRedis注解结束-■■■■■■■■■■■■■■■■■■■■■\n\n\n");
            return html;
        }else{
            Object result=joinPoint.proceed(args);
            String cmtTpl= null;
            cmtTpl = FreemarkerUtils.getTemplate(returnFtl, (Map<String,Object>)JSON.parseObject(result.toString()));
            // 存入缓存
            if(!StringUtils.isEmpty(cmtTpl)){
                soutIfTrue(soutIfTrue,"没有缓存,返回刚查询的数据,并且存入redis");

                redisService.set(key, expireSeconds, cmtTpl);
            }
            soutIfTrue(soutIfTrue,"■■■■■■■■■■■■■■■■■■■■■-AutoRedis注解结束-■■■■■■■■■■■■■■■■■■■■■\n\n\n");
            return cmtTpl;
        }
        */

        // 2.
        if (map!=null) {
            soutIfTrue(soutIfTrue,"有缓存,返回缓存数据");
            String cmtTpl= null;
            cmtTpl = FreemarkerUtils.getTemplate(returnFtl,map);
            soutIfTrue(soutIfTrue,"■■■■■■■■■■■■■■■■■■■■■-AutoRedis注解结束-■■■■■■■■■■■■■■■■■■■■■\n\n\n");
            return cmtTpl;
        }else{
            Object result=joinPoint.proceed(args);
            String cmtTpl= null;
            cmtTpl = FreemarkerUtils.getTemplate(returnFtl, (Map<String,Object>)JSON.parseObject(result.toString()));
            // 存入缓存
            if(!StringUtils.isEmpty(cmtTpl)){
                soutIfTrue(soutIfTrue,"没有缓存,返回刚查询的数据,并且存入redis");
                redisService.set(key, expireSeconds, (Map)JSON.parseObject(result.toString()));
            }
            soutIfTrue(soutIfTrue,"■■■■■■■■■■■■■■■■■■■■■-AutoRedis注解结束-■■■■■■■■■■■■■■■■■■■■■\n\n\n");
            return cmtTpl;
        }

    }

    public void soutIfTrue(Boolean flag,String str){
        if(flag){
            System.out.println(str);
        }
    }

}

 

最后来实验一下

package com.example.redis_freemarker;

import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.HashMap;
import java.util.Map;


@Controller
public class test {

    @Autowired
    private RedisService redisService;

    @RequestMapping(value = "/test")
    @ResponseBody
    public String test(String page) {

        if(StringUtils.isEmpty(page)){
            return "page参数不能为空";
        }

        //判断redis是否有缓存
        String html = redisService.get(page, String.class);
        if (!StringUtils.isEmpty(html)) {
            System.out.println("有缓存,返回缓存数据");
            //System.out.println(html);
            return html;
        }

        // 这里模拟一下查询数据,浪费时间3秒
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Map<String,Object> map=new HashMap<>();
        map.put("NAME","吴彦祖");
        map.put("ID","14567");
        map.put("AGE","18");
        map.put("PAGE",page);


        String cmtTpl= null;
        try {
            cmtTpl = FreemarkerUtils.getTemplate("/test/test.ftl",map);
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 存入缓存
        if(!StringUtils.isEmpty(cmtTpl)){
            System.out.println("没有缓存,返回刚查询的数据,并且存入redis");
            redisService.set(page, 86400, cmtTpl);
        }

        return cmtTpl;
    }


    @RequestMapping("/test2")
    @ResponseBody
    @AutoRedis(key = "HELLO_【page】",returnFtl = "/test/test.ftl",expireSeconds = 86400,soutIfTrue = true)
    public String test2(String page){
        if(StringUtils.isEmpty(page)){
            return "page参数不能为空";
        }
        // 这里模拟一下查询数据,浪费时间3秒
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Map<String,Object> map=new HashMap<>();
        map.put("NAME","吴彦祖");
        map.put("ID","14567");
        map.put("AGE","18");
        map.put("PAGE",page);

        return JSON.toJSONString(map);
    }
}

 

 

 

test.ftl

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
</head>
<body>
<table border="1">
    <thead>
    <tr>
        <th>PAGE</th>
        <th>NAME</th>
        <th>ID</th>
        <th>AGE</th>
    </tr>
    </thead>
    <tbody>
    <tr>
        <td>${PAGE}</td>
        <td>${NAME}</td>
        <td>${ID}</td>
        <td>${AGE}</td>
    </tr>
    </tbody>
</table>
</body>
</html>

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值