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); + } + }