Spring 编程常见问题之三(专栏学习笔记)

spring 扩展中间件列表

Spring Data Commons
Spring Data JPA
Spring Data KeyValue
Spring Data LDAP
Spring Data MongoDB
Spring Data Redis
Spring Data REST
Spring Data for Apache Cassandra
Spring Data for Apache Geode
Spring Data for Apache Solr
Spring Data for Pivotal GemFire
Spring Data Couchbase (community module)
Spring Data Elasticsearch (community module)
Spring Data Neo4j (community module)

Spring Data Redis

RedisTemplate、 stringRedisTemplate 读 写不能混用,混用无法读到别人的数据

原因:
RedisTemplate 使用的是jdk序列化
stringRedisTemplate 使用的 StringRedisSerializer

public class StringRedisSerializer implements RedisSerializer<String> {
   private final Charset charset;
   @Override
   public byte[] serialize(@Nullable String string) {
      return (string == null ? null : string.getBytes(charset));
   }
}
public class JdkSerializationRedisSerializer implements RedisSerializer<Object> {  
   @Override
   public byte[] serialize(@Nullable Object object) {
      if (object == null) {
         return SerializationUtils.EMPTY_ARRAY;
      }
      try {
         return serializer.convert(object);
      } catch (Exception ex) {
         throw new SerializationException("Cannot serialize", ex);
      }
   }
}

public interface Serializer<T> {
    void serialize(T var1, OutputStream var2) throws IOException;

    default byte[] serializeToByteArray(T object) throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
        this.serialize(object, out);
        return out.toByteArray();
    }
}

spring 事务

Spring Data 事务管理包

Spring 事务管理包含两种配置方式

  1. 第一种是使用 XML 进行模糊匹配,绑定事务管理;
  2. 第二种是使用注解,这种方式可以对每个需要进行事务处理的方法进行单独配置,你只需要添加上 @Transactional,然后在注解内添加属性配置即可。

  1. Spring 支持声明式事务机制,它通过在方法上加上 @Transactional,表明该方法需要事务支持。于是,在加载的时候,根据 @Transactional 中的属性,决定对该事务采取什么样的策略;
  2. @Transactional 对 private 方法不生效,所以我们应该把需要支持事务的方法声明为 public 类型;
  3. Spring 处理事务的时候,默认只对 RuntimeException 和 Error 回滚,不会对 Exception 回滚,如果有特殊需要,需要额外声明,例如指明 Transactional 的属性 rollbackFor 为 Exception.class。

事务处理

TransactionAspectSupport.invokeWithinTransaction():


protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
      final InvocationCallback invocation) throws Throwable {
 
   TransactionAttributeSource tas = getTransactionAttributeSource();
   final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
   final PlatformTransactionManager tm = determineTransactionManager(txAttr);
   final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
   if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
      // 是否需要创建一个事务
      TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
      Object retVal = null;
      try {
         // 调用具体的业务方法
         retVal = invocation.proceedWithInvocation();
      }
      catch (Throwable ex) {
         // 当发生异常时进行处理
         completeTransactionAfterThrowing(txInfo, ex);
         throw ex;
      }
      finally {
         cleanupTransactionInfo(txInfo);
      }
      // 正常返回时提交事务
      commitTransactionAfterReturning(txInfo);
      return retVal;
   }
   //......省略非关键代码.....
}
  1. 检查是否需要创建事务;
  2. 调用具体的业务方法进行处理;
  3. 提交事务;
  4. 处理异常。

spring rest template 使用post提交表单,参数应该为MultiValueMap 而非HashMap

错误示例,content 为application/json


RestTemplate template = new RestTemplate();
Map<String, Object> paramMap = new HashMap<String, Object>();
paramMap.put("para1", "001");
paramMap.put("para2", "002");

String url = "http://localhost:8080/hi";
String result = template.postForObject(url, paramMap, String.class);
System.out.println(result);

正确示例,content 为application/x-www-form-urlencoded;

//错误:
//Map<String, Object> paramMap = new HashMap<String, Object>();
//paramMap.put("para1", "001");
//paramMap.put("para2", "002");

//修正代码:
MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<String, Object>();
paramMap.add("para1", "001");
paramMap.add("para2", "002");

原因:
根据当前要提交的 Body 内容,遍历当前支持的所有编解码器,如果找到合适的编解码器,就使用它来完成 Body 的转化。

  1. 先遍历了AbstractJackson2HttpMessageConverter#canWrite: 判断HashMap是能够json序列化的就走了json序列化方式作为body
  2. 如果使用MultiValueMap,则会走表单消息的转化器,FormHttpMessageConverter#canWrite:
public boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType) {
   if (!MultiValueMap.class.isAssignableFrom(clazz)) {
      return false;
   }
   if (mediaType == null || MediaType.ALL.equals(mediaType)) {
      return true;
   }
   for (MediaType supportedMediaType : getSupportedMediaTypes()) {
      if (supportedMediaType.isCompatibleWith(mediaType)) {
         return true;
      }
   }
   return false;
}

解决get请求中,查询参数含特殊字符的问题

RestTemplate restTemplate = new RestTemplate();
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl("http://localhost:8080/hi");
builder.queryParam("para1", "开发测试 001");
URI url = builder.encode().build().toUri();
ResponseEntity<String> forEntity = restTemplate.getForEntity(url, String.class);
System.out.println(forEntity.getBody());

spring boot 的基础加载路径

classpath:META-INF/resources
classpath:resources
classpath:static
classpath:public
src/main/webapp
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值