创建Bean的方法

定义Bean的方法图解

在这里插入图片描述

相关接口介绍

BeanFactory接口

BeanFactory是Spring框架中的一个核心接口,它提供了高级的工厂模式实现,能够管理和维护应用对象的生命周期和依赖关系。BeanFactory是Spring容器的根接口,为Spring容器的基础行为提供了规范。

在Spring中,BeanFactory提供了以下基本功能:

  1. Bean的实例化/创建:容器通过读取配置源(如XML、注解、Java配置)来创建和管理应用对象(称为beans)。

  2. 依赖注入BeanFactory容器会自动处理对象间的依赖关系。当你获取一个bean时,所有它所需的依赖也会被自动注入。

  3. Bean的配置BeanFactory允许配置bean的各种属性,包括作用域(如单例或原型)、生命周期回调等。

  4. 资源管理:它提供了一种机制来访问应用资源,如文件和类路径资源。

  5. 事件发布:它支持事件的发布,允许beans之间进行松耦合的通信。

  6. Bean的查找:可以通过名称、类型或其他属性来查找beans。

BeanFactory接口定义了几个获取bean的方法,最基本的是getBean方法,它有多个变体,如:

  • getBean(String name):根据bean的名称来获取一个bean实例。
  • getBean(String name, Class<T> requiredType):根据bean的名称和类型来获取一个bean实例。
  • getBean(Class<T> requiredType):根据bean的类型来获取一个bean实例。

在实际应用中,通常不会直接使用BeanFactory,而是使用它的子接口ApplicationContext,它提供了更多的企业级功能,如更好的事件发布机制、国际化支持、应用层面的上下文(如WebApplicationContext)等。

ApplicationContextBeanFactory的超集,它在BeanFactory的简单容器功能之上添加了更多的企业支持。然而,在一些资源受限或者需要更精细控制Spring容器创建过程的场景中,直接使用BeanFactory可能更合适。

ApplicationContext接口

在Spring框架中,ApplicationContext 是一个核心接口,它为应用程序提供了配置信息。ApplicationContext 代表了Spring IoC容器,并负责实例化、配置和组装上述的Bean。这个接口在Spring中非常重要,因为它提供了Spring容器的高级功能,例如解析消息、支持国际化、事件传播、创建各种不同类型的Bean以及多种层次的容器(如父子容器结构)等。

ApplicationContext接口的子接口

ClassPathXmlApplicationContext接口

ClassPathXmlApplicationContextApplicationContext 接口的实现之一,它从类路径下的一个或多个XML文件加载配置信息,启动并初始化Spring容器。通过这种方式,Spring容器可以管理我们在配置文件中所声明的beans,并在整个应用程序中执行依赖注入。

当我们这样写代码:

ApplicationContext ac = new ClassPathXmlApplicationContext("springbean.xml");

我们实际上是在做以下几件事情:

  1. 创建 ApplicationContext 实例,这里选择 ClassPathXmlApplicationContext 作为实现,这意味着Spring将会从类路径(classpath)中加载 springbean.xml 配置文件。
  2. 使用 springbean.xml 文件中定义的配置信息来初始化Spring IoC容器。
  3. 返回 ApplicationContext 类型的对象 ac,它是对Spring IoC容器的引用。我们通过这个引用可以获取容器管理的bean,以及使用容器提供的其他服务。

使用 ApplicationContext 类型作为返回值的原因是因为它是一个接口,而接口类型的引用能够指向任何实现了该接口的对象。这样,我们就能够使用不同的 ApplicationContext 实现(例如 FileSystemXmlApplicationContextAnnotationConfigApplicationContext 等),而代码其余部分则不需要任何改变。这是一种编程中常用的设计原则,即“面向接口编程”,它可以提高代码的灵活性和可维护性。

AnnotationConfigApplicationContext接口

AnnotationConfigApplicationContext 是 Spring 框架中的一个功能,它是 ApplicationContext 接口的一个实现,用于从一个或多个基于Java的配置类中加载 Spring 应用上下文。
传统上,Spring 使用 XML 配置文件来配置容器和管理 Bean。然而,随着 Spring 的发展,基于注解的配置变得越来越流行,因为它使得配置更加简洁,并且可以将配置信息保留在与其相关的 Java 类中。AnnotationConfigApplicationContext 支持这种基于 Java 的配置,并且允许开发者通过 Java 类和注解来完成容器的配置,而不是使用 XML。

声明式定义Bean

1.方式一:ClassPathXmlApplicationContext从类路径下加载XML文件

在类路径resources,目录下,创建Spring的XML文件

定义一个User类:

public class User {
    private String username;
    private int age;
    public User() {
    }
    public User(String username, int age) {
        this.username = username;
        this.age = age;
    }
}
//getter,setter,toString方法省略未显示

在main目录下新建一个resource目录,并将其Mark Directory as 为Resources Root,新建一个springbean.xml文件

<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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--     配置bean (BeanDefinition对象  (String beanName, BeanDefinition beanDefinition) -->
<!--     id属性:作为唯一标识  一般以类名首字母小写  作为String beanName -->
<!--     class属性:全类名 利用反射机制根据全类名创建实例 -->
<bean id="user" class="com.igeek.xml.User">
<property name="username" value="小明"/>
<property name="age" value="29"/>
</bean>
<bean id="date" class="java.util.Date"/>
</beans>

获取并使用Bean

public static void main(String[] args) {
        //传统创建对象  主动拥有对象的控制权力
       /* User user = new User();
        user.setUsername("张三");
        user.setAge(20);
        System.out.println("user = " + user);*/

        //通过spring的IOC进行管理 bean --> 对象的控制权力反转
        //1.创建IOC容器(通过加载类路径resources目录下 springbean.xml)
        ApplicationContext ac = new ClassPathXmlApplicationContext("springbean.xml");
        //2.从IOC容器中获取实例 ac.getBean("bean-id属性值",bean实例类类型)
        User user = ac.getBean("user",User.class);
        //3.使用
//        user.setUsername("李四");
//        user.setAge(22);
        System.out.println("user = " + user);

        Date date1 = ac.getBean("date", Date.class);
        System.out.println("date1 = " +date1);

        Date date2 = ac.getBean("date", Date.class);
        System.out.println("date2 = " + date2);

        //默认IOC容器中都是单例的 true
        System.out.println(date1 == date2);

2.方式二:AnnotationConfigApplicationContext加载配置类+注解

2.1 JavaConfig配置类 扫描包@ComponentScan+实例@Component

定义一个Person类,用@Component注解修饰.这个注解告诉 Spring 这个类的实例应该被创建并作为 Bean 管理

//@Component 组件注解 直接将当前实例加入到IOC容器中 默认使用类名首字母小写作为唯一标识 例如:person
@Component
public class Person {
    //使用@Value注解,对该属性注入值
    @Value("李四")
    private String username;
    @Value("22")
    private String password;

    public Person() {
    }

    public Person(String username, String password) {
        this.username = username;
        this.password = password;
    }
}

创建一个配置类MyConfig,用@ComponentScan注解修饰;当你的应用程序启动时,Spring 会扫描指定的包及其子包,查找所有标记为 @Component(及其特化注解 @Service@Repository@Controller)的类,并为它们创建 Bean。

@ComponentScan("com.igeek.config.ch01")
public class MyConfig {

}

测试类:

public class MainTest {
    public static void main(String[] args) {
        //1.创建一个IOC容器 指定配置类的类型
        //AnnotationConfigApplicationContext 用于创建 Spring 应用程序上下文,并传入配置类 MyConfig
        ApplicationContext ac = new AnnotationConfigApplicationContext(MyConfig.class);
        //2.获取person实例--@Component +@ComponentScan
       // Person person = ac.getBean("person", Person.class);
        //3.使用
//        person.setUsername("张三");
//        person.setPassword("123");
       // System.out.println("person = " + person);
    }
}

2.2 配置类+@Bean

@Configuration
public class MyConfig {

    //config配置类+@Bean  方法名称就是实例的标识id 将当前方法的返回值存放到IOC容器中
    @Bean
    public Person pp(){
        return new Person("丽丽","22");
    }
}
public class MainTest {
    public static void main(String[] args) {
    //1.创建一个IOC容器 指定配置类的类型
        ApplicationContext ac = new AnnotationConfigApplicationContext(MyConfig.class);
        //2.获取person实例--@Bean
        Person pp = ac.getBean("pp", Person.class);
        System.out.println("pp = " + pp);
    }
}

编程式定义Bean

方式一:BeanDefinition Bean的定义

  1. 构建BeanDefinition的Bean定义对象
  2. 创建IOC容器,将BeanDefinition注册至IOC容器中,刷新
  3. 获取IOC容器中的实例Bean,且使用

创建一个员工类Employee

public class Employee {
    private String name;
    private double salary;

    public Employee() {
    }

    public Employee(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }
    }
public class BeanDefinitionTest {
    public static void main(String[] args) {
        //1.获取IOC容器
        AnnotationConfigApplicationContext  ac = new AnnotationConfigApplicationContext();

        //2.通过BeanDefinition 直接注册bean的定义 到IOC容器中
        //2.1构建beanDefinition的bean定义对象
        AbstractBeanDefinition beanDefinition =
             BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
        //2.2 设置类的类型
        beanDefinition.setBeanClass(Employee.class);
        //2.3 设置类的属性信息 作用域 singleton单例   prototype原型(非单例)
        beanDefinition.setScope("prototype");
        //2.4 将beanDefinition放入到IOC容器中ac.registerBeanDefinition(唯一标识,bean定义对象)
        ac.registerBeanDefinition("emp",beanDefinition);
        //2.5 手动刷新
        ac.refresh();

        //3.获取实例bean
        Employee emp1 = ac.getBean("emp", Employee.class);
        emp1.setName("小红");
        emp1.setSalary(10000.0);
        System.out.println("emp1 = " + emp1);
        System.out.println("emp1.hashCode() = " + emp1.hashCode());
        //当作用域是prototype时,两个员工实例是不一样的  523691575

        Employee emp2 = ac.getBean("emp", Employee.class);
        emp2.setName("小李");
        emp2.setSalary(20000.0);
        System.out.println("emp2 = " + emp2);
        System.out.println("emp2.hashCode() = " + emp2.hashCode());//1468303011
    }
}

AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();

详细解释一下这句代码:

在Spring框架中,AbstractBeanDefinition是用于创建bean定义的一个抽象类。BeanDefinition是Spring IOC容器中的核心接口,它包含了bean的所有配置信息,比如bean的类类型、作用域、生命周期回调等。

BeanDefinitionBuilder是一个构建器(helper)类,用于提供一种流式的方式来构建BeanDefinition对象。通过BeanDefinitionBuilder,你可以链式地调用方法来设置你想要配置的属性,而不是手动地设置一个个属性。

下面是这段代码的分步解析:

  1. BeanDefinitionBuilder.genericBeanDefinition(): 这个方法创建了一个BeanDefinitionBuilder实例,用于构建一个通用的bean定义。如果需要指定bean的类型,你也可以将类对象作为参数传递给genericBeanDefinition(Class<?> beanClass)方法。

  2. .getBeanDefinition(): 这个方法从BeanDefinitionBuilder获取一个配置好的BeanDefinition实例。在调用这个方法之前,可以链式调用其他方法来进一步配置bean,比如.addPropertyValue("propertyName", value)来设置属性值。

所以,这行代码的目的是使用BeanDefinitionBuilder来创建一个AbstractBeanDefinition实例。这种做法的优点是:

  • 可读性和易用性:使用构建器模式可以使代码更加清晰和易于理解,特别是在配置复杂的bean定义时。
  • 灵活性:通过编程方式定义bean,你可以在运行时动态地构建和配置bean,这在某些复杂的应用场景中非常有用。
  • 避免错误:构建器模式可以减少因直接设置属性而可能引入的错误,因为它提供了一种更加流畅和类型安全的方式来构建对象。

这种方式通常在需要以编程方式定义Spring beans,而不是使用XML配置或注解的场景中使用。例如,当你在编写一个自定义的Spring配置类或模块时,可能会需要动态地注册bean定义。

方式二:FactoryBean Bean工厂

@ComponentScan(“扫描包”) +@Component(“beanName”)

1.实现implements FactoryBean接口,重写TgetObject(),将返回值注册至IOC容器中
2获取IOC容器中的实例Bean.且使用

/**
 * @Description FactoryBean接口
 * FactoryBean接口:
 * 使用方式:实现FactoryBean接口 再添加@Component注解
 * 1.Object getObject() 返回注册在IOC容器中的实例
 * 2.Class<?> getObjectType()返 回注册在IOC容器中的实例的类类型
 * 3.boolean isSingleton() 是否是单例 true代表单例
 *
 * 简单工厂设计模式:
 * BeanFactory接口:通过beanName获取实例bean 根据beanName返回某个类的实例
 * @Bean定义的Bean是会经过完整的Bean生命周期的。
 *
 *
 * 工厂方法设计模式: DaoFactory ServiceFactory
 * FactoryBean接口 :只负责某个对象的实例化  通过getObject()方法返回该对象
 * 通过这种方式创造出来的Bean,只会经过初始化后,其他Spring的生命周期步骤是不会经过的,比如依赖注入。
 */
//@Component("指定bean-id")
@Component("emp2")
public class MyFactoryBean  implements FactoryBean<Employee> {
    @Override
    public Employee getObject() throws Exception {
        return new Employee();
    }

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

    @Override
    public boolean isSingleton() {
        return true;
    }
}
@ComponentScan("com.igeek.config.ch02")
public class FactoryBeanTest {
    public static void main(String[] args) {
        //1.创建IOC容器
        AnnotationConfigApplicationContext ac =
                new AnnotationConfigApplicationContext(FactoryBeanTest.class);
        //2.获取bean
        String[] beanDefinitionNames = ac.getBeanDefinitionNames();
        System.out.println(Arrays.toString(beanDefinitionNames));

//找到FactoryBean的信息 myFactoryBean(默认使用 类名首字母小写 作为bean-id)
//        Object bean = ac.getBean("myFactoryBean");
        Object bean = ac.getBean("emp2");
        System.out.println("bean.getClass() = " + bean.getClass());
        //class com.igeek.config.ch02.Employee
    }
}

方式三:函数式风格

GenericApplicationContext函数式风格:

在Spring框架中,函数式风格(Functional Style)定义Bean是一种基于Java函数式编程特性的方式,它允许开发者通过lambda表达式或方法引用来注册和配置beans。这种方式从Spring 5开始引入,特别是在响应式编程模型Spring WebFlux中得到了广泛应用。

函数式风格定义Bean的实现原理主要基于以下几个核心概念:

  1. ApplicationContext的变种:Spring提供了GenericApplicationContextAnnotationConfigApplicationContext等容器实现,这些容器实现支持函数式风格的bean注册。

  2. BeanDefinition的动态注册:函数式风格利用GenericApplicationContextregisterBean方法动态地注册BeanDefinition。你可以在运行时构建和注册BeanDefinition,而不是在应用启动之前通过配置文件或注解静态定义。

  3. Functional接口:Java 8引入了Functional接口,它允许将函数作为一等公民使用。Spring利用这一特性,通过Supplier<T>, Function<T, R>, Consumer<T>, 和Runnable等函数式接口来声明bean的创建逻辑。

  4. BeanDefinitionCustomizer:这是一个函数式接口,允许在注册bean时对BeanDefinition进行自定义操作。

函数式风格定义Bean的优势在于它的简洁性和灵活性。它允许开发者以声明式和编程式相结合的方式定义beans,同时也使得bean的定义更加紧凑和易于理解。此外,它还提供了更好的控制,允许在运行时动态地注册和配置beans,这在编写自动化测试或模块化配置时非常有用。

代码演示:

/**
 *GenericApplicationContext函数式风格:
 * GenericApplicationContext+Supplier<T> 供给型接口
 * 通过Supplier<T> 供给型接口的返回值 将返回值注册到IOC容器中
 *
 */
public class GenericApplicationContextTest {
    public static void main(String[] args) {
        //1.创建IOC容器
        //AnnotationConfigApplicationContext extends  GenericApplicationContext
        AnnotationConfigApplicationContext ac =
                new AnnotationConfigApplicationContext();
        //2.注册实例bean
        //ac.registerBean(实例的beanid,实例的类类型,存入到ioc中的实例)
        //registerBean方法接受一个bean名称、bean类和一个Supplier函数式接口的实例。Supplier提供了bean的实例化逻辑
        ac.registerBean("emp3",Employee.class,()->new Employee("哈哈哈",10000.0));
        //手动刷新refresh方法用于初始化ApplicationContext,完成bean的注册。
        ac.refresh();

        //3.获取实例
        Employee emp3 = ac.getBean("emp3", Employee.class);
        System.out.println("emp3 = " + emp3);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值