From 07d5ed085cf981d2fd3c93738923fbf03c6ec964 Mon Sep 17 00:00:00 2001 From: DerekYRC <15521077528@163.com> Date: Sun, 27 Dec 2020 14:54:46 +0800 Subject: [PATCH] =?UTF-8?q?@Value=E6=B3=A8=E8=A7=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 +- README_CN.md | 3 +- changelog.md | 47 ++++++++++++++ .../DefaultAdvisorAutoProxyCreator.java | 6 ++ .../PropertyPlaceholderConfigurer.java | 45 ++++++++++---- .../AutowiredAnnotationBeanPostProcessor.java | 61 +++++++++++++++++++ .../beans/factory/annotation/Value.java | 17 ++++++ .../config/ConfigurableBeanFactory.java | 5 ++ .../InstantiationAwareBeanPostProcessor.java | 13 ++++ .../AbstractAutowireCapableBeanFactory.java | 23 +++++++ .../factory/support/AbstractBeanFactory.java | 18 +++++- .../ClassPathBeanDefinitionScanner.java | 6 ++ .../util/StringValueResolver.java | 10 +++ .../org/springframework/test/bean/Car.java | 2 + .../test/ioc/ValueAnnotationTest.java | 22 +++++++ src/test/resources/value-annotation.xml | 16 +++++ 16 files changed, 281 insertions(+), 16 deletions(-) create mode 100644 src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java create mode 100644 src/main/java/org/springframework/beans/factory/annotation/Value.java create mode 100644 src/main/java/org/springframework/util/StringValueResolver.java create mode 100644 src/test/java/org/springframework/test/ioc/ValueAnnotationTest.java create mode 100644 src/test/resources/value-annotation.xml diff --git a/README.md b/README.md index cf7f7b2..76ae567 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,8 @@ If this project can help you, please give a **STAR, thank you!!!** #### Expanding * [PropertyPlaceholderConfigurer](#PropertyPlaceholderConfigurer) * [Package scan](#包扫描) -* [@Autowired and @Value annotation](#基于注解@Autowired和@Value的依赖注入) +* [@Value annotation](#@Value annotation) +* [@Autowired annotation](#@Autowired annotation) * [Type conversion](#类型转换) #### Advanced diff --git a/README_CN.md b/README_CN.md index 7808167..143079e 100644 --- a/README_CN.md +++ b/README_CN.md @@ -41,7 +41,8 @@ #### 扩展篇 * [PropertyPlaceholderConfigurer](#PropertyPlaceholderConfigurer) * [包扫描](#包扫描) -* [基于注解@Autowired和@Value的依赖注入](#基于注解@Autowired和@Value的依赖注入) +* [@Value注解](#@Value注解) +* [基于注解@Autowired的依赖注入](#基于注解@Autowired的依赖注入) * [类型转换](#类型转换) #### 高级篇 diff --git a/changelog.md b/changelog.md index 4a84f45..b01bedf 100644 --- a/changelog.md +++ b/changelog.md @@ -1018,11 +1018,58 @@ public class PackageScanTest { } ``` +## @Value注解 +> 分支:value-annotation +注解@Value和@Autowired通过BeanPostProcessor处理。InstantiationAwareBeanPostProcessor增加postProcessPropertyValues方法,在bean实例化之后设置属性之前执行,查看AbstractAutowireCapableBeanFactory#doCreateBean方法。 +增加AutowiredAnnotationBeanPostProcessor用于处理注解@Value,@Autowired的处理在下一节实现,在ClassPathBeanDefinitionScanner#doScan将其添加到容器中。查看AutowiredAnnotationBeanPostProcessor#postProcessPropertyValues,其中字符解析器StringValueResolver在PropertyPlaceholderConfigurer中添加到BeanFactory中。 +测试: +``` +@Component +public class Car { + + @Value("${brand}") + private String brand; +} +``` +value-annotation.xml +``` + + + + + + + + +``` +car.properties +``` +brand=lamborghini +``` +``` +public class ValueAnnotationTest { + + @Test + public void testValueAnnotation() throws Exception { + ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:value-annotation.xml"); + + Car car = applicationContext.getBean("car", Car.class); + assertThat(car.getBrand()).isEqualTo("lamborghini"); + } +} + +``` diff --git a/src/main/java/org/springframework/aop/framework/autoproxy/DefaultAdvisorAutoProxyCreator.java b/src/main/java/org/springframework/aop/framework/autoproxy/DefaultAdvisorAutoProxyCreator.java index 4153838..8668ac4 100644 --- a/src/main/java/org/springframework/aop/framework/autoproxy/DefaultAdvisorAutoProxyCreator.java +++ b/src/main/java/org/springframework/aop/framework/autoproxy/DefaultAdvisorAutoProxyCreator.java @@ -6,6 +6,7 @@ import org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor; import org.springframework.aop.framework.ProxyFactory; import org.springframework.beans.BeansException; +import org.springframework.beans.PropertyValues; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.config.BeanDefinition; @@ -73,4 +74,9 @@ public Object postProcessBeforeInitialization(Object bean, String beanName) thro public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } + + @Override + public PropertyValues postProcessPropertyValues(PropertyValues pvs, Object bean, String beanName) throws BeansException { + return pvs; + } } diff --git a/src/main/java/org/springframework/beans/factory/PropertyPlaceholderConfigurer.java b/src/main/java/org/springframework/beans/factory/PropertyPlaceholderConfigurer.java index 63fbd30..92124cb 100644 --- a/src/main/java/org/springframework/beans/factory/PropertyPlaceholderConfigurer.java +++ b/src/main/java/org/springframework/beans/factory/PropertyPlaceholderConfigurer.java @@ -7,6 +7,7 @@ import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.Resource; +import org.springframework.util.StringValueResolver; import java.io.IOException; import java.util.Properties; @@ -30,6 +31,10 @@ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) //属性值替换占位符 processProperties(beanFactory, properties); + + //往容器中添加字符解析器,供解析@Value注解使用 + StringValueResolver valueResolver = new PlaceholderResolvingStringValueResolver(properties); + beanFactory.addEmbeddedValueResolver(valueResolver); } /** @@ -69,22 +74,40 @@ private void resolvePropertyValues(BeanDefinition beanDefinition, Properties pro for (PropertyValue propertyValue : propertyValues.getPropertyValues()) { Object value = propertyValue.getValue(); if (value instanceof String) { - //TODO 仅简单支持一个占位符的格式 - String strVal = (String) value; - StringBuffer buf = new StringBuffer(strVal); - int startIndex = strVal.indexOf(PLACEHOLDER_PREFIX); - int endIndex = strVal.indexOf(PLACEHOLDER_SUFFIX); - if (startIndex != -1 && endIndex != -1 && startIndex < endIndex) { - String propKey = strVal.substring(startIndex + 2, endIndex); - String propVal = properties.getProperty(propKey); - buf.replace(startIndex, endIndex + 1, propVal); - propertyValues.addPropertyValue(new PropertyValue(propertyValue.getName(), buf.toString())); - } + value = resolvePlaceholder((String) value, properties); + propertyValues.addPropertyValue(new PropertyValue(propertyValue.getName(), value)); } } } + private String resolvePlaceholder(String value, Properties properties) { + //TODO 仅简单支持一个占位符的格式 + String strVal = value; + StringBuffer buf = new StringBuffer(strVal); + int startIndex = strVal.indexOf(PLACEHOLDER_PREFIX); + int endIndex = strVal.indexOf(PLACEHOLDER_SUFFIX); + if (startIndex != -1 && endIndex != -1 && startIndex < endIndex) { + String propKey = strVal.substring(startIndex + 2, endIndex); + String propVal = properties.getProperty(propKey); + buf.replace(startIndex, endIndex + 1, propVal); + } + return buf.toString(); + } + public void setLocation(String location) { this.location = location; } + + private class PlaceholderResolvingStringValueResolver implements StringValueResolver { + + private final Properties properties; + + public PlaceholderResolvingStringValueResolver(Properties properties) { + this.properties = properties; + } + + public String resolveStringValue(String strVal) throws BeansException { + return PropertyPlaceholderConfigurer.this.resolvePlaceholder(strVal, properties); + } + } } diff --git a/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java b/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java new file mode 100644 index 0000000..e07be4a --- /dev/null +++ b/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java @@ -0,0 +1,61 @@ +package org.springframework.beans.factory.annotation; + +import cn.hutool.core.bean.BeanUtil; +import org.springframework.beans.BeansException; +import org.springframework.beans.PropertyValues; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor; + +import java.lang.reflect.Field; + +/** + * 处理@Autowired和@Value注解的BeanPostProcessor + * + * @author derekyi + * @date 2020/12/27 + */ +public class AutowiredAnnotationBeanPostProcessor implements InstantiationAwareBeanPostProcessor, BeanFactoryAware { + + private ConfigurableListableBeanFactory beanFactory; + + @Override + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.beanFactory = (ConfigurableListableBeanFactory) beanFactory; + } + + @Override + public PropertyValues postProcessPropertyValues(PropertyValues pvs, Object bean, String beanName) throws BeansException { + //处理@Value注解 + Class clazz = bean.getClass(); + Field[] fields = clazz.getDeclaredFields(); + for (Field field : fields) { + Value valueAnnotation = field.getAnnotation(Value.class); + if (valueAnnotation != null) { + String value = valueAnnotation.value(); + value = beanFactory.resolveEmbeddedValue(value); + BeanUtil.setFieldValue(bean, field.getName(), value); + } + } + + //处理@Autowired注解(下一节实现) + + return pvs; + } + + @Override + public Object postProcessBeforeInstantiation(Class beanClass, String beanName) throws BeansException { + return null; + } + + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + return null; + } + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + return null; + } +} diff --git a/src/main/java/org/springframework/beans/factory/annotation/Value.java b/src/main/java/org/springframework/beans/factory/annotation/Value.java new file mode 100644 index 0000000..f585874 --- /dev/null +++ b/src/main/java/org/springframework/beans/factory/annotation/Value.java @@ -0,0 +1,17 @@ +package org.springframework.beans.factory.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author derekyi + * @date 2020/12/27 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) +public @interface Value { + + String value(); +} \ No newline at end of file diff --git a/src/main/java/org/springframework/beans/factory/config/ConfigurableBeanFactory.java b/src/main/java/org/springframework/beans/factory/config/ConfigurableBeanFactory.java index 1f7b0c2..8e83dfb 100644 --- a/src/main/java/org/springframework/beans/factory/config/ConfigurableBeanFactory.java +++ b/src/main/java/org/springframework/beans/factory/config/ConfigurableBeanFactory.java @@ -1,6 +1,7 @@ package org.springframework.beans.factory.config; import org.springframework.beans.factory.HierarchicalBeanFactory; +import org.springframework.util.StringValueResolver; /** * @author derekyi @@ -17,4 +18,8 @@ public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, Single * 销毁单例bean */ void destroySingletons(); + + void addEmbeddedValueResolver(StringValueResolver valueResolver); + + String resolveEmbeddedValue(String value); } diff --git a/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessor.java b/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessor.java index 6025cbc..4d66940 100644 --- a/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessor.java +++ b/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessor.java @@ -1,6 +1,7 @@ package org.springframework.beans.factory.config; import org.springframework.beans.BeansException; +import org.springframework.beans.PropertyValues; /** * @author derekyi @@ -17,4 +18,16 @@ public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor { * @throws BeansException */ Object postProcessBeforeInstantiation(Class beanClass, String beanName) throws BeansException; + + /** + * bean实例化之后,设置属性之前执行 + * + * @param pvs + * @param bean + * @param beanName + * @return + * @throws BeansException + */ + PropertyValues postProcessPropertyValues(PropertyValues pvs, Object bean, String beanName) + throws BeansException; } diff --git a/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java b/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java index dcd6910..619abcf 100644 --- a/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java +++ b/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java @@ -5,6 +5,7 @@ import cn.hutool.core.util.StrUtil; import org.springframework.beans.BeansException; import org.springframework.beans.PropertyValue; +import org.springframework.beans.PropertyValues; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; @@ -64,6 +65,8 @@ protected Object doCreateBean(String beanName, BeanDefinition beanDefinition) { Object bean = null; try { bean = createBeanInstance(beanDefinition); + //在设置bean属性之前,允许BeanPostProcessor修改属性值 + applyBeanPostprocessorsBeforeApplyingPropertyValues(beanName, bean, beanDefinition); //为bean填充属性 applyPropertyValues(beanName, bean, beanDefinition); //执行bean的初始化方法和BeanPostProcessor的前置和后置处理方法 @@ -81,6 +84,26 @@ protected Object doCreateBean(String beanName, BeanDefinition beanDefinition) { return bean; } + /** + * 在设置bean属性之前,允许BeanPostProcessor修改属性值 + * + * @param beanName + * @param bean + * @param beanDefinition + */ + protected void applyBeanPostprocessorsBeforeApplyingPropertyValues(String beanName, Object bean, BeanDefinition beanDefinition) { + for (BeanPostProcessor beanPostProcessor : getBeanPostProcessors()) { + if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) { + PropertyValues pvs = ((InstantiationAwareBeanPostProcessor) beanPostProcessor).postProcessPropertyValues(beanDefinition.getPropertyValues(), bean, beanName); + if (pvs != null) { + for (PropertyValue propertyValue : pvs.getPropertyValues()) { + beanDefinition.getPropertyValues().addPropertyValue(propertyValue); + } + } + } + } + } + /** * 注册有销毁方法的bean,即bean继承自DisposableBean或有自定义的销毁方法 * diff --git a/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java b/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java index f7ca99d..7a5ada7 100644 --- a/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java +++ b/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java @@ -1,18 +1,16 @@ package org.springframework.beans.factory.support; -import cn.hutool.core.lang.Assert; import org.springframework.beans.BeansException; -import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.util.StringValueResolver; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.CopyOnWriteArrayList; /** * @author derekyi @@ -24,6 +22,8 @@ public abstract class AbstractBeanFactory extends DefaultSingletonBeanRegistry i private final Map factoryBeanObjectCache = new HashMap<>(); + private final List embeddedValueResolvers = new ArrayList(); + @Override public Object getBean(String name) throws BeansException { Object sharedInstance = getSingleton(name); @@ -87,4 +87,16 @@ public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) { public List getBeanPostProcessors() { return this.beanPostProcessors; } + + public void addEmbeddedValueResolver(StringValueResolver valueResolver) { + this.embeddedValueResolvers.add(valueResolver); + } + + public String resolveEmbeddedValue(String value) { + String result = value; + for (StringValueResolver resolver : this.embeddedValueResolvers) { + result = resolver.resolveStringValue(result); + } + return result; + } } diff --git a/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java b/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java index 4c7d03a..cfce9cd 100644 --- a/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java +++ b/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java @@ -1,6 +1,7 @@ package org.springframework.context.annotation; import cn.hutool.core.util.StrUtil; +import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.stereotype.Component; @@ -13,6 +14,8 @@ */ public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider { + public static final String AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME = "org.springframework.context.annotation.internalAutowiredAnnotationProcessor"; + private BeanDefinitionRegistry registry; public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) { @@ -34,6 +37,9 @@ public void doScan(String... basePackages) { registry.registerBeanDefinition(beanName, candidate); } } + + //注册处理@Autowired和@Value注解的BeanPostProcessor + registry.registerBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME, new BeanDefinition(AutowiredAnnotationBeanPostProcessor.class)); } /** diff --git a/src/main/java/org/springframework/util/StringValueResolver.java b/src/main/java/org/springframework/util/StringValueResolver.java new file mode 100644 index 0000000..fed2c1e --- /dev/null +++ b/src/main/java/org/springframework/util/StringValueResolver.java @@ -0,0 +1,10 @@ +package org.springframework.util; + +/** + * @author derekyi + * @date 2020/12/27 + */ +public interface StringValueResolver { + + String resolveStringValue(String strVal); +} diff --git a/src/test/java/org/springframework/test/bean/Car.java b/src/test/java/org/springframework/test/bean/Car.java index 6e68b15..c20e35f 100644 --- a/src/test/java/org/springframework/test/bean/Car.java +++ b/src/test/java/org/springframework/test/bean/Car.java @@ -1,5 +1,6 @@ package org.springframework.test.bean; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; /** @@ -9,6 +10,7 @@ @Component public class Car { + @Value("${brand}") private String brand; public String getBrand() { diff --git a/src/test/java/org/springframework/test/ioc/ValueAnnotationTest.java b/src/test/java/org/springframework/test/ioc/ValueAnnotationTest.java new file mode 100644 index 0000000..09e9211 --- /dev/null +++ b/src/test/java/org/springframework/test/ioc/ValueAnnotationTest.java @@ -0,0 +1,22 @@ +package org.springframework.test.ioc; + +import org.junit.Test; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.test.bean.Car; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author derekyi + * @date 2020/12/27 + */ +public class ValueAnnotationTest { + + @Test + public void testValueAnnotation() throws Exception { + ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:value-annotation.xml"); + + Car car = applicationContext.getBean("car", Car.class); + assertThat(car.getBrand()).isEqualTo("lamborghini"); + } +} diff --git a/src/test/resources/value-annotation.xml b/src/test/resources/value-annotation.xml new file mode 100644 index 0000000..3f9f12d --- /dev/null +++ b/src/test/resources/value-annotation.xml @@ -0,0 +1,16 @@ + + + + + + + + + + \ No newline at end of file