JAVASpring

@Configuration注解使用

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、总结


  • Full(proxyBeanMethods = true):这种情况主要用在bean对象的依赖情况下,如果存在一个bean依赖另一个bean时,一般会采用Full模式
  • Lite(proxyBeanMethods = false):当不存在bean对象的依赖问题时,会才有轻量级的配置;只要不通过调用bean方法,而是getBean直接获取的bean也是同一个的。
  • 合理使用Scope注解、Lazy注解;一定要结合bean之间的依赖问题合理使用,否则某些情况下会造成注解的不生效.

在Spring框架中,@Configuration注解是用来定义一个类作为Spring Bean的配置类的。它告诉Spring容器这个类中定义的方法将返回一个或多个Spring Bean。当Spring容器启动时,它会扫描这些类,读取其中的@Bean注解的方法,创建实例化对象,并将它们注册到容器中。因此,@Configuration注解的主要作用是告诉Spring容器哪些类和方法应该被注册为Bean。

而@Autowired注解则是用来实现依赖注入(DI)的。它可以自动地将一个Bean注入到另一个Bean中,从而解决了Bean之间的依赖关系。使用@Autowired注解可以避免手动编写大量的代码来实现Bean之间的依赖关系。

虽然使用@Autowired注解可以方便地实现依赖注入,但是它并不会自动地将所有的Bean都注册到Spring容器中。因此,如果需要在Spring容器中创建一个Bean,就必须使用@Configuration注解来定义一个配置类,并在其中使用@Bean注解来定义这个Bean。这样Spring容器才能识别和管理这个Bean。

总的来说,@Configuration注解和@Autowired注解都是Spring框架中非常重要的注解,它们分别用于定义Bean和实现依赖注入。虽然@Autowired注解可以自动地将Bean注入到另一个Bean中,但是使用@Configuration注解可以更加清晰地定义Bean,同时也能够更加方便地管理Bean的生命周期。

@Configuration注解的主要作用在于将Bean的定义集中在一处,并提供一种清晰、统一的方式来管理Bean的生命周期。使用@Configuration注解,可以将相关的Bean定义放在同一个类中,避免了Bean的散乱定义,同时也使得代码更加易于维护和管理。

另外,使用@Configuration注解还可以实现更加复杂的Bean依赖关系和配置。通过在@Configuration注解的类中定义多个@Bean方法,可以将不同的Bean定义组合在一起,实现复杂的依赖关系。此外,@Configuration注解还支持使用@Import注解导入其他配置类,从而实现更加灵活的配置组合和管理。

总之,@Configuration注解是Spring框架中非常重要的注解,它提供了一种集中式、统一的方式来管理Bean的定义和生命周期,并支持更加复杂的Bean依赖关系和配置。因此,在Spring应用程序中使用@Configuration注解来声明Bean是非常常见的做法。