【死磕 Spring】----- IOC 之 属性填充

简介:
doCreateBean() 主要用于完成 bean 的创建和初始化工作,我们可以将其分为四个过程:

createBeanInstance() 实例化 bean

populateBean() 属性填充

循环依赖的处理

initializeBean() 初始化 bean

第一个过程实例化 bean 已经在前面两篇博客分析完毕了,这篇博客开始分析 属性填充,也就是 populateBean(),该函数的作用是将 BeanDefinition 中的属性值赋值给 BeanWrapper 实例对象(对于 BeanWrapper 我们后续专门写文分析)。

 
  1. protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {

  2. // 没有实例化对象

  3. if (bw == null) {

  4. // 有属性抛出异常

  5. if (mbd.hasPropertyValues()) {

  6. throw new BeanCreationException(

  7. mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");

  8. }

  9. else {

  10. // 没有属性直接返回

  11. return;

  12. }

  13. }


  14. // 在设置属性之前给 InstantiationAwareBeanPostProcessors 最后一次改变 bean 的机会

  15. boolean continueWithPropertyPopulation = true;


  16. // bena 不是"合成"的,即未由应用程序本身定义

  17. // 是否持有 InstantiationAwareBeanPostProcessor

  18. if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {

  19. // 迭代所有的 BeanPostProcessors

  20. for (BeanPostProcessor bp : getBeanPostProcessors()) {

  21. // 如果为 InstantiationAwareBeanPostProcessor

  22. if (bp instanceof InstantiationAwareBeanPostProcessor) {

  23. InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;

  24. // 返回值为是否继续填充 bean

  25. // postProcessAfterInstantiation:如果应该在 bean上面设置属性则返回true,否则返回false

  26. // 一般情况下,应该是返回true,返回 false 的话,

  27. // 将会阻止在此 Bean 实例上调用任何后续的 InstantiationAwareBeanPostProcessor 实例。

  28. if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {

  29. continueWithPropertyPopulation = false;

  30. break;

  31. }

  32. }

  33. }

  34. }


  35. // 如果后续处理器发出停止填充命令,则终止后续操作

  36. if (!continueWithPropertyPopulation) {

  37. return;

  38. }


  39. // bean 的属性值

  40. PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);


  41. if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||

  42. mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {


  43. // 将 PropertyValues 封装成 MutablePropertyValues 对象

  44. // MutablePropertyValues 允许对属性进行简单的操作,

  45. // 并提供构造函数以支持Map的深度复制和构造。

  46. MutablePropertyValues newPvs = new MutablePropertyValues(pvs);


  47. // 根据名称自动注入

  48. if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {

  49. autowireByName(beanName, mbd, bw, newPvs);

  50. }


  51. // 根据类型自动注入

  52. if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {

  53. autowireByType(beanName, mbd, bw, newPvs);

  54. }


  55. pvs = newPvs;

  56. }


  57. // 是否已经注册了 InstantiationAwareBeanPostProcessors

  58. boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();

  59. // 是否需要进行依赖检查

  60. boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE);


  61. if (hasInstAwareBpps || needsDepCheck) {

  62. if (pvs == null) {

  63. pvs = mbd.getPropertyValues();

  64. }


  65. // 从 bw 对象中提取 PropertyDescriptor 结果集

  66. // PropertyDescriptor:可以通过一对存取方法提取一个属性

  67. PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);

  68. if (hasInstAwareBpps) {

  69. for (BeanPostProcessor bp : getBeanPostProcessors()) {

  70. if (bp instanceof InstantiationAwareBeanPostProcessor) {

  71. InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;

  72. // 对所有需要依赖检查的属性进行后处理

  73. pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);

  74. if (pvs == null) {

  75. return;

  76. }

  77. }

  78. }

  79. }

  80. if (needsDepCheck) {

  81. // 依赖检查,对应 depends-on 属性

  82. checkDependencies(beanName, mbd, filteredPds, pvs);

  83. }

  84. }


  85. if (pvs != null) {

  86. // 将属性应用到 bean 中

  87. applyPropertyValues(beanName, mbd, bw, pvs);

  88. }

  89. }

处理流程如下:

根据 hasInstantiationAwareBeanPostProcessors 属性来判断是否需要在注入属性之前给 InstantiationAwareBeanPostProcessors 最后一次改变 bean 的机会,此过程可以控制 Spring 是否继续进行属性填充。

根据注入类型的不同来判断是根据名称来自动注入( autowireByName())还是根据类型来自动注入( autowireByType()),统一存入到 PropertyValues 中,PropertyValues 用于描述 bean 的属性。

判断是否需要进行 BeanPostProcessor 和 依赖检测。

将所有 PropertyValues 中的属性填充到 BeanWrapper 中。

自动注入

Spring 会根据注入类型( byName / byType )的不同,调用不同的方法( autowireByName() / autowireByType())来注入属性值。

autowireByName()

方法 autowireByName() 是根据属性名称完成自动依赖注入的,代码如下:

 
  1. protected void autowireByName(

  2. String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {


  3. // 对 Bean 对象中非简单属性

  4. String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);

  5. for (String propertyName : propertyNames) {

  6. // 如果容器中包含指定名称的 bean,则将该 bean 注入到 bean中

  7. if (containsBean(propertyName)) {

  8. // 递归初始化相关 bean

  9. Object bean = getBean(propertyName);

  10. // 为指定名称的属性赋予属性值

  11. pvs.add(propertyName, bean);

  12. // 属性依赖注入

  13. registerDependentBean(propertyName, beanName);

  14. if (logger.isDebugEnabled()) {

  15. logger.debug("Added autowiring by name from bean name '" + beanName +

  16. "' via property '" + propertyName + "' to bean named '" + propertyName + "'");

  17. }

  18. }

  19. else {

  20. if (logger.isTraceEnabled()) {

  21. logger.trace("Not autowiring property '" + propertyName + "' of bean '" + beanName +

  22. "' by name: no matching bean found");

  23. }

  24. }

  25. }

  26. }

该方法逻辑很简单,获取该 bean 的非简单属性,什么叫做非简单属性呢?就是类型为对象类型的属性,但是这里并不是将所有的对象类型都都会找到,比如 8 个原始类型,String 类型 ,Number类型、Date类型、URL类型、URI类型等都会被忽略,如下:

 
  1. protected String[] unsatisfiedNonSimpleProperties(AbstractBeanDefinition mbd, BeanWrapper bw) {

  2. Set<String> result = new TreeSet<>();

  3. PropertyValues pvs = mbd.getPropertyValues();

  4. PropertyDescriptor[] pds = bw.getPropertyDescriptors();

  5. for (PropertyDescriptor pd : pds) {

  6. if (pd.getWriteMethod() != null && !isExcludedFromDependencyCheck(pd) && !pvs.contains(pd.getName()) &&

  7. !BeanUtils.isSimpleProperty(pd.getPropertyType())) {

  8. result.add(pd.getName());

  9. }

  10. }

  11. return StringUtils.toStringArray(result);

  12. }

过滤条件为:有可写方法、依赖检测中没有被忽略、不是简单属性类型。其实这里获取的就是需要依赖注入的属性。

获取需要依赖注入的属性后,通过迭代、递归的方式初始化相关的 bean,然后调用 registerDependentBean()完成注册依赖,如下:

 
  1. public void registerDependentBean(String beanName, String dependentBeanName) {

  2. String canonicalName = canonicalName(beanName);


  3. synchronized (this.dependentBeanMap) {

  4. Set<String> dependentBeans =

  5. this.dependentBeanMap.computeIfAbsent(canonicalName, k -> new LinkedHashSet<>(8));

  6. if (!dependentBeans.add(dependentBeanName)) {

  7. return;

  8. }

  9. }


  10. synchronized (this.dependenciesForBeanMap) {

  11. Set<String> dependenciesForBean =

  12. this.dependenciesForBeanMap.computeIfAbsent(dependentBeanName, k -> new LinkedHashSet<>(8));

  13. dependenciesForBean.add(canonicalName);

  14. }

  15. }

autowireByType()

 
  1. protected void autowireByType(

  2. String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {


  3. // 获取 TypeConverter 实例

  4. // 使用自定义的 TypeConverter,用于取代默认的 PropertyEditor 机制

  5. TypeConverter converter = getCustomTypeConverter();

  6. if (converter == null) {

  7. converter = bw;

  8. }


  9. Set<String> autowiredBeanNames = new LinkedHashSet<>(4);

  10. // 获取非简单属性

  11. String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);


  12. for (String propertyName : propertyNames) {

  13. try {

  14. // 获取 PropertyDescriptor 实例

  15. PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);


  16. // 不要尝试按类型

  17. if (Object.class != pd.getPropertyType()) {

  18. // 探测指定属性的 set 方法

  19. MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);


  20. boolean eager = !PriorityOrdered.class.isInstance(bw.getWrappedInstance());

  21. DependencyDescriptor desc = new AbstractAutowireCapableBeanFactory.AutowireByTypeDependencyDescriptor(methodParam, eager);


  22. // 解析指定 beanName 的属性所匹配的值,并把解析到的属性名称存储在 autowiredBeanNames 中

  23. // 当属性存在过个封装 bean 时将会找到所有匹配的 bean 并将其注入

  24. Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);


  25. if (autowiredArgument != null) {

  26. pvs.add(propertyName, autowiredArgument);

  27. }


  28. // 迭代方式注入 bean

  29. for (String autowiredBeanName : autowibeanredBeanNames) {

  30. registerDependentBean(autowiredBeanName, beanName);

  31. if (logger.isDebugEnabled()) {

  32. logger.debug("Autowiring by type from bean name '" + beanName + "' via property '" +

  33. propertyName + "' to bean named '" + autowiredBeanName + "'");

  34. }

  35. }

  36. autowiredBeanNames.clear();

  37. }

  38. }

  39. catch (BeansException ex) {

  40. throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex);

  41. }

  42. }

  43. }

其实主要过程和根据名称自动注入差不多都是找到需要依赖注入的属性,然后通过迭代的方式寻找所匹配的 bean,最后调用 registerDependentBean() 注册依赖。不过相对于 autowireByName() 而言,根据类型寻找相匹配的 bean 过程比较复杂,下面我们就分析这个复杂的过程,如下:

 
  1. public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,

  2. @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {


  3. // 初始化参数名称发现器,该方法并不会在这个时候尝试检索参数名称

  4. // getParameterNameDiscoverer 返回 parameterNameDiscoverer 实例,parameterNameDiscoverer 方法参数名称的解析器

  5. descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());


  6. // 依赖类型为 Optional 类型

  7. if (Optional.class == descriptor.getDependencyType()) {

  8. // 创建 Optional 实例依赖类型

  9. return createOptionalDependency(descriptor, requestingBeanName);

  10. }


  11. // 依赖类型为ObjectFactory、ObjectProvider

  12. else if (ObjectFactory.class == descriptor.getDependencyType() ||

  13. ObjectProvider.class == descriptor.getDependencyType()) {

  14. // ObjectFactory / ObjectProvider 用于 用于延迟解析依赖项

  15. return new DefaultListableBeanFactory.DependencyObjectProvider(descriptor, requestingBeanName);

  16. }


  17. else if (javaxInjectProviderClass == descriptor.getDependencyType()) {

  18. // javaxInjectProviderClass 类注入的特殊处理

  19. return new DefaultListableBeanFactory.Jsr330ProviderFactory().createDependencyProvider(descriptor, requestingBeanName);

  20. }

  21. else {

  22. // 为实际依赖关系目标的延迟解析构建代理

  23. // 默认实现返回 null

  24. Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(

  25. descriptor, requestingBeanName);

  26. if (result == null) {

  27. // 通用处理逻辑

  28. result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);

  29. }

  30. return result;

  31. }

  32. }

这里我们关注通用处理逻辑: doResolveDependency(),如下:

 
  1. public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,

  2. @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {


  3. // 注入点

  4. InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);

  5. try {

  6. // 针对给定的工厂给定一个快捷实现的方式,例如考虑一些预先解析的信息

  7. // 在进入所有bean的常规类型匹配算法之前,解析算法将首先尝试通过此方法解析快捷方式。

  8. // 子类可以覆盖此方法

  9. Object shortcut = descriptor.resolveShortcut(this);

  10. if (shortcut != null) {

  11. // 返回快捷的解析信息

  12. return shortcut;

  13. }


  14. // 依赖的类型

  15. Class<?> type = descriptor.getDependencyType();

  16. // 支持 Spring 的注解 @value

  17. Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);

  18. if (value != null) {

  19. if (value instanceof String) {

  20. String strVal = resolveEmbeddedValue((String) value);

  21. BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null);

  22. value = evaluateBeanDefinitionString(strVal, bd);

  23. }

  24. TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());

  25. return (descriptor.getField() != null ?

  26. converter.convertIfNecessary(value, type, descriptor.getField()) :

  27. converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));

  28. }


  29. // 解析复合 bean,其实就是对 bean 的属性进行解析

  30. // 包括:数组、Collection 、Map 类型

  31. Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);

  32. if (multipleBeans != null) {

  33. return multipleBeans;

  34. }


  35. // 查找与类型相匹配的 bean

  36. // 返回值构成为:key = 匹配的 beanName,value = beanName 对应的实例化 bean

  37. Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);

  38. // 没有找到,检验 @autowire 的 require 是否为 true

  39. if (matchingBeans.isEmpty()) {

  40. // 如果 @autowire 的 require 属性为 true ,但是没有找到相应的匹配项,则抛出异常

  41. if (isRequired(descriptor)) {

  42. raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);

  43. }

  44. return null;

  45. }


  46. String autowiredBeanName;

  47. Object instanceCandidate;


  48. if (matchingBeans.size() > 1) {

  49. // 确认给定 bean autowire 的候选者

  50. // 按照 @Primary 和 @Priority 的顺序

  51. autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);

  52. if (autowiredBeanName == null) {

  53. if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {

  54. // 唯一性处理

  55. return descriptor.resolveNotUnique(type, matchingBeans);

  56. } else {

  57. // 在可选的Collection / Map的情况下,默默地忽略一个非唯一的情况:可能它是一个多个常规bean的空集合

  58. return null;

  59. }

  60. }

  61. instanceCandidate = matchingBeans.get(autowiredBeanName);

  62. } else {

  63. // We have exactly one match.

  64. Map.Entry<Staring, Object> entry = matchingBeans.entrySet().iterator().next();

  65. autowiredBeanName = entry.getKey();

  66. instanceCandidate = entry.getValue();

  67. }


  68. if (autowiredBeanNames != null) {

  69. autowiredBeanNames.add(autowiredBeanName);

  70. }

  71. if (instanceCandidate instanceof Class) {

  72. instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);

  73. }

  74. Object result = instanceCandidate;

  75. if (result instanceof NullBean) {

  76. if (isRequired(descriptor)) {

  77. raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);

  78. }

  79. result = null;

  80. }

  81. if (!ClassUtils.isAssignableValue(type, result)) {

  82. throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass());

  83. }

  84. return result;

  85. } finally {

  86. ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);

  87. }

  88. }

到这里就已经完成了所有属性的注入了。 populateBean() 该方法就已经完成了一大半工作了,下一步则是对依赖 bean 的检测和 PostProcessor 处理,这个我们后面分析,下面分析该方法的最后一步: applyPropertyValues()

applyPropertyValues

其实上面只是完成了所有注入属性的获取,将获取的属性封装在 PropertyValues 的实例对象 pvs 中,并没有应用到已经实例化的 bean 中,而 applyPropertyValues() 则是完成这一步骤的。

 
  1. protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {

  2. if (pvs.isEmpty()) {

  3. return;

  4. }


  5. if (System.getSecurityManager() != null && bw instanceof BeanWrapperImpl) {

  6. ((BeanWrapperImpl) bw).setSecurityContext(getAccessControlContext());

  7. }


  8. // MutablePropertyValues 类型属性

  9. MutablePropertyValues mpvs = null;

  10. // 原始类型

  11. List<PropertyValue> original;



  12. if (pvs instanceof MutablePropertyValues) {

  13. mpvs = (MutablePropertyValues) pvs;

  14. if (mpvs.isConverted()) {

  15. try {

  16. // 设置到 BeanWrapper 中去

  17. bw.setPropertyValues(mpvs);

  18. return;

  19. }

  20. catch (BeansException ex) {

  21. throw new BeanCreationException(

  22. mbd.getResourceDescription(), beanName, "Error setting property values", ex);

  23. }

  24. }

  25. original = mpvs.getPropertyValueList();

  26. }

  27. else {

  28. // 如果 pvs 不是 MutablePropertyValues 类型,则直接使用原始类型

  29. original = Arrays.asList(pvs.getPropertyValues());

  30. }


  31. // 获取 TypeConverter

  32. TypeConverter converter = getCustomTypeConverter();

  33. if (converter == null) {

  34. converter = bw;

  35. }


  36. // 获取对应的解析器

  37. BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);


  38. // Create a deep copy, resolving any references for values.

  39. List<PropertyValue> deepCopy = new ArrayList<>(original.size());

  40. boolean resolveNecessary = false;

  41. // 遍历属性,将属性转换为对应类的对应属性的类型

  42. for (PropertyValue pv : original) {

  43. if (pv.isConverted()) {

  44. deepCopy.add(pv);

  45. }

  46. else {

  47. String propertyName = pv.getName();

  48. Object originalValue = pv.getValue();

  49. Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);

  50. Object convertedValue = resolvedValue;

  51. boolean convertible = bw.isWritableProperty(propertyName) &&

  52. !PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);

  53. if (convertible) {

  54. convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);

  55. }

  56. // Possibly store converted value in merged bean definition,

  57. // in order to avoid re-conversion for every created bean instance.

  58. if (resolvedValue == originalValue) {

  59. if (convertible) {

  60. pv.setConvertedValue(convertedValue);

  61. }

  62. deepCopy.add(pv);

  63. }

  64. else if (convertible && originalValue instanceof TypedStringValue &&

  65. !((TypedStringValue) originalValue).isDynamic() &&

  66. !(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) {

  67. pv.setConvertedValue(convertedValue);

  68. deepCopy.add(pv);

  69. }

  70. else {

  71. resolveNecessary = true;

  72. deepCopy.add(new PropertyValue(pv, convertedValue));

  73. }

  74. }

  75. }

  76. if (mpvs != null && !resolveNecessary) {

  77. mpvs.setConverted();

  78. }


  79. // Set our (possibly massaged) deep copy.

  80. try {

  81. bw.setPropertyValues(new MutablePropertyValues(deepCopy));

  82. }

  83. catch (BeansException ex) {

  84. throw new BeanCreationException(

  85. mbd.getResourceDescription(), beanName, "Error setting property values", ex);

  86. }

  87. }

至此, doCreateBean() 第二个过程:属性填充 已经分析完成了,下篇分析第三个过程:循环依赖的处理,其实循环依赖并不仅仅只是在 doCreateBean() 中处理,其实在整个加载 bean 的过程中都有涉及,所以下篇内容并不仅仅只局限于 doCreateBean()


原文发布时间为: 2018-11-05
本文作者Java技术驿站
本文来自云栖社区合作伙伴“Java技术驿站”,了解相关信息可以关注“Java技术驿站”。

相关文章
|
15天前
|
存储 Java 数据安全/隐私保护
|
11天前
|
Java 测试技术 开发者
Spring IoC容器通过依赖注入机制实现控制反转
【4月更文挑战第30天】Spring IoC容器通过依赖注入机制实现控制反转
21 0
|
5天前
|
XML Java 数据格式
Spring 属性注入方式
Spring 属性注入方式
13 2
|
5天前
|
Java 数据库连接 数据库
Spring事务简介,事务角色,事务属性
Spring事务简介,事务角色,事务属性
15 2
|
5天前
|
Java Apache Spring
Spring BeanUtils与Apache BeanUtils提供基本属性复制,适用于简单需求
Spring BeanUtils与Apache BeanUtils提供基本属性复制,适用于简单需求;Cglib BeanCopier用于转换为Cglib代理对象;Apache PropertyUtils处理属性操作;Dozer支持复杂对象映射。选择工具取决于具体需求,如需精细控制或对象映射,推荐Dozer或Apache PropertyUtils。Apache BeanUtils可能因潜在的封装性破坏被禁用。
19 3
|
11天前
|
安全 Java 开发者
在Spring框架中,IoC和AOP是如何实现的?
【4月更文挑战第30天】在Spring框架中,IoC和AOP是如何实现的?
21 0
|
11天前
|
XML Java 程序员
什么是Spring的IoC容器?
【4月更文挑战第30天】什么是Spring的IoC容器?
19 0
|
13天前
|
Java 开发者 Spring
Spring Boot中的资源文件属性配置
【4月更文挑战第28天】在Spring Boot应用程序中,配置文件是管理应用程序行为的重要组成部分。资源文件属性配置允许开发者在不重新编译代码的情况下,对应用程序进行灵活地配置和调整。本篇博客将介绍Spring Boot中资源文件属性配置的基本概念,并通过实际示例展示如何利用这一功能。
22 1
|
13天前
|
Java Spring 容器
【Spring系列笔记】IOC与DI
IoC 和 DI 是面向对象编程中的两个相关概念,它们主要用于解决程序中的依赖管理和解耦问题。 控制反转是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入和依赖查找。
30 2
|
14天前
|
Java 测试技术 数据库连接
Spring中ioc的优点
总之,Spring中的IoC提供了一种更加灵活、可维护、可测试和可扩展的方式来管理组件之间的依赖关系,从而提高了应用程序的质量和可维护性。这使得开发人员能够更专注于业务逻辑而不是底层的技术细节。
32 1