手写SpringIOCXML版本

/**
* 手写Spring专题 XML方式注入bean
*
*
*
*/
public class ClassPathXmlApplicationContext {
// xml路径地址
private String xmlPath; public ClassPathXmlApplicationContext(String xmlPath) {
this.xmlPath = xmlPath;
} public Object getBean(String beanId) throws Exception {
// 1. 读取配置文件
List<Element> elements = readerXml();
if (elements == null) {
throw new Exception("该配置文件没有子元素");
}
// 2. 使用beanId查找对应的class地址
String beanClass = findXmlByIDClass(elements, beanId);
if (StringUtils.isEmpty(beanClass)) {
throw new Exception("未找到对应的class地址");
}
// 3. 使用反射机制初始化,对象
Class<?> forName = Class.forName(beanClass);
return forName.newInstance();
} // 读取配置文件信息
public List<Element> readerXml() throws DocumentException {
SAXReader saxReader = new SAXReader();
if (StringUtils.isEmpty(xmlPath)) {
new Exception("xml路径为空...");
}
Document read = saxReader.read(getClassXmlInputStream(xmlPath));
// 获取根节点信息
Element rootElement = read.getRootElement();
// 获取子节点
List<Element> elements = rootElement.elements();
if (elements == null || elements.isEmpty()) {
return null;
}
return elements;
} // 使用beanid查找该Class地址
public String findXmlByIDClass(List<Element> elements, String beanId) throws Exception {
for (Element element : elements) {
// 读取节点上是否有value
String beanIdValue = element.attributeValue("id");
if (beanIdValue == null) {
throw new Exception("使用该beanId为查找到元素");
}
if (!beanIdValue.equals(beanId)) {
continue;
}
// 获取Class地址属性
String classPath = element.attributeValue("class");
if (!StringUtils.isEmpty(classPath)) {
return classPath;
}
}
return null;
} // 读取xml配置文件
public InputStream getClassXmlInputStream(String xmlPath) {
InputStream resourceAsStream = getClass().getClassLoader().getResourceAsStream(xmlPath);
return resourceAsStream;
} }

手写SpringIOC注解版本

基本思路:    扫包+反射

// 1.使用反射机制获取该包下所有的类已经存在bean的注解类

// 2.使用Java反射机制初始化对象

// 3.使用beanID查找查找对应bean对象                  ---------------@resource

// 4.使用反射读取类的属性,赋值信息

/**
* 手写Spring专题 注解版本注入bean
*
*
*
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public class ClassPathXmlApplicationContext {
// 扫包范围
private String packageName;
ConcurrentHashMap<String, Object> initBean = null; public ClassPathXmlApplicationContext(String packageName) {
this.packageName = packageName;
} // 使用beanID查找对象
public Object getBean(String beanId) throws Exception {
// 1.使用反射机制获取该包下所有的类已经存在bean的注解类
List<Class> listClassesAnnotation = findClassExisService();
if (listClassesAnnotation == null || listClassesAnnotation.isEmpty()) {
throw new Exception("没有需要初始化的bean");
}
// 2.使用Java反射机制初始化对象
initBean = initBean(listClassesAnnotation);
if (initBean == null || initBean.isEmpty()) {
throw new Exception("初始化bean为空!");
}
// 3.使用beanID查找查找对应bean对象
Object object = initBean.get(beanId);
// 4.使用反射读取类的属性,赋值信息
attriAssign(object);
return object;
} // 使用反射读取类的属性,赋值信息 @resource实现
public void attriAssign(Object object) throws IllegalArgumentException, IllegalAccessException {
// 1.获取类的属性是否存在 获取bean注解
Class<? extends Object> classInfo = object.getClass();
Field[] declaredFields = classInfo.getDeclaredFields();
for (Field field : declaredFields) {
// 属性名称
String name = field.getName();
// 2.使用属性名称查找bean容器赋值
Object bean = initBean.get(name);
if (bean != null) {
// 私有访问允许访问
field.setAccessible(true);
// 给属性赋值
field.set(object, bean);
continue;
}
} } // 使用反射机制获取该包下所有的类已经存在bean的注解类
public List<Class> findClassExisService() throws Exception {
// 1.使用反射机制获取该包下所有的类
if (StringUtils.isEmpty(packageName)) {
throw new Exception("扫包地址不能为空!");
}
// 2.使用反射技术获取当前包下所有的类
List<Class<?>> classesByPackageName = ClassUtil.getClasses(packageName);
// 3.存放类上有bean注入注解
List<Class> exisClassesAnnotation = new ArrayList<Class>();
// 4.判断该类上属否存在注解
for (Class classInfo : classesByPackageName) {
ExtService extService = (ExtService) classInfo.getDeclaredAnnotation(ExtService.class);
if (extService != null) {
exisClassesAnnotation.add(classInfo);
continue;
}
}
return exisClassesAnnotation;
} // 初始化bean对象
public ConcurrentHashMap<String, Object> initBean(List<Class> listClassesAnnotation)
throws InstantiationException, IllegalAccessException {
ConcurrentHashMap concurrentHashMap = new ConcurrentHashMap<String, Object>();
for (Class classInfo : listClassesAnnotation) {
// 初始化对象
Object newInstance = classInfo.newInstance();
// 获取父类名称
String beanId = toLowerCaseFirstOne(classInfo.getSimpleName());
concurrentHashMap.put(beanId, newInstance);
}
return concurrentHashMap;
} // 首字母转小写
public static String toLowerCaseFirstOne(String s) {
if (Character.isLowerCase(s.charAt(0)))
return s;
else
return (new StringBuilder()).append(Character.toLowerCase(s.charAt(0))).append(s.substring(1)).toString();
} }

常用反射工具类

public class ClassUtil {

    /**
* 取得某个接口下所有实现这个接口的类
*/
public static List<Class> getAllClassByInterface(Class c) {
List<Class> returnClassList = null; if (c.isInterface()) {
// 获取当前的包名
String packageName = c.getPackage().getName();
// 获取当前包下以及子包下所以的类
List<Class<?>> allClass = getClasses(packageName);
if (allClass != null) {
returnClassList = new ArrayList<Class>();
for (Class classes : allClass) {
// 判断是否是同一个接口
if (c.isAssignableFrom(classes)) {
// 本身不加入进去
if (!c.equals(classes)) {
returnClassList.add(classes);
}
}
}
}
} return returnClassList;
} /*
* 取得某一类所在包的所有类名 不含迭代
*/
public static String[] getPackageAllClassName(String classLocation, String packageName) {
// 将packageName分解
String[] packagePathSplit = packageName.split("[.]");
String realClassLocation = classLocation;
int packageLength = packagePathSplit.length;
for (int i = 0; i < packageLength; i++) {
realClassLocation = realClassLocation + File.separator + packagePathSplit[i];
}
File packeageDir = new File(realClassLocation);
if (packeageDir.isDirectory()) {
String[] allClassName = packeageDir.list();
return allClassName;
}
return null;
} /**
* 从包package中获取所有的Class
*
* @param pack
* @return
*/
public static List<Class<?>> getClasses(String packageName) { // 第一个class类的集合
List<Class<?>> classes = new ArrayList<Class<?>>();
// 是否循环迭代
boolean recursive = true;
// 获取包的名字 并进行替换
String packageDirName = packageName.replace('.', '/');
// 定义一个枚举的集合 并进行循环来处理这个目录下的things
Enumeration<URL> dirs;
try {
dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
// 循环迭代下去
while (dirs.hasMoreElements()) {
// 获取下一个元素
URL url = dirs.nextElement();
// 得到协议的名称
String protocol = url.getProtocol();
// 如果是以文件的形式保存在服务器上
if ("file".equals(protocol)) {
// 获取包的物理路径
String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
// 以文件的方式扫描整个包下的文件 并添加到集合中
findAndAddClassesInPackageByFile(packageName, filePath, recursive, classes);
} else if ("jar".equals(protocol)) {
// 如果是jar包文件
// 定义一个JarFile
JarFile jar;
try {
// 获取jar
jar = ((JarURLConnection) url.openConnection()).getJarFile();
// 从此jar包 得到一个枚举类
Enumeration<JarEntry> entries = jar.entries();
// 同样的进行循环迭代
while (entries.hasMoreElements()) {
// 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
JarEntry entry = entries.nextElement();
String name = entry.getName();
// 如果是以/开头的
if (name.charAt(0) == '/') {
// 获取后面的字符串
name = name.substring(1);
}
// 如果前半部分和定义的包名相同
if (name.startsWith(packageDirName)) {
int idx = name.lastIndexOf('/');
// 如果以"/"结尾 是一个包
if (idx != -1) {
// 获取包名 把"/"替换成"."
packageName = name.substring(0, idx).replace('/', '.');
}
// 如果可以迭代下去 并且是一个包
if ((idx != -1) || recursive) {
// 如果是一个.class文件 而且不是目录
if (name.endsWith(".class") && !entry.isDirectory()) {
// 去掉后面的".class" 获取真正的类名
String className = name.substring(packageName.length() + 1, name.length() - 6);
try {
// 添加到classes
classes.add(Class.forName(packageName + '.' + className));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
} catch (IOException e) {
e.printStackTrace();
} return classes;
} /**
* 以文件的形式来获取包下的所有Class
*
* @param packageName
* @param packagePath
* @param recursive
* @param classes
*/
public static void findAndAddClassesInPackageByFile(String packageName, String packagePath, final boolean recursive,
List<Class<?>> classes) {
// 获取此包的目录 建立一个File
File dir = new File(packagePath);
// 如果不存在或者 也不是目录就直接返回
if (!dir.exists() || !dir.isDirectory()) {
return;
}
// 如果存在 就获取包下的所有文件 包括目录
File[] dirfiles = dir.listFiles(new FileFilter() {
// 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
public boolean accept(File file) {
return (recursive && file.isDirectory()) || (file.getName().endsWith(".class"));
}
});
// 循环所有文件
for (File file : dirfiles) {
// 如果是目录 则继续扫描
if (file.isDirectory()) {
findAndAddClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive,
classes);
} else {
// 如果是java类文件 去掉后面的.class 只留下类名
String className = file.getName().substring(0, file.getName().length() - 6);
try {
// 添加到集合中去
classes.add(Class.forName(packageName + '.' + className));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
}

《四 spring源码》手写springioc框架的更多相关文章

  1. 《四 spring源码》手写springmvc

    手写SpringMVC思路 1.web.xml加载  为了读取web.xml中的配置,我们用到ServletConfig这个类,它代表当前Servlet在web.xml中的配置信息.通过web.xml ...

  2. java架构之路-(spring源码篇)springIOC容器源码解析(上)

    我们这次来叭叭一下Spring的源码,这次博客主要来说说Spring源码,先粗略的撸一遍,下篇博客选几个重点去说,由于过于复杂,我也是看了一点点,我们先来过一遍源码,然后上流程图,最后我们再回头总结一 ...

  3. 《四 spring源码》利用TransactionManager手写spring的aop

    事务控制分类 编程式事务控制          自己手动控制事务,就叫做编程式事务控制. Jdbc代码: Conn.setAutoCommite(false);  // 设置手动控制事务 Hibern ...

  4. Spring源码解析一(框架梳理)

    整体架构 打算开始写这个系列,不为上首页,也不为博取多少关注,只有一个目的:梳理知识,扩充思路:废话不多,开始吧.第一步,大家去spring的官方github下面去下载它的源码,具体的自己谷歌,我已经 ...

  5. 史上最完整promise源码手写实现

    史上最完整的promise源码实现,哈哈,之所以用这个标题,是因为开始用的标题<手写promise源码>不被收录 promise自我介绍 promise : "君子一诺千金,承诺 ...

  6. 《四 spring源码》spring的事务注解@Transactional 原理分析

    先了解什么是注解 注解 Jdk1.5新增新技术,注解.很多框架为了简化代码,都会提供有些注解.可以理解为插件,是代码级别的插件,在类的方法上写:@XXX,就是在代码上插入了一个插件. 注解不会也不能影 ...

  7. spring源码浅析——IOC

    =========================================== 原文链接: spring源码浅析--IOC   转载请注明出处! ======================= ...

  8. Ubuntu搭建Spring源码环境常见问题

    在一心想要学习Spring框架源码时,我们会遇到很多麻烦的问题.开始本文前,你只需要拥有一个装好IDEA的Ubuntu系统就可以愉快启程了.如果还没有IDEA,可以参考在Ubuntu上安装Intell ...

  9. CRUD搬砖两三年了,怎么阅读Spring源码?

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! ‍连读同事写的代码都费劲,还读Spring? 咋的,Spring 很难读! 这个与我们码农朝夕 ...

随机推荐

  1. leetcode 19. Remove Nth Node From End of List(链表)

    Given a linked list, remove the nth node from the end of list and return its head. For example, Give ...

  2. linux进程学习-进程描述符的存储

    当进程被新建时,内核会给进程分配一个8K的空间作为进程的内核堆栈.同时我们知道task_struct结构体也会被创建,但有意思的是,内核不会给task_struct单独分别空间,而是直接将其扔到8k的 ...

  3. xxx referenced from: xxx in xxx.o

    情形一:可能是有一些源码文件没有加入工程所导致的,找到相应的.h和.m文件,将其add进入项目工程即可解决这种问题. 情形二:也有可能是某些framework没有加入项目中, 示例:   Undefi ...

  4. homebrew cask安装launch rocket【转】

    简介 brew cask是一个用命令行管理Mac下应用的工具,它是基于homebrew的一个增强工具. homebrew可以管理Mac下的命令行工具,例如imagemagick, nodejs,如下所 ...

  5. BZOJ_5359_[Lydsy1805月赛]寻宝游戏_DP

    BZOJ5359_[Lydsy1805月赛]寻宝游戏_DP Description begin.lydsy.com/JudgeOnline/upload/201805.pdf 我们需要找到一条权值最大 ...

  6. PHP调用Python快速发送高并发邮件

    1 简介 在PHP中发送邮件,通常都是封装一个php的smtp邮件类来发送邮件.但是PHP底层的socket编程相对于Python来说效率是非常低的.CleverCode同时写过用python写的爬虫 ...

  7. zk 10之:Curator之三:服务的注册及发现

    Service Discovery 我们通常在调用服务的时候,需要知道服务的地址,端口,或者其他一些信息,通常情况下,我们是把他们写到程序里面,但是随着服务越来越多,维护起来也越来越费劲,更重要的是, ...

  8. Nuget-QRCode:jquery-qrcode

    ylbtech-Nuget-QRCode:jquery-qrcode 1.返回顶部 1. <!DOCTYPE html> <html> <head> <tit ...

  9. Elasticsearch的前后台运行与停止(tar包方式)

    备注:在生产环境中,往往一般用后台来运行.jps查看. 1.ES的前台运行 [hadoop@djt002 elasticsearch-2.4.3]$ pwd/usr/local/elasticsear ...

  10. iview之select选择框选中内容后有空格的问题

    导致原因: option组件格式化造成的.此处</Option>在另一行,只要和输出内容一行,就不会有空格了. <Select :label-in-value="true& ...