破尘summer

破尘summer 查看完整档案

合肥编辑  |  填写毕业院校  |  填写所在公司/组织填写个人主网站
编辑

这一路我披荆斩棘,只为了遇见你

个人动态

破尘summer 发布了文章 · 9月21日

Linux学习总结

前言介绍:

根目录下的所有文件(文件夹):

bin   etc   lib64  opt   run   sys  var
boot  home  media  proc  sbin  tmp
dev   lib   mnt    root  srv   usr

1.重启network网卡

service NetworkManager stop 
chkconfig NetworkManager off 永久关闭Manager网卡
service network restart 重启network网卡

2.获取ip

ip addr /ifconfig

3.cd指令(目录跳转指令)

说明:Linux是基于文件(目录)的,文件排列是一种树形结构。
目录间可以进行跳转。(利用cd指令)

cd / 跳转到根目录          cd ~ 跳转到用户目录(root目录)
cd . 跳转到当前目录        cd ..跳转到上级目录
cd -返回上一次操作的目录    cd 返回用户主目录(root目录)
cd /usr/ 进入到usr目录      pwd 查看当前所在的目录

4.ls指令

ls          展示当前目录下的文件(文件夹)的简要信息
ls -l       展示当前目录下的文件(文件夹)的详细信息
ll          相当于(ls -l  指令)
ls *.txt    展示当前目录下所有的txt文件
ls b*       展示当前目录下所有的以b开头的文件和文件夹

5.目录操作(操作前建议跳转到 cd /usr/local/src)

mkdir 创建目录      mkdir a 创建a目录
mkdir a b c d 同时创建四个目录(a、b、c、d)
mkdir -p a/b 创建a目录,并在a目录下创建b目录
mkdir -m 777 a 创建一个权限为777的c目录
rmdir 删除目录(如果目录里有文件,则不能用此命令,不让删除)
rmdir a b c d 同时删除四个目录(a、b、c、d)

6.vi/vim指令

vim a.txt 如果当前目录下有该文件,则打开该文件
          如果当前目录下没有该文件,则创建该文件,并打开
esc 键 切换模式
编辑模式:
        i 在光标前插入
        a 在光标后插入
        o 在下一行插入
        u 插销之前的修改
底行模式:shift+: (冒号)启动底行模式
        :wq       保存后退出
        :q!      不保存,退出
        :/world   在光标前查找world
        :?world  在光标之后查找world 

7.删除文件指令(rm)

rm a.txt     删除当前目录下的a.txt文件,有提示,输入y表示删除,输入n表示不删除
rm -f a.txt  不提示直接删除当前目录下的a.txt文件
rm -rf a     不提示,递归删除a目录以及a目录中的全部内容。(rmdir删除目录时,只能删除空的文件夹)
rm -rf *     删除当前目录下所有的内容
rm -rf /*    删除根目录下的所有内容,慎用,可能导致虚拟机无法使用了!

8.复制(cp)&移动指令(mv)

cp a.txt b.txt (操作文件)
         如果没有b.txt则会创建b.txt,然后将a.txt中的内容复制到b.txt中
         如果有b.txt则会提示是否覆盖原有内容。
cp -r a b  (操作文件夹)   
         如果没有b目录则会创建b目录,然后将a目录中的内容复制到b目录中
         如果有b目录则会将a目录中的内容复制到b目录中。
mv a b  
         若该目录下有a目录,有b目录,该指令运行结果为将a移动到b目录下
         若该目录下有a目录,没有b目录,该指令运行结果为将a重命名为b
mv a.txt b.txt 
         若该目录下有a.txt,有b.txt,该指令运行结果提示是否覆盖 y-将a.txt中的内容赋值到b.txt中,并删除a.txt,名字为b.txt  n-不做任何处理
         若该目录下有a.txt,没有b.txt,该指令运行结果为将a.txt重命名为b.txt

9.解压/压缩指令

tar -xvf 文件(用tab键提示)
    -x 解开tar文件
    -v 显示运行过程的信息
    -f 指定文件名
tar –cvf n.tar ./* 压缩当前目录下的所有文件和目录,文件名为n.tar
查看原文

赞 0 收藏 0 评论 0

破尘summer 发布了文章 · 9月8日

Mybatis出现Mapped Statements collection already contains value for

可能的原因分析:
1.mapper中存在id重复的值
2.mapper中的parameterType或resultType为空。
3. 在使用@Select等注解的情况下,方法名即为mapper的id,重载的方法会报这个错。
4. mapper复制  忘了改namespace指向的类,所以两个mapper指向同一个mapper,所以报了这个错。
查看原文

赞 0 收藏 0 评论 0

破尘summer 发布了文章 · 9月7日

Mybatis框架中的resultMap高级映射---特殊问题(版本mybatis-3.5.5)

多表关联查询时遇到的问题与结论:当使用多表关联查询时collection中的property属性(menuIds)不能为数组(Integer[]),只能写成集合List<Integer>


封装类
@Data
public class SysRoleMenu implements Serializable{

    private static final long serialVersionUID = 8377897338767610433L;
    private Integer id;
    private String name;
    private String note;
    private Integer[] menuIds; //应该改成List<Integer>
}
数据层接口方法Dao

基于id查询,返回一个SysRoleMenu类型的对象

SysRoleMenu selectRole(Integer id);
XML中的SQL语句
<resultMap type="com.cy.pj.sys.pojo.SysRoleMenu" id="findRoleMenu">
    <id property="id" column="id"/>
    <result property="name" column="name"/>
    <result property="note" column="note"/>
    <collection property="menuIds" ofType="integer">   
        <result column="menu_id"/>
    </collection>
</resultMap>
<select id="selectRoleMenu" resultMap="findRoleMenu">
    select u.id,u.name,u.note,r.menu_id
    from sys_roles u left join sys_role_menus r 
    on u.id=r.role_id
    where u.id=#{id}
</select>

嵌套查询时collection中的property属性(menuIds)可以是数组(Integer[]),也可以是集合List<Integer>

查看原文

赞 0 收藏 0 评论 0

破尘summer 发布了文章 · 8月19日

java中的final关键字的用法

final关键字---(最终的)意思

final修饰特点
final修饰的类该类不能被继承extends
final修饰的对象该对象的引用地址不能改变了
final修饰的方法该方法不能被重写
final修饰的变量该变量就是常量(特点就是常量名大写、斜体)
查看原文

赞 0 收藏 0 评论 0

破尘summer 发布了文章 · 8月19日

方法的重载与重写

重载Overload-----(一定在同一个类中)

易错点:不能以返回值类型作为区分重载函数的判断标准

区分重载函数的判断标准

   1.方法名相同
   2.参数列表不同(参数的个数、参数的类型、参数的顺序)

查看原文

赞 0 收藏 0 评论 0

破尘summer 关注了用户 · 8月18日

顾里 @guli_5f3b351d69428

关注 1

破尘summer 发布了文章 · 8月18日

Java-SSM-SpringBoot框架中的常用注解---学习中持续更新--望指正

注解知识回顾

第一:分类

(1)jdk自带的注解(5个):常用的就一个:@Override
(2)元注解(5个):常用的两个:@Target(指定注解使用的位置) @Retention(描述生命周期)
(3)自定义注解:(框架里大部分都是)

第二:元注解

    @Target 指定其他注解可以使用的位置(包上、类上、方法上、属性上)
    @Retention 指定其他注解的生命周期(源文件中、运行时、class文件中)
    @Documented 表示将该注解描述的注解内部的注释部分,也生成到相应的API中

第三:自定义注解

 自定义注解需要配合元注解使用(常用@Target&@Retention )

第四:@Target注解(指定其他注解可以使用的位置)

(1)其他注解使用在单个位置(如何指定?)
    @Target(ElementType.Type)
(2)其他注解使用在多个位置(如何指定?)底层维护的是一个数组
    @Target({ElementType.Type,ElementType.Field}) 
 (3)@Target注解的取值{值被维护在ElementType中}
        ElementType.Type
        ElementType.Field
        ElementType.Method

第五:@Retention注解(指定其他注解的生命周期)

    @Retention注解的取值{值被维护在RetentionPolicy工具类中}
        RetentionPolicy.SOURCE
        RetentionPolicy.CLASS
        RetentionPolicy.RUNTIME

第六:自定义注解阐述

(1)定义:        
          @inteface 注解名{}
(2)配合元注解,指定自定义注解使用的位置,以及自定义注解的生命周期

第七:给注解添加功能---属性(也有人称为方法)

定义(1):

    @inteface Annotation{
        String name();  //没有设置默认值,使用该注解时,该属性必须添加值。
    //String name() default "lisi";//给name赋默认值lisi,设置了默认值后,使用该注解不需要手动设置name的属性值。
    }

使用(1):

    @Annotation(name="zhangsan")
        public void sayhello(){
    
    }

定义(2):

    @inteface Test{
        int value();
        //int value() default 10;//给value赋默认值10
    }

使用(2):

    @Test(value=100)
        public void sayhello(){    
     }
由于value的特殊性,在使用注解时,可以省略“value=”,例如@Test(100)
如果想直接使用@Test不写值100,可以在定义注解时,设置value的默认值为100
@inteface Test{
   int value() default 100;//给value赋默认值100
}

问题分析:

@inteface Test{
    String name() default "lisi";//给name赋默认值lisi
    int value() default 100;//给value赋默认值100
}

(1)可以直接使用这个注解,直接写@Test
(2)保留name的默认值,改value的值("value="可省略)

            @Test(10)

(3)保留value的默认值,改name的值(“value=”可省略)

            @Test(name="张三")

(4)同时改两个值时("value="不能省略)

            @Test(name="张三",value=10)

第八:框架(framework)中常用的注解

(1)@SpringBootApplication

描述SpringBoot工程启动类的特定注解

(2)@SpringBootTest

描述SpringBoot工程测试类的特定注解
使用条件:如果需要在测试类中,引入spring容器机制,这是才是用该注解,否则没必要加。
引入spring容器机制:比如说,我们要在测试类中,通过spring容器来注入某个类的实例时,就需要使用该注解。

(3)@AutoWired

自动装配-由Spring框架为我们指定的属性类型注入值.我们常用接口类型的变量来接收spring框架注入的值(多态的思想,为了降低耦合,让程序更容易维护)
具体的注入(DI)过程:
                @AutoWired
                Student student;
                属性的类型 属性名

(1)第一:(Student为一个类)spring框架会基于属性的类型的首字母小写(student)作为key去bean池(单例)中(多例用的时候才创建)去寻找对应的value(这个value就是对象),在注入给这个属性名(注入的其实是一个地址而并非对象);
(2)第二:(Student为接口类型)若该实现类只有一个,则直接注入;
(3)第三:若该接口的实现类有多个,此时会根据属性名student,去查找对应的实现类创建的对象(这个对象存在map中的key为类名首字母小写),找到直接注入,找不到就直接抛异常:NoUniqueBeanDefinitionException
解决办法两种:(一般开发中都不推荐)

方法1:修改bean的名字(默认是类名首字母小写);@Component  (“指定的名字”),这个指定的名字必须与我们要注入的属性名一摸一样;
方法2:修改属性名(改成某个实现类的类名首字母小写);

常用方法就是指定用哪个实现类!使用( @Qualifier 注解)
@AutoWired(required=false)在项目启动的第一时间,可以不注入值(能注入则注入,不能注入则跳过)。
与@Resource注解装配过程区分,参见@Resource注解(位置:第47个注解)

(4)@Component

不明确这个类属于哪层时可以用该注解,将该类 的实 列交给spring容器创建

(5)@Controller

该注解用于描述控制层,将控制层的实例创建权限 交给spring容器
返回的是view

(6)@Service

该注解用于描述业务层,由spring来创建@Service 描述的类的实例

(7)@Lazy

该注解用于延迟对象的创建,在springboot项目中,对于 单例对象默认在项目启动时创建,这样会耗时耗资源-单例对象是要存 到bean池中的,通常配合[@Scope(singleton)]注解使用,对于 @Scope("prototype")描述的类的对象就是在需要时创建,按需加 载,故@Lazy注解对多利作用域对象不起作用

(8)@Scope

有两个取值@Scope("prototype")、 @Scope("singleton")

(9)@Test

(单元测试的注解)

满足以下条件
(1)访问修饰符不能为private
(2)返回值必须为void
(3)方法参数必须时无参
(4)测试方法允许抛出异常

扩展----测试的第二种方法(CommandLineRunner)
在启动类(@SpringBootApplicaton)中实现这个接口,重写 run()方法,在run()方法中写我们测试代码;当启动类启动时会自动执行run()方法

(10)@Bean

通常用于配置类中,与@Configuration配合使用
@Bean描述的方法的返回值(返回的是一个对象)交给spring去 管理。此时可以配合@Lazy注解使用

(11)@Mapper

添加在数据层的接口上,通知spring,该接口的实现类有mybatis负责实现,该实现类的对象有mybatis创建,但是交给spring管理

(12)@MapperScan("接口文件的包名")

添加在主启动类上,利用包扫描的方式,为指定包下的接口创建代理类以及代理对象并交给spring管理
优势:不用在每个映射的接口上使用@Mapper注解了
前提:要将所有的映射文件放在同一个包下。

(13)@Repository

添加在数据层接口的实现类上,将这个类交给Spring管理

(14)@Param

当mybatis版本相对较低时,在动态sql中想要使用方法中的参数变量来获取数据,就需要使用该注解对参数变量进行描述
也与jdk的版本有关(往下看~~~~)
原理:mybatis中规定,默认条件下可以进行单值传递 后端用任意的参数接收都可以。
有时候可能进行多值传递,需要将多值封装为map集合进行参数的传递(sql中通过key来取值)
旧版本时如果需要封装为单值,则必须添加@Param注解
新版本时可以自动添加@param,前提条件是多值传递
单值:数组 collection="array" (mapper层传的是数组,此时数组就是一个单值,此时xml中的collection="array")
单值:集合 collection="list" (mapper层传的是list集合,此时数组就是一个单值,此时xml中的collection="list")
多值:利用@param注解封装 collection="key"(mapper层传的是map,此时这个map就是一个单值,此时xml中的collection="key")

(15)@ReponseBody

1.描述的方法的返回值不是view(不是一个页面),
2.比如说可以是一个字符串String---直接返回该字符串
3.当方法的返回值是一个或多个pojo对象(也可以是map)时,springmvc去查看这个方法上是否有该注解,若有,就会pojo对象转成JSON格式的字符串(字符串数组)

(16)@RestController

@Controller+@ReponseBody
一般适用于ajax,不走视图解析器,并且返回json数据

(17)@PathVariable

当我们的方法参数要从url中获取参数时,就需要使用该注解--restful风格中常用!
如果url中的名字与方法参数名不一致,还可以指定
url="menu/menulist"

1.url中的名字与方法参数名一致,可以直接加上@PathVariable,不用指定别名
@RequestMapping("{do}/{dolist}")
public String domain(@PathVariable String dolist){
return dolist;
}

@RequestMapping("{do}/{dolist}")
public String domain(@PathVariable String do){
return do;
}

* * *

2.url中的名字与方法参数名不一致,还可以指定,表明方法参数中的变量时来自url中的哪一个
(一般不用这样的,直接写成一样就可以了(第一种))
@RequestMapping("{do}/{dolist}")
public String domain(@PathVariable("dolist")String name){
return name;
}
@RequestMapping("{do}/{dolist}")
public String domain(@PathVariable("do")String name){
return name;
}

(18)@RequestMapping

请求映射-提供映射路径-可以使用在类上、方法上

(19)@ControllerAdvice

该注解描述类为全局异常处理类
当Spring mvc(web)的控制层出现异常,首先会检查controller中是否有对应的异常处理方法,有则直接执行,若没有,就会检索是否有@ControllerAdvice描述的类,若有,则再去检索类中是否有对应的异常处理方法。。

(20)@ExceptionHandler

该注解描述的方法,为一个异常处理的方法,注解中需要指定能处理的异常类型(RuntimException.class-字节码对象),表示该方法能够处理该类型的异常以及子类异常。
在异常处理方法中,通常需要一个异常参数,用来接收异常对象。
方法的返回值通常是一个对象(json格式-满足响应式布局),通常与@ReponseBody注解结合使用

(21)@Aspect

该注解描述的类为一个切面(aop)

(22)@Around(环绕通知)优先级最高

执行目标方法之前Spring会检测是否有切面(@Aspect描述的类),然后检测是否有通知方法(@Around注解描述的方法),有该方法则执行该方法,在方法中调用通过ProceedingJoinPoint对象jp调用proceed()方法(该方法会执行目标方法)

(23)@Before

当@Around注解描述的方法中,要执行目标方法之前(proceed方法执行之前),执行该注解描述的方法

(24)@After

目标方法执行之后,执行此注解描述的方法(如果目标方法有异常该方法不执行)

(25)@AfterReturnning

@After注解描述的方法执行之后,(这个方法执行了,说明目标方法没有抛出异常)执行该注解描述的方法。

(26)@AfterThrowing

@Around注解描述的方法中的目标方法执行时抛出异常了,就直接执行该注解描述的方法。

(27)@Slf4j(lombok中)

应用在类上,此类中就可以使用log对象
就相当于在这个类中添加了一行如下代码

Logger log=LoggerFactory.getLogger(类名.class);

(28)@PointCut

该注解用来定义切入点方法

@Pointcut("bean(sysUserServiceImpl)")

 **public** **void** logPointCut() {}

切入点表示有四种:
bean execution within @annocation

(29)@Order(1)

该注解用于指定切面的优先级,数字越小,优先级越高,默认值是整数的最大值即默认值的优先级是最低的

(30)@Transactional

该注解可以描述类和方法,
当描述类时,表示类中的所有方法在执行之前开启事务,方法执行之后结束事务。
当描述方法时,表示该方法在执行之前开启事务,方法执行之后结束事务。
优先级问题:当类上和方法上都有该注解时,方法上的优先级高于类上的。
该注解中的属性也很重要:
timeout:设置超时时间
isolation:设置隔离级别
rollbackfor:当遇到什么异常以及子类异常时要执行回滚操作。
read-only:指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置read-only为true。对添加,修改,删除业务read-only的值应该为false。
propagation:
Propagation.REQUIRED(如果没有事务创建新事务, 如果当前有事务参与当前事务, Spring 默认的事务传播行为是PROPAGATION_REQUIRED,它适合于绝大多数的情况。)
Propagation.REQUIRES_NEW
如果有当前事务, 挂起当前事务并且开启新事务

(31)@EnableAsync

注解应用到启动类上,表示开启异步,spring容器启动时会创建线程池
spring中线程池的配置:

spring:
    task:
        execution:
          pool:
            queue-capacity: 128
            core-size: 5
            max-size: 128
            keep-alive: 60000
            thread-name-prefix: db-service-task-

(32)@Async

在需要异步执行的业务方法上,使用@Async方法进行异步声明。

说明:AsyncResult对象可以对异步方法的执行结果进行封装,假如外界需要异步方法结果时,可以通过Future对象的get方法获取结果。
为了简便某些情况下,我们可以将方法的返回值写成void,这样就不要这么复杂的封装(前提时方法的返回值我们不使用)。

@Transactional(propagation = Propagation.REQUIRES_NEW) 
@Async
@Override
public Future<Integer> saveObject(SysLog entity) {
System.out.println("SysLogServiceImpl.save:"+
Thread._currentThread_().getName());
int rows=sysLogDao.insertObject(entity);
//try{Thread._sleep_(5000);}catch(Exception e) {}
 return new AsyncResult<Integer>(rows);

}

(33)@EnableCaching

在项目(SpringBoot项目)的启动类上添加@EnableCaching注解,以启动缓存配置。

在需要进行缓存的业务方法上通过@Cacheable注解对方法进行相关描述.表示方法的

返回值要存储到Cache中,假如在更新操作时需要将cache中的数据移除,可以在更新方法上使用@CacheEvict注解对方法进行描述。

@Cacheable(value = "menuCache")
@Transactional(readOnly = **true**)
**public** List<Map<String,Object>> findObjects() {
....
}
value属性的值表示要使用的缓存对象,名字自己指定,其中底层为一个map对象,当向cache中添加数据时,key默认为方法实际参数的组合。

(34)@Value

说明:该注解单独使用(不配合@PropertySource注解)时,此时配置文件已经被加载到spring容器中,表示从spring容器(加载的配置文件)中取值。
   该注解配合@PropertySource注解使用时,可以从指定的配置文件中取值,value属性指定配置文件的所在路径@PropertySource(value="classpath:/properties/image.properties",encoding="utf-8")
该注解用于描述属性的值,可以用来取出配置文件中的value值(根据key来取值)(properties、yml)、
properties配置文件的优先级高于yml配置文件的优先级
当这两个文件中都用同一个属性时,优先读取properties配置文件中的,然后读取yml配置文件中的,所有最后得到的是yml>中的(key相同值被覆盖)。
@value(“${server.port}”)
private String port;
说明:
因为配置文件中的value值都是字符串类型,所以这里需要用String类型接收。
该注解配合spel表达式取出配置文件中对应key的值。

(35)@PropertySource(value="classpath:/properties/image.properties",encoding="utf-8")

当配置文件已经被加载到spring容器中了,就不要使用该注解指明加载的配置文件了,容器中有直接用@Value取值即可。
说明:该注解用于类上,作用:读取指定位置的配置文件信息,加载该文件到spring容器中
value属性用来指定配置文件的路径
classpath:/ 指定类目录下的路径(resource目录下)
encoding:用来指定配置文件的编码类型
@PropertySource(value="classpath:/properties/image.properties",encoding="utf-8")
public class ReadProperties{

}

(36)@Data(lombok)

该注解用在pojo类上,作用是生成对应的get/set/toString...方法
toString()方法只会重写自己的属性,不会添加父类的属性。

(37)@Setter(lombok)

该注解用在pojo类上,生成set方法

(38)@Getter(lombok)

该注解用在pojo类上,生成get方法

(39)@NoArgsConstructor(lombok)

该注解用在pojo类上,生成无参的构造方法

(40)@NoArgsConstructor(lombok)

该注解用在pojo类上,生成全参的构造方法

(41)@Accessors(chain=true)(lombok提供)

提供链式加载(对于set方法)
User user=new User();
user.setName("张三").setAge(26).setScore(98);

(42)@TableName(“表名”)(mybatisplus提供)

该注解用在pojo类上,用来指定该类的对象,与数据库中的哪张表映射
当pojo类名忽略大小写时,与数据库中要映射的表名一致时,可以不指定表名。如直接写@TableName注解即可。但不能省略这个注解。

(43)@TableId(type=IdType.Auto)(mybatisplus提供)

该注解用于pojo类中的属性上,用来指定表中的主键要映射到pojo类中的哪个属性。

(44)@TableField(value="字段名")(mybatisplus提供)

@TableField(fill = FieldFill.INSERT)指定哪个属性,在新增时有效,一般是创建时间
@TableField(fill = FieldFill.INSERT_UPDATE)指定哪个属性,在新增和修改时有效,一般是创建时间/修改时间
该注解用于pojo类中的属性上,用来指定表中的字段(除主键外的其他字段)映射到pojo类中的哪个属性。
当字段名与属性名一致时,或者,驼峰命名后字段名与属性名一致时,可以省略这个注解@TableField,即pojo中的属性上不写该注解。

(45)@GetMapping("/page/{moduleName}")

该注解用来处理查询请求,其他请求无效。
该注解相当于
@RequestMapping(value="/page/{moduleName}",method=RequestM>ethod.GET)
restful风格:可以根据请求的方式,来执行特定的业务功能。
TYPE=GET     处理查询业务
TYPE=POST    处理新增业务
TYPE=PUT     处理修改业务
TYPE=DELETE  处理删除业务
result风格总结:
1.可以获取url中的参数配合@PathVariable注解
2.根据请求方式,执行特定的业务功能

(46)@RestControllerAdvice

@RestControllerAdvide=@ResponseBody+@ControllerAdvide
说明:
该注解应用于类上,表示为通知方法。类中的方法的返回值都不是view,返回的是JSON格式的数据。
该注解描述的类为全局异常处理类,处理Controller层出现的异常。

(47)@Resource

@Resource的作用相当于@Autowired,只不过@Autowired按byType自动注入,而@Resource默认按 byName自动注入罢了。@Resource有两个属性是比较重要的,分是name和type,Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不指定name也不指定type属性,这时将通过反射机制使用byName自动注入策略。

##### @Resource装配顺序

  1. 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常
  2. 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常
  3. 如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常
  4. 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配;
查看原文

赞 12 收藏 8 评论 0

破尘summer 关注了用户 · 8月14日

Jason @jason_5f0dbb9eaae42

以终为始,闭环迭代,持续提高。

关注 1782

破尘summer 赞了文章 · 8月14日

本地缓存(Cache)系统简易设计

本地Cache系统简易设计

为什么使用缓存?

  • 降低数据库的访问压力。
  • 提高查询效率。
  • 改善用户体验。

你都了解哪些缓存?

  • 数据库内置缓存(DBA修改)。
  • 数据层缓存(由持久层框架决定,例如mybatis)
  • 业务层缓存(由业务层框架以及第三缓存产品决定:本地缓存+分布式缓存)
  • 浏览器缓存(Cache-Control)

设计缓存都应该考虑什么问题?

  • 存储结构:使用什么结构存储数据?(数组,链表,散列存储-哈希存储)
  • 淘汰算法:有限容量(LRU,FIFO,.....),不限容量(GC)
  • 并发安全:保证线程安全。
  • 任务调度:每隔多长时间清理一下缓存。
  • 日志记录:是否命中?(命中率)

缓存系统设计基础

缓存标准定义

package com.cy.java.cache;  
/\*\* Cache接口设计\*/  
public interface Cache {  
 public void putObject(Object key,Object value);  
 public Object getObject(Object key);  
 public Object removeObject(Object key);  
 public void clear();  
 public int size();  
}

简易Cache实现

场景应用:
  • 存储数据量比较小(因为没有考虑淘汰机制)
  • 没有线程共享(一个线程的内部缓存)
  • 缓存对象生命周期比较短
package com.cy.java.cache;  
import java.util.HashMap;  
import java.util.Map;  
/**负责真正存储数据的一个对象,将数据存储到一个map中*/  
public class PerpetualCache implements Cache {  
/** 特点:线程不安全,key不允许重复,不能保证key的顺序  */  
private Map<Object,Object> cache=new HashMap<>();  
@Override  
public void putObject(Object key, Object value) {  
cache.put(key, value);  
}  
@Override  
public Object getObject(Object key) {  
return cache.get(key);  
}  
@Override  
public Object removeObject(Object key) {  
return cache.remove(key);  
}  
@Override  
public void clear() {  
 cache.clear();  
}  
@Override  
public int size() {  
return cache.size();  
}  
@Override  
public String toString() {  
return cache.toString();  
}  
public static void main(String\[\] args) {  
 Cache cache=new PerpetualCache();  
 cache.putObject("A", 100);  
 cache.putObject("B", 200);  
 cache.putObject("C", 300);  
 System.out.println(cache);  
 cache.removeObject("D");  
 cache.clear();  
 System.out.println(cache.size());  
}  
}

构建线程安全Cache对象

场景应用:并发环境
package com.cy.java.cache;  
/**线程安全的cache对象*/  
public class SynchronizedCache implements Cache{  
private Cache cache;  
public SynchronizedCache(Cache cache) {  
this.cache=cache;  
}  
@Override  
public synchronized void putObject(Object key, Object value) {  
cache.putObject(key, value);  
}  
@Override  
public synchronized Object getObject(Object key) {  
// TODO Auto-generated method stub  
return cache.getObject(key);  
}  
@Override  
public synchronized Object removeObject(Object key) {  
// TODO Auto-generated method stub  
return cache.removeObject(key);  
}  
@Override  
public synchronized void clear() {  
cache.clear();  
}  
@Override  
public synchronized int size() {  
return cache.size();  
}  
@Override  
public String toString() {  
return cache.toString();  
}  
public static void main(String\[\] args) {  
SynchronizedCache cache=

 new SynchronizedCache(new PerpetualCache());  
cache.putObject("A", 100);  
cache.putObject("B", 200);  
cache.putObject("C", 300);  
System.out.println(cache);  
}  
  
}

思考:对于SynchronizedCache 有什么优势,劣势?

支持日志记录的Cache实现

package com.cy.java.cache;  
/** 用于记录命中率的日志cache*/  
public class LoggingCache implements Cache {  
private Cache cache;  
/**记录请求次数*/  
private int requests;  
/**记录命中次数*/  
private int hits;  
public LoggingCache(Cache cache) {  
this.cache=cache;  
}  
@Override  
public void putObject(Object key, Object value) {  
cache.putObject(key, value);  
}  
@Override  
public Object getObject(Object key) {  
requests++;  
Object obj=cache.getObject(key);  
if(obj!=null)hits++;  
System.out.println("Cache hit Ratio : "+hits\*1.0/requests);  
return obj;  
}  
@Override  
public Object removeObject(Object key) {  
return cache.removeObject(key);  
}  
@Override  
public void clear() {  
cache.clear();  
}  
@Override  
public int size() {  
return cache.size();  
}  
@Override  
public String toString() {  
// TODO Auto-generated method stub  
return cache.toString();  
}  
public static void main(String\[\] args) {  
SynchronizedCache cache=  
 new SynchronizedCache(  
new LoggingCache(  
new PerpetualCache()));  
cache.putObject("A", 100);  
cache.putObject("B", 200);  
cache.putObject("C", 300);  
System.out.println(cache);  
cache.getObject("D");  
cache.getObject("A");  
}  
  
}

思考:你觉得LoggingCache记录日志的方式有什么不好的地方?(信息的完整性,同步问题)

LruCache实现

应用场景:基于LRU算法的的基本实现
package com.cy.java.cache;  
import java.util.LinkedHashMap;  
import java.util.Map;  
  
/** 缓存淘汰策略:LRU(最近最少使用算法)*/  
public class LruCache implements Cache {  
 private Cache cache;  
 /**通过此属性记录要移除的数据对象*/  
 private Object eldestKey;  
 /**通过此map记录key的访问顺序*/  
 private Map<Object,Object> keyMap;  
 @SuppressWarnings("serial")  
 public LruCache(Cache cache,int maxCap) {  
 this.cache=cache;  
//LinkedHashMap可以记录key的添加顺序或者访问顺序  
 this.keyMap=

 new LinkedHashMap<Object,Object>(maxCap, 0.75f, true)

 {//accessOrder  
//此方法每次执行keyMap的put操作时调用  
@Override  
protected boolean removeEldestEntry

 (java.util.Map.Entry<Object, Object> eldest) {  
boolean isFull=size()>maxCap;  
if(isFull)eldestKey=eldest.getKey();  
return isFull;  
}  
 };  
}  
@Override  
public void putObject(Object key, Object value) {  
//存储数据对象  
cache.putObject(key, value);  
//记录key的访问顺序,假如已经满了,就要从cache中移除数据  
keyMap.put(key, key);//此时会执行keyMap对象的removeEldestEntry  
if(eldestKey!=null) {  
cache.removeObject(eldestKey);  
eldestKey=null;  
}  
}  
@Override  
public Object getObject(Object key) {  
keyMap.get(key);//记录key的访问顺序  
return cache.getObject(key);  
}  
  
@Override  
public Object removeObject(Object key) {  
return cache.removeObject(key);  
}  
  
@Override  
public void clear() {  
cache.clear();  
keyMap.clear();  
}  
@Override  
public int size() {  
return cache.size();  
}  
@Override  
public String toString() {  
return cache.toString();  
}  
  
public static void main(String\[\] args) {  
SynchronizedCache cache=  
 new SynchronizedCache(  
new LoggingCache(  
new LruCache(new PerpetualCache(),3)));  
cache.putObject("A", 100);  
cache.putObject("B", 200);  
cache.putObject("C", 300);  
cache.getObject("A");  
cache.getObject("C");  
cache.putObject("D", 400);  
cache.putObject("E", 500);  
System.out.println(cache);  
  
}  
}

设置Cache淘汰算法:FIFO算法

package com.cy.java.cache;  
import java.util.Deque;  
import java.util.LinkedList;  
/**  
* FifoCache :基于FIFO算法(对象满了要按先进先出算法移除对象)实现cache对象  
*/  
public class FifoCache implements Cache{  
/**借助此对象存储数据*/  
private Cache cache;  
/**借助此队列记录key的顺序*/  
private Deque<Object> keyOrders;  
/**通过此变量记录cache可以存储的对象个数*/  
private int maxCap;  
public FifoCache(Cache cache,int maxCap) {  
this.cache=cache;  
keyOrders=new LinkedList<>();  
this.maxCap=maxCap;  
}  
@Override  
public void putObject(Object key, Object value) {  
//1.记录key的顺序(起始就是存储key,添加在队列最后位置)  
keyOrders.addLast(key);  
//2.检测cache中数据是否已满,满了则移除。  
if(keyOrders.size()>maxCap) {  
Object eldestKey=keyOrders.removeFirst();  
cache.removeObject(eldestKey);  
}  
//3.放新的对象  
cache.putObject(key, value);  
}  
@Override  
public Object getObject(Object key) {  
return cache.getObject(key);  
}  
  
@Override  
public Object removeObject(Object key) {  
Object obj=cache.removeObject(key);  
keyOrders.remove(key);  
return obj;  
}  
@Override  
public void clear() {  
cache.clear();  
keyOrders.clear();  
}  
@Override  
public int size() {  
return cache.size();  
}  
@Override  
public String toString() {  
// TODO Auto-generated method stub  
return cache.toString();  
}  
public static void main(String\[\] args) {  
Cache cache=

 new SynchronizedCache(  
new LoggingCache(  
 new FifoCache(  
  new PerpetualCache(),3)));  
cache.putObject("A",100);  
cache.putObject("B",200);  
cache.putObject("C",300);  
cache.getObject("A");  
cache.putObject("D",400);  
cache.putObject("E",500);  
System.out.println(cache);  
}   
}

序列化Cache的实现

场景:存储到cache的是对象的字节
package com.cy.java.cache;  
import java.io.ByteArrayInputStream;  
import java.io.ByteArrayOutputStream;  
import java.io.ObjectInputStream;  
import java.io.ObjectOutputStream;  
  
public class SerializedCache implements Cache {  
  
private Cache cache;  
public SerializedCache(Cache cache) {  
this.cache=cache;  
}  
/**序列化*/  
private byte\[\] serialize(Object value) {  
//1.构建流对象  
ByteArrayOutputStream bos=null;  
ObjectOutputStream oos=null;  
try {  
//1.2构建字节数组输出流,此流对象内置可扩容的数组。  
bos=new ByteArrayOutputStream();  
//1.3构建对象输出流  
oos=new ObjectOutputStream(bos);  
//2.对象序列化  
oos.writeObject(value);

 //此时对象会以字节的方式写入到字节数组输出流  
oos.flush();  
return bos.toByteArray();  
}catch (Exception e) {  
 throw new RuntimeException(e);  
}finally {  
//3.关闭流对象  
if(bos!=null)

 try{bos.close();bos=null;}catch(Exception e) {}  
if(oos!=null)

 try{oos.close();oos=null;}catch (Exception e2) {}  
}  
}  
/**反序列化*/  
public Object deserialize(byte\[\] value) {  
//1.创建流对象  
ByteArrayInputStream bis=null;  
ObjectInputStream ois=null;  
try {  
//1.1构建字节数组输入流,此对象可以直接读取数组中的字节信息  
bis=new ByteArrayInputStream(value);  
//1.2构建对象输入流(对象反序列化)  
ois=new ObjectInputStream(bis);  
//2.反序列化对象  
Object obj=ois.readObject();  
return obj;  
}catch(Exception e) {  
throw new RuntimeException(e);  
}finally {  
//3.关闭流对象  
if(bis!=null)

 try{bis.close();bis=null;}catch(Exception e) {}  
if(ois!=null)

 try{ois.close();ois=null;}catch (Exception e2) {}  
}  
}  
@Override  
public void putObject(Object key, Object value) {  
cache.putObject(key, serialize(value));  
}  
@Override  
public Object getObject(Object key) {  
return deserialize((byte\[\])cache.getObject(key));  
}  
@Override  
public Object removeObject(Object key) {  
return cache.removeObject(key);  
}  
@Override  
public void clear() {  
cache.clear();  
}  
@Override  
public int size() {  
return cache.size();  
}  
public static void main(String\[\] args) {  
Cache cache=new SerializedCache(new PerpetualCache());  
cache.putObject("A", 200);  
cache.putObject("B", 300);  
Object v1=cache.getObject("A");  
Object v2=cache.getObject("A");  
System.out.println(v1==v2);  
System.out.println(v1);  
System.out.println(v2);  
}  
}

软件引用Cache实现

应用场景:内存不足时淘汰缓存中数据
package com.cy.java.cache;  
  
import java.lang.ref.ReferenceQueue;  
import java.lang.ref.SoftReference;  
/**软引用*/  
public class SoftCache implements Cache {  
private Cache cache;  
private ReferenceQueue<Object> garbageOfRequenceQueue=

 new ReferenceQueue<>();  
public SoftCache(Cache cache) {  
this.cache=cache;  
}  
@Override  
public void putObject(Object key, Object value) {  
//1.移除一些垃圾对象(Soft引用引用的已经被回收的对象)  
removeGarbageObjects();  
//2.将对象存储到cache(key不变,Value为为soft引用对象)  
cache.putObject(key, 

 new SoftEntry(key, value, garbageOfRequenceQueue));  
}  
  
@Override  
public Object getObject(Object key) {  
//1.基于key获取软引用对象并判断  
SoftEntry softEntry=(SoftEntry)cache.getObject(key);  
if(softEntry==null)return null;  
//2.基于软引用对象获取它引用的对象并判断  
Object target = softEntry.get();  
if(target==null)cache.removeObject(key);  
return target;  
}  
  
@Override  
public Object removeObject(Object key) {  
//1.移除一些垃圾对象(Soft引用引用的已经被回收的对象)  
removeGarbageObjects();  
//2.从cache中移除对象  
Object removedObj=cache.removeObject(key);  
return removedObj;  
}  
  
@Override  
public void clear() {  
//1.移除一些垃圾对象(Soft引用引用的已经被回收的对象)  
removeGarbageObjects();  
//2.清空cache  
cache.clear();  
}  
@Override  
public int size() {  
removeGarbageObjects();  
return cache.size();  
}  
private void removeGarbageObjects() {  
SoftEntry softEntry=null;  
//1.从引用队列中获取已经被GC的一些对象的引用  
 while((softEntry=

 (SoftEntry)garbageOfRequenceQueue.poll())!=null){  
//softEntry不为null表示softEntry引用的对象已经被移除  
//2.从cache中将对象引用移除。  
cache.removeObject(softEntry.key);  
}  
}  
/\*\*定义软引用类型\*/  
private static class SoftEntry extends SoftReference<Object\>{  
private final Object key;  
public SoftEntry(Object key,

 Object referent, ReferenceQueue<? super Object> rQueue) {  
super(referent, rQueue);  
this.key=key;  
}  
}  
@Override  
public String toString() {  
// TODO Auto-generated method stub  
return cache.toString();  
}  
  
public static void main(String\[\] args) {  
Cache cache=new SoftCache(new PerpetualCache());  
cache.putObject("A", new byte\[1024\*1024\]);  
cache.putObject("B", new byte\[1024\*1024\]);  
cache.putObject("C", new byte\[1024\*1024\]);  
cache.putObject("D", new byte\[1024\*1024\]);  
cache.putObject("E", new byte\[1024\*1024\]);  
System.out.println(cache.size());  
System.out.println(cache);  
}  
  
}

弱Cache对象实现

应用场景:GC触发清除缓存对象
package com.cy.java.cache;  
  
import java.lang.ref.ReferenceQueue;  
import java.lang.ref.WeakReference;  
/\*\*弱引用\*/  
public class WeakCache implements Cache {  
private Cache cache;  
private ReferenceQueue<Object> garbageOfRequenceQueue=

 new ReferenceQueue<>();  
public WeakCache(Cache cache) {  
this.cache=cache;  
}  
  
@Override  
public void putObject(Object key, Object value) {  
//1.移除一些垃圾对象(Soft引用引用的已经被回收的对象)  
removeGarbageObjects();  
//2.将对象存储到cache(key不变,Value为为soft引用对象)  
cache.putObject(key, 

 new WeakEntry(key, value, garbageOfRequenceQueue));  
}  
  
@Override  
public Object getObject(Object key) {  
//1.基于key获取软引用对象并判断  
WeakEntry softEntry=(WeakEntry)cache.getObject(key);  
if(softEntry==null)return null;  
//2.基于软引用对象获取它引用的对象并判断  
Object target = softEntry.get();  
if(target==null)cache.removeObject(key);  
return target;  
}  
  
@Override  
public Object removeObject(Object key) {  
//1.移除一些垃圾对象(Soft引用引用的已经被回收的对象)  
removeGarbageObjects();  
//2.从cache中移除对象  
Object removedObj=cache.removeObject(key);  
return removedObj;  
}  
  
@Override  
public void clear() {  
//1.移除一些垃圾对象(Soft引用引用的已经被回收的对象)  
removeGarbageObjects();  
//2.清空cache  
cache.clear();  
}  
  
@Override  
public int size() {  
removeGarbageObjects();  
return cache.size();  
}  
private void removeGarbageObjects() {  
WeakEntry softEntry=null;  
//1.从引用队列中获取已经被GC的一些对象的引用  
while((softEntry=

 (WeakEntry)garbageOfRequenceQueue.poll())!=null) {  
//softEntry不为null表示softEntry引用的对象已经被移除  
//2.从cache中将对象引用移除。  
cache.removeObject(softEntry.key);  
}  
}  
/**定义软引用类型*/  
private static class WeakEntry extends WeakReference<Object\>{  
private final Object key;  
public WeakEntry(Object key,

 Object referent, ReferenceQueue<? super Object> rQueue) {  
super(referent, rQueue);  
this.key=key;  
}  
}  
@Override  
public String toString() {  
return cache.toString();  
}  
public static void main(String\[\] args) {  
Cache cache=new WeakCache(new PerpetualCache());  
cache.putObject("A", new byte\[1024\*1024\]);  
cache.putObject("B", new byte\[1024\*1024\]);  
cache.putObject("C", new byte\[1024\*1024\]);  
cache.putObject("D", new byte\[1024\*1024\]);  
cache.putObject("E", new byte\[1024\*1024\]);  
cache.putObject("F", new byte\[1024\*1024\]);  
cache.putObject("G", new byte\[1024\*1024\]);  
System.out.println(cache.size());  
System.out.println(cache);  
}  
  
}

缓存系统设计进阶

缓存应用需求升级

  • 缓存系统既要保证线程安全又要保证性能。
  • 缓存日志的记录要写到文件,而且是异步写
  • 向缓存中写数据时要提高序列化性能。

缓存对象读写锁应用

package com.cy.java.cache;  
  
import java.util.concurrent.locks.ReentrantReadWriteLock;  
  
/**  
* 构建线程安全对象,基于ReentrantReadWriteLock对象实现读写锁应用。  
* @author qilei  
*/  
public class ReentrantLockCache implements Cache {  
  
private Cache cache;  
/**  
* 此对象提供了读锁,写锁应用方式.  
* 1)写锁:排他锁  
* 2)读锁:共享锁  
* 说明:读写不能同时执行。  
*/  
private final ReentrantReadWriteLock readWriteLock =  
new ReentrantReadWriteLock();  
public ReentrantLockCache(Cache cache) {  
this.cache=cache;  
// TODO Auto-generated constructor stub  
}  
@Override  
public void putObject(Object key, Object value) {  
readWriteLock.writeLock().lock();  
try {  
cache.putObject(key, value);  
}finally {  
readWriteLock.writeLock().unlock();  
}  
}  
  
@Override  
public Object getObject(Object key) {  
readWriteLock.readLock().lock();  
try {  
 Object object=cache.getObject(key);  
 return object;  
}finally{  
readWriteLock.readLock().unlock();  
}  
}  
  
@Override  
public Object removeObject(Object key) {  
readWriteLock.writeLock().lock();  
try {  
 Object object=cache.removeObject(key);  
 return object;  
}finally{  
readWriteLock.writeLock().unlock();  
}  
}  
  
@Override  
public void clear() {  
readWriteLock.writeLock().lock();  
try {  
 cache.clear();  
}finally{  
readWriteLock.writeLock().unlock();  
}  
}  
  
@Override  
public int size() {  
readWriteLock.readLock().lock();  
try {  
 int size=cache.size();  
 return size;  
}finally{  
readWriteLock.readLock().unlock();  
}  
}  
}

异步日志Cache实现

第一步:添加依赖
` <dependency>  
 <groupId>ch.qos.logback</groupId>  
 <artifactId>logback-classic</artifactId>  
 <version>1.2.3</version>  
 </dependency> `
第二步:添加配置文件logback.xml (参考项目代码)
 
<?xml version="1.0" encoding="UTF-8"?>  
<configuration>  
  
<logger name="com.cy" level="TRACE" />  
<appender name="FILE"  
class\="ch.qos.logback.core.rolling.RollingFileAppender"\>  
<rollingPolicy  
class\="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"\>  
<!--文件路径,定义了日志的切分方式----把每一天的日志归档到一个文件中,以防止日志填满整个磁盘空间 -->  
<fileNamePattern>logs/context-log.%d{yyyy-MM-dd}.log  
</fileNamePattern>  
<!--只保留最近30天的日志 -->  
<maxHistory>30</maxHistory>  
</rollingPolicy>  
<encoder charset="UTF-8"\>  
<pattern>\[%-5level\] %date --%thread-- \[%logger\] %msg %n</pattern>  
</encoder>  
</appender>  
  
<appender name="ASYNC\_FILE"  
class\="ch.qos.logback.classic.AsyncAppender"\>  
<discardingThreshold>0</discardingThreshold>  
<queueSize>256</queueSize>  
<appender-ref ref="FILE" />  
</appender>  
  
<root level="debug"\>  
<appender-ref ref="ASYNC\_FILE" />  
</root>  
  
</configuration>
第三步:构建AsyncLoggingCache
package com.cy.java.cache;  
  
import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;  
  
/**  
* 通过此对象异步记录查询操作的命中率  
* 1)选择日志库  
* 2)执行异步写操作。  
*/  
public class AsyncLoggingCache implements Cache {  
 //日志门面应用  
 private static Logger log=LoggerFactory.getLogger(LoggingCache.class);  
private Cache cache;  
/**表示请求次数*/  
private int requests;  
/**命中次数(命中表示从缓存中取到数据了)*/  
private int hits;  
public AsyncLoggingCache(Cache cache) {  
this.cache=cache;  
}  
@Override  
public void putObject(Object key, Object value) {  
cache.putObject(key, value);  
}  
@Override  
public Object getObject(Object key) {  
requests++;  
 Object obj=cache.getObject(key);  
 if(obj!=null)hits++;  
 //记录日志耗时  
 log.info("Cache hit Ratio:{}",hits\*1.0/requests);  
return obj;  
}  
@Override  
  
public Object removeObject(Object key) {  
return cache.removeObject(key);  
}  
@Override  
public void clear() {  
cache.clear();  
}  
@Override  
public int size() {  
return cache.size();  
}  
public static void main(String\[\] args) {  
Cache cache= 

 new AsyncLoggingCache(new PerpetualCache());  
cache.putObject("A", 100);  
cache.putObject("B", 200);  
cache.putObject("C", 300);  
cache.putObject("D", 400);  
//System.out.println(cache);  
cache.getObject("E");  
cache.getObject("A");  
cache.getObject("B");  
}  
  
}

Kryo构建序列化Cache

第一步:添加依赖
<dependency>  
 <groupId>com.esotericsoftware</groupId>  
 <artifactId>kryo</artifactId>  
 <version>5.0.0-RC5</version>  
</dependency>
第二步:构建项目工具类
public class KryoUtils {  
  
/**  
* 多线程并发执行时,可能会出现线程不安全,具体原因是什么?  
* 1)多个线程的并发  
* 2)多个线程有数据共享  
* 3)多个线程在共享数据集上的操作不是原子操作  
*  
* 分析:当出现了线程不安全,如何进行修改来保证线程安全  
* 1)将多线程改为单线程。  
* 2)取消共享 (例如在当前应用中我们一个线程一个Kryo对象)  
* 3)加锁+CAS  
*  
* ThreadLocal提供了这样的一种机制:  
* 1)可以将对象绑定到当前线程(其实是将对象存储到当前线程的map中)  
* 2)可以从当前线程获取绑定的对象(从当前线程的map中获取对象)  
*/  
static private final ThreadLocal<Kryo> kryos = new ThreadLocal<Kryo>() {  
  protected Kryo initialValue() {  
  Kryo kryo = new Kryo();  
  // Configure the Kryo instance.  
  kryo.setRegistrationRequired(false);  
  //....  
  return kryo;  
  };  
 };  
 public static Object deserialize(byte\[\] array){  
Kryo kryo=kryos.get();  
 Input input = new Input(new ByteArrayInputStream(array));  
 Object obj=kryo.readClassAndObject(input);  
return obj;  
 }  
 public static byte\[\] serialize(Object object){  
 //从当前线程获取kryo对象,当前线程没有会调用ThreadLocal的initialValue方法创建对象并绑定线程  
 Kryo kryo=kryos.get();  
 ByteArrayOutputStream bos=new ByteArrayOutputStream();  
 Output output = new Output(bos);  
 kryo.writeClassAndObject(output, object);  
  output.close();  
 return bos.toByteArray();  
 }  
 }

> 构建高性能序列化Cache

public class KryoSerializedCache implements Cache {  
  
private Cache cache;  
public KryoSerializedCache(Cache cache) {  
this.cache=cache;  
}  
  
@Override  
public void putObject(Object key, Object value) {  
//1.将对象序列化  
byte\[\] array=KryoUtils.serialize(value);  
//2.将序列化后的字节数组引用存储到cache  
cache.putObject(key,array);  
}  
  
@Override  
public Object getObject(Object key) {  
//1.基于key获取缓存中的字节数组引用  
byte\[\] array=(byte\[\])cache.getObject(key);  
//2.将字节数组反序列化为对象  
return KryoUtils.deserialize(array);  
}  
  
@Override  
public Object removeObject(Object key) {  
return KryoUtils.deserialize((byte\[\])cache.removeObject(key));  
}  
  
@Override  
public void clear() {  
cache.clear();  
}  
@Override  
public int size() {  
return cache.size();  
}  
public static void main(String\[\] args) {  
Cache cache=new KryoSerializedCache(new PerpetualCache());  
cache.putObject("A", 500);  
Object a1=cache.getObject("A");  
Object a2=cache.getObject("A");  
System.out.println("a1="+a1);  
System.out.println("a2="+a2);  
System.out.println(a1==a2);//false  
}  
  
}
查看原文

赞 31 收藏 9 评论 4

破尘summer 关注了专栏 · 8月13日

CodeGuide | 程序员编码指南

开源仓库:https://github.com/fuzhengwei/CodeGuide/wiki | 作者小傅哥多年从事一线互联网Java开发的学习历程技术汇总,旨在为大家提供一个清晰详细的学习教程,侧重点更倾向编写Java核心内容。如果能为您提供帮助,请给予支持(关注、点赞、分享)!

关注 5238

认证与成就

  • 获得 12 次点赞
  • 获得 1 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 1 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 8月13日
个人主页被 225 人浏览