26. SpringBoot 初识缓存及 SimpleCacheConfiguration源码解析
1.引入一下starter:
web、cache、Mybatis、MySQL
@MapperScan("com.everjiankang.cache.dao")
@SpringBootApplication
@EnableCaching //启用缓存
public class Springboot01CacheApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot01CacheApplication.class, args);
}
}
@Service
public class UserServiceImpl implements UserService { @Autowired
UserMapper userMapper; /**
* 将方法的运行结果进行缓存;以后再要相同的数据,直接从缓存中获取,不用调用方法;
* CacheManager管理多个Cache组件的,对缓存的真正CRUD操作在Cache组件中,每一个缓存组件有自己唯一一个名字;
*
*
* 原理:
* 1、自动配置类;CacheAutoConfiguration
* 2、缓存的配置类
* org.springframework.boot.autoconfigure.cache.GenericCacheConfiguration
* org.springframework.boot.autoconfigure.cache.JCacheCacheConfiguration
* org.springframework.boot.autoconfigure.cache.EhCacheCacheConfiguration
* org.springframework.boot.autoconfigure.cache.HazelcastCacheConfiguration
* org.springframework.boot.autoconfigure.cache.InfinispanCacheConfiguration
* org.springframework.boot.autoconfigure.cache.CouchbaseCacheConfiguration
* org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration
* org.springframework.boot.autoconfigure.cache.CaffeineCacheConfiguration
* org.springframework.boot.autoconfigure.cache.GuavaCacheConfiguration
* org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration【默认】
* org.springframework.boot.autoconfigure.cache.NoOpCacheConfiguration
* 3、哪个配置类默认生效:SimpleCacheConfiguration;
*
* 4、给容器中注册了一个CacheManager:ConcurrentMapCacheManager
* 5、可以获取和创建ConcurrentMapCache类型的缓存组件;他的作用将数据保存在ConcurrentMap中;
*
* 运行流程:
* @Cacheable:
* 1、方法运行之前,先去查询Cache(缓存组件),按照cacheNames指定的名字获取;
* (CacheManager先获取相应的缓存),第一次获取缓存如果没有Cache组件会自动创建。
* 2、去Cache中查找缓存的内容,使用一个key,默认就是方法的参数;
* key是按照某种策略生成的;默认是使用keyGenerator生成的,默认使用SimpleKeyGenerator生成key;
* SimpleKeyGenerator生成key的默认策略;
* 如果没有参数;key=new SimpleKey();
* 如果有一个参数:key=参数的值
* 如果有多个参数:key=new SimpleKey(params);
* 3、没有查到缓存就调用目标方法;
* 4、将目标方法返回的结果,放进缓存中
*
* @Cacheable标注的方法执行之前先来检查缓存中有没有这个数据,默认按照参数的值作为key去查询缓存,
* 如果没有就运行方法并将结果放入缓存;以后再来调用就可以直接使用缓存中的数据;
*
* 核心:
* 1)、使用CacheManager【ConcurrentMapCacheManager】按照名字得到Cache【ConcurrentMapCache】组件
* 2)、key使用keyGenerator生成的,默认是SimpleKeyGenerator
*
*
* 几个属性:
* cacheNames/value:指定缓存组件的名字;将方法的返回结果放在哪个缓存中,是数组的方式,可以指定多个缓存;
*
* key:缓存数据使用的key;可以用它来指定。默认是使用方法参数的值 1-方法的返回值
* 编写SpEL; #i d;参数id的值 #a0 #p0 #root.args[0]
* getEmp[2]
*
* 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:是否使用异步模式
* @param id
* @return
*
*/
@Cacheable(cacheNames = {"user"})
@Override
public User selectByPrimaryKey(Integer id) {
return userMapper.selectByPrimaryKey(id);
} }
package org.springframework.boot.autoconfigure.cache; @Configuration
@ConditionalOnClass({CacheManager.class})
@ConditionalOnBean({CacheAspectSupport.class})
@ConditionalOnMissingBean(
value = {CacheManager.class},
name = {"cacheResolver"}
)
@EnableConfigurationProperties({CacheProperties.class})
@AutoConfigureAfter({CouchbaseAutoConfiguration.class, HazelcastAutoConfiguration.class, HibernateJpaAutoConfiguration.class, RedisAutoConfiguration.class})
@Import({CacheAutoConfiguration.CacheConfigurationImportSelector.class})
public class CacheAutoConfiguration {
public CacheAutoConfiguration() {
} @Bean
@ConditionalOnMissingBean
public CacheManagerCustomizers cacheManagerCustomizers(ObjectProvider<CacheManagerCustomizer<?>> customizers) {
return new CacheManagerCustomizers((List)customizers.orderedStream().collect(Collectors.toList()));
} @Bean
public CacheAutoConfiguration.CacheManagerValidator cacheAutoConfigurationValidator(CacheProperties cacheProperties, ObjectProvider<CacheManager> cacheManager) {
return new CacheAutoConfiguration.CacheManagerValidator(cacheProperties, cacheManager);
} static class CacheConfigurationImportSelector implements ImportSelector {
CacheConfigurationImportSelector() {
}
/**在Spring Boot项目启动的过程中,导入缓存的配置组件*/
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
CacheType[] types = CacheType.values();
String[] imports = new String[types.length]; for(int i = 0; i < types.length; ++i) {
imports[i] = CacheConfigurations.getConfigurationClass(types[i]);
}
/** imports的值,缓存配置类
org.springframework.boot.autoconfigure.cache.GenericCacheConfiguration
org.springframework.boot.autoconfigure.cache.JCacheCacheConfiguration
org.springframework.boot.autoconfigure.cache.EhCacheCacheConfiguration
org.springframework.boot.autoconfigure.cache.HazelcastCacheConfiguration
org.springframework.boot.autoconfigure.cache.InfinispanCacheConfiguration org.springframework.boot.autoconfigure.cache.CouchbaseCacheConfiguration
org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration
org.springframework.boot.autoconfigure.cache.CaffeineCacheConfiguration
org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration
org.springframework.boot.autoconfigure.cache.NoOpCacheConfiguration
*/
return imports;
}
} static class CacheManagerValidator implements InitializingBean {
private final CacheProperties cacheProperties;
private final ObjectProvider<CacheManager> cacheManager; CacheManagerValidator(CacheProperties cacheProperties, ObjectProvider<CacheManager> cacheManager) {
this.cacheProperties = cacheProperties;
this.cacheManager = cacheManager;
} public void afterPropertiesSet() {
Assert.notNull(this.cacheManager.getIfAvailable(), () -> {
return "No cache manager could be auto-configured, check your configuration (caching type is '" + this.cacheProperties.getType() + "')";
});
}
} @Configuration
@ConditionalOnClass({LocalContainerEntityManagerFactoryBean.class})
@ConditionalOnBean({AbstractEntityManagerFactoryBean.class})
protected static class CacheManagerJpaDependencyConfiguration extends EntityManagerFactoryDependsOnPostProcessor {
public CacheManagerJpaDependencyConfiguration() {
super(new String[]{"cacheManager"});
}
}
}

在配置文件中加入
debug=true
然后启动项目,控制台关于缓存的启动log如下:
SimpleCacheConfiguration matched:
- Cache org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration automatic cache type (CacheCondition)
- @ConditionalOnMissingBean (types: org.springframework.cache.CacheManager; SearchStrategy: all) did not find any beans (OnBeanCondition) CaffeineCacheConfiguration:
Did not match:
- @ConditionalOnClass did not find required class 'com.github.benmanes.caffeine.cache.Caffeine' (OnClassCondition) CouchbaseCacheConfiguration:
Did not match:
- @ConditionalOnClass did not find required classes 'com.couchbase.client.java.Bucket', 'com.couchbase.client.spring.cache.CouchbaseCacheManager' (OnClassCondition) EhCacheCacheConfiguration:
Did not match:
- @ConditionalOnClass did not find required class 'net.sf.ehcache.Cache' (OnClassCondition) GenericCacheConfiguration:
Did not match:
- @ConditionalOnBean (types: org.springframework.cache.Cache; SearchStrategy: all) did not find any beans of type org.springframework.cache.Cache (OnBeanCondition)
Matched:
- Cache org.springframework.boot.autoconfigure.cache.GenericCacheConfiguration automatic cache type (CacheCondition) HazelcastCacheConfiguration:
Did not match:
- @ConditionalOnClass did not find required classes 'com.hazelcast.core.HazelcastInstance', 'com.hazelcast.spring.cache.HazelcastCacheManager' (OnClassCondition) InfinispanCacheConfiguration:
Did not match:
- @ConditionalOnClass did not find required class 'org.infinispan.spring.provider.SpringEmbeddedCacheManager' (OnClassCondition) JCacheCacheConfiguration:
Did not match:
- @ConditionalOnClass did not find required class 'javax.cache.Caching' (OnClassCondition) NoOpCacheConfiguration:
Did not match:
- @ConditionalOnMissingBean (types: org.springframework.cache.CacheManager; SearchStrategy: all)
found beans of type 'org.springframework.cache.CacheManager' cacheManager (OnBeanCondition)
Matched:
- Cache org.springframework.boot.autoconfigure.cache.NoOpCacheConfiguration automatic cache type (CacheCondition) RedisCacheConfiguration:
Did not match:
- @ConditionalOnClass did not find required class 'org.springframework.data.redis.connection.RedisConnectionFactory' (OnClassCondition)
@Configuration
@ConditionalOnMissingBean({CacheManager.class})
@Conditional({CacheCondition.class})
class SimpleCacheConfiguration {
private final CacheProperties cacheProperties;
private final CacheManagerCustomizers customizerInvoker; SimpleCacheConfiguration(CacheProperties cacheProperties, CacheManagerCustomizers customizerInvoker) {
this.cacheProperties = cacheProperties;
this.customizerInvoker = customizerInvoker;
} @Bean
public ConcurrentMapCacheManager cacheManager() {
ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();
List<String> cacheNames = this.cacheProperties.getCacheNames();
if (!cacheNames.isEmpty()) {
cacheManager.setCacheNames(cacheNames);
} return (ConcurrentMapCacheManager)this.customizerInvoker.customize(cacheManager);
}
}
package org.springframework.cache; import java.util.Collection;
import org.springframework.lang.Nullable; public interface CacheManager {
@Nullable
Cache getCache(String var1); Collection<String> getCacheNames();
}
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
// package org.springframework.cache.concurrent; public class ConcurrentMapCacheManager implements CacheManager, BeanClassLoaderAware {
private final ConcurrentMap<String, Cache> cacheMap = new ConcurrentHashMap(16);
private boolean dynamic = true;
private boolean allowNullValues = true;
private boolean storeByValue = false;
@Nullable
private SerializationDelegate serialization; public ConcurrentMapCacheManager() {
} public ConcurrentMapCacheManager(String... cacheNames) {
this.setCacheNames(Arrays.asList(cacheNames));
} public void setCacheNames(@Nullable Collection<String> cacheNames) {
if (cacheNames != null) {
Iterator var2 = cacheNames.iterator(); while(var2.hasNext()) {
String name = (String)var2.next();
this.cacheMap.put(name, this.createConcurrentMapCache(name));
} this.dynamic = false;
} else {
this.dynamic = true;
} } public void setAllowNullValues(boolean allowNullValues) {
if (allowNullValues != this.allowNullValues) {
this.allowNullValues = allowNullValues;
this.recreateCaches();
} } public boolean isAllowNullValues() {
return this.allowNullValues;
} public void setStoreByValue(boolean storeByValue) {
if (storeByValue != this.storeByValue) {
this.storeByValue = storeByValue;
this.recreateCaches();
} } public boolean isStoreByValue() {
return this.storeByValue;
} public void setBeanClassLoader(ClassLoader classLoader) {
this.serialization = new SerializationDelegate(classLoader);
if (this.isStoreByValue()) {
this.recreateCaches();
} } public Collection<String> getCacheNames() {
return Collections.unmodifiableSet(this.cacheMap.keySet());
} @Nullable
public Cache getCache(String name) {
Cache cache = (Cache)this.cacheMap.get(name);
if (cache == null && this.dynamic) {
ConcurrentMap var3 = this.cacheMap;
synchronized(this.cacheMap) {
cache = (Cache)this.cacheMap.get(name);
if (cache == null) {
cache = this.createConcurrentMapCache(name);
this.cacheMap.put(name, cache);
}
}
} return cache;
} private void recreateCaches() {
Iterator var1 = this.cacheMap.entrySet().iterator(); while(var1.hasNext()) {
Entry<String, Cache> entry = (Entry)var1.next();
entry.setValue(this.createConcurrentMapCache((String)entry.getKey()));
} } protected Cache createConcurrentMapCache(String name) {
SerializationDelegate actualSerialization = this.isStoreByValue() ? this.serialization : null;
return new ConcurrentMapCache(name, new ConcurrentHashMap(256), this.isAllowNullValues(), actualSerialization);
}
}
package org.springframework.cache.concurrent; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Function;
import org.springframework.cache.Cache.ValueRetrievalException;
import org.springframework.cache.Cache.ValueWrapper;
import org.springframework.cache.support.AbstractValueAdaptingCache;
import org.springframework.core.serializer.support.SerializationDelegate;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert; public class ConcurrentMapCache extends AbstractValueAdaptingCache {
private final String name;
private final ConcurrentMap<Object, Object> store; //真正存数据的地方
@Nullable
private final SerializationDelegate serialization; public ConcurrentMapCache(String name) {
this(name, new ConcurrentHashMap(256), true);
} public ConcurrentMapCache(String name, boolean allowNullValues) {
this(name, new ConcurrentHashMap(256), allowNullValues);
} public ConcurrentMapCache(String name, ConcurrentMap<Object, Object> store, boolean allowNullValues) {
this(name, store, allowNullValues, (SerializationDelegate)null);
} protected ConcurrentMapCache(String name, ConcurrentMap<Object, Object> store, boolean allowNullValues, @Nullable SerializationDelegate serialization) {
super(allowNullValues);
Assert.notNull(name, "Name must not be null");
Assert.notNull(store, "Store must not be null");
this.name = name;
this.store = store;
this.serialization = serialization;
} public final boolean isStoreByValue() {
return this.serialization != null;
} public final String getName() {
return this.name;
} public final ConcurrentMap<Object, Object> getNativeCache() {
return this.store;
} @Nullable
protected Object lookup(Object key) {
return this.store.get(key);
} @Nullable
public <T> T get(Object key, Callable<T> valueLoader) {
return this.fromStoreValue(this.store.computeIfAbsent(key, (r) -> {
try {
return this.toStoreValue(valueLoader.call());
} catch (Throwable var5) {
throw new ValueRetrievalException(key, valueLoader, var5);
}
}));
} public void put(Object key, @Nullable Object value) {
this.store.put(key, this.toStoreValue(value));
} @Nullable
public ValueWrapper putIfAbsent(Object key, @Nullable Object value) {
Object existing = this.store.putIfAbsent(key, this.toStoreValue(value));
return this.toValueWrapper(existing);
} public void evict(Object key) {
this.store.remove(key);
} public void clear() {
this.store.clear();
} protected Object toStoreValue(@Nullable Object userValue) {
Object storeValue = super.toStoreValue(userValue);
if (this.serialization != null) {
try {
return this.serializeValue(this.serialization, storeValue);
} catch (Throwable var4) {
throw new IllegalArgumentException("Failed to serialize cache value '" + userValue + "'. Does it implement Serializable?", var4);
}
} else {
return storeValue;
}
} private Object serializeValue(SerializationDelegate serialization, Object storeValue) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream(); byte[] var4;
try {
serialization.serialize(storeValue, out);
var4 = out.toByteArray();
} finally {
out.close();
} return var4;
} protected Object fromStoreValue(@Nullable Object storeValue) {
if (storeValue != null && this.serialization != null) {
try {
return super.fromStoreValue(this.deserializeValue(this.serialization, storeValue));
} catch (Throwable var3) {
throw new IllegalArgumentException("Failed to deserialize cache value '" + storeValue + "'", var3);
}
} else {
return super.fromStoreValue(storeValue);
}
} private Object deserializeValue(SerializationDelegate serialization, Object storeValue) throws IOException {
ByteArrayInputStream in = new ByteArrayInputStream((byte[])((byte[])storeValue)); Object var4;
try {
var4 = serialization.deserialize(in);
} finally {
in.close();
} return var4;
}
}
package org.springframework.cache.interceptor;
public abstract class CacheAspectSupport extends AbstractCacheInvoker implements BeanFactoryAware, InitializingBean, SmartInitializingSingleton {
@Nullable
private Object execute(CacheOperationInvoker invoker, Method method, CacheAspectSupport.CacheOperationContexts contexts) {
if (contexts.isSynchronized()) {
CacheAspectSupport.CacheOperationContext context = (CacheAspectSupport.CacheOperationContext)contexts.get(CacheableOperation.class).iterator().next();
if (this.isConditionPassing(context, CacheOperationExpressionEvaluator.NO_RESULT)) {
Object key = this.generateKey(context, CacheOperationExpressionEvaluator.NO_RESULT);
Cache cache = (Cache)context.getCaches().iterator().next();
try {
return this.wrapCacheValue(method, cache.get(key, () -> {
return this.unwrapReturnValue(this.invokeOperation(invoker));
}));
} catch (ValueRetrievalException var10) {
throw (ThrowableWrapper)var10.getCause();
}
} else {
return this.invokeOperation(invoker);
}
} else {
this.processCacheEvicts(contexts.get(CacheEvictOperation.class), true, CacheOperationExpressionEvaluator.NO_RESULT);
ValueWrapper cacheHit = this.findCachedItem(contexts.get(CacheableOperation.class));
List<CacheAspectSupport.CachePutRequest> cachePutRequests = new LinkedList();
if (cacheHit == null) {
this.collectPutRequests(contexts.get(CacheableOperation.class), CacheOperationExpressionEvaluator.NO_RESULT, cachePutRequests);
}
Object cacheValue;
Object returnValue;
if (cacheHit != null && !this.hasCachePut(contexts)) {
cacheValue = cacheHit.get();
returnValue = this.wrapCacheValue(method, cacheValue);
} else {
returnValue = this.invokeOperation(invoker);
cacheValue = this.unwrapReturnValue(returnValue);
}
this.collectPutRequests(contexts.get(CachePutOperation.class), cacheValue, cachePutRequests);
Iterator var8 = cachePutRequests.iterator();
while(var8.hasNext()) {
CacheAspectSupport.CachePutRequest cachePutRequest = (CacheAspectSupport.CachePutRequest)var8.next();
cachePutRequest.apply(cacheValue);
}
this.processCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue);
return returnValue;
}
}
private void performCacheEvict(CacheAspectSupport.CacheOperationContext context, CacheEvictOperation operation, @Nullable Object result) {
Object key = null;
Iterator var5 = context.getCaches().iterator();
while(var5.hasNext()) {
Cache cache = (Cache)var5.next();
if (operation.isCacheWide()) {
this.logInvalidating(context, operation, (Object)null);
this.doClear(cache);
} else {
if (key == null) {
key = this.generateKey(context, result);
}
this.logInvalidating(context, operation, key);
this.doEvict(cache, key);
}
}
}
}
Key生成策略
package org.springframework.cache.interceptor;
import java.lang.reflect.Method;
public class SimpleKeyGenerator implements KeyGenerator {
public SimpleKeyGenerator() {
}
public Object generate(Object target, Method method, Object... params) {
return generateKey(params);
}
public static Object generateKey(Object... params) {
if (params.length == 0) {
return SimpleKey.EMPTY;
} else {
if (params.length == 1) {
Object param = params[0];
if (param != null && !param.getClass().isArray()) {
return param;
}
}
return new SimpleKey(params);
}
}
}

26. SpringBoot 初识缓存及 SimpleCacheConfiguration源码解析的更多相关文章
- .Net Core缓存组件(Redis)源码解析
上一篇文章已经介绍了MemoryCache,MemoryCache存储的数据类型是Object,也说了Redis支持五中数据类型的存储,但是微软的Redis缓存组件只实现了Hash类型的存储.在分析源 ...
- .Net Core缓存组件(MemoryCache)源码解析
一.介绍 由于CPU从内存中读取数据的速度比从磁盘读取快几个数量级,并且存在内存中,减小了数据库访问的压力,所以缓存几乎每个项目都会用到.一般常用的有MemoryCache.Redis.MemoryC ...
- Springboot集成RabbitMQ之MessageConvert源码解析
问题 最近在使用RabbitMq时遇到了一个问题,明明是转换成json发送到mq中的数据,消费者接收到的却是一串数字也就是byte数组,但是使用mq可视化页面查看数据却是正常的,之前在使用过程中从未遇 ...
- SpringBoot(1.5.6.RELEASE)源码解析
转自 https://www.cnblogs.com/dylan-java/p/7450914.html 启动SpringBoot,需要在入口函数所在的类上添加@SpringBootApplicati ...
- springboot ---> spring ioc 注册流程 源码解析 this.prepareContext 部分
现在都是在springboot 中 集成 spirng,那我们就从springboot 开始. 一:springboot 启动main 函数 public static void main(Strin ...
- springboot自动扫描添加的BeanDefinition源码解析
1. springboot启动过程中,首先会收集需要加载的bean的定义,作为BeanDefinition对象,添加到BeanFactory中去. 由于BeanFactory中只有getBean之类获 ...
- Android 开源项目源码解析(第二期)
Android 开源项目源码解析(第二期) 阅读目录 android-Ultra-Pull-To-Refresh 源码解析 DynamicLoadApk 源码解析 NineOldAnimations ...
- SpringBoot 2.0.3 源码解析
前言 用SpringBoot也有很长一段时间了,一直是底层使用者,没有研究过其到底是怎么运行的,借此机会今天试着将源码读一下,在此记录...我这里使用的SpringBoot 版本是 2.0.3.RE ...
- SpringBoot事件监听机制源码分析(上) SpringBoot源码(九)
SpringBoot中文注释项目Github地址: https://github.com/yuanmabiji/spring-boot-2.1.0.RELEASE 本篇接 SpringApplicat ...
随机推荐
- Codeforces 1079D Barcelonian Distance(计算几何)
题目链接:Barcelonian Distance 题意:给定方格坐标,方格坐标上有两个点A,B和一条直线.规定:直线上沿直线走,否则沿方格走.求A到B的最短距离. 题解:通过直线到达的:A.B两点都 ...
- Eclipse Memory Analyzer(MAT)使用
https://user.qzone.qq.com/731573705/blog/1436389384 Eclipse Memory Analyzer(MAT)使用 一.OutOfMemoryErr ...
- [FJOI2015]火星商店问题(分治+可持久化)
题目描述 火星上的一条商业街里按照商店的编号1,2 ,…,n ,依次排列着n个商店.商店里出售的琳琅满目的商品中,每种商品都用一个非负整数val来标价.每个商店每天都有可能进一些新商品,其标价可能与已 ...
- 单片机pwm控制基本原理详解
前言 PWM是Pulse Width Modulation的缩写,它的中文名字是脉冲宽度调制,一种说法是它利用微处理器的数字输出来对模拟电路进行控制的一种有效的技术,其实就是使用数字信号达到一个模拟信 ...
- 20165223 week3蓝墨云测试总结
1. 表达式0xaa | 0x55的值为 答案: 解析: 0xaa用二进制表示为10101010,0x55用二进制表示为01010101,按位或后为11111111,十进制表示为255,十六进制表示为 ...
- A1014. Waiting in Line
Suppose a bank has N windows open for service. There is a yellow line in front of the windows which ...
- DK location not found. Define location with sdk.dir in the local.properties file or with an ANDROID_HOME
根据提示,我们可以新建一个项目或者以前自己使用过没问题的工程,从中把local.properties文件copy到我们从github中想要导入的工程中,我自己就是这样的,然后这个问题就解决了. ndk ...
- (链表 双指针) leetcode 142. Linked List Cycle II
Given a linked list, return the node where the cycle begins. If there is no cycle, return null. To r ...
- makefile解析:一些常用函数
#======================================================================= #指定目标文件名,makefile中的变量直接使用不用 ...
- Mac iOS Mac Watch 应用和游戏编程开发工具推荐
今日分享「iOS / Mac / Watch 应用和游戏开发工具」推荐,这期专题主要为iOS开发者推荐一些优秀的设计和开发工具,这些工具包含移动原型的设计.程序的开发等,可以大大提高开发的效率!专题会 ...