一、前言
2020.03.28 :本来打算把源码分析一遍,这周时间不够了,下周分析好再更新一遍。
2020.04.05 :人类的本质果然是鸽子,没错,我鸽了。。。
二、简介
首先明白Spring几个关键类:
-
BeanFactory :
BeanFactory定义了 IOC 容器的最基本形式,并提供了 IOC 容器应遵守的的最基本的接口,也就是Spring IOC 所遵守的最底层和最基本的编程规范。在 Spring 代码中, BeanFactory 只是个接口,并不是 IOC容器的具体实现,但是 Spring 容器给出了很多种实现,如 DefaultListableBeanFactory 、 XmlBeanFactory 、ApplicationContext 等,都是附加了某种功能的实现。 -
ApplicationContext :
ApplicationContext继承了BeanFactory。是Spring在 BeanFactory基础上的一个扩展类,提供了更多的功能,比如 国际化、资源获取、AOP等。并且ApplicationContext加载Bean是启动加载,而BeanFactory是懒加载的方式。 -
BeanDefination:
这个接口,可以理解为xml bean元素的数据载体。通过对比xml bean标签的属性列表和BeanDefinition的属性列表一看便知。我的理解,是解析XML的过程,就是 xml 元素内容 转换为BeanDefinition对象的过程。而且这个接口,支持层级,对应对象的继承。有一个类BeanDefinitionHolder,BeanDefinitionHolder,根据名称或者别名持有beanDefinition,它承载了name和BeanDefinition的映射信息。BeanWarpper:提供对标准javabean的分析和操作方法:单个或者批量获取和设置属性值,获取属性描述符,查询属性的可读性和可写性等。支持属性的嵌套设置,深度没有限制。
三、手写IOC框架
手写首先需要了解IOC整个原理。等下周把源码分析了会更详细的写一写,莫要捉急。。。
简单来说 :
- 服务启动后扫描开始扫描指定的路径及其子路径下的文件。
- 过滤出其中的字节码文件(class文件)。
- 通过反射来获取 Class,判断类上是否被 @SelfComponent 注解修饰
- 若被 @SelfComponent 修饰,则说明该类的生命周期控制反转,交由容器来控制其创建销毁。代码中的表现是 创建实例,并保存到beanMap 中。
- 当我们把所有的所有 被 @SelfComponent 修饰 的对象保存到 beanMap中后,开始对这些对象中的属性进行一个判断: 若属性被 @SelfAutowired 修饰 , 则表明需要容器给他注入实例,这时候从beanMap中拿出对应的实例,进行一个赋值处理。
下面贴一些关键代码
1. 自定义注解
自定义 SelfAutowired 、SelfComponent 两个注解,见名知意。
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target({ElementType.METHOD, ElementType.FIELD})
public @interface SelfAutowired {
}
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target({ElementType.TYPE})
public @interface SelfComponent {
}
2. SpringIocApplication
SpringIocApplication 作为主要实现类,主要在 SpringIocApplication 中实现了 IOC 功能
package com.kingfish.ioc;
import com.google.common.collect.Maps;
import com.kingfish.ioc.annotation.SelfAutowired;
import com.kingfish.ioc.annotation.SelfComponent;
import com.kingfish.ioc.utils.ClassUtils;
import com.kingfish.ioc.utils.lambda.Lambda;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
/**
* @Author : wlhao
* @Email : kingfishx@163.com
* @Data: 2020/3/29 14:08
* @Des: SpringIoc 的启动类
*/
@Slf4j
public class SpringIocApplication {
private static volatile SpringIocApplication springIocApplication;
private Map<String, Object> beanMap = Maps.newConcurrentMap();
private SpringIocApplication() {
}
public static SpringIocApplication getInstance() {
if (springIocApplication == null) {
synchronized (SpringIocApplication.class) {
if (springIocApplication == null) {
springIocApplication = new SpringIocApplication();
}
}
}
return springIocApplication;
}
/**
* 启动方法
*
* @param aClass
* @return
*/
public SpringIocApplication run(Class<?> aClass) {
try {
String packageName = aClass.getPackage().getName();
List<Class<?>> allClass = ClassUtils.getAllClassByPakcage(packageName);
initBean(allClass);
initBeanFields();
} catch (IOException | IllegalAccessException e) {
e.printStackTrace();
}
return this;
}
/**
* 初始化Bean,将需要的bean保存到容器beanMap中
*
* @param classList
*/
private void initBean(List<Class<?>> classList) {
classList.stream().filter(aClass -> existComponent(aClass))
.forEach(Lambda.forEach(aClass -> {
Class<?>[] interfaces = aClass.getInterfaces();
String beanName = ArrayUtils.isEmpty(interfaces) ? toLowerCaseFirstWord(aClass.getSimpleName()) : toLowerCaseFirstWord(interfaces[0].getSimpleName());
if (beanMap.containsKey(beanName)) {
throw new RuntimeException("bean already existed");
}
beanMap.put(beanName, aClass.newInstance());
}));
}
/**
* 初始化容器中的bean 的属性字段
*/
private void initBeanFields() throws IllegalAccessException {
for (Object bean : beanMap.values()) {
Field[] declaredFields = bean.getClass().getDeclaredFields();
for (Field field : declaredFields) {
if (existAutowired(field)) {
String fieldName = toLowerCaseFirstWord(field.getName());
if (!beanMap.containsKey(fieldName)) {
throw new RuntimeException("class not found");
}
Object beanObject = beanMap.getOrDefault(fieldName, null);
// 允许访问私有属性
field.setAccessible(true);
// 给bean 的field属性赋值为beanObject
field.set(bean, beanObject);
}
}
}
}
/**
* 是否存在SelfComponent 注解
*
* @param aclass
* @return
*/
private boolean existComponent(Class<?> aclass) {
return aclass.getAnnotation(SelfComponent.class) != null && !aclass.isAnnotation();
}
/**
* 是否存在SelfAutowired 注解
*
* @param field
* @return
*/
private boolean existAutowired(Field field) {
return field.getAnnotation(SelfAutowired.class) != null;
}
/**
* 获取bean
*
* @param beanName
* @param tClass
* @param <T>
* @return
*/
public <T> T getBean(String beanName, Class<T> tClass) {
if (!beanMap.containsKey(beanName)) {
throw new RuntimeException("not found");
}
return (T) beanMap.getOrDefault(beanName, null);
}
/**
* 首字母转小写
*
* @param s
* @return
*/
private String toLowerCaseFirstWord(String s) {
return Character.isLowerCase(s.charAt(0)) ? s
: (new StringBuilder().append(Character.toLowerCase(s.charAt(0)))).append(s.substring(1)).toString();
}
}
3. ClassUtils
一个辅助的工具类。
package com.kingfish.ioc.utils;
import com.google.common.collect.Lists;
import org.apache.commons.lang3.StringUtils;
import java.io.*;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
/**
* @Author : wlhao
* @Email : kingfishx@163.com
* @Data: 2020/3/29 14:07
* @Des: 类操作的辅助类
*/
public class ClassUtils {
/**
* 根据包路径获取路径下的所有类
*
* @param packageName
* @return
*/
public static List<Class<?>> getAllClassByPakcage(String packageName) throws IOException {
List<Class<?>> classList = Lists.newArrayList();
String packagePath = packageName.replace(".", "/");
Enumeration<URL> resources = Thread.currentThread().getContextClassLoader().getResources(packagePath);
while (resources.hasMoreElements()) {
URL url = resources.nextElement();
String protocol = url.getProtocol();
if ("file".equals(protocol)) {
System.out.println("file类型的扫描");
String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
findAllClassByPath(packageName, filePath, classList);
} else if ("jar".equals(protocol)) {
System.out.println("jar类型的扫描");
}
}
return classList;
}
/**
* 获取给定路径下的所有class
*
* @param packageName 包名
* @param filePath 路径
* @param classList 返回列表
*/
public static void findAllClassByPath(String packageName, String filePath, List<Class<?>> classList) {
try {
File rootFile = new File(filePath);
if (!rootFile.exists()) {
throw new RuntimeException(filePath);
}
if (rootFile.isDirectory()) {
Arrays.stream(rootFile.listFiles(file -> (StringUtils.endsWith(file.getName(), "class") || file.isDirectory())))
.forEach(file -> findAllClassByPath(packageName + "." + file.getName(), file.getAbsolutePath(), classList));
} else{
String className = StringUtils.substringBeforeLast(packageName, ".");
classList.add(Class.forName(className));
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
4. 测试
总的来说,只有两个关键类 SpringIocApplication、ClassUtils。依次创建IocController、IocService、IocDao 来进行一个测试就行了
public class SpringApplicationServer {
public static void main(String[] args) {
SpringIocApplication springIocApplication = SpringIocApplication.getInstance();
springIocApplication.run(SpringApplicationServer.class);
IocController iocController = springIocApplication.getBean("iocController", IocController.class);
iocController.iocController();
}
}
/**
* @Author : wlhao
* @Email : kingfishx@163.com
* @Data: 2020/3/29 14:56
* @Des:
*/
@SelfComponent
public class IocController {
@SelfAutowired
private IocService iocService;
public void iocController() {
System.out.println("IocDao.iocController");
iocService.iocService();
}
}
结果如下
5. 项目地址
https://github.com/HKingFish/ioc-demo.git
未完待遇…
以上:内容部分参考
https://blog.csdn.net/woshilijiuyi/article/details/82219585
如有侵扰,联系删除。 内容仅用于自我记录学习使用。如有错误,欢迎指正