springboot自定義配置覆蓋原來預設的配置的原理以rabbitMQ為例子(比如更換預設的序列化器--使物件成為JSON形式)
阿新 • • 發佈:2020-08-19
springboot自定義配置覆蓋原來預設的配置的原理(即定製自己想要的功能,比如更換預設的序列化器--使物件成為JSON形式)
1.通過更換rabbitMQ的序列化形式作為例子
@Configuration public class RabbitConfig { /** * 更換rabbitMQ預設的MessageConverter,因為MessageConverter是為了實現序列化的,現在轉成JSON資料 * 自定義的配置類會後於預設的配置類起作用的,springboot的原理就是後加載的配置類返回相同的bean會覆蓋先前注入容器的 * 原理是: * 在預設的配置中是 private MessageConverter messageConverter = new SimpleMessageConverter(); * 此時 messageConverter 使用的是 SimpleMessageConverter()物件裡的 fromMessage(Message message)進行預設的序列化,所以物件到rabbitmq佇列中就是序列化的形式 * 在本方法 * public MessageConverter messageConverter(){ * return new Jackson2JsonMessageConverter(); * } * 中,返回了new Jackson2JsonMessageConverter(); 出去。而new Jackson2JsonMessageConverter();是間接繼承了MessageConverter,此時是自定義配置類 * 自定義配置類會覆蓋先前容器中相同型別的bean,也就是MessageConverter messageConverter = new SimpleMessageConverter(); * 在這個方法執行完之後,messageConverter不再是new SimpleMessageConverter(); 而是 messageConverter = new Jackson2JsonMessageConverter(); * 使用的就是Jackson2JsonMessageConverter();中的fromMessage(Message message)方法,這個方法的作用就是將物件序列化成JSON資料 * * 以上就是自定義的配置類的原理,主要思想是將自定義的(預設配置有不是想要的功能的但是同類型的)bean注入到容器,覆蓋掉預設的容器中的bean(這個bean是我們想要的功能的) * 這樣就能達到想要的效果,主要是要準確找到我們不需要的,或者說想更換的功能的bean是哪個,然後自己寫一個自定義同類型的bean放到容器,覆蓋掉原來的 *@return */ @Bean public MessageConverter messageConverter(){ return new Jackson2JsonMessageConverter(); } }
找到自己需要更換功能的bean如下:
然後一步步找到rabbitTemplate中預設的序列化器
private MessageConverter messageConverter = new SimpleMessageConverter();
public class RabbitTemplate extends RabbitAccessor //NOSONAR type line count implements BeanFactoryAware, RabbitOperations, MessageListener, ListenerContainerAware, PublisherCallbackChannel.Listener, BeanNameAware, DisposableBean { private MessageConverter messageConverter = new SimpleMessageConverter();
是SimpleMessageConverter();類中的fromMessage(Message message) 方法;
此時思路就清晰了:
1.找到能夠同樣返回MessageConverter 這個序列化器 (訊息轉換器)的bean(springboot一般都會自帶有,看哪些類繼承或者實現了MessageConverter 並且功能是自己想要的)
2.編寫自定義配置類,然後放到容器進而覆蓋預設的bean,也就是
MessageConverter messageConverter = new SimpleMessageConverter();
使messageConverter (物件名,型別是MessageConverter)
等於我們自定義的,是想要的功能的那個bean,也就是上面那個RabbitConfig 配置類
@Bean public MessageConverter messageConverter(){ return new Jackson2JsonMessageConverter(); }
方法執行後 messageConverter就會是:messageConverter= new Jackson2JsonMessageConverter();
從而實現功能的轉換(自定義功能)
public class SimpleMessageConverter extends WhiteListDeserializingMessageConverter implements BeanClassLoaderAware { /** * Converts from a AMQP Message to an Object. */ @Override public Object fromMessage(Message message) throws MessageConversionException { Object content = null; MessageProperties properties = message.getMessageProperties(); if (properties != null) { String contentType = properties.getContentType(); if (contentType != null && contentType.startsWith("text")) { String encoding = properties.getContentEncoding(); if (encoding == null) { encoding = this.defaultCharset; } try { content = new String(message.getBody(), encoding); } catch (UnsupportedEncodingException e) { throw new MessageConversionException( "failed to convert text-based Message content", e); } } else if (contentType != null && contentType.equals(MessageProperties.CONTENT_TYPE_SERIALIZED_OBJECT)) { try { content = SerializationUtils.deserialize( createObjectInputStream(new ByteArrayInputStream(message.getBody()), this.codebaseUrl)); } catch (IOException | IllegalArgumentException | IllegalStateException e) { throw new MessageConversionException( "failed to convert serialized Message content", e); } } } if (content == null) { content = message.getBody(); } return content; }
以下是Redis更換序列化形式,使物件成為JSON的例子
package com.springboot.cache01.config; import org.springframework.cache.CacheManager; import org.springframework.cache.interceptor.KeyGenerator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; 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.lang.reflect.Method; import java.net.UnknownHostException; import java.time.Duration; import java.util.Arrays; /** * @author dominick * @2020/8/17 - 15:33 * @function: */ @Configuration public class RedisConfig { @Bean public RedisTemplate<Object,?> myRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { RedisTemplate<Object, ?> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); Jackson2JsonRedisSerializer<?> serializer = new Jackson2JsonRedisSerializer(Object.class); template.setDefaultSerializer(serializer); return template; } /** * 這些配置為什麼會覆蓋掉自動配置類? * 原因就是這定義配置會 後於 自動(預設)配置進行載入 * 後加載的配置檔案的 bean,覆蓋先載入的配置檔案的bean。DefaultListableBeanFactory類中, * @return */ @Bean public CacheManager cacheManager(RedisConnectionFactory factory){ RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofDays(1)) .disableCachingNullValues() .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())); return RedisCacheManager.builder(factory).cacheDefaults(cacheConfiguration).build();} /** * 在同一個配置檔案中,不能存在id相同的兩個bean,否則會報錯。 * 但是在兩個不同的spring配置檔案中(也就是兩個不同的配置類有@Configuration註解),可以存在id相同的兩個bean,啟動時,不會報錯。 * 這是因為spring ioc容器在載入bean的過程中,類DefaultListableBeanFactory會對id相同的bean進行處理: * 後加載的配置檔案的 bean,覆蓋先載入的配置檔案的bean。DefaultListableBeanFactory類中, * 有個屬性 allowBeanDefinitionOverriding,預設值為true,該值就是用來指定出現兩個bean的id相同的情況下, * 如何進行處理。 如果該值為false,則不會進行覆蓋,而是丟擲異常。 * @return */ // @Bean(name = "myKeyGenerator01") // public KeyGenerator keyGenerator(){ // return new KeyGenerator() { // @Override // public Object generate(Object target, Method method, Object... params) { // return method.getName() + '[' + Arrays.asList(params).toString() + ']'; // } // }; // } }
以下是更換redis的keyGenerator (自己定義的key生成策略)
/** * @author dominick * @2020/8/12 - 18:27 * @function: */ @Configuration public class MyKeyGenerator { /** * 自己定義的key生成策略 * @return 返回的是一個自定義的 KeyGenerator */ @Bean(name = "myKeyGenerator01") public KeyGenerator keyGenerator(){ return new KeyGenerator() { @Override public Object generate(Object target, Method method, Object... params) { return method.getName() + '[' + Arrays.asList(params).toString() + ']'; } }; } }