《四 spring源码》手写springioc框架
手写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框架的更多相关文章
- 《四 spring源码》手写springmvc
手写SpringMVC思路 1.web.xml加载 为了读取web.xml中的配置,我们用到ServletConfig这个类,它代表当前Servlet在web.xml中的配置信息.通过web.xml ...
- java架构之路-(spring源码篇)springIOC容器源码解析(上)
我们这次来叭叭一下Spring的源码,这次博客主要来说说Spring源码,先粗略的撸一遍,下篇博客选几个重点去说,由于过于复杂,我也是看了一点点,我们先来过一遍源码,然后上流程图,最后我们再回头总结一 ...
- 《四 spring源码》利用TransactionManager手写spring的aop
事务控制分类 编程式事务控制 自己手动控制事务,就叫做编程式事务控制. Jdbc代码: Conn.setAutoCommite(false); // 设置手动控制事务 Hibern ...
- Spring源码解析一(框架梳理)
整体架构 打算开始写这个系列,不为上首页,也不为博取多少关注,只有一个目的:梳理知识,扩充思路:废话不多,开始吧.第一步,大家去spring的官方github下面去下载它的源码,具体的自己谷歌,我已经 ...
- 史上最完整promise源码手写实现
史上最完整的promise源码实现,哈哈,之所以用这个标题,是因为开始用的标题<手写promise源码>不被收录 promise自我介绍 promise : "君子一诺千金,承诺 ...
- 《四 spring源码》spring的事务注解@Transactional 原理分析
先了解什么是注解 注解 Jdk1.5新增新技术,注解.很多框架为了简化代码,都会提供有些注解.可以理解为插件,是代码级别的插件,在类的方法上写:@XXX,就是在代码上插入了一个插件. 注解不会也不能影 ...
- spring源码浅析——IOC
=========================================== 原文链接: spring源码浅析--IOC 转载请注明出处! ======================= ...
- Ubuntu搭建Spring源码环境常见问题
在一心想要学习Spring框架源码时,我们会遇到很多麻烦的问题.开始本文前,你只需要拥有一个装好IDEA的Ubuntu系统就可以愉快启程了.如果还没有IDEA,可以参考在Ubuntu上安装Intell ...
- CRUD搬砖两三年了,怎么阅读Spring源码?
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 连读同事写的代码都费劲,还读Spring? 咋的,Spring 很难读! 这个与我们码农朝夕 ...
随机推荐
- python--面向对象(最全讲解)
http://www.cnblogs.com/Eva-J/articles/7293890.html 阅读目录 楔子 面向过程vs面向对象 初识面向对象 类的相关知识 对象的相关知识 对象之间的交互 ...
- java Class类
java Class类 Class类(在java.lang包中,Instances of the class Classrepresent classes and interfaces in a ru ...
- CodeForces 484B Maximum Value (数学,其实我也不知道咋分类)
B. Maximum Value time limit per test 1 second memory limit per test 256 megabytes input standard inp ...
- 打包python文件为exe文件(PyInstaller工具使用方法)
最近做的新浪微博爬虫程序,打算打包成.exe软件以方便使用,网上找到一个很好的打包工具pyinstaller,这里记录一下打包的方法. 一.下载pyinstaller 我使用的版本为PyInstall ...
- Ajax的调试错误信息的输出
error: function(xhr, status, error) { console.log(xhr); console.log(status); console.log(error); }
- Do not have XXX handler in current page
这种错误没有什么技术含量,也很容易解决. 一般就是wxml里面的button/form之类的,你用bindtap/bindsubmit给它绑了一个XXX函数,但是呢,你没有在相关js页面里面定义这个函 ...
- ACM-ICPC2018徐州网络赛 Features Track(二维map+01滚动)
Features Track 31.32% 1000ms 262144K Morgana is learning computer vision, and he likes cats, too. ...
- ssm重新开发计科院新闻网站
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"% ...
- ue4 svn备份目录
http://blog.csdn.net/sh15285118586/article/details/55737480 UE4工程文件备份目录有:Config.Content.Plugins.Sour ...
- js实现页面的上滑下拉功能
这两天做项目,用到了上滑和下拉的功能,主要是通过监听touchmove,touchstart,touchend三个事件去判断页面上滑状态还是下拉状态. 同时加一个知识点:有时在监听时会报错,这个错是这 ...