详解Spring Boot 使用Java代码创建Bean并注册到Spring中

从 Spring3.0 开始,增加了一种新的途经来配置Bean Definition,这就是通过 Java Code 配置 Bean Definition。

与Xml和Annotation两种配置方式不同点在于:

前两种Xml和Annotation的配置方式为预定义方式,即开发人员通过 XML 文件或者 Annotation 预定义配置 bean 的各种属性后,启动 spring 容器,Spring 容器会首先解析这些配置属性,生成对应都?Bean Definition,装入到 DefaultListableBeanFactory 对象的属性容器中去。与此同时,Spring 框架也会定义一些内部使用的 Bean 定义,如 bean 名为”org.springframework.context.annotation.internalConfigurationAnnotationProcessor”的 ConfigurationClassPostProcessor 定义。

而后此刻不会做任何 Bean Definition 的定义解析动作,Spring 框架会根据前两种配置,过滤出 BeanDefinitionRegistryPostProcessor 类型的 Bean 定义,并通过 Spring 框架生成其对应的 Bean 对象(如 ConfigurationClassPostProcessor 实例)。结合 Spring 上下文源码可知这个对象是一个 processor 类型工具类,Spring 容器会在实例化开发人员所定义的 Bean 前先调用该 processor 的 postProcessBeanDefinitionRegistry(…) 方法。此处实现基于 Java Code 配置Bean Definition的处理。

基于 Java Code 解析 Bean 的顺序图(查看大图)

基于 Java Code 的配置方式,其执行原理不同于前两种。它是在 Spring 框架已经解析了基于 XML 和 Annotation 配置后,通过加入 BeanDefinitionRegistryPostProcessor 类型的 processor 来处理配置信息,让开发人员通过 Java 编程方式定义一个 Java 对象。其优点在于可以将配置信息集中在一定数量的 Java 对象中,同时通过 Java 编程方式,比基于 Annotation 方式具有更高的灵活性。并且该配置方式给开发人员提供了一种非常好的范例来增加用户自定义的解析工具类。其主要缺点在于与 Java 代码结合紧密,配置信息的改变需要重新编译 Java 代码,另外这是一种新引入的解析方式,需要一定的学习成本。

另外提及一点的就是,Spring框架有3个主要的Hook类,分别是:

org.springframework.context.ApplicationContextAware 

它的setApplicationContext 方法将在Spring启动之前第一个被调用。我们用来同时启动Jdon框架。

org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor 

它的postProcessBeanDefinitionRegistry 和 postProcessBeanFactory 方法是第二和第三被调用,它们在Bean初始化创建之前启动,如果Spring的bean需要的其他第三方中的组件,我们在这里将其注入给Spring。

org.springframework.context.ApplicationListener 

用于在初始化完成后做一些事情,当Spring所有XML或元注解的Bean都启动被创建成功了,这时会调用它的唯一方法onApplicationEvent。

下面我们来完成一个,自己通过java代码创建bean,并注册为Spring管理。

本例中,我们创建一个接口,然后创建该接口的2个实现类,分别命名不同的名字,然后在需要注入的地方使用@Qualifier 指定注入对应的实例。

1、接口Shanhy.java

package org.springboot.sample.config;

public interface Shanhy {

  void display();

}

2、实现类ShanhyA.java

package org.springboot.sample.config;

public class ShanhyA implements Shanhy {

  @Override
  public void display() {
    System.out.println("AAAAAAAAAAAA");
  }

}

3、实现类ShanhyB.java

package org.springboot.sample.config;

public class ShanhyB implements Shanhy {

  @Override
  public void display() {
    System.out.println("BBBBBBBBBBBB");
  }

}

4、定义接口BeanDefinitionRegistryPostProcessor的实现

package org.springboot.sample.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.AnnotationScopeMetadataResolver;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ScopeMetadata;
import org.springframework.context.annotation.ScopeMetadataResolver;

/**
 * 实现自己实例化bean并注册为Spring管理
 *
 * @author  单红宇(365384722)
 * @create  2016年1月21日
 */
@Configuration
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {

  private static final Logger logger = LoggerFactory.getLogger(MyBeanDefinitionRegistryPostProcessor.class);

  private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver();
  private BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();

  @Override
  public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    logger.info("Invoke Metho postProcessBeanFactory");
    // 这里可以设置属性,例如
    BeanDefinition bd = beanFactory.getBeanDefinition("dataSourceA");
    MutablePropertyValues mpv = bd.getPropertyValues();
    mpv.addPropertyValue("driverClassName", "com.mysql.jdbc.Driver");
    mpv.addPropertyValue("url", "jdbc:mysql://localhost:3306/test");
    mpv.addPropertyValue("username", "root");
    mpv.addPropertyValue("password", "123456");
  }

  @Override
  public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
    logger.info("Invoke Metho postProcessBeanDefinitionRegistry");
    registerBean(registry, "shanhyA", ShanhyA.class);
    registerBean(registry, "shanhyB", ShanhyB.class);
    registerBean(registry, "dataSourceA", org.apache.tomcat.jdbc.pool.DataSource.class);
  }

  private void registerBean(BeanDefinitionRegistry registry, String name, Class<?> beanClass){
    AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);

    ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
    abd.setScope(scopeMetadata.getScopeName());
    // 可以自动生成name
    String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, registry));

    AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);

    BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
    BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
  }
}

5、使用测试

和平常一样可以直接注入我们的对象,对于同样接口的我们需要指定name

/**
 * 测试参数注入
 *
 * @author  单红宇(365384722)
 * @create  2016年1月13日
 */
@Configuration
public class MyConfiguration {

  @Bean
  public FilterRegistrationBean filterRegistrationBean(@Qualifier("shanhyB") Shanhy shanhy) {
    FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
    shanhy.display();
    // 省略代码
    return filterRegistration;
  }
}

使用@Resource 或者 @Autowired并指定@Qualifier 也可以

@RestController
@RequestMapping("/hello")
public class HelloController {

  @Resource(name="shanhyA")
  private Shanhy shanhyA;

  @Autowired
  @Qualifier("shanhyB")
  private Shanhy shanhyB;

  // 省略代码

}

这里有点经验要说一下,在 @Configuration 中,不能使用注入属性的方式注入,只能通过参数的方式注入,其原因就是@Configuration的类一开始变被加载,此时你想进行属性注入,需要注入的bean对象都还不存在呢。

下一篇文章,我们将使用这种方法动态创建基于MyBatis的多数据源。

下面的代码片段也可以注册Bean,比较简单:

@Configuration
@Import(Registrar.class)
public class TestConfig {

}

class Registrar implements ImportBeanDefinitionRegistrar {

  private static final String BEAN_NAME = "myTestBean";

  @Override
  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

    if (!registry.containsBeanDefinition(BEAN_NAME)) {
      GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
      beanDefinition.setBeanClass(ExamplePostProcessor.class);
      beanDefinition.setSynthetic(true);
      registry.registerBeanDefinition(BEAN_NAME, beanDefinition);
    }
  }

}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Spring Boot如何动态创建Bean示例代码

    前言 本文主要给大家介绍了关于Spring Boot动态创建Bean的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. SpringBoot测试版本:1.3.4.RELEASE 参考代码如下: package com.spring.configuration; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.su

  • JSP 开发之Spring Boot 动态创建Bean

    JSP 开发之Spring Boot 动态创建Bean 1.通过注解@Import导入方式创建 a.新建MyImportBeanDefinitionRegistrar注册中心 Java代码 import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.GenericBeanDefinition; import org

  • Spring boot将配置属性注入到bean类中

    一.@ConfigurationProperties注解的使用 看配置文件,我的是yaml格式的配置: // file application.yml my: servers: - dev.bar.com - foo.bar.com - jiaobuchong.com 下面我要将上面的配置属性注入到一个Java Bean类中,看码: import org.springframework.boot.context.properties.ConfigurationProperties; import

  • 详解Spring Boot 使用Java代码创建Bean并注册到Spring中

    从 Spring3.0 开始,增加了一种新的途经来配置Bean Definition,这就是通过 Java Code 配置 Bean Definition. 与Xml和Annotation两种配置方式不同点在于: 前两种Xml和Annotation的配置方式为预定义方式,即开发人员通过 XML 文件或者 Annotation 预定义配置 bean 的各种属性后,启动 spring 容器,Spring 容器会首先解析这些配置属性,生成对应都?Bean Definition,装入到 DefaultL

  • 使用Spring Boot搭建Java web项目及开发过程图文详解

    一.Spring Boot简介 Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置.通过这种方式,Boot致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者.SpringMVC是非常伟大的框架,开源,发展迅速.优秀的设计必然会划分.解耦.所以,spring有很多子项目,比如core.context.

  • 详解json string转换为java bean及实例代码

    详解json string转换为java bean及实例代码 pom中添加如下两个库: <dependency> <groupId>org.codehaus.jackson </groupId> <artifactId>jackson-core-asl</artifactId> <version>1.9.2</version> <scope>provided</scope> </depende

  • Java BigDecimal详解_动力节点Java学院整理

    1.引言 借用<Effactive Java>这本书中的话,float和double类型的主要设计目标是为了科学计算和工程计算.他们执行二进制浮点运算,这是为了在广域数值范围上提供较为精确的快速近似计算而精心设计的.然而,它们没有提供完全精确的结果,所以不应该被用于要求精确结果的场合.但是,商业计算往往要求结果精确,例如银行存款数额,这时候BigDecimal就派上大用场啦. 2.BigDecimal简介 BigDecimal 由任意精度的整数非标度值 和32 位的整数标度 (scale) 组

  • Java System类详解_动力节点Java学院整理

    System类是jdk提供的一个工具类,有final修饰,不可继承,由名字可以看出来,其中的操作多数和系统相关.其功能主要如下: • 标准输入输出,如out.in.err • 外部定义的属性和环境变量的访问,如getenv()/setenv()和getProperties()/setProperties() • 加载文件和类库的方法,如load()和loadLibrary(). • 一个快速拷贝数组的方法:arraycopy() • 一些jvm操作,如gc().runFinalization()

  • Java Runtime类详解_动力节点Java学院整理

    一.概述 Runtime类封装了运行时的环境.每个 Java 应用程序都有一个 Runtime 类实例,使应用程序能够与其运行的环境相连接.一般不能实例化一个Runtime对象,应用程序也不能创建自己的 Runtime 类实例,但可以通过 getRuntime 方法获取当前Runtime运行时对象的引用.一旦得到了一个当前的Runtime对象的引用,就可以调用Runtime对象的方法去控制Java虚拟机的状态和行为. 当不被信任的代码调用任何Runtime方法时,常常会引起SecurityExc

  • 详解 Corba开发之Java实现Service与Client

    详解 Corba开发之Java实现Service与Client 1      概述 CORBA(Common Object Request Broker Architecture,公共对象请求代理体系结构)是由OMG组织制订的一种标准的面向对象应用程 序体系规范.或者说 CORBA体系结构是OMG为解决分布式处理环境(DCE)中,硬件和软件系统的互连而提出的一种解决方案. OMG:Object Management Group,对象管理组织.是一个国际化的.开放成员的.非盈利性的计算机行业标准协

  • VsCode搭建Spring Boot项目并进行创建、运行、调试

    源码地址:https://github.com/YANGKANG01/Spring-Boot-Demo 安装扩展 安装如下两个主要扩展即可,这两个扩展已关联java项目开发主要使用的maven.springboot等所需要的扩展. 开始步骤: 在 Visual Studio Code 中打开扩展视图(Ctrl+Shift+X). 输入"java"搜索商店扩展插件. 找到并安装Java Extension Pack (Java 扩展包),如果你已经安装了Language Support

  • Java Scaner类详解_动力节点Java学院整理

    Java.util.Scanner是Java5.0的新特征,主要功能是简化文本扫描.这个类最实用的地方表现在获取控制台输入,其他的功能都很鸡肋,尽管Java API文档中列举了大量的API方法,但是都不怎么地. 一.扫描控制台输入  这个例子是常常会用到,但是如果没有Scanner,你写写就知道多难受了. 当通过new Scanner(System.in)创建一个Scanner,控制台会一直等待输入,直到敲回车键结束,把所输入的内容传给Scanner,作为扫描对象.如果要获取输入的内容,则只需要

  • Spring Boot利用Java Mail实现邮件发送

    本文实例为大家分享了Spring Boot利用Java Mail实现邮件发送的具体代码,供大家参考,具体内容如下 实现邮件发送的方法有很多,这里只是简单记录一个demo实现 1. 引入maven依赖 <!-- https://mvnrepository.com/artifact/org.springframework/spring-context-support --> <dependency> <groupId>org.springframework</grou

随机推荐