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 5fe0b56b955a10ae2d74c8eda527300dc86978f5..0254a6d5da495f3644467d0a349372429afcb3bb 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 2f741e7ffcf8c36f188bef0e2c96374254ef2cc5..15dff2eb3224087ad0c9d5424e56f9bddf6cf276 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 49c39a03959062f82a883d9df0029ea3c7a9dd9e..5c727fda0c0d79b481e253d51f6bce77cb482e5c 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 677e47a8aa34a6f68192ee122076fb4d31f0ccc6..eae4b53b5a962443464565f934740efad2864b18 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 0000000000000000000000000000000000000000..66e8353e2214c46eea1b81d0302d41e3935eb07e --- /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 0000000000000000000000000000000000000000..6d2b9903b831f1818b5a4648f1567ecb7d34c0ca --- /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 0000000000000000000000000000000000000000..96392caca89a7cc8cc62f40c873913a916ee81a0 --- /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 0000000000000000000000000000000000000000..ad8d99cc055deb0f91ed8ed23e3a1f5348c82cd0 --- /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 7ffe38a302a12fefafdee50ad5f40bae280a9943..6cccbe4730f32c8de88c3a338b8b9fe3e91fc2fb 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 e2c93ef12e3c2c1801ffc2786b8a72b85159bc28..f5fb7e66692f50a74a479c6008d0bd51238913de 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 8986aa40178813f7e809587921c8c6c81720f3c3..76862a221d9fdbac1e9679943a1054430c15b0ea 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 c8bd96e4debcdb962e9289b1800c4a93a93b3aec..f09851ee15f6eec7fce82031c29de0c20b282d25 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 ffcc4341233fb85fc72ab20800cba8ffeefa232d..3fcdc1b052c7d06446d293381b1a3db38fc1d860 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 0000000000000000000000000000000000000000..c7b8f46713bf8eee90b009079d4fa58162e804a1 --- /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); +}