13. SpringBoot Configuration class @ Configuration

Posted by latinofever on Wed, 19 Jan 2022 04:43:31 +0100

01. General

Configuration class: the class marked by @ configuration or @ SpringBootConfiguration in springboot is called configuration class.

02. Function & purpose

Many @ Bean methods can be defined in the configuration class, and these @ beans can be decorated to load the spring framework into the ioc container.

03. Why does the configuration exist

  • It is convenient for you to override the underlying configuration class
  • A mechanism that allows you to extend bean s.

04. What are the loaded bean s in a springboot project?

  • Beans developed by programmers themselves, such as classes with @ Service, @ Mapper, @ Controller, @ RestController, @ Component, @ Repository, will be loaded into the ioc container.

    If a class is annotated, can it be loaded into the ioc container?

    No, you need @ ComponentScan to scan the package. The springboot annotation is just a composite annotation, which contains the @ ComponentScan annotation. Then the springboot startup class will scan the package and add all these annotated bean s to the ioc container

  • The configuration class + @ Bean provided by Starter will also be loaded into the ioc container.

05. Think about why there are configuration classes?

  • It is actually a mechanism for additional extension and loading bean s.
  • It is convenient for additional extensions and references to third-party bean s. If there is no such mechanism, all middleware, such as mybatis,redis, jpa and kafka, must be initialized and put into the ioc container by themselves.
  • In order to liberate program developers, springboot provides a starter mechanism, and the principles of these starter mechanisms are all: configuration class + @ Bean
  • If you can't meet the official requirements, of course, you can take the defined configuration class and @ Bean method and load them into the ioc container for additional extension.

06. Define a configuration class

1: Prepare an interface:

package com.kuangstudy.service;


public interface IUserService {
    
    public void sayHello();
}

2: Prepare an implementation class

package com.kuangstudy.service;

import org.springframework.stereotype.Service;


public class UserServiceImpl implements IUserService {

    @Override
    public void sayHello() {
        System.out.println("nihao");
    }
}

3: Define the configuration for the bean and initialize it

package com.kuangstudy.config;

import com.kuangstudy.service.IUserService;
import com.kuangstudy.service.UserServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
public class UserConfiguration {

    @Bean
    public IUserService getUserService() {
        return new UserServiceImpl();
    }

}

  • Note 1: @ Bean must be defined in the Configuration class with @ Configuration or spring annotations (@ Service, @ Mapper, @ Controller, @ RestController, @ Component, @ Repository). It will not be valid without @ Bean.

  • Note 2: in case of packet scanning (@ Mapper@Service Etc.): the default is to map the class name as a lowercase key. If it is @ Bean, use the method name (XXXXXXXXXXX below) as the key for container mapping. If you are in @ Bean("oxksxjxjxxhxxjxhxhxjxhxj"), then map with the name you specify.

  • Note 3: Although @ Bean can be combined with other annotations of spring and loaded into ioc container, it is recommended to use @ Configuration, which is more explicit. Because they are golden partners.

    05. Condition notes

By default, assuming there is no conditional annotation, what happens? Because if the Bean specifies @ Configuration and @ Bean, they must be loaded into the ioc container

Think about a problem: the starter mechanism of springboot provides 100 ~ 200 configuration classes. We can't let the ioc container load so many configuration classes, because redis and mybatis may be used in the project. The other 100 should not be loaded. Therefore, conditional annotations are specially used for filtering. Load if the configuration is satisfied, and ignore if it is not satisfied.

/*
 * Copyright 2012-2021 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.boot.autoconfigure.data.redis;

import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;

/**
 * {@link EnableAutoConfiguration Auto-configuration} for Spring Data's Redis support.
 *
 * @author Dave Syer
 * @author Andy Wilkinson
 * @author Christian Dupuis
 * @author Christoph Strobl
 * @author Phillip Webb
 * @author EddĂș MelĂ©ndez
 * @author Stephane Nicoll
 * @author Marco Aust
 * @author Mark Paluch
 * @since 1.0.0
 */
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {

	@Bean
	@ConditionalOnMissingBean(name = "redisTemplate")
	@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
	public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
		RedisTemplate<Object, Object> template = new RedisTemplate<>();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

	@Bean
	@ConditionalOnMissingBean
	@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
	public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
		return new StringRedisTemplate(redisConnectionFactory);
	}

}

07. What about the same type?

package com.kuangstudy.config;

import com.kuangstudy.service.IUserService;
import com.kuangstudy.service.UserServiceImpl;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;


@Configuration
public class UserConfiguration {


    @Bean
    public IUserService getUserService1() {
        System.out.println("2222222222222222222222222222222");
        return new UserServiceImpl();
    }

    @Bean
    public IUserService getUserService2() {
        System.out.println("11111111111111111111111111");
        return new UserServiceImpl();
    }

}

The above code: an interface has two subclasses. If it is initialized, an error will be reported:

Solution 1: use @ Qualifier

@Autowired
@Qualifier("getUserService1")
private IUserService userService;

Solution 2: name matching

@Autowired
private IUserService getUserService1;
@Autowired
private IUserService getUserService2;

Solution 3: @ Primary

package com.kuangstudy.config;

import com.kuangstudy.service.IUserService;
import com.kuangstudy.service.UserServiceImpl;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;

@Configuration
public class UserConfiguration {


    @Bean
    @Primary
    public IUserService getUserService1() {
        System.out.println("2222222222222222222222222222222");
        return new UserServiceImpl();
    }

    @Bean
    public IUserService getUserService2() {
        System.out.println("11111111111111111111111111");
        return new UserServiceImpl();
    }

}

08. What about bean s with the same name?

spring:
  main:
    allow-bean-definition-overriding: true

The above configuration tells that if a bean with the same name appears in the project, the key of the container map is allowed to be overwritten when injection is required. The default value is false. Error reporting is not allowed. Do not open the project because the same name is not allowed. You should have repeatedly initialized your project. You should delete one or change the name, which is the correct opening method. Otherwise, you will cause misunderstandings and unreasonable problems in your project.

09. Summary

  • Why are springboot starter s developed with configuration classes?
  • Why not scan package + @ Service, @ Mapper, @ Controller, @ RestController, @ Component, @ Repository?

Because the package name of the component redis JPA cannot be unified, redis may be called: com redis. XXX for example: JPA may be: org jpa. XXX, it's definitely not advisable for you to sweep the bag. Therefore, we must find a mechanism that can load ioc containers without configuration and modification, of course: @ Configuration+@Bean. Their combination can be separated from the mechanism of package scanning + annotation, and can be changed flexibly and at will. This is why configuration classes are a mechanism for you to extend, define, and load other people's beans. Reflection: if there is no such mechanism, how do you think these third-party beans can be loaded into ioc for our project?

The configuration class + @ bean is a mechanism to load beans into the ioc container.

Topics: Java Spring Spring Boot