首页 > 其他分享 >Spring Bean基础

Spring Bean基础

时间:2023-06-13 16:01:21浏览次数:34  
标签:name Spring 基础 class Person Bean public BeanDefinition

在接下来的一段时间里,主要分享一些Spring相关的知识文章!

Spring官方文档

BeanDefinition元信息

BeanDefinition是定义 Bean 的配置元信息接口,主要作用是描述一个Bean,里面存储Bean的相关信息;包括类名、构造器、属性、方法、作用域、自动绑定的模式,生命周期回调等等。

概述

他的类图继承关系如下:

Spring Bean基础_spring

image-20230225171608444

从图中可以看出,BeanDefinition从下派生出 AnnotatedBeanDefinition 接口,以及常用子类 RootBeanDefinitionGenericBeanDefinition等。

BeanDefinition可以构建的信息在我们注册对象到Spring容器中(不管是使用xml、@Bean)都可以从中感受到。在Spring构建Bean的过程中,通常我们会在BeanDefinition中进行管理的Bean元信息主要包括:

  • • Bean 全类名,必须是具体类,不能用抽象类或接口
  • • Bean 的名称或者 ID
  • • Bean 的作用域(如:singleton、prototype 等)
  • • Bean 构造器参数(用于依赖注入)
  • • Bean 属性设置(用于依赖注入)
  • • Bean 自动绑定模式(如:通过名称 byName)
  • • Bean 延迟初始化模式(延迟和非延迟)
  • • Bean 初始化回调方法名称
  • • Bean 销毁回调方法名称

基础使用

构建bean的BeanDefinition的方式主要有两种:

  1. 1. 通过 BeanDefinitionBuilder构建
public static void main(String[] args) {
  // 创建一个Spring上下文对象
  DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

  // 创建BeanDefinition构建器
  BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(Person.class);
  definitionBuilder.addPropertyValue("name", "张三")
    .addPropertyValue("age", 30);
  // 获得BeanDefinition
  BeanDefinition beanDefinition = definitionBuilder.getBeanDefinition();


  // 将BeanDefinition注册到Spring上下文中
  beanFactory.registerBeanDefinition("person", beanDefinition);

  // 在执行getBean之前,即使注册到Spring上下文之后修改BeanDefinition值依旧生效
  beanDefinition.getPropertyValues()
    .add("age", 45);


  Person person = beanFactory.getBean("person", Person.class);
  System.out.println(person);
}

public static class Person {
  
  private String name;

  private String age;

  // 。。。。 省略get、set和toString方法
}

输出结果:

Person{name='张三', age='45'}
  1. 1. 使用AbstractBeanDefinition 派生类进行创建
public static void main(String[] args) {
  // 创建一个Spring上下文对象
  DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

  // 创建BeanDefinition派生类
  GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
  // 设置对象相关元信息
  genericBeanDefinition.setBeanClass(Person.class);
  MutablePropertyValues propertyValues = new MutablePropertyValues();
  propertyValues.add("name", "王武")
    .add("age", "33");
  genericBeanDefinition.setPropertyValues(propertyValues);

  // 将BeanDefinition注册到Spring上下文中
  beanFactory.registerBeanDefinition("person", genericBeanDefinition);

  // 从上下文获取Bean对象
  Person person = beanFactory.getBean(Person.class);
  System.out.println(person);
}

输出结果:

Person{name='王武', age='33'}

BeanDefinition其实很简单,他其实本质就是用来描述一个对象的,他记录了一个对象的名称、属性、构造函数、生命周期等等。我们借助BeanDefinition可以很简单的完整的创建出一个对象。

注册BeanDefinition

首先需要阐述一点,注册BeanDefinition不等于实例化bean,因为BeanDefinition元信息只能看作一个一个对象的组成部分,而实例化bean是需要有一个完整的BeanDefinition之后才能完成的。就如同堆积木一样,一块块的积木就是一个元信息,而最后堆出来的完整样式才是实例化出来的数据。我们要拼出完整的积木首先要准备好所有的积木。

将对象信息注册到BeanDefinition主要有如下几种方式:

  • • XML配置元信息
<bean name="user" class="cn.phshi.entity.User">
  <property name="name" value="张三"/>
  <property name="age" value="33"/>
</bean>

<bean name="supperUser" class="cn.phshi.entity.SupperUser"
      parent="user" scope="prototype" primary="true">
  <property name="phone" value="13311111111"/>
</bean>
  • • Java 注解配置元信息
  • • @Bean
public class RegisterBeanDefinitionDemo {
 
   public static void main(String[] args) {
     // 获取上下文对象
     AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
     applicationContext.register(RegisterBeanDefinitionDemo.class);
     applicationContext.refresh();
 
     Person person = applicationContext.getBean(Person.class);
     System.out.println(person);
   }
 
   // 注册bean信息
   @Bean
   public Person person() {
     // 创建了一个对象的同时其实也是把Person中class相关的元信息注册到容器中了
     return new Person("张三", 32);
   }
 
   public static class Person {
     private String name;
 
     private Integer age;
     // 。。。。 省略构造器,get/set方法喝toString方法
   }
 }
  • • @Component 常见方式,该处不做例子
  • • @Import
// 使用Import导入Config的配置信息
 @Import(RegisterBeanDefinitionDemo.Config.class)
 public class ImportBeanDefinitionDemo {
 
   public static void main(String[] args) {
     AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
     applicationContext.register(ImportBeanDefinitionDemo.class);
     applicationContext.refresh();
 
     RegisterBeanDefinitionDemo.Person bean = applicationContext.getBean(RegisterBeanDefinitionDemo.Person.class);
     System.out.println(bean);
   }
 }
 
 public class RegisterBeanDefinitionDemo {
 
   // 在ImportBeanDefinitionDemo中使用Import导入了该对象
   public static class Config{
     // 依旧是使用Bean方式注入
     @Bean
     public Person person() {
       return new Person("张三", 32);
     }
   }
 
   public static class Person {
     private String name;
 
     private Integer age;
     // 。。。。 省略构造器,get/set方法喝toString方法
   }
 }
  • • Java API 配置元信息
  • • BeanDefinitionRegistry:在上一章基础使用中将BeanDefinitionBuilder中有相关案例,案例中使用的beanfactoryDefaultListableBeanFactory,但是实际上DefaultListableBeanFactory就是继承的BeanDefinitionRegistry
  • • BeanDefinitionReaderUtils
public static void main(String[] args) {
   DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
 
   //Bean的定义信息 
   AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Person.class)
     .addPropertyValue("name", "王武")
     .addPropertyValue("age", 23)
     .getBeanDefinition();
 
   // 注册到DefaultListableBeanFactory中
   BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, beanFactory);
   Person bean = beanFactory.getBean(Person.class);
   System.out.println(bean);
 }
  • • AnnotatedBeanDefinitionReader
  • • SingletonBeanRegistry

Bean的命名

每个 Bean 拥有一个或多个标识符(identifiers),这些标识符在 Bean 所在的容器必须是唯一的。通常,一个 Bean 仅有一个标识符,如果需要额外的,可考虑使用别名(Alias)来 扩充。当然,Bean 的 id 或 name 属性并非必须制定,如果留空的话,容器会为 Bean 自动生成一个唯一 的名称。Bean 的命名尽管没有限制,不过官方建议采用驼峰的方式,更符合 Java 的命名约 定。

Spring中Bean名称生成器

Spring提供了一个接口BeanNameGenerator来自定义实现Bean的名称生成。类图如下:

Spring Bean基础_实例化_02

image-20230225181046807

他主要有三个实现:

  • • DefaultBeanNameGenerator: 默认通用 BeanNameGenerator 实现;
  • • AnnotationBeanNameGenerator: 基于注解扫描的 BeanNameGenerator 实现;
  • • FullyQualifiedAnnotationBeanNameGenerator: AnnotationBeanNameGenerator的一个扩展,主要是为了解决具有相同名称但存在于不同包中的类而导致命名冲突的问题。

Bean 的别名

我们可以在bean的id和name之外,为bean再提供多个名称,这样设置的意义在于我们可以对一个bean进行场景化命名,也就是可以允许在不同场景使用不同的名称获取bean,但是最后获得的对象是一样的。

public static void main(String[] args) {
  BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/bean-definitions-context.xml");
  User user = beanFactory.getBean("user", User.class);

  User yunNanUser = beanFactory.getBean("yunnan-user", User.class);

  System.out.println("获得的两个对象是否相等:" + (user == yunNanUser));
}

public class User {

  private String name;

  private Integer age;

  // 。。。。 省略get、set和toString方法
}

xml文件中的配置:

<bean name="user" class="cn.phshi.entity.User">
  <property name="name" value="张三"/>
  <property name="age" value="33"/>
</bean>

<alias name="user" alias="yunnan-user"/>

输出结果:

获得的两个对象是否相等:true

也可以使用@Bean的方式进行配置:

@Bean(name = {"user", "huansi-user"})
public User user() {
  User user = new User();
  user.setName("黄四");
  user.setAge(20);
  return user;
}

实例化Bean

实例化bean其实就是通过前面的BeanDefinition元信息来创建一个或者Bean的实例对象。

实例化Bean的方式有很多种,我们分别来看一下相关方式:

  • • 通过构造器实例化:

通过构造器进行实例化的方式比较常见,通常情况有XML配置方式,@Bean,@Component等注解的配置方式等,这种方式创建对象上面也演示了很多,这里就不再次赘述了。

  • • 通过静态工厂方法实例化:

静态工厂方法:

public final class UserStaticFactory {

    private static User user = new User("张三",54);

    private UserStaticFactory() {}

    public static User userInstance() {
        return user;
    }
}

XML配置文件:

<?xml versinotallow="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
                           http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
  <!-- 静态方法实例化 Bean -->
  <bean name="staticUser" class="cn.phshi.bean.factory.UserStaticFactory" factory-method="userInstance"/>
</beans>
  • • 通过实工厂方法实例化:

实例方法:

public class UserInstanceFactory {

  private static User user = new User("lisi",29);

  public User userInstance() {
    return user;
  }
}

XML配置文件:

<?xml versinotallow="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
                           http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">

  <!-- 实例化静态工厂 -->
  <bean name="userInstanceFactory" class="cn.phshi.bean.factory.UserInstanceFactory"/>
  <!--  使用实例化工厂创建对象  -->
  <bean name="user" factory-bean="userInstanceFactory" factory-method="userInstance"/>
</beans>
  • • 通过FactoryBean方式实例化:

FactoryBean对象:

public class UserFactoryBean implements FactoryBean {
  @Override
  public Object getObject() throws Exception {
    return new User("王五", 22);
  }

  @Override
  public Class<?> getObjectType() {
    return User.class;
  }
}

XML配置文件:

<?xml versinotallow="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
                           http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">

  <!-- FactoryBean实例化 Bean -->
  <bean name="factory-bean-user" class="cn.phshi.bean.factory.UserFactoryBean"/>
</beans>

初始化Bean

主要用于Bean创建时候做一些初始化操作

初始化Bean的方法有:

  • • @PostConstruct注解:
public class Person {

  private String name;

  private Integer age;

  // 改方法声明PostConstruct,将在创建Bean时候执行改方法
  @PostConstruct
  public void postConstruct() {
    System.out.println("@PostConstruct方法执行~~~");
  }
  
  // 省略构造器、get/set、toString方法
}
  • • 实现 InitializingBean 接口的 afterPropertiesSet() 方法
// 实现 InitializingBean 接口
public class Person implements InitializingBean {

  private String name;

  private Integer age;

  public Person(String name, Integer age) {
    this.name = name;
    this.age = age;
  }

  @Override
  public void afterPropertiesSet() throws Exception {
    System.out.println(" InitializingBean 接口的 afterPropertiesSet() 方法");
  }
  
  // 省略构造器、get/set、toString方法
}
  • • 自定义初始化方法
  • • 使用@Bean中initMethod定义的初始化方法
@Bean(initMethod = "initMethod")
 public Person person() {
   return new Person("张三", 33);
 }
  • • xml配置的方式
<bean name="user" class="cn.phshi.entity.User" init-method="initMethod">
  <property name="name" value="张三"/>
  <property name="age" value="33"/>
</bean>
  • • AbstractBeanDefinition#setInitMethodName(String)方式注入
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Person.class)
                .addPropertyValue("name", "王武")
                .addPropertyValue("age", 23)
                              // 使用BeanDefinition构建Bean的元信息
                .setInitMethodName("initMethod")
                .getBeanDefinition();

三种方式同时使用的执行顺序:

@PostConstruct -> InitializingBean -> @Bean

延迟初始化Bean

延迟初始化的方式:

  • • Java 注解:@Lazy(true)
  • • XML 配置:<bean lazy-init=”true” ... />

销毁Bean

和初始化相仿,主要作用是在Bean摧毁的时候完成Bean的资源释放操作。

Bean的摧毁方式有(由于和初始化相仿,这里不做示例):

  • • @PreDestroy 标注方法
  • • 实现 DisposableBean 接口的 destroy() 方法
  • • 自定义销毁方法
  • • Java 注解:@Bean(destroy=”destroy”)
  • • XML 配置:<bean destroy=”destroy” ... />
  • • Java API:AbstractBeanDefinition#setDestroyMethodName(String)

参考文章

什么是 BeanDefinition?


标签:name,Spring,基础,class,Person,Bean,public,BeanDefinition
From: https://blog.51cto.com/u_16115561/6471015

相关文章

  • springboot rabbitmq配置
    YMLrabbitmq:host:xxx.xxx.xxx.xxxport:5672virtual-host:devusername:xxxpassword:xxxpublisher-confirm-type:correlatedpublisher-returns:truelistener:direct:acknowledge-mode:autosimple:......
  • 完美解决SpringBoot上传图片之后,需要重服务才能访问
    上传图片后需要重新编译才能访问图片添加一个配置文件WebMvcConfigctrl+shift+alt+/选择Registry,勾选compiler.automake.allow.when.app.running勾选添加一个配置文件WebMvcConfigpackagecom.fans.common.config;importorg.springframework.context.annotation.Configu......
  • window下正常的springboot到mac下运行却报错
    Errorcreatingbeanwithname'defaultValidator'definedinclasspathresource[org/springframework/boot/autoconfigure/validation/ValidationAutoConfiguration.class]:Invocationofinitmethodfailed;nestedexceptionisjava.lang.NoClassDefFoun......
  • 公司已有springboot项目引入swagger
    公司已有springboot项目引入swagger1、swagger介绍官网:https://swagger.io/Swagger是一个用于生成、描述和调用RESTful接口的Web服务。通俗的来讲,Swagger就是将项目中所有(想要暴露的)接口展现在页面上,并且可以进行接口调用和测试的服务2、引入目的Swagger有以下3个重要的作......
  • Quarkus vs. Spring Boot: A head-to-head comparison
    作者:SamuelCatalanohttps://medium.com/@samuelcatalano/quarkus-vs-spring-boot-a-head-to-head-comparison-3b9502c0a345Whenitcomestobuildingmicroservicesandothercloud-nativeapplications,therearemanyoptionsavailabletodevelopers.Twopopularf......
  • 搭建springbootweb环境
    搭建springboot环境(idea环境)实现步骤:1.基础环境配置2.maven配置3.编写第一个程序helloworld(可能有两个小问题)4.运行(jar包运行,命令行运行)一.基础环境配置进入idea,点击file->new->project,在弹出的页面上,选择springinitiallzr并进行相关的配置点击next点击create,完成sp......
  • 【python基础】复杂数据类型-字典(嵌套)
    有时候,需要将一系列字典存储在列表中,或将列表作为值存储在字典中,这称为嵌套。我们可以在列表中嵌套字典、在字典中嵌套列表、在字典中嵌套字典。1.列表嵌套字典我们可以把一个人的信息放在字典中,但是多个人的信息我们无法放在同一个字典中,所以就需要字典列表。其语法格式:[字典......
  • spring boot连接Mybatis数据库的配置文件(MySql、SQLserver、Oracle)
    序号类型地址1MySQLMySQL操作之概念、SQL约束(一)2MySQLMySQL操作之数据定义语言(DDL)(二)3MySQLMySQL操作之数据操作语言(DML)(三)4MySQLMySQL操作之数据查询语言:(DQL)(四-1)(单表操作)5MySQLMySQL操作之数据查询语言:(DQL)(四-2)(多表查询)6MySQLMySQL操作之数据控制语言:(DC)(五)7MySQLMySQL操作之数......
  • Spring Cloud Gateway简单使用
    文章目录一、简介1、功能特点:2、术语解释3、网关技术二、快速开始1、创建Springboot工程2、启动引导类开启注册中心Eureka客户端发现3、配置文件`appliation.yml`4、编写路由规则三、路由配置(转发)(predicates)1、Query属性2、Header3、Method4、RemoteAddr5、Host6、Cookie7、B......
  • docker基础安装使用
    文章目录一、简介1、虚拟化2、Docker组件2.1Docker服务器与客户端2.2Docker镜像与容器2.3Registry(注册中⼼)3、小结4、Docker和虚拟机的区别二、安装Docker1、安装docker2、设置`ustc`的镜像3、docker的启动与停止三、常用命令1、镜像相关命令1.1查看镜像1.2搜索镜像1.3拉取......