`

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

阅读更多

 

原文链接:http://blog.csdn.net/catoop/article/details/50558333

从 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();

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

2、实现类ShanhyA.java

package org.springboot.sample.config;

public class ShanhyA implements Shanhy {

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

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

3、实现类ShanhyB.java

package org.springboot.sample.config;

public class ShanhyB implements Shanhy {

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

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

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)
 * @myblog  http://blog.csdn.net/catoop/
 * @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);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68

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

/**
 * 测试参数注入
 *
 * @author   单红宇(365384722)
 * @myblog  http://blog.csdn.net/catoop/
 * @create    2016年1月13日
 */
@Configuration
public class MyConfiguration {

    @Bean
    public FilterRegistrationBean filterRegistrationBean(@Qualifier("shanhyB") Shanhy shanhy) {
        FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
        shanhy.display();
        // 省略代码
        return filterRegistration;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

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

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

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

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

    // 省略代码

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

这里有点经验要说一下,在 @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);
        }
    }

}
分享到:
评论

相关推荐

    41. Spring Boot 使用Java代码创建Bean并注册到Spring中【从零开始学Spring Boot】

    在本篇博文中,我们将深入探讨如何在Spring Boot应用中使用Java代码来创建Bean并将其注册到Spring容器中。这是Spring Boot的核心特性之一,它使得我们能够以编程方式管理对象,而无需XML配置。让我们逐步解析这个...

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

    下面是一个简单的例子,展示如何通过Java代码创建Bean并注册到Spring管理: 1. 定义接口: ```java package org.springboot.sample.config; public interface Shanhy { void display(); } ``` 2. 创建接口实现类:...

    Spring bean 动态注册,jar包热替换

    Spring bean 一般通过配置文件和注解进行加载,如果要实现jar或class...测试示例中是spring boot 的部分代码,动态加载的内容为接口实现类,且初始化时加载本地的实现类,动态加载后改为非程序加载目录中的jar实现类。

    Spring Boot技术知识点:Bean装配1

    6. **配置类与属性源**:Spring Boot允许从application.properties或application.yml文件中读取配置属性,并通过@ConfigurationProperties注解将这些属性映射到Java对象上,方便在Bean装配时使用。 7. **条件注解...

    java ee开发的颠覆者spring-boot源代码

    在Spring Boot应用中,基于`@EnableAutoConfiguration`注解,系统会自动识别并配置相关的bean,如数据库连接、缓存、消息代理等,这大大减少了手动编写配置文件的工作量。 其次,Spring Boot的起步依赖是其简化配置...

    17. Spring Boot普通类调用bean【从零开始学Spring Boot】

    Spring Boot普通类调用bean【从零开始学Spring Boot】”旨在指导初学者如何在非Spring管理的类中访问和使用Spring容器中的bean。下面将详细讲解这个主题。 首先,了解Spring Boot的基础概念是必要的。Spring Boot...

    Spring Boot实战派(源码)

    《Spring Boot实战派》源码提供了丰富的学习材料,旨在帮助开发者深入理解并...通过分析《Spring Boot实战派》源码,读者不仅可以了解上述技术点,还能学习到如何在实际项目中应用这些技术,提升开发效率和代码质量。

    spring boot 例子代码

    在"spring boot 例子代码"中,我们可以预期找到一些关于如何在实际项目中使用 Spring Boot 的示例代码。Spring Boot 的核心特性包括自动配置、内嵌式 web 服务器(如 Tomcat 或 Jetty)以及起步依赖(starters),...

    如何使用Java Spring Boot执行RAG架构GenAI项目的示例.zip

    在本示例中,我们将深入探讨如何利用Java Spring Boot框架来执行一个基于RAG(Red-Amber-Green)架构的GenAI项目。RAG是一种广泛应用于项目管理的颜色代码系统,用于评估任务完成状态:红色代表问题,琥珀色表示部分...

    11-Spring Boot面试题(92题).pdf

    - @ComponentScan:表示自动扫描当前包以及子包下使用了@Component、@Service等注解的类,并注册成bean。 运行Spring Boot应用的方式: 1. 打包并运行:可以将Spring Boot应用打包成一个jar文件,使用java -jar命令...

    关于spring boot中几种注入方法的一些个人看法

    在 Spring Boot 中,注入是一种非常重要的机制,用于将 bean 对象注入到其他 bean 对象中,以便实现松耦合和高内聚的设计目标。下面我们将对 Spring Boot 中的几种注入方法进行详细的介绍和分析。 1. @Autowired @...

    Spring boot基于java的配置

    在 Spring Boot 中,我们使用 `@Bean` 注解来声明一个方法将返回一个 Spring 管理的 bean。例如,创建一个简单的 `HelloService`: ```java @Configuration public class AppConfig { @Bean public HelloService...

    java maven工程 spring boot 学习源码

    Spring Boot 是一个基于 Java 的框架,它简化了创建和配置微服务级的Spring应用程序的过程。Maven 是一个项目管理和综合工具,它帮助开发者管理Java项目的构建、依赖和生命周期。本学习资源包“java maven工程 ...

    spring-boot中文开发指南

    《Spring Boot中文开发指南》是一本专为中文开发者编写的实战型教程,旨在帮助读者深入理解和熟练运用Spring Boot这一强大的Java开发框架。Spring Boot简化了Spring应用的初始搭建以及开发过程,通过“约定优于配置...

    Spring Boot实战 ,丁雪丰 (译者).zip

    在讲解基础知识后,书中的实战部分会深入到Spring Boot的实际应用,包括如何创建RESTful API、使用Thymeleaf或Freemarker进行视图渲染、集成MyBatis或JPA进行数据库操作。书中还会涵盖Spring Security,它是Spring...

    spring-boot-reference

    这部分还包含了创建第一个Spring Boot应用的步骤,从建立项目结构,添加必要的依赖到编写代码并运行示例。 Spring Boot Reference Guide通过指导如何使用不同的构建系统来构建Spring Boot项目,例如Maven和Gradle,...

    在spring boot中使用java线程池ExecutorService的讲解

    在 Spring Boot 中使用线程池非常简单,我们可以使用 @Configuration 注解创建一个线程池配置类,然后使用 @Bean 注解创建一个线程池实例。 ``` @Configuration public class ThreadPoolConfig { @Bean public ...

    Spring Boot揭秘 PDF

    书中对Spring Boot的启动原理进行了详细分析,包括如何加载配置、如何初始化Bean、自动配置的工作原理等,这些都是理解Spring Boot工作方式的关键。通过阅读本书,读者可以深入理解Spring Boot的设计理念,提高在...

    spring-boot-2.7.0.zip源码

    6. **条件注解**:Spring Boot使用`@Conditional`系列注解,如`@ConditionalOnClass`、`@ConditionalOnMissingBean`等,使得配置的启用基于特定条件,增加了代码的灵活性。 7. **属性绑定**:`@Value`和`@...

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

    在Spring Boot中,属性注入是核心特性之一,它使得我们可以方便地将配置文件中的参数值注入到Bean类的属性中,从而实现灵活的配置管理。本文将详细讲解如何利用`@ConfigurationProperties`注解以及与`@...

Global site tag (gtag.js) - Google Analytics