一文理解Spring IOC 中的@Configuration和@Bean

目录

@Bean 注解详解

@Configuration 注解详解

使用和不使用@Configuration有什么区别?

@Configuration 和 @Bean 的总结

 

Spring 通过 @Configuration@Bean 注解来管理应用中的所有对象(即 bean)。这两个注解的组合使得 Spring 配置类不仅可以定义 bean,还能确保 bean 之间的依赖关系得到正确的注入和管理。接下来我们将结合这两个注解的用法,进行详细的分析。

@Bean 注解详解

@Bean 注解介绍

@Bean 是一个方法级别的注解,用于声明一个 bean。每个带有 @Bean 注解的方法都将返回一个 Spring 容器管理的对象。通常,@Bean 被放置在 @Configuration 注解的类中,但也可以单独使用。

在 Spring 中,@Bean 注解所标注的方法会告诉 Spring 该方法返回的对象应该作为 Spring 容器中的一个 bean,并将其注册到 Spring 容器中。

使用 @Bean 创建 Bean 的基本示例:

public class AppConfig {

    // 通过 @Bean 注解定义了一个名为 myService 的 bean
    @Bean
    public MyService myService() {
        return new MyService();
    }
}

public class MyService {
    public void query() {
        System.out.println("Service query method called!");
    }
}
  • 在上述代码中,myService() 方法返回一个 MyService 对象,Spring 会将这个对象注册为一个 bean,且默认的 bean 名称是 myService(即方法名的小写形式)。

  • @Bean 的方法就像是一个工厂方法,负责生成一个 bean 实例。

使用 @Bean 注解的方法可以接受参数

@Bean 注解标注的方法不仅可以返回一个 bean,还可以接收其他 bean 作为参数,这样可以实现 bean 之间的依赖注入。

@Configuration
public class AppConfig {

    @Bean
    public MyService myService() {
        return new MyService();
    }

    @Bean
    public UserService userService() {
        // userService 方法依赖 myService bean,因此直接调用 myService() 方法
        return new UserService(myService());
    }
}

public class UserService {
    private MyService myService;

    // 构造方法注入
    public UserService(MyService myService) {
        this.myService = myService;
        System.out.println("UserService created with MyService: " + myService);
    }
}

public class MyService {
    public void query() {
        System.out.println("Service query method called!");
    }
}

 userService() 方法依赖 myService() 方法返回的 MyService 对象,Spring 会自动将 myService() 返回的对象注入到 UserService 构造函数中。

@Configuration 注解详解

在 Spring 中,@Configuration 注解用于标记一个类为配置类,这意味着该类的目的主要是用来定义和管理 Spring 容器中的 bean。配置类通常与 @Bean 注解一起使用,@Bean 用来声明容器中的单个 bean。

示例:

@Configuration
public class AppConfig {

    // 定义一个 Bean
    @Bean
    public MyService myService() {
        return new MyService();
    }

    @Bean
    public UserService userService() {
        return new UserService(myService());
    }
}

@Configuration 表示 AppConfig 类是一个配置类,Spring 会处理这个类中的 bean 定义。配置类中的方法返回的对象会自动注册为 Spring 的 bean。

@Configuration 的特殊之处 

与普通类不同,使用了 @Configuration 的类在 Spring 中有一些特殊的处理。Spring 会将这个类增强为 CGLIB 代理类。代理类的主要目的是保证方法调用之间的依赖注入是通过 Spring 容器进行的,而不是简单的直接调用。这就避免了手动实例化的问题。

CGLIB 代理有什么作用?

使用 CGLIB 代理技术能够确保配置类中的方法调用从 Spring 容器中获取已有的 bean,而不是重新实例化它们。简而言之,@Configuration 修饰的类使得配置类变成了一个代理对象,能够通过代理类进行方法调用的拦截。

使用和不使用@Configuration有什么区别?

当你在一个类上使用 @Configuration 注解时,Spring 会对该类进行特别处理:

  • CGLIB 代理增强:

    • Spring 会将配置类增强为一个 CGLIB 动态代理类。通过这个代理,Spring 确保在配置类内部调用 @Bean 注解的方法时,从容器中获取的是同一个实例,而不是重新实例化对象。

  • 内部方法调用的依赖注入:

    • 在配置类内部,通过 @Bean 注解的方法互相调用时,Spring 会确保这些方法返回的 bean 来自容器,而不是手动创建新的对象。这意味着,即使你在同一个配置类内调用 @Bean 方法,Spring 也能保证它们是通过容器注入的,而不是简单地调用方法。

示例:

@Configuration
public class AppConfig {

    @Bean
    public MyService myService() {
        return new MyService();
    }

    @Bean
    public UserService userService() {
        // 依赖 myService bean,这里会调用 myService() 方法
        return new UserService(myService());
    }
}

public class MyService {
    // 服务类
}

public class UserService {
    private final MyService myService;
    // 构造器注入 MyService
    public UserService(MyService myService) {
        this.myService = myService;
        System.out.println("UserService created with MyService: " + myService);
    }
}

@Test
public void test() {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
    UserService userService = context.getBean(UserService.class); // 获取 userService bean
    context.close();
}

此时的输出为:

UserService created with MyService: MyService@内存地址x

 在这个例子中,userService 方法调用了 myService() 方法,Spring 会确保 myService 返回的是容器中的 MyService 实例,而不是重新创建一个新的 MyService 对象。

如果不使用 @Configuration 注解,Spring 就不会将该类视为一个配置类,也不会增强它为 CGLIB 代理类。这会导致一些行为的不同:

  • 方法调用直接执行:

    • 如果没有 @Configuration,配置类就只是一个普通的 Java 类,Spring 不会拦截方法调用。因此,当你在配置类内部调用 @Bean 注解的方法时,这些方法不会通过 Spring 容器获取实例,而是会直接执行方法,返回新的实例。

  • 可能导致多例问题:

    • 如果没有 @Configuration,即使你调用 @Bean 注解的方法,它们也会直接返回新创建的实例,而不是从 Spring 容器中获取已创建的实例。这会导致每次调用都创建新的对象,可能会导致不必要的性能开销,或者无法实现单例模式。

直接把@COnfiguration注解去除,输出变成:

UserService created with MyService: MyService@内存地址x
UserService created with MyService: MyService@内存地址y

由此可以看到,在这个示例中:

  • 使用 @Configuration 时:

    • userService() 方法依赖 myService(),通过 Spring 容器获取 MyService 实例,并保证是同一个对象。

  • 没有 @Configuration 时:

    • userService() 方法每次调用 myService() 时都会创建一个新的 MyService 实例,而不是从容器中获取它。这导致了每次调用都创建了新的 MyService 对象,可能会导致意外的结果。

关于这个问题的总结:

  • @Configuration 的情况下:

    • 配置类会通过 CGLIB 代理增强,保证内部 @Bean 方法之间的依赖关系是通过容器管理的,避免了多例对象的问题。

  • 没有 @Configuration 的情况下:

    • 配置类只是一个普通的类,Spring 不会对它进行代理,导致 @Bean 注解的方法每次都会执行,返回新创建的对象,无法保证内部方法调用的依赖注入。

使用 @Configuration 可以确保 Spring 容器正确地管理 Bean 的生命周期和依赖关系,避免了不必要的重复创建对象,同时也保证了依赖注入的一致性。

@Configuration@Bean 的总结

@Configuration 如何工作?

当我们使用 @Configuration 注解时,Spring 会在容器启动时通过 ConfigurationClassPostProcessor 对这个类进行解析,注册其中的 @Bean 定义的 bean。Spring 还会将该类转变为 CGLIB 代理类,以保证内部方法调用之间的依赖注入。

@Bean 如何工作?

Spring 会执行 @Bean 注解标记的方法来创建对应的 bean。如果方法之间有依赖关系,Spring 会自动处理这些依赖,确保每个 bean 都从容器中获取。

@Configuration@Bean 有什么关系?

  • @Configuration 注解标记的类是一个配置类,表示这个类主要用于定义 Spring 容器中的 bean。

  • @Bean 注解标记的方法则用于定义一个具体的 bean。

在一个 @Configuration 注解的类中,我们通常会看到多个 @Bean 注解的方法。通过这些方法,Spring 容器可以实例化并管理应用所需要的所有 bean。

@Bean 注解的作用

  • 定义 bean@Bean 表明当前方法返回的对象是 Spring 管理的 bean。每次调用该方法时,Spring 会为我们返回该对象实例。

  • 依赖注入:通过在 userService() 方法中调用 myService() 方法,我们可以在 UserService 中自动注入 MyService,从而实现了 bean 的依赖关系。

@Configuration 注解的作用:

  • 用来标记一个类为配置类,该类包含多个 @Bean 定义。

  • 会被 Spring 转换为 CGLIB 代理类,确保方法调用之间的依赖注入。

@Bean 方法的每个返回值都会成为容器中的一个 bean。@Bean 注解通常用于什么地方?

  • 定义那些没有默认构造函数的类实例。

  • 自定义初始化方法和销毁方法。

  • 通过 @Bean 方法返回的 bean 可以直接访问和使用容器中的其他 bean。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值