2024-03-12 09:58:09 +00:00
|
|
|
|
## BeanResolver
|
|
|
|
|
|
|
|
|
|
- [BeanResolver](#BeanResolver)
|
|
|
|
|
- [一、基本信息](#一基本信息)
|
|
|
|
|
- [二、知识储备](#二知识储备)
|
|
|
|
|
- [三、基本描述](#三基本描述)
|
|
|
|
|
- [四、主要功能](#四主要功能)
|
|
|
|
|
- [五、接口源码](#五接口源码)
|
|
|
|
|
- [六、主要实现](#六主要实现)
|
|
|
|
|
- [七、最佳实践](#七最佳实践)
|
|
|
|
|
- [八、与其他组件的关系](#八与其他组件的关系)
|
|
|
|
|
- [九、常见问题](#九常见问题)
|
|
|
|
|
|
|
|
|
|
### 一、基本信息
|
|
|
|
|
|
|
|
|
|
✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading)
|
|
|
|
|
|
|
|
|
|
### 二、知识储备
|
|
|
|
|
|
|
|
|
|
1. **Spring表达式语言(SpEL)**
|
|
|
|
|
|
|
|
|
|
+ 了解SpEL的语法和用法,包括如何使用表达式来访问和操作对象的属性、调用方法等。因为`BeanResolver`通常用于在SpEL中解析Bean,所以对SpEL的理解至关重要。
|
|
|
|
|
|
|
|
|
|
2. **Spring容器**
|
|
|
|
|
|
|
|
|
|
+ 理解Spring容器的概念和工作原理,包括BeanFactory和ApplicationContext之间的区别、Bean的生命周期、Bean的作用域等。因为`BeanResolver`通常用于从Spring容器中解析Bean,所以对Spring容器的了解是必要的。
|
|
|
|
|
|
|
|
|
|
3. **反射(Reflection)**
|
|
|
|
|
|
|
|
|
|
+ 理解Java反射的基本原理和用法,包括如何在运行时获取和操作类的信息。`BeanResolver`通常需要使用反射来动态地创建和操作对象,所以对反射有一定的了解是很有帮助的。
|
|
|
|
|
|
|
|
|
|
4. **设计模式**
|
|
|
|
|
|
|
|
|
|
+ 了解设计模式的基本原理和常见的设计模式,如工厂模式、策略模式等。因为`BeanResolver`接口通常用于实现依赖注入等功能,对设计模式的了解有助于编写更灵活、可扩展的解决方案。
|
|
|
|
|
|
|
|
|
|
### 三、基本描述
|
|
|
|
|
|
|
|
|
|
`BeanResolver`接口是Spring框架中的一个关键接口,用于在Spring表达式语言(SpEL)中解析Bean。它定义了一个`resolve`方法,接收一个`EvaluationContext`对象和一个Bean的名称作为参数,然后返回相应的Bean实例。通过实现`BeanResolver`接口,可以在SpEL表达式中轻松地引用和操作Spring容器中的Bean,使得表达式更加灵活和强大。
|
|
|
|
|
|
|
|
|
|
### 四、主要功能
|
|
|
|
|
|
|
|
|
|
1. **解析Bean**
|
|
|
|
|
|
|
|
|
|
+ 提供了解析Bean的方法,可以根据给定的Bean名称从Spring容器中获取相应的Bean实例。这样可以在运行时动态地获取和操作Spring容器中的Bean。
|
|
|
|
|
|
|
|
|
|
2. **支持SpEL**
|
|
|
|
|
|
|
|
|
|
+ 作为Spring表达式语言(SpEL)的一部分,`BeanResolver`允许在SpEL表达式中引用和操作Spring容器中的Bean。通过`BeanResolver`,可以在表达式中使用特殊的语法来引用Bean,并进行各种操作,如访问属性、调用方法等。
|
|
|
|
|
|
|
|
|
|
3. **提供上下文支持**
|
|
|
|
|
|
|
|
|
|
+ `BeanResolver`接口通常与`EvaluationContext`对象一起使用,它提供了表达式所需的上下文信息,包括变量、函数等。这样可以确保在解析Bean时具有必要的上下文信息。
|
|
|
|
|
|
|
|
|
|
4. **定制解析逻辑**
|
|
|
|
|
|
|
|
|
|
+ 尽管默认情况下`BeanResolver`的实现由Spring容器提供,但你可以根据需要自定义`BeanResolver`的实现。这样可以灵活地定制Bean的解析逻辑,以满足特定的业务需求。例如,你可以实现一个特殊的`BeanResolver`,用于按照特定的规则解析Bean,或者从非标准的地方获取Bean实例。
|
|
|
|
|
|
|
|
|
|
5. **解耦业务逻辑**
|
|
|
|
|
|
|
|
|
|
+ 通过使用`BeanResolver`,可以将业务逻辑与具体的Bean获取方式解耦。这样,在不同的环境或场景下,可以轻松地切换和替换`BeanResolver`的实现,而不影响业务逻辑的其他部分。
|
|
|
|
|
|
|
|
|
|
### 五、接口源码
|
|
|
|
|
|
|
|
|
|
`BeanResolver` 接口允许在Spring的表达式语言(SpEL)中解析Bean引用。通过注册到评估上下文中,它可以根据给定的Bean名称查找相应的实例,支持使用 `@myBeanName` 和 `&myBeanName` 表达式来引用Bean,其中 `&` 前缀允许访问工厂Bean。
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
/**
|
|
|
|
|
* BeanResolver接口可以注册到评估上下文中,并且会为Bean引用 {@code @myBeanName} 和 {@code &myBeanName} 表达式提供解析支持。
|
|
|
|
|
* 当需要访问工厂Bean时,{@code &} 变体语法允许访问工厂Bean。
|
|
|
|
|
*
|
|
|
|
|
* @author Andy Clement
|
|
|
|
|
* @since 3.0.3
|
|
|
|
|
*/
|
|
|
|
|
public interface BeanResolver {
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 根据给定的名称查找Bean,并返回相应的实例。
|
|
|
|
|
* 对于尝试访问工厂Bean,名称需要以 <tt>&</tt> 前缀。
|
|
|
|
|
* @param context 当前的评估上下文
|
|
|
|
|
* @param beanName 要查找的Bean的名称
|
|
|
|
|
* @return 表示Bean的对象
|
|
|
|
|
* @throws AccessException 如果解析Bean时出现意外问题
|
|
|
|
|
*/
|
|
|
|
|
Object resolve(EvaluationContext context, String beanName) throws AccessException;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
`BeanFactoryResolver` 实现了 `BeanResolver` 接口,用于在Spring的Bean工厂中解析Bean。它包含一个构造函数用于初始化,并实现了 `resolve` 方法来根据给定的Bean名称从Bean工厂中获取相应的Bean实例。
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
/**
|
|
|
|
|
* 用于与Spring的Bean工厂交互的EL bean解析器。
|
|
|
|
|
*
|
|
|
|
|
* @author Juergen Hoeller
|
|
|
|
|
* @since 3.0.4
|
|
|
|
|
*/
|
|
|
|
|
public class BeanFactoryResolver implements BeanResolver {
|
|
|
|
|
|
|
|
|
|
private final BeanFactory beanFactory;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 为给定的工厂创建一个新的BeanFactoryResolver。
|
|
|
|
|
*
|
|
|
|
|
* @param beanFactory 要解析Bean名称的Bean工厂
|
|
|
|
|
*/
|
|
|
|
|
public BeanFactoryResolver(BeanFactory beanFactory) {
|
|
|
|
|
Assert.notNull(beanFactory, "BeanFactory must not be null");
|
|
|
|
|
this.beanFactory = beanFactory;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public Object resolve(EvaluationContext context, String beanName) throws AccessException {
|
|
|
|
|
try {
|
|
|
|
|
return this.beanFactory.getBean(beanName);
|
|
|
|
|
} catch (BeansException ex) {
|
|
|
|
|
throw new AccessException("无法根据Bean工厂解析Bean引用", ex);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 六、主要实现
|
|
|
|
|
|
|
|
|
|
1. **BeanFactoryResolver**
|
2024-03-12 10:01:47 +00:00
|
|
|
|
|
2024-03-12 09:58:09 +00:00
|
|
|
|
+ `BeanFactoryResolver` 是一个实现了 `BeanResolver` 接口的类,在 Spring 框架中用于从 Bean 工厂中解析 Bean。通过构造函数接收一个 BeanFactory 实例,在调用 resolve 方法时,根据给定的 Bean 名称从 BeanFactory 中获取相应的 Bean 实例。
|
|
|
|
|
|
|
|
|
|
### 七、最佳实践
|
|
|
|
|
|
|
|
|
|
使用 `BeanResolver` 接口和 `BeanFactoryResolver` 实现类来解析 Spring 容器中的 Bean。首先,通过注解配置方式创建了一个 BeanFactory,然后使用 SpEL 表达式解析器解析表达式 `@myBean`,并将 `BeanFactoryResolver` 设置为评估上下文的 BeanResolver。最后,通过解析 SpEL 表达式获取到相应的 Bean 实例,并进行打印输出。
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
public class BeanResolverDemo {
|
|
|
|
|
public static void main(String[] args) {
|
|
|
|
|
// 创建 BeanFactory
|
|
|
|
|
// 这里使用注解配置的方式创建 BeanFactory
|
|
|
|
|
BeanFactory beanFactory = new AnnotationConfigApplicationContext(MyBean.class).getBeanFactory();
|
|
|
|
|
|
|
|
|
|
// 创建一个SpEL表达式解析器
|
|
|
|
|
ExpressionParser parser = new SpelExpressionParser();
|
|
|
|
|
|
|
|
|
|
// 创建一个标准的评估上下文
|
|
|
|
|
StandardEvaluationContext context = new StandardEvaluationContext();
|
|
|
|
|
// 将 BeanFactoryResolver 设置为上下文的 BeanResolver
|
|
|
|
|
context.setBeanResolver(new BeanFactoryResolver(beanFactory));
|
|
|
|
|
|
|
|
|
|
// 解析 SpEL 表达式,获取 Bean 实例
|
|
|
|
|
Object myBean = parser.parseExpression("@myBean").getValue(context);
|
|
|
|
|
|
|
|
|
|
// 打印 Bean 实例
|
|
|
|
|
System.out.println("myBean = " + myBean);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
运行结果,表达式 `@myBean` 成功解析,并获取到了名为 `myBean` 的 Bean 实例。
|
|
|
|
|
|
|
|
|
|
```properties
|
|
|
|
|
myBean = com.xcs.spring.MyBean@34123d65
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 八、与其他组件的关系
|
|
|
|
|
|
|
|
|
|
1. **ExpressionParser**
|
|
|
|
|
|
|
|
|
|
+ `ExpressionParser` 是 Spring 框架中用于解析表达式的接口,它通常与 `BeanResolver` 接口一起使用。`ExpressionParser` 负责解析 SpEL 表达式,而 `BeanResolver` 则负责解析表达式中的 Bean 引用。
|
|
|
|
|
|
|
|
|
|
2. **EvaluationContext**
|
|
|
|
|
|
|
|
|
|
+ `EvaluationContext` 是 Spring 表达式解析过程中的上下文对象,用于提供表达式所需的变量、函数等信息。`BeanResolver` 接口通常作为 `EvaluationContext` 的一部分,用于解析表达式中的 Bean 引用。
|
|
|
|
|
|
|
|
|
|
3. **BeanFactory**:
|
|
|
|
|
|
|
|
|
|
+ `BeanFactory` 是 Spring 框架中的核心接口之一,用于管理和获取 Bean 实例。`BeanFactoryResolver` 类实现了 `BeanResolver` 接口,用于在 BeanFactory 中解析 Bean 引用。
|
|
|
|
|
|
|
|
|
|
### 九、常见问题
|
|
|
|
|
|
|
|
|
|
1. **如何自定义 BeanResolver 的实现?**
|
2024-03-12 10:01:47 +00:00
|
|
|
|
|
2024-03-12 09:58:09 +00:00
|
|
|
|
- 我们可能想要根据特定需求自定义 `BeanResolver` 的实现,例如从不同的数据源中获取 Bean 实例。解决方法通常包括创建一个新的类实现 `BeanResolver` 接口,并根据需要覆盖 `resolve` 方法。
|
2024-03-12 10:01:47 +00:00
|
|
|
|
|
2024-03-12 09:58:09 +00:00
|
|
|
|
2. **如何处理 Bean 解析失败的情况?**
|
2024-03-12 10:01:47 +00:00
|
|
|
|
|
2024-03-12 09:58:09 +00:00
|
|
|
|
- 当 `BeanResolver` 无法解析请求的 Bean 时,可能会抛出异常。开发人员需要考虑如何处理这种异常情况,例如记录日志、返回默认值或者向用户提供友好的错误消息。
|
2024-03-12 10:01:47 +00:00
|
|
|
|
|
2024-03-12 09:58:09 +00:00
|
|
|
|
3. **如何在 SpEL 表达式中引用 Bean?**
|
2024-03-12 10:01:47 +00:00
|
|
|
|
|
2024-03-12 09:58:09 +00:00
|
|
|
|
- 我们可能需要在 SpEL 表达式中引用 Spring 容器中的 Bean,以便执行特定的逻辑。通常情况下,可以使用 `@beanName` 或 `&beanName` 表达式来引用 Bean,其中 `@` 表示获取 Bean 实例,`&` 表示获取 Bean 的工厂实例。
|
2024-03-12 10:01:47 +00:00
|
|
|
|
|
2024-03-12 09:58:09 +00:00
|
|
|
|
4. **如何解决循环依赖问题?**
|
2024-03-12 10:01:47 +00:00
|
|
|
|
|
2024-03-12 09:58:09 +00:00
|
|
|
|
- 当存在循环依赖的 Bean 时,可能会导致 `BeanResolver` 无法正常解析 Bean。我们需要谨慎设计 Bean 之间的依赖关系,或者使用延迟初始化等技术来解决循环依赖问题。
|