由于业务需要使用文件级别的缓存,特此采用protostuff加上基本的TXT文件操作,来实现基于文件的缓存。

实现以下功能:

文件级别缓存的对象工具类

1、传入一个对象和缓存时间还有缓存名称对这个对象进行缓存

2、传入一个缓存名称查询是否存在这个名称的缓存

3、传入一个缓存名称和该类的类型对象获取对应的缓存

4、传入一个缓存名称删除该名称的缓存

模块一:protostuff序列化使用类


package tool;

import io.protostuff.LinkedBuffer;
import io.protostuff.ProtostuffIOUtil;
import io.protostuff.runtime.RuntimeSchema;

/**
 * Created by yy on 2017/9/28.
 * 用与序列化以及反序列化对象的类
 * 1、传入一个对象,返回该对象序列化后的字节数组
 * 2、传入字节数组和类的类型对象,获取这个数据的反序列对象
 */
public class Serialization {
    /**
     * 传入一个对象,返回这个对象的序列化后的字节数组
     * @param object 需要实例化的对象
     * @param <T> 传入对象的类型
     * @return 返回对应的字节数组
     * @throws Exception 序列化失败继续抛出异常
     */
    public static <T> byte[] sequence(T object) throws Exception{
        //安全判断,要是传入的对象为空返回对应的也是空
        if(object==null){
            return null;
        }
        //获取对应的类
        Class<T> objectClass = (Class<T>) object.getClass();
        //使用LinkedBuffer分配一块默认大小的buffer空间;
        LinkedBuffer linkedBuffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
        try {
            //生成对应的图,返回对应的字节数组
            //使用ProtoStuff-runtime生成模式,以便在运行时通过反射进行缓存和使用。
            RuntimeSchema<T> runtimeSchema = RuntimeSchema.createFrom(objectClass);
            return ProtostuffIOUtil.toByteArray(object, runtimeSchema, linkedBuffer);
        } catch (Exception e) {
            //继续抛出异常
            throw new RuntimeException("对象序列化失败!",e);
        } finally {
            //关闭Buffer空间
            linkedBuffer.clear();
        }
    }

    /**
     * 根据穿进来的字节数组和具体的类的类型对象,获取对应反序列的结果对象
     * @param info 序列化完成的字节数组对象
     * @param classInfo 类的类型对象
     * @param <T> 具体要转换的类类型
     * @return 序列化成功返回序列化前的对象,要是传入的数据为空返回空对象
     * @throws Exception 抛出反序列化的异常
     */
    public static <T> T reverse(byte[] info,Class<T> classInfo)throws Exception{
        //安全判断
        if(info.length==0||classInfo==null){
            return null;
        }
        //根据传进来的数据反序列具体对象
        try{
            RuntimeSchema<T> runtimeSchema = RuntimeSchema.createFrom(classInfo);
            T object =runtimeSchema.newMessage();
            ProtostuffIOUtil.mergeFrom(info,object,runtimeSchema);
            return object;
        }catch(Exception e){
            throw new RuntimeException("反序列对象失败!",e);
        }
    }
}



模块二:MD5加密(用于文件名生成)


package tool;

import java.security.MessageDigest;

/**
 * md5的生成类
 * Created by yy on 2017/10/11.
 */
public class MD5maker {
    public static String make(String value){
        String md5str = "";
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] input = value.getBytes();
            byte[] buff = md.digest(input);
            md5str = bytesToHex(buff);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return md5str;
    }

    /**
     * 换成16进制
     * @param bytes
     * @return
     */
    private static String bytesToHex(byte[] bytes) {
        StringBuffer md5str = new StringBuffer();
        int digital;
        for (int i = 0; i < bytes.length; i++) {
            digital = bytes[i];
            if (digital < 0) {
                digital += 256;
            }
            if (digital < 16) {
                md5str.append("0");
            }
            md5str.append(Integer.toHexString(digital));
        }
        return md5str.toString().toUpperCase();
    }
}

模块三:缓存工具类

package tool;

import org.apache.commons.lang3.StringEscapeUtils;

import java.io.*;

/**
 * Created by yy on 2017/9/29.
 * 文件级别缓存的对象工具类
 * 1、传入一个对象和缓存时间还有缓存名称对这个对象进行缓存
 * 2、传入一个名称查询是否存在这个名称的缓存
 * 3、传入一个名称和该类的类型对象获取对应的缓存
 * 4、传入一个缓存名称删除该名称的缓存
 */
public class CacheBF {
    /**
     * 根据穿进来的名称和实体缓存对象到文件中
     * @param name 键名
     * @param object 需要保存的实体
     * @param time 过期时间秒
     * @param <T> 传入的实体类型
     * @return 保存成功返回true,失败返回false
     */
    public static <T> boolean  cache(String name,T object,int time) throws Exception{

        File file=CacheBF.getCacheFile(name);//获取缓存文件
        FileWriter fw=new FileWriter(file);
        BufferedWriter bw = new BufferedWriter(fw);
        //设置过期时间
        bw.write((System.currentTimeMillis()+(time*1000))+"");
        bw.newLine();
        //设置缓存信息
        bw.write(StringEscapeUtils.escapeJava(new String(Serialization.sequence(object))));//添加转义
        bw.close();
        fw.close();
        return true;
    }

    /**
     * 根据传进来的文件名,和类的类型获取缓存的实体类
     * @param name 缓存名称
     * @param objectClass 需要取得的实体类型
     * @param <T> 旋回对应实例
     * @return
     * @throws Exception
     */
    public static <T> T get(String name,Class<T> objectClass)throws Exception{

        T vo=null;
        File file=new File(CacheBF.getCacheFileName(name));//获取缓存文件
        if (file.exists()){//判断文件是否存在
            FileReader fr=new FileReader(file);
            BufferedReader br=new BufferedReader(fr);
            Long now =System.currentTimeMillis();
            Long time=Long.parseLong(br.readLine());//判断文件缓存是否过期
            String object=StringEscapeUtils.unescapeJava(br.readLine());//返回程序,反转义字符
            br.close();
            fr.close();
            if (time>now){
                vo= Serialization.reverse(object.getBytes(),objectClass);
            }else{
                file.delete();//过期删除文件,删除前关闭输出流
            }
        }
        return vo;
    }

    /**
     * 传入一个缓存名称,查询是否存在此缓存
     * @param name 缓存名称
     * @return 存在并且不过期返回true,否则返回false
     * @throws Exception
     */
    public static boolean check(String name)throws Exception{

        boolean back=false;
        File file=new File(CacheBF.getCacheFileName(name));//获取缓存文件
        if (file.exists()){
            FileReader fr=new FileReader(file);
            BufferedReader br=new BufferedReader(fr);
            Long now=System.currentTimeMillis();
            Long time=Long.parseLong(br.readLine());//判断文件缓存是否过期
            br.close();
            fr.close();
            if (time>now){
                back=true;
            }else{
                file.delete();//过期删除文件
            }
        }
        return back;
    }

    /**
     * 删除穿进来的缓存名称的文件
     * @param name 需要删除的缓存的名称
     * @throws Exception
     */
    public static void delete(String name)throws Exception{

        File file=new File(CacheBF.getCacheFileName(name));
        if (file.exists()){
            file.delete();
        }

    }
    /**
     * 根据缓存的名称获取需要操作的文件对象
     * 1、判断父类目录cache是否存在,不存在则创建
     * 2、判断当前文件是否存在,存在删除后创建,不存在直接创建
     * 3、文件名MD5加密
     * @param name 需要得到的缓存的名称
     * @return
     * @throws Exception
     */
    private static File getCacheFile(String name)throws Exception{

        File file=new File(CacheBF.getCacheFileName(name));//获取操作对象
        //判断父目录是否存在
        if (!file.getParentFile().exists()){
            file.getParentFile().mkdir();
        }
        //判断当前文件是否存在
        if (!file.exists()){
            file.createNewFile();
        }else {
            file.delete();
            file.createNewFile();
        }
        return file;
    }

    /**
     * 传入缓存名获取缓存的文件名
     * @param name 缓存名
     * @return
     */
    private static String getCacheFileName(String name){
        name=MD5maker.make(name)+".txt";//获取文件名
        StringBuffer fileName=new StringBuffer(System.getProperty("user.dir"))
                .append(File.separator).append("src")
                .append(File.separator).append("main")
                .append(File.separator).append("webapp")
                .append(File.separator).append("WEB-INF")
                .append(File.separator).append("cache")
                .append(File.separator).append(name);//获取文件路径
        return fileName.toString();
    }
}




模块四:测试

import tool.CacheBF;
import tool.Serialization;

import java.io.File;

/**
 * Created by yy on 2017/10/11.
 * 文件缓存测试
 */
public class test {
    public static void main(String args[]) {
        Data data= new Data();
        data.setInfo("hello");
        data.setName("world");
        try {
            CacheBF.cache("123",data,100);

            System.out.println("缓存是否存在:"+CacheBF.check("123"));
            Data da=CacheBF.get("123",Data.class);
            System.out.println(da);

            CacheBF.delete("123");
            System.out.println("缓存是否存在:"+CacheBF.check("123"));
            Data dd=CacheBF.get("123",Data.class);
            System.out.println(dd);
        }catch (Exception e){
            e.printStackTrace();
        }


    }
}
//测试使用
class Data{
    private String name;
    private String info;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getInfo() {
        return info;
    }

    public void setInfo(String info) {
        this.info = info;
    }

    @Override
    public String toString() {
        return "Data{" +
                "name='" + name + '\'' +
                ", info='" + info + '\'' +
                '}';
    }
}


模块五:测试输出

缓存是否存在:true
Data{name='world', info='hello'}
缓存是否存在:false
null



模块六:文件内容(A8AE104615CB4E966DDB435F3E575A02.txt)

1508081000098
\n\u0005world\u0012\u0005hello