提交 7ca6c526 编写于 作者: 希川's avatar 希川

<feat>: 自动扫描 Bean 对象

上级 a3ca3770
......@@ -58,6 +58,11 @@
<artifactId>hutool-all</artifactId>
<version>5.5.0</version>
</dependency>
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.openjdk.jol/jol-cli -->
<dependency>
<groupId>org.openjdk.jol</groupId>
......
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<BeanDefinition> 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;
}
}
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<BeanDefinition> findCandidateComponents(String basePackage){
Set<BeanDefinition> candidates = new LinkedHashSet<>();
Set<Class<?>> classes = ClassUtil.scanPackageByAnnotation(basePackage, Cube.class);
for (Class<?> clazz : classes) {
candidates.add(new BeanDefinition(clazz));
}
return candidates;
}
}
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";
}
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;
}
}
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 "";
}
package cn.noexception.container.factory.utils;
import cn.noexception.container.ApplicationListener;
import cn.noexception.container.factory.stereotype.Cube;
import java.util.Set;
/**
* ClassUtils
......
......@@ -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<Element> 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<Element> 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);
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册