一、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;
}
}