1. 程式人生 > 實用技巧 >springboot自定義配置覆蓋原來預設的配置的原理以rabbitMQ為例子(比如更換預設的序列化器--使物件成為JSON形式)

springboot自定義配置覆蓋原來預設的配置的原理以rabbitMQ為例子(比如更換預設的序列化器--使物件成為JSON形式)

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() + ']';
            }
        };
    }
}