Spring bean注册到容器的总结
作者:君莫Junmo
本文总结的是如何使用注解方式把对象加入ioc容器管理(永无止境,一直会更新).
来自作者的吐槽:好烦,他妈的提供这么多用法,然后那些 SpringBoot starter 框架里都是想用什么就用什么,导致看源码理解度很痛苦,其实我感觉种类方式多了,反而丢失了规范,增加理解成本,真的很烦!!!!!!!!!
注意:所有的使用方式皆来自Spring framework, SpringBoot不在本文讨论之内。
@Component + @ComponentScan
首先上场的运用最常用、最广泛的方式。元注解延伸出去的@Controller、@Service、@Repository等都一样。
容器bean名称:默认就是类名首字母小写(app)
建议使用场景:自己项目中的类需要交给Spring管理。
@Component public class App { }
@Configuration + @Bean
其实@Configuration也是@Component,所以你用@Component也可以,至于为什么要这样设计,应该为了见名知意。可以通过@Bean的方式来自己构建代码的方式来构建bean对象,然后塞入到Spring中,可以类似Spring会回调你的@Bean上的方法,将你自己定义创建的对象注入到Spring,你可以自己定义构造初始化等等。
容器bean名称:默认就是对应方法名(a)
建议使用场景:比如在引入的jar包里的对象,你想加入到Spring中,一般会用这个。还有就是你想定义类生成的具体过程,这个方式比较灵活。
@Configuration public class AppConfiguration { @Bean public A a() { return new A("junmo", 27); } }
@Import
说起这个注解之前请你一定看看官方的说明
/** * 用于导入一个或多个<em>组件类</em>,通常是{@link Configuration @Configuration}类。 * * <p>功能类似于Spring XML中的{@code <import/>}元素。 * 允许导入{@code @Configuration}类、{@link ImportSelector}和 * {@link ImportBeanDefinitionRegistrar}的实现类,以及普通的组件类(从4.2开始支持,类似于 * {@link AnnotationConfigApplicationContext#register}方法的功能)。 * * <p>在导入的{@code @Configuration}类中定义的{@code @Bean}可以通过 * {@link org.springframework.beans.factory.annotation.Autowired @Autowired}注解注入。 * 开发者可以选择直接注入Bean,也可以注入声明该Bean的配置类的实例,后者便于在IDE中快速导航配置方法。 * * <p>该注解可以直接用在类上,也可以作为元注解被其他注解使用。 * * <p>如果需要导入XML或其他非{@code @Configuration}的资源,应使用 * {@link ImportResource @ImportResource}注解。 */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Import { /** * 指定要导入的类,包括以下几类: * 1. 标注为{@link Configuration @Configuration}的类。 * 2. 实现了{@link ImportSelector}接口的类。 * 3. 实现了{@link ImportBeanDefinitionRegistrar}接口的类。 * 4. 普通的组件类。 */ Class<?>[] value(); }
1. @Import(简单)
@Import({A.class, B.class}) @Configuration public class App { }
从这里示例上能看出来,用法比较简单,感觉跟@Component一样无脑快速。而且它支持批量哦!
容器bean名称:默认就是全类名(xxx.xxx.App)
建议使用场景: 这个注解就有说明,指示要导入的一个或多个组件类,批量简单快速导入一堆对象,可以用这个。(前提是这个类不是你项目中的类,是从外边引入的类,而且需要加入到 ioc 中,不然@Component也可以做到)
缺点:很明显这个方式对于构建复杂对象初始化不太适合,不如@Bean。
2. @Import+实现ImportSelector接口(这个 SpringBoot 疯狂用它)
很明显这个可以写代码在运行时自己定义加入。这样的好处是我可以选择哪些对象加入到 ioc 中,毕竟只认最后的返回 String[].
@Import(MyImportSelector.class) @Configuration public class App { } public class MyImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { // importingClassMetadata:这个是目标类上的注解, 就是上面 App 类上的所有注解(@Import, @Configuration)有什么用?能力有限, 暂时未知 haha // 这里可以择别一些条件选择性加载 if(xxxxxxxxx) { return new String[]{"com.dao.daocloud.A", "com.dao.daocloud.B"}; } else { return new String[]{"com.dao.daocloud.A", "com.dao.daocloud.C"}; } } } }
容器bean名称:默认就是全类名(xxx.xxx.App)
建议使用场景: 也支持批量,同时你的项目中需要泽别条件,选择性的加载 bean,可以用这个。
缺点:很明显这个方式对于构建复杂对象初始化不太适合,不如@Bean,而且还要实现一个接口,比较繁琐。
3. @Import+实现ImportBeanDefinitionRegistrar接口
这个最大的好处是可以拿到 ioc bean注册器(BeanDefinitionRegistry),它的作用很多,也就是你可以为所欲为在程序中.
@Import(MyImportBeanDefinitionRegistrar.class) @Configuration public class App { } public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) { // 这个 A 就是你的对象类 BeanDefinition aBeanDefinition = new RootBeanDefinition(A.class); registry.registerBeanDefinition("可以定义自己的bean名称", aBeanDefinition); } }
容器bean名称:可以自己定义 bean 名称
建议使用场景: 对比上面两种,这个可以拿到BeanDefinitionRegistry,可以自己定义 bean 名称,还可以做一些别的事.
FactoryBean(非注解)
从第一眼看上去,翻译下就是工厂 bean,看意思就是知道这个方式可以来生产出 bean 对象,它其实就是用了一个载体来生产 bean,它本身也是一个 bean,它的用法与上面几种完全不一样。形象举个例子:这个就像是一个冰壶(FactoryBean)放了水(bean),冰壶可以倒出水。
@Component // 当然这里你也可以用 @Bean 把它加载到 ioc 中 public class AFactoryBean implements FactoryBean<A> { @Override public A getObject() throws Exception { // 这里感觉就和@Bean一样,可以自己构建自己的对象并且初始化. return new A("junmo", 27); } @Override public Class<?> getObjectType() { // 这个我就不理解了,为什么要提供这个?不是在 getObject 对象都能拿到,Class 信息还拿不到吗,还要开发者必须重写实现这个方法,不理解,不知道什么样的场景会用到这个方法。 return A.class; } @Override // 这个是设置单例还是多例的,不在本文讨论范围之内,但是你要知道它可以做到这个事情 public boolean isSingleton() { return FactoryBean.super.isSingleton(); } }
容器bean名称:这个有点特殊。它的 bean 名称在 ioc 容器中会是AFactoryBean,然后你拿到的对象类确会是 A.class。(很明显它调用getObject方法给你生产对象了,也就是说它为什么叫FactoryBean的含义)
建议使用场景: 动态对象创建、根据条件生成不同 Bean、创建代理类、生成多实例或生命周期管理需求。但是我觉得与@Bean也没多大区别。但是外面很多框架都用了这个方式来定义 bean。还是有个区别,因为它会调用 getObject方法来生产对象,这个可以在项目运行后也可以生效,可以动态改生成 bean 的方式。
另外说一句:如果你要获取AFactoryBean本身对象,可以用在前面加"&"来获取该对象,算了,这个不重要,一般不太会用到(从下面源码中很好理解).
public interface BeanFactory { /** * Used to dereference a {@link FactoryBean} instance and distinguish it from * beans <i>created</i> by the FactoryBean. For example, if the bean named * {@code myJndiObject} is a FactoryBean, getting {@code &myJndiObject} * will return the factory, not the instance returned by the factory. */ String FACTORY_BEAN_PREFIX = "&"; }
到此这篇关于Spring bean注册到容器的总结的文章就介绍到这了,更多相关Spring bean注册到容器内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!