Skip to content

Commit

Permalink
@value注解
Browse files Browse the repository at this point in the history
  • Loading branch information
DerekYRC committed Dec 27, 2020
1 parent be7526f commit 07d5ed0
Show file tree
Hide file tree
Showing 16 changed files with 281 additions and 16 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion README_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@
#### 扩展篇
* [PropertyPlaceholderConfigurer](#PropertyPlaceholderConfigurer)
* [包扫描](#包扫描)
* [基于注解@Autowired@Value的依赖注入](#基于注解@Autowired和@Value的依赖注入)
* [@Value注解](#@Value注解)
* [基于注解@Autowired的依赖注入](#基于注解@Autowired的依赖注入)
* [类型转换](#类型转换)

#### 高级篇
Expand Down
47 changes: 47 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<bean class="org.springframework.beans.factory.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:car.properties" />
</bean>
<context:component-scan base-package="org.springframework.test.bean"/>
</beans>
```
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");
}
}
```



Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -30,6 +31,10 @@ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)

//属性值替换占位符
processProperties(beanFactory, properties);

//往容器中添加字符解析器,供解析@Value注解使用
StringValueResolver valueResolver = new PlaceholderResolvingStringValueResolver(properties);
beanFactory.addEmbeddedValueResolver(valueResolver);
}

/**
Expand Down Expand Up @@ -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);
}
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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();
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.springframework.beans.factory.config;

import org.springframework.beans.factory.HierarchicalBeanFactory;
import org.springframework.util.StringValueResolver;

/**
* @author derekyi
Expand All @@ -17,4 +18,8 @@ public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, Single
* 销毁单例bean
*/
void destroySingletons();

void addEmbeddedValueResolver(StringValueResolver valueResolver);

String resolveEmbeddedValue(String value);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.springframework.beans.factory.config;

import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;

/**
* @author derekyi
Expand All @@ -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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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的前置和后置处理方法
Expand All @@ -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或有自定义的销毁方法
*
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -24,6 +22,8 @@ public abstract class AbstractBeanFactory extends DefaultSingletonBeanRegistry i

private final Map<String, Object> factoryBeanObjectCache = new HashMap<>();

private final List<StringValueResolver> embeddedValueResolvers = new ArrayList<StringValueResolver>();

@Override
public Object getBean(String name) throws BeansException {
Object sharedInstance = getSingleton(name);
Expand Down Expand Up @@ -87,4 +87,16 @@ public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
public List<BeanPostProcessor> 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;
}
}
Loading

0 comments on commit 07d5ed0

Please sign in to comment.