From 2bb37d5d7aec84da9ef32c7739718ca7c0143cac Mon Sep 17 00:00:00 2001 From: lance Date: Tue, 9 Nov 2021 13:36:10 +0800 Subject: [PATCH] =?UTF-8?q?:=20=E9=80=9A=E8=BF=87=E6=B3=A8=E8=A7=A3?= =?UTF-8?q?=EF=BC=8C=E6=B3=A8=E5=85=A5=E5=B1=9E=E6=80=A7=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DefaultAdvisorAutoProxyCreator.java | 6 ++ .../support/AbstractApplicationContext.java | 5 ++ .../container/factory/BeanFactory.java | 2 + .../PropertyPlaceholderConfigurer.java | 46 ++++++++--- .../AutowiredAnnotationBeanPostProcessor.java | 82 +++++++++++++++++++ .../container/factory/annotation/Inject.java | 17 ++++ .../factory/annotation/InputValue.java | 16 ++++ .../factory/annotation/Priority.java | 17 ++++ .../config/ConfigurableBeanFactory.java | 5 ++ .../InstantiationAwareBeanPostProcessor.java | 11 +++ .../AbstractAutowiredCapableBeanFactory.java | 25 ++++++ .../factory/support/AbstractBeanFactory.java | 20 +++++ .../support/DefaultListableBeanFactory.java | 18 ++++ .../factory/utils/StringValueResolver.java | 13 +++ 14 files changed, 272 insertions(+), 11 deletions(-) create mode 100644 src/main/java/cn/noexception/container/factory/annotation/AutowiredAnnotationBeanPostProcessor.java create mode 100644 src/main/java/cn/noexception/container/factory/annotation/Inject.java create mode 100644 src/main/java/cn/noexception/container/factory/annotation/InputValue.java create mode 100644 src/main/java/cn/noexception/container/factory/annotation/Priority.java create mode 100644 src/main/java/cn/noexception/container/factory/utils/StringValueResolver.java diff --git a/src/main/java/cn/noexception/container/aop/framework/autoproxy/DefaultAdvisorAutoProxyCreator.java b/src/main/java/cn/noexception/container/aop/framework/autoproxy/DefaultAdvisorAutoProxyCreator.java index 5fe0b56..0254a6d 100644 --- a/src/main/java/cn/noexception/container/aop/framework/autoproxy/DefaultAdvisorAutoProxyCreator.java +++ b/src/main/java/cn/noexception/container/aop/framework/autoproxy/DefaultAdvisorAutoProxyCreator.java @@ -1,6 +1,7 @@ package cn.noexception.container.aop.framework.autoproxy; import cn.noexception.container.BeansException; +import cn.noexception.container.PropertyValues; import cn.noexception.container.aop.*; import cn.noexception.container.aop.aspectj.AspectJExpressionPointcutAdvisor; import cn.noexception.container.aop.framework.ProxyFactory; @@ -78,4 +79,9 @@ public class DefaultAdvisorAutoProxyCreator implements InstantiationAwareBeanPos private boolean isInfrastructureClass(Class beanClass) { return Advice.class.isAssignableFrom(beanClass) || Pointcut.class.isAssignableFrom(beanClass) || Advisor.class.isAssignableFrom(beanClass); } + + @Override + public PropertyValues postProcessPropertyValues(PropertyValues pvs, Object bean, String beanName) throws BeansException { + return pvs; + } } diff --git a/src/main/java/cn/noexception/container/context/support/AbstractApplicationContext.java b/src/main/java/cn/noexception/container/context/support/AbstractApplicationContext.java index 2f741e7..15dff2e 100644 --- a/src/main/java/cn/noexception/container/context/support/AbstractApplicationContext.java +++ b/src/main/java/cn/noexception/container/context/support/AbstractApplicationContext.java @@ -132,4 +132,9 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader i // 执行单利 bean 的销毁方法 getBeanFactory().destroySingletons(); } + + @Override + public T getBean(Class requiredType) throws BeansException { + return getBeanFactory().getBean(requiredType); + } } diff --git a/src/main/java/cn/noexception/container/factory/BeanFactory.java b/src/main/java/cn/noexception/container/factory/BeanFactory.java index 49c39a0..5c727fd 100644 --- a/src/main/java/cn/noexception/container/factory/BeanFactory.java +++ b/src/main/java/cn/noexception/container/factory/BeanFactory.java @@ -17,4 +17,6 @@ public interface BeanFactory { Object getBean(String name, Object... args) throws BeansException; T getBean(String name, Class requiredType) throws BeansException; + + T getBean(Class requiredType) throws BeansException; } diff --git a/src/main/java/cn/noexception/container/factory/PropertyPlaceholderConfigurer.java b/src/main/java/cn/noexception/container/factory/PropertyPlaceholderConfigurer.java index 677e47a..eae4b53 100644 --- a/src/main/java/cn/noexception/container/factory/PropertyPlaceholderConfigurer.java +++ b/src/main/java/cn/noexception/container/factory/PropertyPlaceholderConfigurer.java @@ -7,6 +7,7 @@ import cn.noexception.container.factory.config.BeanDefinition; import cn.noexception.container.factory.config.BeanFactoryPostProcessor; import cn.noexception.container.factory.io.DefaultResourceLoader; import cn.noexception.container.factory.io.Resource; +import cn.noexception.container.factory.utils.StringValueResolver; import java.util.Properties; @@ -42,19 +43,14 @@ public class PropertyPlaceholderConfigurer implements BeanFactoryPostProcessor { for (PropertyValue propertyValue : propertyValues.getPropertyValues()) { Object value = propertyValue.getValue(); if (!(value instanceof String)) continue; - String strVal = (String) value; - StringBuilder buffer = new StringBuilder(strVal); - int startIdx = strVal.indexOf(DEFAULT_PLACEHOLDER_PREFIX); - int stopIdx = strVal.indexOf(DEFAULT_PLACEHOLDER_SUFFIX); - if (startIdx != -1 && stopIdx != -1 && startIdx < stopIdx) { - String propKey = strVal.substring(startIdx+2, stopIdx); - String propVal = properties.getProperty(propKey); - buffer.replace(startIdx, stopIdx+1, propVal); - propertyValues.addPropertyValue(new PropertyValue(propertyValue.getName(), buffer.toString())); - } + value = resolvePlaceholder((String) value, properties); + propertyValues.addPropertyValue(new PropertyValue(propertyValue.getName(), value)); } - } + StringValueResolver valueResolver = new PlaceholderResolvingStringValueResolver(properties); + beanFactory.addEmbeddedValueResolver(valueResolver); + + }catch (Exception ex){ throw new BeansException(" properties 加载失败", ex); } @@ -63,4 +59,32 @@ public class PropertyPlaceholderConfigurer implements BeanFactoryPostProcessor { public void setLocation(String location) { this.location = location; } + + private class PlaceholderResolvingStringValueResolver implements StringValueResolver{ + + private final Properties properties; + + private PlaceholderResolvingStringValueResolver(Properties properties) { + this.properties = properties; + } + + @Override + public String resolveStringValue(String strVal) { + return PropertyPlaceholderConfigurer.this.resolvePlaceholder(strVal, properties); + } + } + + private String resolvePlaceholder(String strVal, Properties properties) { + StringBuilder buffer = new StringBuilder(strVal); + int startIdx = strVal.indexOf(DEFAULT_PLACEHOLDER_PREFIX); + int stopIdx = strVal.indexOf(DEFAULT_PLACEHOLDER_SUFFIX); + if (startIdx != -1 && stopIdx != -1 && startIdx < stopIdx) { + String propKey = strVal.substring(startIdx + 2, stopIdx); + String propVal = properties.getProperty(propKey); + buffer.replace(startIdx, stopIdx + 1, propVal); + } + return buffer.toString(); + } + + } diff --git a/src/main/java/cn/noexception/container/factory/annotation/AutowiredAnnotationBeanPostProcessor.java b/src/main/java/cn/noexception/container/factory/annotation/AutowiredAnnotationBeanPostProcessor.java new file mode 100644 index 0000000..66e8353 --- /dev/null +++ b/src/main/java/cn/noexception/container/factory/annotation/AutowiredAnnotationBeanPostProcessor.java @@ -0,0 +1,82 @@ +package cn.noexception.container.factory.annotation; + +import cn.hutool.core.bean.BeanUtil; +import cn.noexception.container.BeansException; +import cn.noexception.container.PropertyValues; +import cn.noexception.container.factory.BeanFactory; +import cn.noexception.container.factory.BeanFactoryAware; +import cn.noexception.container.factory.ConfigurableListableBeanFactory; +import cn.noexception.container.factory.config.InstantiationAwareBeanPostProcessor; +import cn.noexception.container.factory.utils.ClassUtils; + +import java.lang.reflect.Field; + +/** + * AutowiredAnnotationBeanPostProcessor + * 扫描自定义注解 + * + * @author 吕滔 + * @Date 2021/11/9 11:08 + */ +public class AutowiredAnnotationBeanPostProcessor implements InstantiationAwareBeanPostProcessor, BeanFactoryAware { + + private ConfigurableListableBeanFactory beanFactory; + + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + return null; + } + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + return null; + } + + @Override + public Object postProcessBeforeInstantiation(Class beanClass, String beanName) throws BeansException { + return null; + } + + @Override + public PropertyValues postProcessPropertyValues(PropertyValues pvs, Object bean, String beanName) throws BeansException { + // 1. 处理注解 @InputValue + Class clazz = bean.getClass(); + clazz = ClassUtils.isCglibProxyClass(clazz) ? clazz.getSuperclass() : clazz; + + Field[] declaredFields = clazz.getDeclaredFields(); + + for (Field field : declaredFields) { + InputValue valueAnnotation = field.getAnnotation(InputValue.class); + if (null != valueAnnotation) { + // 将注解中的占位符表达式转换成值 + String value = valueAnnotation.value(); + value = beanFactory.resolveEmbeddedValue(value); + BeanUtil.setFieldValue(bean, field.getName(), value); + } + } + + // 2. 处理注解 @Inject + for (Field field : declaredFields) { + Inject injectAnnotation = field.getAnnotation(Inject.class); + if (null != injectAnnotation) { + Class fieldType = field.getType(); + String dependentBeanName = null; + Priority priorityAnnotation = field.getAnnotation(Priority.class); + Object dependentBean = null; + if (null != priorityAnnotation) { + dependentBeanName = priorityAnnotation.value(); + dependentBean = beanFactory.getBean(dependentBeanName, fieldType); + } else { + dependentBean = beanFactory.getBean(fieldType); + } + BeanUtil.setFieldValue(bean, field.getName(), dependentBean); + } + } + return pvs; + } + + @Override + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.beanFactory = (ConfigurableListableBeanFactory) beanFactory; + } +} diff --git a/src/main/java/cn/noexception/container/factory/annotation/Inject.java b/src/main/java/cn/noexception/container/factory/annotation/Inject.java new file mode 100644 index 0000000..6d2b990 --- /dev/null +++ b/src/main/java/cn/noexception/container/factory/annotation/Inject.java @@ -0,0 +1,17 @@ +package cn.noexception.container.factory.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Inject + * + * @author 吕滔 + * @Date 2021/11/9 10:59 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD}) +public @interface Inject { +} diff --git a/src/main/java/cn/noexception/container/factory/annotation/InputValue.java b/src/main/java/cn/noexception/container/factory/annotation/InputValue.java new file mode 100644 index 0000000..96392ca --- /dev/null +++ b/src/main/java/cn/noexception/container/factory/annotation/InputValue.java @@ -0,0 +1,16 @@ +package cn.noexception.container.factory.annotation; + +import java.lang.annotation.*; + +/** + * InputValue + * + * @author 吕滔 + * @Date 2021/11/9 11:03 + */ +@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface InputValue { + String value(); +} diff --git a/src/main/java/cn/noexception/container/factory/annotation/Priority.java b/src/main/java/cn/noexception/container/factory/annotation/Priority.java new file mode 100644 index 0000000..ad8d99c --- /dev/null +++ b/src/main/java/cn/noexception/container/factory/annotation/Priority.java @@ -0,0 +1,17 @@ +package cn.noexception.container.factory.annotation; + +import java.lang.annotation.*; + +/** + * Priority + * + * @author 吕滔 + * @Date 2021/11/9 11:01 + */ +@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@Documented +public @interface Priority { + String value() default ""; +} diff --git a/src/main/java/cn/noexception/container/factory/config/ConfigurableBeanFactory.java b/src/main/java/cn/noexception/container/factory/config/ConfigurableBeanFactory.java index 7ffe38a..6cccbe4 100644 --- a/src/main/java/cn/noexception/container/factory/config/ConfigurableBeanFactory.java +++ b/src/main/java/cn/noexception/container/factory/config/ConfigurableBeanFactory.java @@ -1,6 +1,7 @@ package cn.noexception.container.factory.config; import cn.noexception.container.factory.HierarchicalBeanFactory; +import cn.noexception.container.factory.utils.StringValueResolver; /** * ConfigurableBeanFactory @@ -18,4 +19,8 @@ public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, Single * 销毁单例对象 */ void destroySingletons(); + + void addEmbeddedValueResolver(StringValueResolver valueResolver); + + String resolveEmbeddedValue(String value); } diff --git a/src/main/java/cn/noexception/container/factory/config/InstantiationAwareBeanPostProcessor.java b/src/main/java/cn/noexception/container/factory/config/InstantiationAwareBeanPostProcessor.java index e2c93ef..f5fb7e6 100644 --- a/src/main/java/cn/noexception/container/factory/config/InstantiationAwareBeanPostProcessor.java +++ b/src/main/java/cn/noexception/container/factory/config/InstantiationAwareBeanPostProcessor.java @@ -1,6 +1,7 @@ package cn.noexception.container.factory.config; import cn.noexception.container.BeansException; +import cn.noexception.container.PropertyValues; /** * InstantiationAwareBeanPostProcessor @@ -13,4 +14,14 @@ public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor { * 在 Bean 对象执行初始化方法之前,执行此方法 */ 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/cn/noexception/container/factory/support/AbstractAutowiredCapableBeanFactory.java b/src/main/java/cn/noexception/container/factory/support/AbstractAutowiredCapableBeanFactory.java index 8986aa4..76862a2 100644 --- a/src/main/java/cn/noexception/container/factory/support/AbstractAutowiredCapableBeanFactory.java +++ b/src/main/java/cn/noexception/container/factory/support/AbstractAutowiredCapableBeanFactory.java @@ -32,6 +32,8 @@ public abstract class AbstractAutowiredCapableBeanFactory extends AbstractBeanFa } // 实例化 Bean bean = createBeanInstance(beanDefinition, beanName, args); + // 在设置 Bean 属性之前,允许 BeanPostProcessor 修改属性值 + applyBeanPostProcessorsBeforeApplyingPropertyValues(beanName, bean, beanDefinition); // 给 Bean 填充属性值 applyPropertyValues(beanName, bean, beanDefinition); // 执行 Bean 的初始化方法和 BeanPostProcessor 的前置和后置处理方法 @@ -49,6 +51,29 @@ public abstract class AbstractAutowiredCapableBeanFactory extends AbstractBeanFa 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 (null != pvs) { + for (PropertyValue propertyValue : pvs.getPropertyValues()) { + beanDefinition.getPropertyValues().addPropertyValue(propertyValue); + } + } + + } + } + + + } + protected Object resolveBeforeInstantiation(String beanName, BeanDefinition beanDefinition) { Object bean = applyBeanPostProcessorsBeforeInstantiation(beanDefinition.getBeanClass(), beanName); if (null != bean) { diff --git a/src/main/java/cn/noexception/container/factory/support/AbstractBeanFactory.java b/src/main/java/cn/noexception/container/factory/support/AbstractBeanFactory.java index c8bd96e..f09851e 100644 --- a/src/main/java/cn/noexception/container/factory/support/AbstractBeanFactory.java +++ b/src/main/java/cn/noexception/container/factory/support/AbstractBeanFactory.java @@ -7,6 +7,7 @@ import cn.noexception.container.factory.config.BeanDefinition; import cn.noexception.container.factory.config.BeanPostProcessor; import cn.noexception.container.factory.config.ConfigurableBeanFactory; import cn.noexception.container.factory.utils.ClassUtils; +import cn.noexception.container.factory.utils.StringValueResolver; import java.util.ArrayList; import java.util.List; @@ -22,6 +23,11 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp private final List beanPostProcessors = new ArrayList(); + /** + * String resolvers to apply e.g. to annotation attribute values + */ + private final List embeddedValueResolvers = new ArrayList<>(); + @Override public Object getBean(String name) throws BeansException { return doGetBean(name, null); @@ -77,4 +83,18 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp public ClassLoader getBeanClassLoader() { return beanClassLoader; } + + @Override + public void addEmbeddedValueResolver(StringValueResolver valueResolver) { + this.embeddedValueResolvers.add(valueResolver); + } + + @Override + 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/cn/noexception/container/factory/support/DefaultListableBeanFactory.java b/src/main/java/cn/noexception/container/factory/support/DefaultListableBeanFactory.java index ffcc434..3fcdc1b 100644 --- a/src/main/java/cn/noexception/container/factory/support/DefaultListableBeanFactory.java +++ b/src/main/java/cn/noexception/container/factory/support/DefaultListableBeanFactory.java @@ -4,7 +4,9 @@ import cn.noexception.container.BeansException; import cn.noexception.container.factory.ConfigurableListableBeanFactory; import cn.noexception.container.factory.config.BeanDefinition; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -56,4 +58,20 @@ public class DefaultListableBeanFactory extends AbstractAutowiredCapableBeanFact public void preInstantiateSingletons() throws BeansException { beanDefinitionMap.keySet().forEach(this::getBean); } + + @Override + public T getBean(Class requiredType) throws BeansException { + List beanNames = new ArrayList<>(); + for (Map.Entry entry : beanDefinitionMap.entrySet()) { + Class beanClass = entry.getValue().getBeanClass(); + if (requiredType.isAssignableFrom(beanClass)) { + beanNames.add(entry.getKey()); + } + } + if (1 == beanNames.size()) { + return getBean(beanNames.get(0), requiredType); + } + + throw new BeansException(requiredType + "expected single bean but found " + beanNames.size() + ": " + beanNames); + } } diff --git a/src/main/java/cn/noexception/container/factory/utils/StringValueResolver.java b/src/main/java/cn/noexception/container/factory/utils/StringValueResolver.java new file mode 100644 index 0000000..c7b8f46 --- /dev/null +++ b/src/main/java/cn/noexception/container/factory/utils/StringValueResolver.java @@ -0,0 +1,13 @@ +package cn.noexception.container.factory.utils; + +/** + * StringValueResolver + *

+ * 解析字符串接口 + * + * @author 吕滔 + * @Date 2021/11/9 10:37 + */ +public interface StringValueResolver { + String resolveStringValue(String strVal); +} -- GitLab