1、前言

  1. @Configuration是Spring的注解,不是SpringBoot的!早在Spring框架的时候就有使用,但是由于那个时候配置文件还是比较流行,因此@Configuration注解并没有太盛行,甚至很多人就认为它是SpringBoot的注解。

  2. @Configuration注解的作用:声明一个类为配置类,用于取代bean.xml配置文件注册bean对象。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {

	@AliasFor(annotation = Component.class)
	String value() default "";

	boolean proxyBeanMethods() default true;
}
  • 底层代码就两个属性,一个用于声明配置类的名称,一个用于声明是否是代理对象方法(重点)。

  • 由于有@Component注解的加持,那么被声明的配置类本身也是一个组件!



2、基础使用

@Configuration注解常常一起搭配使用的注解有@Bean、@Scope、@Lazy三个比较常见:

  • @Bean:等价于Spring中的bean标签用于注册bean对象的,内部有一些初始化、销毁的属性…

  • @Scope:用于声明该bean的作用域,作用域有singleton、prototype、request、session。

  • @Lazy:标记该bean是否开启懒加载。

这里准备User和Dog两个类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private String name;
    private int age;
    private Dog dog;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Dog {
    private String name;
    private int age;
}

2.1、@Bean注解

先来看看如何声明配置一个bean对象的注册

@Configuration
public class MyConfig {

    @Bean("user")
    public User getUser(){
    	System.out.println("User对象进行创建!");
        return new User("用户", 22, getDog());
    }

    @Bean("dog")
    public Dog getDog(){
    	System.out.println("Dog对象进行创建!");
        return new Dog("金毛", 3);
    }
}
  • 这样就声明配置了两个bean,在Spring中相当于注册到xml配置文件中

  • 默认情况下是以饿汉单例的形式进行创建,即IOC容器创建时立即创建这两个bean。

@SpringBootApplication
public class MainApplication {

    public static void main(String[] args) {
        // 获取IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
        System.out.println("====================================================");
        User user = run.getBean("user", User.class);
        System.out.println(user.toString());
        Dog dog = run.getBean("dog", Dog.class);
        System.out.println(dog.toString());
        System.out.println("====================================================");
    }

}

在这里插入图片描述


2.2、@Scope注解

之前通过简单的bean创建可以看到这些bean默认情况下都是饿汉的形式加载,并没有看出是单例的形式。这里使用Scope注解可以进行配置!

@Configuration
public class MyConfig {

    @Bean("user")
    @Scope(SCOPE_PROTOTYPE)         //多例
    public User getUser(){
        System.out.println("User对象进行创建!");
        return new User("用户", 22, getDog());
    }

    @Bean("dog")
    @Scope(SCOPE_SINGLETON)         //单例
    public Dog getDog(){
        System.out.println("Dog对象进行创建!");
        return new Dog("金毛", 3);
    }
}
@SpringBootApplication
public class MainApplication {

    public static void main(String[] args) {
        //获得IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
        
        System.out.println("====================================================");
        User user1 = run.getBean("user", User.class);
        User user2 = run.getBean("user", User.class);
        System.out.println("user1 == user2 ? " + (user1 == user2));
        Dog dog1 = run.getBean("dog", Dog.class);
        Dog dog2 = run.getBean("dog", Dog.class);
        System.out.println("dog1 == dog2 ? " + (dog1 == dog2));
        System.out.println("====================================================");
    }
}

在这里插入图片描述

  • User对象使用多例的形式创建bean,只要每次从IOC中获取bean都会是不一样的

  • Dog对象采用单例的形式创建,从IOC中获取都会是一样的结果。

  • 默认情况下不指定Scope时采用单例的形式

  • 并且可以看到在多例的情况下IOC容器创建的时候不会对多例对象进行创建,而是采用懒加载的形式在使用的时候创建。


2.3、@Lazy注解

懒加载在一些情况下是非常有必要的,并且懒加载的时候需要注意依赖对象的懒加载问题!

@Configuration
public class MyConfig {

    @Bean("user")
    @Lazy(false)        //立即加载
    public User getUser(){
        System.out.println("User对象进行创建!");
        return new User();
        //return new User("用户", 22, getDog());      //这时dog的懒加载失效,因为user是立即加载,依赖dog
    }

    @Bean("dog")
    @Lazy(true)         //懒加载
    public Dog getDog(){
        System.out.println("Dog对象进行创建!");
        return new Dog("金毛", 3);
    }
}
@SpringBootApplication
public class MainApplication {

    public static void main(String[] args) {
        //获得IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
        
        System.out.println("====================================================");
        User user1 = run.getBean("user", User.class);
        User user2 = run.getBean("user", User.class);
        System.out.println("user1 == user2 ? " + (user1 == user2));
        Dog dog1 = run.getBean("dog", Dog.class);
        Dog dog2 = run.getBean("dog", Dog.class);
        System.out.println("dog1 == dog2 ? " + (dog1 == dog2));
        System.out.println("====================================================");
    }
}

在这里插入图片描述

  • 默认情况下是立即加载,即饿汉式

  • 如果User对象是一个立即加载,Dog对象是一个懒加载;且User创建时需要依赖Dog对象,那么Dog对象的懒加载失败会变为立即加载

  • 如果不依赖那么不会造成影响。



3、@Configuration注解的属性

  • @Configuration注解中有@Component注解的加持,因此它自己本身也是一个bean对象,可以通过Context的进行获取。

  • @Configuration中的属性proxyBeanMethods是及其重要的,设置true/false会得到不同的效果。

3.1、proxyBeanMethods属性值
@Configuration(proxyBeanMethods = false)
public class MyConfig {
}
MyConfig bean = run.getBean(MyConfig.class);
System.out.println(bean);
/*
	输出: com.splay.config.MyConfig@c9d82f9
*/

@Configuration(proxyBeanMethods = true)
public class MyConfig {
}
MyConfig bean = run.getBean(MyConfig.class);
System.out.println(bean);
/*
	输出: com.splay.config.MyConfig$$EnhancerBySpringCGLIB$$eefc6c50@3605c4d3
*/
  • 值为false:那么MyConfig类是一个lite的配置类,没有代理功能

  • 值为true:该类是一个Full的配置类,使用cglib代理!


3.2、proxyBeanMethods = true
@Configuration(proxyBeanMethods = true)
public class MyConfig {

    @Bean("user")
    public User getUser(){
        System.out.println("User对象进行创建!");
        return new User();
        //return new User("用户", 22, getDog());      //这时dog的懒加载失效,因为user是立即加载,依赖dog
    }

    @Bean("dog")
    public Dog getDog(){
        System.out.println("Dog对象进行创建!");
        return new Dog("金毛", 3);
    }
}
@SpringBootApplication
public class MainApplication {
	// proxyBeanMethods = true
    public static void main(String[] args) {
        // 获得IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
		
        MyConfig bean = run.getBean(MyConfig.class);
        System.out.println(bean);
        User user = run.getBean(User.class);
        Dog dog = run.getBean(Dog.class);
        System.out.println(bean.getUser() == user);
        System.out.println(bean.getDog() == dog);
        System.out.println(user.getDog() == dog);
        
    }
}

在这里插入图片描述

proxyBeanMethods = true的情况下:

  • 通过配置类调用方法,还是通过getBean直接从IOC容器中获取对象,获取到的都是同一个!(单例)

  • 外部无论对配置类中的这个组件注册方法调用多少次都是直接注册到容器中的单实例对象!

  • 代理对象调用方法时SpringBoot会检查这个bean对象是否在容器中,保持单实例。


3.2、proxyBeanMethods = false
@Configuration(proxyBeanMethods = false)
public class MyConfig {

    @Bean("user")
    public User getUser(){
        System.out.println("User对象进行创建!");
        return new User("用户", 22, getDog());
    }

    @Bean("dog")
    public Dog getDog(){
        System.out.println("Dog对象进行创建!");
        return new Dog("金毛", 3);
    }
}

在这里插入图片描述

  • 不进行检查IOC容器中是否存在,而是简单的调用方法进行创建对象。

  • 通过直接getBean方式获取IOC容器中的对象获取还是一样的

  • 无法保证bean的单一,失去了单例的意义!



4、总结

  1. Full(proxyBeanMethods = true):这种情况主要用在bean对象的依赖情况下,如果存在一个bean依赖另一个bean时,一般会采用Full模式

  2. Lite(proxyBeanMethods = false):当不存在bean对象的依赖问题时,会才有轻量级的配置;只要不通过调用bean方法,而是getBean直接获取的bean也是同一个的。

  3. 合理使用Scope注解、Lazy注解;一定要结合bean之间的依赖问题合理使用,否则某些情况下会造成注解的不生效.

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐