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