使用Spring如何实现加载Bean
本篇文章给大家分享的是有关使用Spring如何实现加载Bean,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。
创新互联公司是一家集网站建设,延边朝鲜族企业网站建设,延边朝鲜族品牌网站建设,网站定制,延边朝鲜族网站建设报价,网络营销,网络优化,延边朝鲜族网站推广为一体的创新建站企业,帮助传统企业提升企业形象加强企业竞争力。可充分满足这一群体相比中小企业更为丰富、高端、多元的互联网需求。同时我们时刻保持专业、时尚、前沿,时刻以成就客户成长自我,坚持不断学习、思考、沉淀、净化自己,让我们为更多的企业打造出实用型网站。
1 定义bean的方式
常见的定义Bean的方式有:
通过xml的方式,例如:
通过注解的方式,在Class上使用@Component等注解,例如
@Component public class xxxServicer{ .... }
通过在@Configuration类下的@Bean的方式,例如
@Configuration public class xxxConfiguration{ @Bean public myBean myBean(){ return new myBean(); } }
虽然这三种定义Bean的方式不一样,对应的处理细节也不一样,但是从大的逻辑上来看,都是一样。主要的流程如下图: 最关键的就是问题就是这么去找到定义Bean的方式,然后生成BeanDefinition后注册到Spring上下文中,由Spring自动创建Bean的实例。
2 BeanDefinition
BeanDefinition是一个接口,用来描述一个Bean实例,例如是SINGLETON还是PROTOTYPE,属性的值是什么,构造函数的参数是什么等。简单来说,通过一个BeanDefinition我们就可以完成一个Bean实例化。 BeanDefinition及其主要的子类:
下面简单说一下各个子类:
- RootBeanDefinition和ChildBeanDefinition: 这2个BeanDefinition是相对的关系,自Spring 2.5 出来以后,已经被GenericBeanDefinition代替。因为这样强迫我们在编写代码的时候就必须知道他们之间的关系。
- GenericBeanDefinition: 相比于RootBeanDefinition和ChildBeanDefinition在定义的时候就必须硬编码,GenericBeanDefinition的优点可以动态的为GenericBeanDefinition设置parent。
- AnnotatedBeanDefinition:看名字就是知道是用来读取通过注解定义Bean。
3 通过xml文件定义Bean
通过xml定义Bean是最早的Spring定义Bean的方式。因此,怎么把xml标签解析为BeanDefinition(), 入口是在org.springframework.beans.factory.xml.XmlBeanDefinitionReader这个类,但是实际干活的是在org.springframework.beans.factory.xml.BeanDefinitionParserDelegate。代码很多,但实际逻辑很简单,就是解析Spring定义的
4 通过@Component等Spring支持的注解加载Bean
如果要使用@Component等注解定义Bean,一个前提条件是:有
4.1
由于
@Override public BeanDefinition parse(Element element, ParserContext parserContext) { //获取base-package标签 String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE); basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage); String[] basePackages = StringUtils.tokenizeToStringArray(basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); // 实际处理类是ClassPathBeanDefinitionScanner ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element); //扫描basePackage下所有的类,如果有@Component等标签就是注册到Spring中 SetbeanDefinitions = scanner.doScan(basePackages); registerComponents(parserContext.getReaderContext(), beanDefinitions, element); return null; }
4.2 @ComponentScan
注解对应生成的类是org.springframework.context.annotation.ComponentScanAnnotationParser 其实最后实际干活的还是ClassPathBeanDefinitionScanner这个。ComponentScanAnnotationParser类的生成是伴随着@Configuration这个注解处理过程中(意思说@ComponentScan必须和@Configuration一起使用)。而处理@Configuration其实是org.springframework.context.annotation.ConfigurationClassPostProcessor。是不是感觉有点绕。
其实简单来说,在处理@Configuration的时候发现有@ComponentScan注解,就会生成ComponentScanAnnotationParser去扫描@Component注解
4.3 ClassPathBeanDefinitionScanner
上面说到了,无论注解还是标签的方式,最后都会交给ClassPathBeanDefinitionScanner这个类来处理,这个类做的就是1.扫描basePackage下所有class,如果有@Component等注解,读取@Component相关属性,生成ScannedGenericBeanDefinition,注册到Spring中。
5 通过@Bean方式
前面说了@ComponentScan是在@Configuration处理过程中的一环,既然@Bean注解也是必须和@Configuration一起使用,那么说明@Bean的处理也是在@Configuration中,其实最后是交给ConfigurationClassBeanDefinitionReader这个类来处理的,关键代码:
private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) { //如果自己是通过@Import注解定义的,那么需要把自己注册到Spring中 if (configClass.isImported()) { registerBeanDefinitionForImportedConfigurationClass(configClass); } //这里就是处理方法上的@Bean for (BeanMethod beanMethod : configClass.getBeanMethods()) { loadBeanDefinitionsForBeanMethod(beanMethod); } //处理@ImportResource,里面解析xml就是上面说到的解析xml的XmlBeanDefinitionReader loadBeanDefinitionsFromImportedResources(configClass.getImportedResources()); loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars()); }
6 把BeanDefinition实例化
前面分别说了怎么把不同定义Bean的方式转换为BeanDefinition加入到Spring中去(确切来说是保持在BeanFactory的BeanDefinitionMap中),实例是在ApplicationContext最后阶段,关键代码在DefaultListableBeanFactory中
@Override public void preInstantiateSingletons() throws BeansException { for (String beanName : beanNames) { RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { if (isFactoryBean(beanName)) { final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName); boolean isEagerInit; if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) { isEagerInit = AccessController.doPrivileged(new PrivilegedAction() { @Override public Boolean run() { return ((SmartFactoryBean<?>) factory).isEagerInit(); } }, getAccessControlContext()); } else { isEagerInit = (factory instanceof SmartFactoryBean && ((SmartFactoryBean<?>) factory).isEagerInit()); } if (isEagerInit) { getBean(beanName); } } else { getBean(beanName); } } } }
通过getBean最后最后实例的代码,在AbstractAutowireCapableBeanFactory中
protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) { //处理xxAware接口 if (System.getSecurityManager() != null) { AccessController.doPrivileged(new PrivilegedAction
从上面初始化可以看出,InitializeBean和BeanPostProcessors的调用顺序
7 总结
综上分析,Spring加载Bean其实大的思想都是一样的,先读取相关信息生成BeanDefinition,然后通过BeanDefinition初始化Bean。如果知道了上面了套路以后,就可以清楚怎么自定义Xml标签或者自定义注解向Spring中注入Bean。
以上就是使用Spring如何实现加载Bean,小编相信有部分知识点可能是我们日常工作会见到或用到的。希望你能通过这篇文章学到更多知识。更多详情敬请关注创新互联行业资讯频道。
网站名称:使用Spring如何实现加载Bean
当前地址:http://scyanting.com/article/jisicc.html