[代码重构](master): Java文档更新

Spring错误排查2
master
土豆兄弟 2 years ago
parent 64c6903d18
commit 2e4a09ca3b

@ -431,6 +431,130 @@ DataService dataService;
@Component @Component
- 一旦找出这些 Bean 的信息,就可以生成这些 Bean 的名字然后组合成一个个BeanDefinitionHolder 返回给上层。这个过程关键步骤可以查看下图的代码片段 - 一旦找出这些 Bean 的信息,就可以生成这些 Bean 的名字然后组合成一个个BeanDefinitionHolder 返回给上层。这个过程关键步骤可以查看下图的代码片段
ClassPathBeanDefinitionScanner#doScan ClassPathBeanDefinitionScanner#doScan
- ![显式引用Bean时首字母忽略大小写](pic/显式引用Bean时首字母忽略大小写.png)
- 基本匹配我们前面描述的过程其中方法调用BeanNameGenerator#generateBeanName 即用来产生 Bean 的名字,它有两种实现方式。因为 DataService 的实现都是使用注解标记的,
式。因为 DataService 的实现都是使用注解标记的,的其实是 AnnotationBeanNameGenerator#generateBeanName 这种实现方式,我们可以看下它的具体实现,代码如下:
```java
@Override
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
if (definition instanceof AnnotatedBeanDefinition) {
String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
if (StringUtils.hasText(beanName)) {
// Explicit bean name found.
return beanName;
}
}
// Fallback: generate a unique default bean name.
return buildDefaultBeanName(definition, registry);
}
```
- 大体流程只有两步:看 Bean 有没有显式指明名称,如果有则用显式名称,如果没有则产生一个默认名称。
- 很明显,在我们的案例中,是没有给 Bean 指定名字的,所以产生的 Bean 的名称就是生成的默认名称,查看默认名的产生方法 buildDefaultBeanName
实现如下:
```java
protected String buildDefaultBeanName(BeanDefinition definition) {
String beanClassName = definition.getBeanClassName();
Assert.state(beanClassName != null, "No bean class name set");
String shortClassName = ClassUtils.getShortName(beanClassName);
return Introspector.decapitalize(shortClassName);
}
```
- 首先,获取一个简短的 ClassName然后调用 Introspector#decapitalize 方法,设置首字母大写或小写,具体参考下面的代码实现:
```java
public static String decapitalize(String name) {
if (name == null || name.length() == 0) {
return name;
}
if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
Character.isUpperCase(name.charAt(0))){
return name;
}
char chars[] = name.toCharArray();
chars[0] = Character.toLowerCase(chars[0]);
return new String(chars);
}
```
- 到这,我们很轻松地明白了前面两个问题出现的原因:**如果一个类名是以两个大写字母开头的,则首字母不变,其它情况下默认首字母变成小写。**
- 结合我们之前的案例SQLiteDataService 的 Bean其名称应该就是类名本身而 CassandraDataService 的Bean 名称则变成了首字母小写cassandraDataService
- **问题修正**
- 方法1 : 引用处纠正首字母大小写问题:
```java
@Autowired
@Qualifier("cassandraDataService")
DataService dataService;
```
- 方法2 : 定义处显式指定 Bean 名字我们可以保持引用代码不变而通过显式指明CassandraDataService 的 Bean 名称为 CassandraDataService 来纠正这个问题
```java
@Repository("CassandraDataService")
@Slf4j
public class CassandraDataService implements DataService {
//省略实现
}
```
- 我们的程序就可以精确匹配到要找的 Bean 了。比较一下这两种修改方法的话,如果你不太了解源码,不想纠结于首字母到底是大写还是小写,建议你用第二种方法去避免困扰。
#### C. 引用内部类的 Bean 遗忘类名
- 我们需要定义一个内部类来实现一种新的 DataService代码如下
```java
public class StudentController {
@Repository
public static class InnerClassDataService implements DataService{
@Override
public void deleteStudent(int id) {
//空实现
}
}
//省略其他非关键代码
}
```
- 遇到这种情况,我们一般都会很自然地用下面的方式直接去显式引用这个 Bean
```java
@Autowired
@Qualifier("innerClassDataService")
DataService innerClassDataService;
```
- 很明显,有了案例 2 的经验,我们上来就直接采用了首字母小写以避免案例 2 中的错误 但这样的代码是不是就没问题了呢?实际上,仍然会报错“找不到 Bean”这是为什么
- **案例解析**
- 实际上,我们遭遇的情况是“如何引用内部类的 Bean”。解析案例 2 的时候,我曾经贴出了如何产生默认 Bean 名的方法即AnnotationBeanNameGenerator#buildDefaultBeanName
当时我们只关注了首字母是否小写的代码片段,而在最后变换首字母之前,有一行语句是对 class 名字的处理,代码如下:
```java
String shortClassName = ClassUtils.getShortName(beanClassName);
```
- 我们可以看下它的实现,参考 ClassUtils#getShortName 方法:
```java
public static String getShortName(String className) {
Assert.hasLength(className, "Class name must not be empty");
int lastDotIndex = className.lastIndexOf(PACKAGE_SEPARATOR);
int nameEndIndex = className.indexOf(CGLIB_CLASS_SEPARATOR);
if (nameEndIndex == -1) {
nameEndIndex = className.length();
}
String shortName = className.substring(lastDotIndex + 1, nameEndIndex);
shortName = shortName.replace(INNER_CLASS_SEPARATOR, PACKAGE_SEPARATOR);
return shortName;
}
```
- 很明显,假设我们是一个内部类,例如下面的类名:
- com.spring.puzzle.class2.example3.StudentController.InnerClassDataService
- 在经过这个方法的处理后,我们得到的其实是下面这个名称:
- StudentController.InnerClassDataService
- 最后经过 Introspector.decapitalize 的首字母变换,最终获取的 Bean 名称如下:
- studentController.InnerClassDataService
- 所以我们在案例程序中,直接使用 innerClassDataService 自然找不到想要的 Bean
- **问题修正**
- 上面源码的跟踪结果显示, 通过下面的方法进行修复
```java
@Autowired
@Qualifier("studentController.InnerClassDataService")
DataService innerClassDataService;
```
- 这个引用看起来有些许奇怪但实际上是可以工作的反而直接使用innerClassDataService 来引用倒是真的不可行。
- **对源码的学习是否全面决定了我们以后犯错的可能性大小**

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 KiB

Loading…
Cancel
Save