diff --git a/pom.xml b/pom.xml
index 982b560ca81743ac72cd1d215c902bf106d958f8..ef93be4aa286774f1805a816befd9aa5554602fc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -58,6 +58,11 @@
hutool-all
5.5.0
+
+ org.dom4j
+ dom4j
+ 2.1.3
+
org.openjdk.jol
diff --git a/src/main/java/cn/noexception/container/context/annotation/ClassPathBeanDefinitionScanner.java b/src/main/java/cn/noexception/container/context/annotation/ClassPathBeanDefinitionScanner.java
new file mode 100644
index 0000000000000000000000000000000000000000..b26a0fa4c60e1f38cf877c0feb9a004729f5a3f2
--- /dev/null
+++ b/src/main/java/cn/noexception/container/context/annotation/ClassPathBeanDefinitionScanner.java
@@ -0,0 +1,57 @@
+package cn.noexception.container.context.annotation;
+
+import cn.hutool.core.util.StrUtil;
+import cn.noexception.container.factory.config.BeanDefinition;
+import cn.noexception.container.factory.stereotype.Cube;
+import cn.noexception.container.factory.support.BeanDefinitionRegistry;
+
+import java.util.Set;
+
+/**
+ * ClassPathBeanDefinitionScanner
+ *
+ * 扫描包处理
+ *
+ * @author 吕滔
+ * @Date 2021/11/8 17:21
+ */
+public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider {
+ private BeanDefinitionRegistry registry;
+
+ public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {
+ this.registry = registry;
+ }
+
+ public void doScan(String... basePackages) {
+ for (String basePackage : basePackages) {
+ Set candidates = findCandidateComponents(basePackage);
+ for (BeanDefinition beanDefinition : candidates) {
+ // 解析 Bean 的作用域 singleton、prototype
+ String beanScope = resolveBeanScope(beanDefinition);
+ if (StrUtil.isNotEmpty(beanScope)) {
+ beanDefinition.setScope(beanScope);
+ }
+ registry.registerBeanDefinition(determineBeanName(beanDefinition), beanDefinition);
+ }
+ }
+ }
+
+ private String determineBeanName(BeanDefinition beanDefinition) {
+ Class> beanClass = beanDefinition.getBeanClass();
+ Cube cube = beanClass.getAnnotation(Cube.class);
+ String value = cube.value();
+ if (StrUtil.isEmpty(value)) {
+ value = StrUtil.lowerFirst(beanClass.getSimpleName());
+ }
+ return value;
+ }
+
+ private String resolveBeanScope(BeanDefinition beanDefinition) {
+ Class> beanClass = beanDefinition.getBeanClass();
+ Scope scope = beanClass.getAnnotation(Scope.class);
+ if (null != scope) return scope.value();
+ return StrUtil.EMPTY;
+ }
+
+
+}
diff --git a/src/main/java/cn/noexception/container/context/annotation/ClassPathScanningCandidateComponentProvider.java b/src/main/java/cn/noexception/container/context/annotation/ClassPathScanningCandidateComponentProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..e22e790e79bf30ca094c6140a630b2f872aa0a08
--- /dev/null
+++ b/src/main/java/cn/noexception/container/context/annotation/ClassPathScanningCandidateComponentProvider.java
@@ -0,0 +1,26 @@
+package cn.noexception.container.context.annotation;
+
+import cn.hutool.core.util.ClassUtil;
+import cn.noexception.container.factory.config.BeanDefinition;
+import cn.noexception.container.factory.stereotype.Cube;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+/**
+ * ClassPathScanningCandidateComponentProvider
+ *
+ * @author 吕滔
+ * @Date 2021/11/8 16:55
+ */
+public class ClassPathScanningCandidateComponentProvider {
+ public Set findCandidateComponents(String basePackage){
+ Set candidates = new LinkedHashSet<>();
+ Set> classes = ClassUtil.scanPackageByAnnotation(basePackage, Cube.class);
+ for (Class> clazz : classes) {
+ candidates.add(new BeanDefinition(clazz));
+ }
+ return candidates;
+ }
+
+}
diff --git a/src/main/java/cn/noexception/container/context/annotation/Scope.java b/src/main/java/cn/noexception/container/context/annotation/Scope.java
new file mode 100644
index 0000000000000000000000000000000000000000..795ac3b27a1be92471aa8f1767b334ed2760daaa
--- /dev/null
+++ b/src/main/java/cn/noexception/container/context/annotation/Scope.java
@@ -0,0 +1,23 @@
+package cn.noexception.container.context.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * Scope
+ * 作用域注解
+ *
+ * @author 吕滔
+ * @Date 2021/11/8 16:40
+ */
+@Target({ElementType.TYPE, ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface Scope {
+ /**
+ * 方便通过配置 Bean 对象注解的时候,拿到 Bean 对象的作用域。
+ *
+ * @return 默认使用 singleton
+ */
+ String value() default "singleton";
+
+}
diff --git a/src/main/java/cn/noexception/container/factory/PropertyPlaceholderConfigurer.java b/src/main/java/cn/noexception/container/factory/PropertyPlaceholderConfigurer.java
new file mode 100644
index 0000000000000000000000000000000000000000..677e47a8aa34a6f68192ee122076fb4d31f0ccc6
--- /dev/null
+++ b/src/main/java/cn/noexception/container/factory/PropertyPlaceholderConfigurer.java
@@ -0,0 +1,66 @@
+package cn.noexception.container.factory;
+
+import cn.noexception.container.BeansException;
+import cn.noexception.container.PropertyValue;
+import cn.noexception.container.PropertyValues;
+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 java.util.Properties;
+
+/**
+ * PropertyPlaceholderConfigurer
+ * 占位符配置
+ *
+ * @author 吕滔
+ * @Date 2021/11/8 16:14
+ */
+public class PropertyPlaceholderConfigurer implements BeanFactoryPostProcessor {
+
+ private static final String DEFAULT_PLACEHOLDER_PREFIX = "${";
+
+ private static final String DEFAULT_PLACEHOLDER_SUFFIX = "}";
+
+ private String location;
+
+ @Override
+ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
+ // 加载属性文件
+ try {
+ DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
+ Resource resource = resourceLoader.getResource(location);
+ Properties properties = new Properties();
+ properties.load(resource.getInputStream());
+
+ // 占位符替换
+ String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
+ for (String beanName : beanDefinitionNames) {
+ BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
+ PropertyValues propertyValues = beanDefinition.getPropertyValues();
+ 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()));
+ }
+ }
+
+ }
+ }catch (Exception ex){
+ throw new BeansException(" properties 加载失败", ex);
+ }
+ }
+
+ public void setLocation(String location) {
+ this.location = location;
+ }
+}
diff --git a/src/main/java/cn/noexception/container/factory/stereotype/Cube.java b/src/main/java/cn/noexception/container/factory/stereotype/Cube.java
new file mode 100644
index 0000000000000000000000000000000000000000..a7a502babdad928f32b22c2c7a240d24c7561415
--- /dev/null
+++ b/src/main/java/cn/noexception/container/factory/stereotype/Cube.java
@@ -0,0 +1,17 @@
+package cn.noexception.container.factory.stereotype;
+
+import java.lang.annotation.*;
+
+/**
+ * Cube
+ * 定义容器组件注解标记
+ *
+ * @author 吕滔
+ * @Date 2021/11/8 16:53
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface Cube {
+ String value() default "";
+}
diff --git a/src/main/java/cn/noexception/container/factory/utils/ClassUtils.java b/src/main/java/cn/noexception/container/factory/utils/ClassUtils.java
index 6d640eb08b42a4a9a6c80a9dc401462cb16ac2ff..69d273b051ed40c082ff348f469402ae72d656da 100644
--- a/src/main/java/cn/noexception/container/factory/utils/ClassUtils.java
+++ b/src/main/java/cn/noexception/container/factory/utils/ClassUtils.java
@@ -1,6 +1,9 @@
package cn.noexception.container.factory.utils;
import cn.noexception.container.ApplicationListener;
+import cn.noexception.container.factory.stereotype.Cube;
+
+import java.util.Set;
/**
* ClassUtils
diff --git a/src/main/java/cn/noexception/container/factory/xml/XmlBeanDefinitionReader.java b/src/main/java/cn/noexception/container/factory/xml/XmlBeanDefinitionReader.java
index fca38b92c51337410b26967433ff65621321a186..3c3bff3aaf4f8cebcccb9e273e7984eaebad5557 100644
--- a/src/main/java/cn/noexception/container/factory/xml/XmlBeanDefinitionReader.java
+++ b/src/main/java/cn/noexception/container/factory/xml/XmlBeanDefinitionReader.java
@@ -4,18 +4,21 @@ import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.XmlUtil;
import cn.noexception.container.BeansException;
import cn.noexception.container.PropertyValue;
+import cn.noexception.container.context.annotation.ClassPathBeanDefinitionScanner;
import cn.noexception.container.factory.config.BeanDefinition;
import cn.noexception.container.factory.config.BeanReference;
import cn.noexception.container.factory.io.Resource;
import cn.noexception.container.factory.io.ResourceLoader;
import cn.noexception.container.factory.support.AbstractBeanDefinitionReader;
import cn.noexception.container.factory.support.BeanDefinitionRegistry;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.NodeList;
+import org.dom4j.Document;
+import org.dom4j.DocumentException;
+import org.dom4j.Element;
+import org.dom4j.io.SAXReader;
import java.io.IOException;
import java.io.InputStream;
+import java.util.List;
/**
* XmlBeanDefinitionReader
@@ -38,7 +41,7 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
try (InputStream inputStream = resource.getInputStream()) {
doLoadBeanDefinition(inputStream);
}
- } catch (IOException | ClassNotFoundException e) {
+ } catch (IOException | ClassNotFoundException | DocumentException e) {
throw new BeansException("IOException parsing XML document from " + resource, e);
}
}
@@ -64,28 +67,32 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
}
}
- private void doLoadBeanDefinition(InputStream inputStream) throws ClassNotFoundException {
- Document doc = XmlUtil.readXML(inputStream);
- Element root = doc.getDocumentElement();
- NodeList childNodes = root.getChildNodes();
-
- for (int i = 0; i < childNodes.getLength(); i++) {
- // 判断元素
- if (!(childNodes.item(i) instanceof Element))
- continue;
- // 判断对象
- if (!"bean".equals(childNodes.item(i).getNodeName()))
- continue;
-
- // 解析标签
- Element bean = (Element) childNodes.item(i);
- String id = bean.getAttribute("id");
- String name = bean.getAttribute("name");
- String className = bean.getAttribute("class");
- String initMethod = bean.getAttribute("init-method");
- String destroyMethodName = bean.getAttribute("destroy-method");
+ private void doLoadBeanDefinition(InputStream inputStream) throws ClassNotFoundException, DocumentException {
+ SAXReader reader = new SAXReader();
+ Document doc = reader.read(inputStream);
+ Element root = doc.getRootElement();
+
+ // 解析 context:component-scan 标签, 扫描包中的类并提取相关信息,用于组装 BeanDefinition
+ Element componentScan = root.element("component-scan");
+ if (null != componentScan) {
+ String scanPath = componentScan.attributeValue("base-package");
+ if (StrUtil.isEmpty(scanPath)) {
+ throw new BeansException("base-package 为空。");
+ }
+ scanPackage(scanPath);
+ }
+
+
+ List beanList = root.elements("bean");
+
+ for (Element bean : beanList) {
+ String id = bean.attributeValue("id");
+ String name = bean.attributeValue("name");
+ String className = bean.attributeValue("class");
+ String initMethod = bean.attributeValue("init-method");
+ String destroyMethodName = bean.attributeValue("destroy-method");
// 增加了关于 Bean 对象配置中 scope 的解析,并把这个属性信息填充到 Bean 定义中
- String beanScope = bean.getAttribute("scope");
+ String beanScope = bean.attributeValue("scope");
// 获取 Class,方便获取类中的名称
Class> clazz = Class.forName(className);
@@ -102,29 +109,32 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
if (StrUtil.isNotEmpty(beanScope)) {
beanDefinition.setScope(beanScope);
}
+
+ List propertyList = bean.elements("property");
// 读取属性并填充
- for (int j = 0; j < bean.getChildNodes().getLength(); j++) {
- if (!(bean.getChildNodes().item(j) instanceof Element))
- continue;
- if (!"property".equals(bean.getChildNodes().item(j).getNodeName()))
- continue;
- // 解析标签 property
- Element property = (Element) bean.getChildNodes().item(j);
- String attrName = property.getAttribute("name");
- String attrValue = property.getAttribute("value");
- String attrRef = property.getAttribute("ref");
+ for (Element property : propertyList) {
+ String attrName = property.attributeValue("name");
+ String attrValue = property.attributeValue("value");
+ String attrRef = property.attributeValue("ref");
// 获取属性值:引入对象、值对象
- Object value = StrUtil.isNotEmpty(attrRef)?new BeanReference(attrRef):attrValue;
+ Object value = StrUtil.isNotEmpty(attrRef) ? new BeanReference(attrRef) : attrValue;
// 创建属性信息
PropertyValue propertyValue = new PropertyValue(attrName, value);
beanDefinition.getPropertyValues().addPropertyValue(propertyValue);
}
if (getRegistry().containsBeanDefinition(beanName)) {
- throw new BeansException("Duplicate beanName["+beanName+"] is not allowed");
+ throw new BeansException("Duplicate beanName[" + beanName + "] is not allowed");
}
// 注册 BeanDefinition
getRegistry().registerBeanDefinition(beanName, beanDefinition);
}
}
+
+ private void scanPackage(String scanPath) {
+ String[] basesPackages = StrUtil.splitToArray(scanPath, ',');
+ ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(getRegistry());
+ scanner.doScan(basesPackages);
+ }
+
}