第七章 Spring cache与异步调用笔记

一、Spring cache缓存

1、缓存配置

1.1、xml文件配置

<!-- 配置文件加载 -->
<bean id="propertyConfigurerForCache" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="order" value="6" />
    <property name="ignoreUnresolvablePlaceholders" value="true" />
    <property name="locations">
        <list>
            <value>classpath:config/redis/redis.properties</value>
            <value>classpath:config/mongodb/mongodb.properties</value>
        </list>
    </property>
</bean>
<!-- 缓存注解开启 -->
<cache:annotation-driven cache-manager="cacheManager"/>
<!-- redis连接池 -->
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
    <property name="maxIdle" value="${redis.maxIdle}" />
    <property name="maxWaitMillis" value="${redis.maxWait}" />
    <property name="testOnBorrow" value="${redis.testOnBorrow}" />
</bean>
<bean id="jedisConnectionFactory"
      class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
    <property name="hostName" value="${redis.host}"></property>
    <property name="port" value="${redis.port}"></property>
    <!-- <property name="password" value="${redis.pass}"></property> -->
    <property name="poolConfig" ref="poolConfig"></property>
</bean>
<!-- redis模板 -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
    <property name="connectionFactory" ref="jedisConnectionFactory" />
</bean>
<!--credentials的配置形式是:用户名:密码@默认数据库-->
<mongo:mongo-client id="mongoClient" host="${mongo.host}" port="${mongo.port}">
    <mongo:client-options write-concern="SAFE" connections-per-host="${mongo.connectionsPerHost}"
threads-allowed-to-block-for-connection-multiplier="${mongo.threadsAllowedToBlockForConnectionMultiplier}"
                          connect-timeout="${mongo.connectTimeout}"
                          max-wait-time="${mongo.maxWaitTime}"
                          socket-timeout="${mongo.socketTimeout}"/>
</mongo:mongo-client>
<mongo:db-factory id="mongoDbFactory" dbname="${mongo.dbname}" mongo-ref="mongoClient"/>
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
    <constructor-arg name="mongoDbFactory" ref="mongoDbFactory"/>
</bean>
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
    <property name="caches">
        <set>
            <!-- 这里可以配置多个redis -->
            <bean class="com.chj.cache.RedisCache">
                <property name="redisTemplate" ref="redisTemplate" />
                <property name="name" value="redisCache"/>
                <!-- name对应的名称要在类或方法的注解中使用 -->
            </bean>
            <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
                <property name="name" value="mapCache"/>
            </bean>
            <bean class="com.chj.cache.MongodbCache">
                <property name="collection" value="mongo_cache"/>
                <property name="name" value="mongoCache"/>
                <property name="mongoTemplate" ref="mongoTemplate"/>
            </bean>
        </set>
    </property>
</bean>

1.2、开启缓存功能:

加上@EnableCaching注解

缓存里面有的元素:cacheManager、cache

@Component
@EnableCaching
public class CacheBean {

    @Bean
    public Cache redisCache(RedisTemplate redisTemplate) {
        RedisCache cache = new RedisCache();
        cache.setName("redisCache");
        cache.setRedisTemplate(redisTemplate);
        return cache;
    }

    @Bean
    public FactoryBean<ConcurrentMapCache> mapCache() {
        ConcurrentMapCacheFactoryBean bean = new ConcurrentMapCacheFactoryBean();
        bean.setName("mapCache");
        return bean;
    }

    @Bean
    public CacheManager simpleCacheManager(@Qualifier("redisCache") Cache redisCache, @Qualifier("mapCache") Cache concurrentMapCacheFactoryBean) {
        SimpleCacheManager simpleCacheManager = new SimpleCacheManager();
        List<Cache> list = new ArrayList<>();
        list.add(redisCache);
        list.add(concurrentMapCacheFactoryBean);
        simpleCacheManager.setCaches(list);
        return simpleCacheManager;
    }
}

首先需要创建缓存管理器:

@Bean
public CacheManager simpleCacheManager(@Qualifier("redisCache") Cache redisCache, @Qualifier("mapCache") Cache concurrentMapCacheFactoryBean) {
    SimpleCacheManager simpleCacheManager = new SimpleCacheManager();
    List<Cache> list = new ArrayList<>();
    list.add(redisCache);
    list.add(concurrentMapCacheFactoryBean);
    simpleCacheManager.setCaches(list);
    return simpleCacheManager;
}

缓存管理器中管理了缓存对象,比如redis缓存,map缓存,mongodb缓存,这些缓存对象都是些了Cache顶层接口。

1.3、缓存注解使用

缓存使用就只需要在业务方法上面加注解就行了

@Service
public class CacheServiceImpl implements CacheService {
    @Autowired
    private CommonMapper commonMapper;
    @Cacheable(cacheNames = "redisCache",key = "'jack' + #id")
    @Override
    public String queryData(String id) {
        System.out.println("======CacheServiceImpl.queryData");
        List<ConsultConfigArea> areas = commonMapper.queryAreaById(id);
        return JSONObject.toJSONString(areas);
    }
    @CachePut(cacheNames = "redisCache",key = "'jack' + #id")
    @Override
    public String putCache(String id) {
        System.out.println("======CacheServiceImpl.queryData");
        List<ConsultConfigArea> areas = commonMapper.queryAreaById(id);
        return JSONObject.toJSONString(areas);
    }
    @Cacheable(cacheNames = "redisCache",key = "'jack' + #id")
    @Override
    public String getCache(String id) {
        return null;
    }
    @Cacheable(cacheNames = "mapCache",key = "'jack' + #id")
    @Override
    public String mapCache(String id) {
        System.out.println("=========CacheServiceImpl.mapCache");
        return "数据存储在map中";
    }
}

@Cacheable是先从缓存拿(查询),如果有则直接返回,如果没有则调用被代理方法拿到返回值然后存到缓存中。

@CachePut只管存(插入),调用到被代理方法后把返回值存到缓存中。

在使用注解是需要指定使用哪一个缓存,因为缓存管理器中可能会有多个缓存,redis缓存,mongodb缓存,map缓存等等,需要用一个name来建立跟这些缓存对象的映射关系。

2、两种缓存配置

2.1、redis配置

@PropertySource("classpath:redis/redis.properties")
@Component
public class RedisConfig {
    @Value("${redis.maxIdle}")
    private Integer maxIdle;
    @Value("${redis.maxWait}")
    private Long maxWaitMillis;
    @Value("${redis.testOnBorrow}")
    private Boolean testOnBorrow;
    @Value("${redis.host}")
    private String hostName;
    @Value("${redis.port}")
    private Integer port;

    @Bean
    public JedisPoolConfig jedisPoolConfig(){
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxIdle(maxIdle);
        jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
        jedisPoolConfig.setTestOnBorrow(testOnBorrow);
        return jedisPoolConfig;
    }
    
    @Bean
    public RedisTemplate<String, String> redisTemplate(JedisConnectionFactory jedisConnectionFactory){
        RedisTemplate template = new RedisTemplate();
        template.setConnectionFactory(jedisConnectionFactory);
        return template;
    }

    private void setSerializer(StringRedisTemplate template){
        @SuppressWarnings({ "rawtypes", "unchecked" })
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        template.setValueSerializer(jackson2JsonRedisSerializer);
    }

    @Bean
    public JedisConnectionFactory jedisConnectionFactory(JedisPoolConfig jedisPoolConfig){
        JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
        jedisConnectionFactory.setHostName(hostName);
        jedisConnectionFactory.setPort(port);
        jedisConnectionFactory.setPoolConfig(jedisPoolConfig);
        return jedisConnectionFactory;
    }
}

2.2、redis缓存方法封装

public class RedisCache implements Cache {
    private RedisTemplate<String, Object> redisTemplate;
    private String name;
    public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public void clear() {
        System.out.println("-------緩存清理------");
        redisTemplate.execute(new RedisCallback<String>() {
            @Override
            public String doInRedis(RedisConnection connection) throws DataAccessException {
                connection.flushDb();
                return "ok";
            }
        });
    }
    @Override
    public void evict(Object key) {
        System.out.println("-------緩存刪除------");
        final String keyf=key.toString();
        redisTemplate.execute(new RedisCallback<Long>() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.del(keyf.getBytes());
            }

        });
    }
    @Override
    public ValueWrapper get(Object key) {
        System.out.println("------缓存获取-------"+key.toString());
        final String keyf = key.toString();
        Object object = null;
        object = redisTemplate.execute(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                byte[] key = keyf.getBytes();
                byte[] value = connection.get(key);
                if (value == null) {
                    System.out.println("------缓存不存在-------");
                    return null;
                }
                return SerializationUtils.deserialize(value);
            }
        });
        ValueWrapper obj=(object != null ? new SimpleValueWrapper(object) : null);
        System.out.println("------获取到内容-------"+(obj != null ? obj.get():""));
        return  obj;
    }
    @Override
    public <T> T get(Object key, Class<T> type) {
        return null;
    }
    @Override
    public void put(Object key, Object value) {
        System.out.println("-------加入缓存------");
        System.out.println("key----:"+key);
        System.out.println("value----:"+value);
        final String keyString = key.toString();
        final Object valuef = value;
        final long liveTime = 86400;
        redisTemplate.execute(new RedisCallback<Long>() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                byte[] keyb = keyString.getBytes();
                byte[] valueb = SerializationUtils.serialize((Serializable) valuef);
                connection.set(keyb, valueb);
                if (liveTime > 0) {
                    connection.expire(keyb, liveTime);
                }
                return 1L;
            }
        });
    }
    @Override
    public <T> T get(Object key, Callable<T> valueLoader) {
        return null;
    }
    @Override
    public String getName() {
        return this.name;
    }
    @Override
    public Object getNativeCache() {
        return this.redisTemplate;
    }
    @Override
    public ValueWrapper putIfAbsent(Object arg0, Object arg1) {
        return null;
    }
    public RedisTemplate<String, Object> getRedisTemplate() {
        return redisTemplate;
    }
}

2.3、MongodbCache配置

public class MongodbCacheBean {
    private byte[] id;
    private byte[] value;
    public byte[] getId() {
        return this.id;
    }
    public void setId(byte[] id) {
        this.id = id;
    }
    public byte[] getValue() {
        return this.value;
    }
    public void setValue(byte[] value) {
        this.value = value;
    }
}

2.4、分装MongodbCache增删改查方法:

com.chj.cache.MongodbCache

public class MongodbCache implements Cache {
    private String name;
    private MongoTemplate mongoTemplate;
    private String collection;
    public void setCollection(String collection) {
        this.collection = collection;
    }
    public void setName(String name) {
        this.name = name;
    }
    public void setMongoTemplate(MongoTemplate mongoTemplate) {
        this.mongoTemplate = mongoTemplate;
    }
    @Override
    public String getName() {
        return this.name;
    }
    @Override
    public Object getNativeCache() {
        return this.mongoTemplate;
    }
    @Override
    public ValueWrapper get(Object key) {
        System.out.println("------缓存获取mongodb-------"+key.toString());
        Object value = null;
        String keyf = key.toString();
        byte[] keyb = keyf.getBytes();
        MongodbCacheBean bean = mongoTemplate.findOne(new Query(Criteria.where("id").is(keyb)),
                MongodbCacheBean.class,
                collection);
        if(bean != null) {
            System.out.println("------获取到数据为:" + bean.getId() + ":" + bean.getValue() + "---------");
            value = SerializationUtils.deserialize(bean.getValue());
        }
        return value != null ? new SimpleValueWrapper(value) : null;
    }
    @Override
    public <T> T get(Object key, Class<T> type) {
        return null;
    }
    @Override
    public <T> T get(Object key, Callable<T> valueLoader) {
        return null;
    }
    @Override
    public void put(Object key, Object value) {
        System.out.println("-------加入缓存mongodb------");
        System.out.println("--------key----:"+key);
        System.out.println("--------value----:"+value);
        String keyString  = key.toString();
        byte[] keyb = keyString.getBytes();
        byte[] valueb = SerializationUtils.serialize((Serializable) value);
        MongodbCacheBean bean = new MongodbCacheBean();
        bean.setId(keyb);
        bean.setValue(valueb);
        mongoTemplate.insert(bean,collection);
    }
    @Override
    public ValueWrapper putIfAbsent(Object key, Object value) {
        return null;
    }
    @Override
    public void evict(Object key) {
        mongoTemplate.remove(new Query(Criteria.where("id").is(key)),collection);
    }
    @Override
    public void clear() {
        mongoTemplate.remove(new Query(),collection);
    }
}

3、缓存源码分析

3.1、配置入口分析

进入组合注解@EnableCaching类如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(CachingConfigurationSelector.class)
public @interface EnableCaching {
   boolean proxyTargetClass() default false;
   AdviceMode mode() default AdviceMode.PROXY;
   int order() default Ordered.LOWEST_PRECEDENCE;
}

org.springframework.cache.annotation.CachingConfigurationSelector

public class CachingConfigurationSelector extends AdviceModeImportSelector<EnableCaching> {
   private static final String PROXY_JCACHE_CONFIGURATION_CLASS =
         "org.springframework.cache.jcache.config.ProxyJCacheConfiguration";
   private static final String CACHE_ASPECT_CONFIGURATION_CLASS_NAME =
         "org.springframework.cache.aspectj.AspectJCachingConfiguration";
   private static final String JCACHE_ASPECT_CONFIGURATION_CLASS_NAME =
         "org.springframework.cache.aspectj.AspectJJCacheConfiguration";
   private static final boolean jsr107Present;
   private static final boolean jcacheImplPresent;
   static {
      ClassLoader classLoader = CachingConfigurationSelector.class.getClassLoader();
      jsr107Present = ClassUtils.isPresent("javax.cache.Cache", classLoader);
      jcacheImplPresent = ClassUtils.isPresent(PROXY_JCACHE_CONFIGURATION_CLASS, classLoader);
   }
   @Override
   public String[] selectImports(AdviceMode adviceMode) {
      switch (adviceMode) {
         case PROXY:
            return getProxyImports();
         case ASPECTJ:
            return getAspectJImports();
         default:
            return null;
      }
   }

org.springframework.cache.annotation.CachingConfigurationSelector#getProxyImports

private String[] getProxyImports() {
   List<String> result = new ArrayList<>(3);
   result.add(AutoProxyRegistrar.class.getName());
   result.add(ProxyCachingConfiguration.class.getName());
   if (jsr107Present && jcacheImplPresent) {
      result.add(PROXY_JCACHE_CONFIGURATION_CLASS);
   }
   return StringUtils.toStringArray(result);
}

从这里开始,主要分析AutoProxyRegistrar与ProxyCachingConfiguration两个配置类

把AOP入口类封装成beanDefinition对象,要实例化

org.springframework.context.annotation.AutoProxyRegistrar#registerBeanDefinitions

@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
   boolean candidateFound = false;
   Set<String> annoTypes = importingClassMetadata.getAnnotationTypes();
   for (String annoType : annoTypes) {
      AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annoType);
      if (candidate == null) {
         continue;
      }
      Object mode = candidate.get("mode");
      Object proxyTargetClass = candidate.get("proxyTargetClass");
      if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() &&
            Boolean.class == proxyTargetClass.getClass()) {
         candidateFound = true;
         if (mode == AdviceMode.PROXY) {
            //注册事务AOP的入口类InfrastructureAdvisorAutoProxyCreator,实际上这个AOP入口类起不了作用
            AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
            if ((Boolean) proxyTargetClass) {
               AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
               return;
            }
         }
      }
   }

org.springframework.aop.config.AopConfigUtils#registerOrEscalateApcAsRequired

@Nullable
private static BeanDefinition registerOrEscalateApcAsRequired(
      Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
   Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
   if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
      BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
      if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
         int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
         int requiredPriority = findPriorityForClass(cls);
         if (currentPriority < requiredPriority) {
            apcDefinition.setBeanClassName(cls.getName());
         }
      }
      return null;
   }
   //把AOP入口类封装成beanDefinition对象,要实例化
   RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
   beanDefinition.setSource(source);
   beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
   beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
   //注解aop入口类的beanName名称 AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME
   registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
   return beanDefinition;
}

3.2、缓存代理配置ProxyCachingConfiguration

@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyCachingConfiguration extends AbstractCachingConfiguration {
   @Bean(name = CacheManagementConfigUtils.CACHE_ADVISOR_BEAN_NAME)
   @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
   public BeanFactoryCacheOperationSourceAdvisor cacheAdvisor() {
      BeanFactoryCacheOperationSourceAdvisor advisor = new BeanFactoryCacheOperationSourceAdvisor();
      advisor.setCacheOperationSource(cacheOperationSource());
      advisor.setAdvice(cacheInterceptor());
      if (this.enableCaching != null) {
         advisor.setOrder(this.enableCaching.<Integer>getNumber("order"));
      }
      return advisor;
   }
   @Bean
   @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
   public CacheOperationSource cacheOperationSource() {
      return new AnnotationCacheOperationSource();
   }
   @Bean
   @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
   public CacheInterceptor cacheInterceptor() {
      CacheInterceptor interceptor = new CacheInterceptor();
      interceptor.configure(this.errorHandler, this.keyGenerator, this.cacheResolver, this.cacheManager);
      interceptor.setCacheOperationSource(cacheOperationSource());
      return interceptor;
   }
}

org.springframework.cache.interceptor.CacheInterceptor

public class CacheInterceptor extends CacheAspectSupport implements MethodInterceptor, Serializable {
   @Override
   @Nullable
   public Object invoke(final MethodInvocation invocation) throws Throwable {
      Method method = invocation.getMethod();
      CacheOperationInvoker aopAllianceInvoker = () -> {
         try {
            return invocation.proceed();
         } catch (Throwable ex) {
            throw new CacheOperationInvoker.ThrowableWrapper(ex);
         }
      };
      try {
         return execute(aopAllianceInvoker, invocation.getThis(), method, invocation.getArguments());
      } catch (CacheOperationInvoker.ThrowableWrapper th) {
         throw th.getOriginal();
      }
   }
}

至此开始,源码的处理逻辑与事务处理类似,不再详述,主要分为两部分,第一解析配置分装到RootBeanDefinition对象中;第二通过spring aop增强类实现缓存的切面功能。

方法invocation.proceed();进入advise增强链的调用中;execute()方法执行具体缓存增、删、改、查等具体操作。

3.3、execute()方法分析

org.springframework.cache.interceptor.CacheAspectSupport#execute(CacheOperationInvoker,Method, CacheOperationContexts)

@Nullable
private Object execute(final CacheOperationInvoker invoker, Method method, CacheOperationContexts contexts) {
   // Special handling of synchronized invocation
   if (contexts.isSynchronized()) {
      CacheOperationContext context = contexts.get(CacheableOperation.class).iterator().next();
      if (isConditionPassing(context, CacheOperationExpressionEvaluator.NO_RESULT)) {
         Object key = generateKey(context, CacheOperationExpressionEvaluator.NO_RESULT);
         Cache cache = context.getCaches().iterator().next();
         try {
            return wrapCacheValue(method, cache.get(key, () -> unwrapReturnValue(invokeOperation(invoker))));
         }catch (Cache.ValueRetrievalException ex) {
            // The invoker wraps any Throwable in a ThrowableWrapper instance so we
            // can just make sure that one bubbles up the stack.
            throw (CacheOperationInvoker.ThrowableWrapper) ex.getCause();
         }
      } else {
         // No caching required, only call the underlying method
         return invokeOperation(invoker);
      }
   }
   // Process any early evictions
   processCacheEvicts(contexts.get(CacheEvictOperation.class), true,
         CacheOperationExpressionEvaluator.NO_RESULT);
   // Check if we have a cached item matching the conditions
   Cache.ValueWrapper cacheHit = findCachedItem(contexts.get(CacheableOperation.class));
   // Collect puts from any @Cacheable miss, if no cached item is found
   List<CachePutRequest> cachePutRequests = new LinkedList<>();
   if (cacheHit == null) {
      collectPutRequests(contexts.get(CacheableOperation.class),
            CacheOperationExpressionEvaluator.NO_RESULT, cachePutRequests);
   }
   Object cacheValue;
   Object returnValue;
   if (cacheHit != null && !hasCachePut(contexts)) {
      // If there are no put requests, just use the cache hit
      cacheValue = cacheHit.get();
      returnValue = wrapCacheValue(method, cacheValue);
   }else {
      // Invoke the method if we don't have a cache hit
      returnValue = invokeOperation(invoker);
      cacheValue = unwrapReturnValue(returnValue);
   }
   // Collect any explicit @CachePuts
   collectPutRequests(contexts.get(CachePutOperation.class), cacheValue, cachePutRequests);
   // Process any collected put requests, either from @CachePut or a @Cacheable miss
   for (CachePutRequest cachePutRequest : cachePutRequests) {
      cachePutRequest.apply(cacheValue);
   }
   // Process any late evictions
   processCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue);
   return returnValue;
}

3.4、查找缓存方法分析

org.springframework.cache.interceptor.CacheAspectSupport#findCachedItem

org.springframework.cache.interceptor.CacheAspectSupport#findInCaches

@Nullable
private Cache.ValueWrapper findInCaches(CacheOperationContext context, Object key) {
   for (Cache cache : context.getCaches()) {
      Cache.ValueWrapper wrapper = doGet(cache, key);
      if (wrapper != null) {
         if (logger.isTraceEnabled()) {
            logger.trace("Cache entry for key '" + key + "' found in cache '" + cache.getName() + "'");
         }
         return wrapper;
      }
   }
   return null;
}

org.springframework.cache.interceptor.AbstractCacheInvoker#doGet

@Nullable
protected Cache.ValueWrapper doGet(Cache cache, Object key) {
   try {
      return cache.get(key);
   }catch (RuntimeException ex) {
      getErrorHandler().handleCacheGetError(ex, cache, key);
      return null;  // If the exception is handled, return a cache miss
   }
}

3.5、方法回调

此时cache调用的方法是org.springframework.cache.Cache接口中定义的方法,比如我们自定义实现了redis和MongoDB的缓存方法。

 

com.chj.cache.RedisCache#get(java.lang.Object)

@Override
public ValueWrapper get(Object key) {
    System.out.println("------缓存获取-------"+key.toString());
    final String keyf = key.toString();
    Object object = null;
    object = redisTemplate.execute(new RedisCallback<Object>() {
        @Override
        public Object doInRedis(RedisConnection connection) throws DataAccessException {
            byte[] key = keyf.getBytes();
            byte[] value = connection.get(key);
            if (value == null) {
                System.out.println("------缓存不存在-------");
                return null;
            }
            return SerializationUtils.deserialize(value);
        }
    });
    ValueWrapper obj=(object != null ? new SimpleValueWrapper(object) : null);
    System.out.println("------获取到内容-------"+(obj != null ? obj.get():""));
    return  obj;
}

二、异步调用@Async

1、@EnableAsync开启异步:

@Component
@EnableAsync
public class EnableAsyncBean {
    private int corePoolSize = 10;
    private int maxPoolSize = 200;
    private int queueCapacity = 10;
    private String ThreadNamePrefix = "JackExecutor-";
    @Bean
    public Executor executor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(maxPoolSize);
        executor.setQueueCapacity(queueCapacity);
        executor.setThreadNamePrefix(ThreadNamePrefix);
        // rejection-policy:当pool已经达到max size的时候,如何处理新任务
        // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }
}

异步没什么好讲的,注解在业务方法上面加上异步注解即可

@Async
@Transactional
TargetSource("ds1")
@Override
public String queryAccount(String id) {
    System.out.println("==========AccountServiceImpl.queryAccount");
    return "==========AccountServiceImpl.queryAccount";
}

这里需要注意一下,如果采用异步,那么事务传播属性就会实现,从上一个事务中是拿不到绑定的连接对象的,也就是说是一个新的事务了。

2、异步注解分析

org.springframework.scheduling.annotation.EnableAsync

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {
   Class<? extends Annotation> annotation() default Annotation.class;
   boolean proxyTargetClass() default false;
   AdviceMode mode() default AdviceMode.PROXY;
   int order() default Ordered.LOWEST_PRECEDENCE;
}

2.1、配置选择类分析

org.springframework.scheduling.annotation.AsyncConfigurationSelector

public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {
   private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME =
         "org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";
   @Override
   @Nullable
   public String[] selectImports(AdviceMode adviceMode) {
      switch (adviceMode) {
         case PROXY:
            return new String[] {ProxyAsyncConfiguration.class.getName()};
         case ASPECTJ:
            return new String[] {ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME};
         default:
            return null;
      }
   }
}

2.2、异步代理配置

org.springframework.scheduling.annotation.ProxyAsyncConfiguration

@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration {
   @Bean(name = TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME)
   @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
   public AsyncAnnotationBeanPostProcessor asyncAdvisor() {
      Assert.notNull(this.enableAsync, "@EnableAsync annotation metadata was not injected");
      AsyncAnnotationBeanPostProcessor bpp = new AsyncAnnotationBeanPostProcessor();
      bpp.configure(this.executor, this.exceptionHandler);
      Class<? extends Annotation> customAsyncAnnotation = this.enableAsync.getClass("annotation");
      if (customAsyncAnnotation != AnnotationUtils.getDefaultValue(EnableAsync.class, "annotation")) {
         bpp.setAsyncAnnotationType(customAsyncAnnotation);
      }
      bpp.setProxyTargetClass(this.enableAsync.getBoolean("proxyTargetClass"));
      bpp.setOrder(this.enableAsync.<Integer>getNumber("order"));
      return bpp;
   }
}

发布了85 篇原创文章 · 获赞 7 · 访问量 6030
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览