缓存(Cache,Redis)

1.pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.4</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.example</groupId><artifactId>springboot_01_cache</artifactId><version>0.0.1-SNAPSHOT</version><name>springboot_01_cache</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId><version>2.4.4</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><version>2.4.4</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.3</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

2.peoperties

spring.datasource.url=jdbc:mysql://localhost:3306/mysql2
spring.datasource.username=root
spring.datasource.password=123456
#spring.datasource.driver-class-name=com.mysql.jdbc.Driver# 开启驼峰命名匹配规则
mybatis.configuration.map-underscore-to-camel-case=true# 打印sql语句日志
logging.level.com.example.cache.mapper=debug#自动配置的报告
#debug=true#redis配置
spring.redis.host=192.168.45.128
spring.redis.password=123456

3.启动类

package com.example.cache;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;/*** 一、 搭建基本环境* 1、导入数据库文件,创建department和employee表* 2、创建JavaBean封装数据* 3、整合MyBatis操作数据*       1.配置数据源信息*       2.使用注解版MyBatis;*        1). @MapperScan指定需要扫描的mapper接口所在的包* 二、 快速体验缓存*        步骤*           1、开启基于注解的缓存 @EnableCaching*           2、标注缓存注解即可*              @Cacheable*              @CacheEvict*              @CachePut*默认使用的是ConcurrentMapCacheManager==ConcurrentMapCache:将数据保存在ConcurrentMap<Object,Object>*在开发中使用缓存中间件:redis、memcached、ehcache;** 三、整合redis作为缓存* Redis是一个开源(BSD)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。*     在bin目录中执行 redis-server /usr/local/redis/redis-5.0.2/etc/redis.conf*     再执行 redis-cli 即可启动。*     引入redis的starter*     配置redis*     测试缓存*        原理:CacheManager===Cache 缓存组件来实际给缓存中存取数据*        1)、引入redis的starter,容器中保存的是RedisCacheManager;*        2)、RedisCacheManager帮我们创建RedisCache来作为缓存组件;RedisCache通过操作redis缓存数据*        3)、默认保存数据 k-v 都是Object;利用序列化保存;如何保存为json?*           1.引入redis的starter,cacheManager变为RedisCacheManager;*           2.默认创建的RedisCacheManager 操作redis的时候使用的是RedisTemplate<Object,Object> </>*           3.RedisTemplate<Object,Object>是默认使用jdk的序列化机制*        4)、自定义CacheManager;**/
@MapperScan("com.example.cache.mapper")
@SpringBootApplication
@EnableCaching //开启缓存
public class Springboot01CacheApplication {public static void main(String[] args) {SpringApplication.run(Springboot01CacheApplication.class, args);}}

4.两个实体类

package com.example.cache.bean;import java.io.Serializable;/*** @PackageName: com.example.cache.bean* @ClassName: Department* @author: zhangpan* @data: 2022/11/7 16:16*/
public class Department implements Serializable {private Integer id;private String departmentName;public Department(Integer id, String departmentName) {this.id = id;this.departmentName = departmentName;}public Department() {}@Overridepublic String toString() {return "Department{" +"id=" + id +", departmentName='" + departmentName + '\'' +'}';}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getDepartmentName() {return departmentName;}public void setDepartmentName(String departmentName) {this.departmentName = departmentName;}
}
package com.example.cache.bean;import java.io.Serializable;/*** @PackageName: com.example.cache.bean* @ClassName: Employee* @author: zhangpan* @data: 2022/11/7 16:16*/
public class Employee implements Serializable {private Integer id;private String lastName;private String email;private Integer gender;private Integer dId;@Overridepublic String toString() {return "Employee{" +"id=" + id +", lastName='" + lastName + '\'' +", email='" + email + '\'' +", gender=" + gender +", dId=" + dId +'}';}public Employee(Integer id, String lastName, String email, Integer gender, Integer dId) {this.id = id;this.lastName = lastName;this.email = email;this.gender = gender;this.dId = dId;}public Employee() {}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getLastName() {return lastName;}public void setLastName(String lastName) {this.lastName = lastName;}public String getEmail() {return email;}public void setEmail(String email) {this.email = email;}public Integer getGender() {return gender;}public void setGender(Integer gender) {this.gender = gender;}public Integer getdId() {return dId;}public void setdId(Integer dId) {this.dId = dId;}
}

5.两个mapper接口

package com.example.cache.mapper;import com.example.cache.bean.Department;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;@Mapper
public interface DepartmentMapper {@Select("select * from department where id = #{id}")Department getDeptById(Integer id);
}
package com.example.cache.mapper;import org.apache.ibatis.annotations.*;
import com.example.cache.bean.Employee;@Mapper
public interface EmployeeMapper {@Select("select * from employee where id = #{id}")public Employee getEmpById(Integer id);@Select("select * from employee where lastName = #{lastName}")public Employee getEmpByLastName(String lastName);@Update("update employee set lastName=#{lastName},email=#{email},gender=#{gender},d_id=#{dId} where id = #{id}")public void updateEmp(Employee employee);@Delete("delete from employee where id = #{id}")public void deleteEmp(Integer id);@Insert("insert into employee(lastName,email,gender,d_id) values(#{lastName},#{email},#{gender},#{dId})")public void insertEmp(Employee employee);}

6.两个配置类

6.1 Cache注解配置类

package com.example.cache.config;import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.lang.reflect.Method;
import java.util.Arrays;/*** @PackageName: com.example.cache.config* @ClassName: MyCacheConfig* @author: zhangpan* @data: 2022/11/8 14:54*/
@Configuration
public class MyCacheConfig {@Bean("myKeyGenerator")public KeyGenerator keyGenerator() {return new KeyGenerator(){@Overridepublic Object generate(Object target, Method method, Object... params) {return method.getName()+"["+ Arrays.asList(params).toString() +"]";}};}
}

6.2redis配置类

package com.example.cache.config;import com.example.cache.bean.Department;
import com.example.cache.bean.Employee;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;import java.time.Duration;
import java.util.Map;/*** @PackageName: com.example.cache.config* @ClassName: MyRedisConfig* @author: zhangpan* @data: 2022/11/8 19:23*/
@Configuration
public class MyRedisConfig {@Beanpublic RedisTemplate<Object, Employee> employeeRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws Exception {RedisTemplate<Object, Employee> template = new RedisTemplate<>();template.setConnectionFactory(redisConnectionFactory);Jackson2JsonRedisSerializer<Employee> serializer = new Jackson2JsonRedisSerializer<Employee>(Employee.class);template.setDefaultSerializer(serializer);return template;}@Beanpublic CacheManager employeeCacheManager(RedisConnectionFactory factory){RedisCacheConfiguration cacheConfiguration =RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofDays(1)).disableCachingNullValues().serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(newGenericJackson2JsonRedisSerializer()));return RedisCacheManager.builder(factory).cacheDefaults(cacheConfiguration).build();}@Beanpublic RedisTemplate<Object, Department> depRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws Exception {RedisTemplate<Object, Department> template = new RedisTemplate<>();template.setConnectionFactory(redisConnectionFactory);Jackson2JsonRedisSerializer<Department> serializer = new Jackson2JsonRedisSerializer<Department>(Department.class);template.setDefaultSerializer(serializer);return template;}@Primary //@Primary作用 指定默认的CacheManager@Beanpublic RedisCacheManager departmentCacheManager(RedisConnectionFactory factory){RedisCacheConfiguration cacheConfiguration =RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofDays(1)).disableCachingNullValues().serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(newGenericJackson2JsonRedisSerializer()));return RedisCacheManager.builder(factory).cacheDefaults(cacheConfiguration).build();}//    @Bean
//    public RedisCacheManager employeeCacheManager(RedisTemplate<Object, Employee> empRedisTemplate){//        RedisCacheManager cacheManager = new RedisCacheManager(empRedisTemplate);
//        //key多了一个前缀
//        //使用前缀,默认会将CacheName作为key的前缀
//        cacheManager.setUsePrefix(true);
//        return cacheManager;
//    }}

7.service层

7.1使用cache注解做缓存

package com.example.cache.service;import com.example.cache.bean.Employee;
import com.example.cache.mapper.EmployeeMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.*;
import org.springframework.stereotype.Service;/*** @PackageName: com.example.cache* @ClassName: EmployeeService* @author: zhangpan* @data: 2022/11/7 16:48*/
//@CacheConfig(cacheNames="emp") //下面的注解中不需要写emp了 (抽取缓存的公共配置)
@Service
public class EmployeeService {@Autowired(required = false)public EmployeeMapper employeeMapper;/*** @Cacheable注解的作用:*      将方法运行结果进行缓存,以后再要相同的数据,直接从缓存中获取,不用调用方法。* CacheManager管理多个Cache组件的,对缓存的真正CRUD操作在Cache组件中,每一个缓存组件有自己唯一一个名字;* 几个属性:*      cacheNames/value:指定缓存组件的名字;将方法的返回结果放在哪个缓存中,是数组的方式,可以指定多个缓存。*      key:缓存数据使用的key;可以用它来指定。默认是使用方法参数的值 1-方法的返回值*              编写SqEl; #id;参数id的值  #a0  #p0  #root.args[0]*      keyGenerator:key的生成器;可以自己指定key的生成器的组件id*                  key/keyGenerator: 二选一使用*      cacheManager:指定缓存管理器,获取cacheResolver指定获取解析器*      condition:指定符合条件的情况下才缓存: ,condition = "#id>0"*                  condition = "#a0>1":第一个参数的值 > 1 的时候才进行缓存*      unless:否定缓存,当unless指定的条件为true,方法的返回值就不会被缓存,可以获取到的结果进行判断*             unless = "#result == null"*             例如:,unless = "#a0==2":如果第一个参数的值是2,结果不缓存*      sync:是否使用异步模式** 原理:*    1、自动配置类:CacheAutoConfiguration*    2、缓存配置类(一共有10个) 【默认使用SimpleCacheConfiguration】*    3、那个配置默认生效:SimpleCacheConfiguration;*    4、给容器中注册一个CacheManager:ConcurrentMapCacheManager*    5、可以获取和创建ConcurrentMapCache类型的缓存组件,他的作用将数据保存在ConcurrentMap中。** 运行流程:*    @Cacheable:*    1、方法运行之前,先去查询Cache(缓存组件),按照cacheNames指定的名字获取;*      (CacheManage先获取相应的缓存),第一次获取缓存如果没有Cache组件会自动创建。*    2、去Cache中查找缓存的内容,使用一个key,默认就是方法的参数;*      key是按照某种策略生成的;默认是使用keyGenerator生成的,默认使用keyGenerator生成key;*          SimpleKeyGenerator生成key的默认策略;*              如果没有参数:key = new SimpleKey();*              如果有一个参数:key = 参数的值*              如果有多个参数:key = new SimpleKey(params);*    3、没有查到缓存就调用目标方法;*    4、没有目标方法返回的结果,放进缓存中** @Cacheable标注的方法执行之前先来检查缓存中有没有这个数据,默认按照参数的值作为key去查询缓存。* 如果没有就运行方法并将结果放入缓存:以后再来调用就可以直接使用缓存中的数据。** 核心:*      1)、使用CacheManager【ConcurrentMapCacheManager】按照名字得到Cache【ConcurrentMapCache】组件*      2)、key使用keyGenerator生成的,默认是SimpleKeyGenerator* @param id* @return*///注解的作用就是将方法的运行结果进行缓存@Cacheable(cacheNames = "emp"/*,keyGenerator = "myKeyGenerator",condition = "#a0>1",unless = "#a0==2"*/)public Employee getEmp(Integer id) {System.out.println("查询"+id+"号员工");Employee emp = employeeMapper.getEmpById(id);return emp;}/*** @CachePut:即调用方法,又更新缓存数据;同步更新缓存(更新数据库同时更新缓存)* 修改了数据库的某个数据,同时更新缓存;* 运行时机:*    1、先调用目标方法*    2、将目标方法的结果缓存起来** 测试步骤:*    1、查询1号员工,查到的结果会放在缓存中*          key: 1  value: lastName:张三*    2、以后查询还是之前的结果*    3、更新1号员工:【lastName:zhangsan; gender:0】*          将方法的返回值也放进缓存了;*          key:传入的employee对象。值:返回的employee对象;*    4、查询1号员工?*         应该是更新后的员工;*              key = "#employee.id" :使用传入的参数的员工id;*              key = “#result.id” :使用返回后的id*                  @Cacheable的key是不能用#result*         为什么是没更新前的?【1号员工没有在缓存中更新】**/@CachePut(value = "emp",key = "#result.id")public Employee updateEmp(Employee employee) {System.out.println("updateEmp:"+employee);employeeMapper.updateEmp(employee);return employee;}/*** @CacheEvict:缓存清除* key:指定要清除的数据*    allEntries = true : 指定清除这个缓存中所有的数据*    beforeInvocation = false : 缓存的清除是否在方法之前执行*       默认代表缓存清除操作是在方法之后执行;如果出现异常缓存就不会清除**    beforeInvocation = true:*       代表清除缓存操作是在方法运行之前执行,无论方法是否出现异常,缓存都清除*/@CacheEvict(value = "emp"/*, key = "#id"*//*, allEntries = true*/,beforeInvocation = true)public void deleteEmp(Integer id) {System.out.println("deleteEmp:"+id);
//        employeeMapper.deleteEmp(id);
//        int i = 10/0;}/*** 定义复杂的缓存规则* @param lastName* @return*/@Caching(cacheable = {@Cacheable(value = "emp",key = "#lastName")},put = {@CachePut(value = "emp",key = "#result.id"),@CachePut(value = "emp",key = "#result.email")})public Employee getEmpLastName(String lastName) {return employeeMapper.getEmpByLastName(lastName);}
}

7.2使用redis做缓存

package com.example.cache.service;import com.example.cache.bean.Department;
import com.example.cache.mapper.DepartmentMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.stereotype.Service;/*** @PackageName: com.example.cache.service* @ClassName: DeptService* @author: zhangpan* @data: 2022/11/10 17:11*/
@Service
public class DeptService {@Autowired(required = false)DepartmentMapper departmentMapper;//    @Qualifier("departmentCacheManager")
//    @Autowired
//    RedisCacheManager departmentCacheManager; //注入缓存管理器 (使用编码的方式操作缓存)/*** 缓存* @param id* @return*/@Cacheable(cacheNames = "dept")public Department getDeptById(Integer id) {System.out.println("查询部门的id为:"+id);Department department = departmentMapper.getDeptById(id);return department;}/*** 使用编码操作缓存数据* @param id* @return*/
//    public Department getDeptByIdCache(Integer id) {//        System.out.println("查询部门的id为:"+id);
//        Department department = departmentMapper.getDeptById(id);
//        //获取某个缓存
//        Cache dept = departmentCacheManager.getCache("dept");
//        dept.put("dept:1",department);
//        return department;
//    }
}

8.controller

8.1注解controller

package com.example.cache.controller;import com.example.cache.bean.Employee;
import com.example.cache.service.EmployeeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;/*** @PackageName: com.example.cache.controller* @ClassName: EmployeeController* @author: zhangpan* @data: 2022/11/7 16:52*/
@Controller
public class EmployeeController {@Autowired(required = false)public EmployeeService employeeService;@RequestMapping(value = "/emp/{id}",method = RequestMethod.GET)@ResponseBodypublic Employee getEmp(@PathVariable("id") Integer id) {Employee emp = employeeService.getEmp(id);return emp;}//    @RequestMapping(value = "/emp/{id}",method = RequestMethod.PUT)
//    @ResponseBody
//    public Employee updateEmp(@RequestBody Employee employee,@PathVariable("id")Integer id) {//        Employee emp = employeeService.updateEmp(employee);
//        return emp;
//    }//    @ResponseBody
//    @RequestMapping(value = "emp/{id}", method = RequestMethod.DELETE)
//    public String deleteEmp(@PathVariable("id") Integer id) {//        employeeService.deleteEmp(id);
//        return "success";
//    }//    @ResponseBody
//    @RequestMapping(value = "emp/{lastName}", method = RequestMethod.GET)
//    public String getEmpLastName(@PathVariable("lastName") String lastName){//        employeeService.getEmpLastName(lastName);
//        return "success";
//    }
}

8.2redis controller

package com.example.cache.controller;import com.example.cache.bean.Department;
import com.example.cache.service.DeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;/*** @PackageName: com.example.cache.controller* @ClassName: DeptController* @author: zhangpan* @data: 2022/11/10 17:20*/
@RestController
public class DeptController {@Autowiredprivate DeptService deptService;@RequestMapping(value = "/dept/{id}", method = RequestMethod.GET)public Department getDeptById(@PathVariable("id") Integer id) {Department dept = deptService.getDeptById(id);return dept;}}

缓存(cache、Redis)相关推荐

  1. 七十六、SpringBoot 的数据缓存cache+Redis(三)

    @Author:Runsen 来源:尚硅谷 下面建议读者学习尚硅谷的B站的SpringBoot视频,我是学雷丰阳视频入门的. 具体链接如下:B站尚硅谷SpringBoot教程 文章目录 Redis P ...

  2. python3 之 天天生鲜 项目 缓存cache

    settings # 缓存 CACHES = {"default": {"BACKEND": "django_redis.cache.RedisCac ...

  3. redis缓存(redis缓存工具封装篇)

    1.0什么是缓存? 缓存(Cache),就是数据交换的缓冲区,俗称的缓存就是缓冲区内的数据,一般从数据库中获取,存储于本地代码(例如: 例1:Static final ConcurrentHashMa ...

  4. Spring Boot 实践折腾记(12):支持数据缓存Cache

    不管是什么类型的应用程序,都离不开数据,即便如现在的手机APP,我们依然需要使用数数据库,对于不懂的人,当然,我们可以告诉他们一些高大上的概念,但是作为专业人士,就一定要明白背后的真实原理到底是什么. ...

  5. Spring Boot缓存实战 Redis

    Spring boot默认使用的是SimpleCacheConfiguration,即使用ConcurrentMapCacheManager来实现缓存.但是要切换到其他缓存实现也很简单.下面是使用re ...

  6. Springboot 集成 mybatis 开启二级缓存(redis)

    首先来了解下mybatis 缓存,mybatis缓存分为一级缓存和二级缓存.一级缓存是默认开启的,无需其他配置操作,二级缓存则需要手动设置开启. 一级缓存原理: Mybatis的一级缓存是指同一个Sq ...

  7. Spring Boot缓存实战 Redis 设置有效时间和自动刷新缓存-2

    问题 上一篇Spring Boot Cache + redis 设置有效时间和自动刷新缓存,时间支持在配置文件中配置,说了一种时间方式,直接扩展注解的Value值,如: @Override @Cach ...

  8. Spring Boot缓存实战 Redis 设置有效时间和自动刷新缓存,时间支持在配置文件中配置

    问题描述 Spring Cache提供的@Cacheable注解不支持配置过期时间,还有缓存的自动刷新. 我们可以通过配置CacheManneg来配置默认的过期时间和针对每个缓存容器(value)单独 ...

  9. 【SpringBoot整合缓存】-----Redis缓存篇

    本专栏将从基础开始,循序渐进,以实战为线索,逐步深入SpringBoot相关知识相关知识,打造完整的SpringBoot学习步骤,提升工程化编码能力和思维能力,写出高质量代码.希望大家都能够从中有所收 ...

  10. Shiro的缓存交给redis管理

    Shiro的缓存交给redis管理 标签(空格分隔):Shiro redis 前言: 以下方式只是单机版的redis使用 一.导入Shiro和redis的jar jedis-2.7.3.jarshir ...

最新文章

  1. 你说我做丨为你定制「大局观版」职场设计课
  2. 关于AJAX跨域调用ASP.NET MVC或者WebAPI服务的问题及解决方案
  3. 利用ado.net和winform连接ms Server可以做什么?
  4. 西餐美食店响应式网站模板
  5. STM32 - 定时器的设定 - 基础- 02 - Capture/compare channels 和相关设置寄存器 - 和STM32缩写词条解释
  6. 使用 HTML5 File API 实现client log
  7. Spring Cloud与微服务学习总结(10)——Spring Cloud 常见优化项的总结
  8. 蓝桥杯 ALGO-14 算法训练 回文数
  9. JVM判断对象是否存活
  10. IOSday04 UIButton使用
  11. a标签href不跳转_HTML常用标签
  12. 双系统ubuntu无法进入_win10下安装ubuntu双系统
  13. Excel数据导入sql临时表操作步骤
  14. 图解TCPIP 学习笔记(一)
  15. 使用BASIC语言控制ESP8266
  16. 电能终端服务器,抄表终端服务器设置教程
  17. html切图软件,切图工具/插件介绍
  18. dedecms---一个简单酷站的构建及解析
  19. JQuery 实现文件下载的常用方法分析
  20. 02 Jmonkey3.2.0+Nifty1.4.2实现GUI

热门文章

  1. iOS 逆向编程(三)实操越狱详细流程
  2. 6种无线通信技术对比(WiFi,Zigbee,蓝牙,Lora,NB-lot,4G)。
  3. flyme最新7基于android,终于来了,魅族开始基于Android 7.0版本的Flyme内测
  4. 《写给大家看的设计书》《写给大家看的色彩书》《点石成金》《形式感》学习笔记...
  5. matlab中寻找矩阵元素并替换
  6. 有哪些能支持epub、txt格式的电子书阅读器?能在MAC上用的?
  7. 《那些年啊,那些事——一个程序员的奋斗史》——40
  8. 【前端】【JavaScript】通过成绩判断等级
  9. Dubbo线程池耗尽问题
  10. Kafka 中的这些设计思想值得一学!