定义Bean的方法图解
相关接口介绍
BeanFactory接口
BeanFactory
是Spring框架中的一个核心接口,它提供了高级的工厂模式实现,能够管理和维护应用对象的生命周期和依赖关系。BeanFactory
是Spring容器的根接口,为Spring容器的基础行为提供了规范。
在Spring中,BeanFactory
提供了以下基本功能:
-
Bean的实例化/创建:容器通过读取配置源(如XML、注解、Java配置)来创建和管理应用对象(称为beans)。
-
依赖注入:
BeanFactory
容器会自动处理对象间的依赖关系。当你获取一个bean时,所有它所需的依赖也会被自动注入。 -
Bean的配置:
BeanFactory
允许配置bean的各种属性,包括作用域(如单例或原型)、生命周期回调等。 -
资源管理:它提供了一种机制来访问应用资源,如文件和类路径资源。
-
事件发布:它支持事件的发布,允许beans之间进行松耦合的通信。
-
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)等。
ApplicationContext
是BeanFactory
的超集,它在BeanFactory
的简单容器功能之上添加了更多的企业支持。然而,在一些资源受限或者需要更精细控制Spring容器创建过程的场景中,直接使用BeanFactory
可能更合适。
ApplicationContext接口
在Spring框架中,ApplicationContext
是一个核心接口,它为应用程序提供了配置信息。ApplicationContext
代表了Spring IoC容器,并负责实例化、配置和组装上述的Bean。这个接口在Spring中非常重要,因为它提供了Spring容器的高级功能,例如解析消息、支持国际化、事件传播、创建各种不同类型的Bean以及多种层次的容器(如父子容器结构)等。
ApplicationContext接口的子接口
ClassPathXmlApplicationContext接口
ClassPathXmlApplicationContext
是 ApplicationContext
接口的实现之一,它从类路径下的一个或多个XML文件加载配置信息,启动并初始化Spring容器。通过这种方式,Spring容器可以管理我们在配置文件中所声明的beans,并在整个应用程序中执行依赖注入。
当我们这样写代码:
ApplicationContext ac = new ClassPathXmlApplicationContext("springbean.xml");
我们实际上是在做以下几件事情:
- 创建
ApplicationContext
实例,这里选择ClassPathXmlApplicationContext
作为实现,这意味着Spring将会从类路径(classpath)中加载springbean.xml
配置文件。 - 使用
springbean.xml
文件中定义的配置信息来初始化Spring IoC容器。 - 返回
ApplicationContext
类型的对象ac
,它是对Spring IoC容器的引用。我们通过这个引用可以获取容器管理的bean,以及使用容器提供的其他服务。
使用 ApplicationContext
类型作为返回值的原因是因为它是一个接口,而接口类型的引用能够指向任何实现了该接口的对象。这样,我们就能够使用不同的 ApplicationContext
实现(例如 FileSystemXmlApplicationContext
、AnnotationConfigApplicationContext
等),而代码其余部分则不需要任何改变。这是一种编程中常用的设计原则,即“面向接口编程”,它可以提高代码的灵活性和可维护性。
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的定义
- 构建BeanDefinition的Bean定义对象
- 创建IOC容器,将BeanDefinition注册至IOC容器中,刷新
- 获取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
,你可以链式地调用方法来设置你想要配置的属性,而不是手动地设置一个个属性。
下面是这段代码的分步解析:
-
BeanDefinitionBuilder.genericBeanDefinition()
: 这个方法创建了一个BeanDefinitionBuilder
实例,用于构建一个通用的bean定义。如果需要指定bean的类型,你也可以将类对象作为参数传递给genericBeanDefinition(Class<?> beanClass)
方法。 -
.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的实现原理主要基于以下几个核心概念:
-
ApplicationContext
的变种:Spring提供了GenericApplicationContext
和AnnotationConfigApplicationContext
等容器实现,这些容器实现支持函数式风格的bean注册。 -
BeanDefinition
的动态注册:函数式风格利用GenericApplicationContext
的registerBean
方法动态地注册BeanDefinition
。你可以在运行时构建和注册BeanDefinition
,而不是在应用启动之前通过配置文件或注解静态定义。 -
Functional
接口:Java 8引入了Functional
接口,它允许将函数作为一等公民使用。Spring利用这一特性,通过Supplier<T>
,Function<T, R>
,Consumer<T>
, 和Runnable
等函数式接口来声明bean的创建逻辑。 -
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);
}
}