我们知道,Spring主要是由IoC和AOP两个部分组成,这篇文章只讨论IoC。
IoC的中文翻译是“控制反转”,我觉得这个名称特别唬人,看起来好像非常厉害,但是又不知道具体是做什么的。其实用大白话说,IoC容器就是一段用来控制和管理Java中bean对象实例化以及调用的程序。
我们使用IoC容器大体上有两个需求:
其一,使得我们的程序的耦合性降低,当需求发生变更的时候,只需要修改配置文件即可(即尽量少地在原来的程序中做修改)。
其二,每一个类我们只需要其中的一个实例,比如我们的Controller类,所有相同的请求都可以使用同一个实例来响应,这样可以节省系统资源。
下面我将记录一个简易的IoC容器,该教程来自手写框架!3小时搞懂Spring IoC核心源码_哔哩哔哩_bilibili
程序架构我们的程序总体结构是这样的
具体实现
BeanDefinition.java 是用来存放bean对象的基本数据结构。
public class BeanDefinition {
private String beanName;
private Class beanClass;
public BeanDefinition(String beanName, Class beanClass) {
this.beanName = beanName;
this.beanClass = beanClass;
}
public String getBeanName() {
return beanName;
}
public void setBeanName(String beanName) {
this.beanName = beanName;
}
public Class getBeanClass() {
return beanClass;
}
public void setBeanClass(Class beanClass) {
this.beanClass = beanClass;
}
}
MyTools.java 用来扫描某个包下的所有java类
public class MyTools {
public static Set> getClasses(String pack) {
// 第一个class类的集合
Set> classes = new linkedHashSet>();
// 是否循环迭代
boolean recursive = true;
// 获取包的名字 并进行替换
String packageName = pack;
String packageDirName = packageName.replace('.', '/');
// 定义一个枚举的集合 并进行循环来处理这个目录下的things
Enumeration 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");
// 以文件的方式扫描整个包下的文件 并添加到集合中
findClassesInPackageByFile(packageName, filePath, recursive, classes);
} else if ("jar".equals(protocol)) {
// 如果是jar包文件
// 定义一个JarFile
System.out.println("jar类型的扫描");
JarFile jar;
try {
// 获取jar
jar = ((JarURLConnection) url.openConnection()).getJarFile();
// 从此jar包 得到一个枚举类
Enumeration entries = jar.entries();
findClassesInPackageByJar(packageName, entries, packageDirName, recursive, classes);
} catch (IOException e) {
// log.error("在扫描用户定义视图时从jar包获取文件出错");
e.printStackTrace();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
return classes;
}
private static void findClassesInPackageByJar(String packageName, Enumeration entries, String packageDirName, final boolean recursive, Set> classes) {
// 同样的进行循环迭代
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) {
// .error("添加用户自定义视图类错误 找不到此类的.class文件");
e.printStackTrace();
}
}
}
}
}
}
private static void findClassesInPackageByFile(String packageName, String packagePath, final boolean recursive, Set> classes) {
// 获取此包的目录 建立一个File
File dir = new File(packagePath);
// 如果不存在或者 也不是目录就直接返回
if (!dir.exists() || !dir.isDirectory()) {
// log.warn("用户定义包名 " + packageName + " 下没有任何文件");
return;
}
// 如果存在 就获取包下的所有文件 包括目录
File[] dirfiles = dir.listFiles(new FileFilter() {
// 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
@Override
public boolean accept(File file) {
return (recursive && file.isDirectory()) || (file.getName().endsWith(".class"));
}
});
// 循环所有文件
for (File file : dirfiles) {
// 如果是目录 则继续扫描
if (file.isDirectory()) {
findClassesInPackageByFile(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));
// 经过回复同学的提醒,这里用forName有一些不好,会触发static方法,没有使用classLoader的load干净
classes.add(Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className));
} catch (ClassNotFoundException e) {
// log.error("添加用户自定义视图类错误 找不到此类的.class文件");
e.printStackTrace();
}
}
}
}
}
定义一些注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
String value() default "";
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Qualifier {
String value();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Value {
String value();
}
MyAnnotationConfigApplicationContext.java IoC容器的启动类。首先遍历响应的包,找到目标类,然后根据响应信息创建bean,最后根据需要将属性赋值(有些文章也称为属性注入)。
public class MyAnnotationConfigApplicationContext {
private Map ioc = new HashMap<>();
private List beanNames = new ArrayList<>();
public MyAnnotationConfigApplicationContext(String pack) {
//遍历包,找到目标类(原材料)
Set beanDefinitions = findBeanDefinitions(pack);
//根据原材料创建bean
createObject(beanDefinitions);
//自动装载
autowireObject(beanDefinitions);
}
public void autowireObject(Set beanDefinitions){
Iterator iterator = beanDefinitions.iterator();
while (iterator.hasNext()) {
BeanDefinition beanDefinition = iterator.next();
Class clazz = beanDefinition.getBeanClass();
Field[] declaredFields = clazz.getDeclaredFields();
for (Field declaredField : declaredFields) {
Autowired annotation = declaredField.getAnnotation(Autowired.class);
if(annotation!=null){
Qualifier qualifier = declaredField.getAnnotation(Qualifier.class);
if(qualifier!=null){
//byName
try {
String beanName = qualifier.value();
Object bean = getBean(beanName);
String fieldName = declaredField.getName();
String methodName = "set"+fieldName.substring(0, 1).toUpperCase()+fieldName.substring(1);
Method method = clazz.getMethod(methodName, declaredField.getType());
Object object = getBean(beanDefinition.getBeanName());
method.invoke(object, bean);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}else{
//byType
}
}
}
}
}
public Object getBean(String beanName){
return ioc.get(beanName);
}
public String[] getBeanDefinitionNames(){
return beanNames.toArray(new String[0]);
}
public Integer getBeanDefinitionCount(){
return beanNames.size();
}
public void createObject(Set beanDefinitions){
Iterator iterator = beanDefinitions.iterator();
while (iterator.hasNext()) {
BeanDefinition beanDefinition = iterator.next();
Class clazz = beanDefinition.getBeanClass();
String beanName = beanDefinition.getBeanName();
try {
//创建的对象
Object object = clazz.getConstructor().newInstance();
//完成属性的赋值
Field[] declaredFields = clazz.getDeclaredFields();
for (Field declaredField : declaredFields) {
Value valueAnnotation = declaredField.getAnnotation(Value.class);
if(valueAnnotation!=null){
String value = valueAnnotation.value();
String fieldName = declaredField.getName();
String methodName = "set"+fieldName.substring(0, 1).toUpperCase()+fieldName.substring(1);
Method method = clazz.getMethod(methodName,declaredField.getType());
//完成数据类型转换
Object val = null;
switch (declaredField.getType().getName()){
case "java.lang.Integer":
val = Integer.parseInt(value);
break;
case "java.lang.String":
val = value;
break;
case "java.lang.Float":
val = Float.parseFloat(value);
break;
}
method.invoke(object, val);
}
}
//存入缓存
ioc.put(beanName, object);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
public Set findBeanDefinitions(String pack){
//1、获取包下的所有类
Set> classes = MyTools.getClasses(pack);
Iterator> iterator = classes.iterator();
Set beanDefinitions = new HashSet<>();
while (iterator.hasNext()) {
//2、遍历这些类,找到添加了注解的类
Class> clazz = iterator.next();
Component componentAnnotation = clazz.getAnnotation(Component.class);
if(componentAnnotation!=null){
//获取Component注解的值
String beanName = componentAnnotation.value();
if("".equals(beanName)){
//获取类名首字母小写
String className = clazz.getName().replaceAll(clazz.getPackage().getName() + ".", "");
beanName = className.substring(0, 1).toLowerCase()+className.substring(1);
}
//3、将这些类封装成BeanDefinition,装载到集合中
beanDefinitions.add(new BeanDefinition(beanName, clazz));
beanNames.add(beanName);
}
}
return beanDefinitions;
}
}
Test.java
public class Test {
public static void main(String[] args) {
String pack = "MYSPRING.ENTITY";
MyAnnotationConfigApplicationContext applicationContext = new MyAnnotationConfigApplicationContext(pack);
System.out.println("加载的bean数量:"+applicationContext.getBeanDefinitionCount());
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println("bean的名称为:"+beanDefinitionName);
System.out.println("bean的具体值为:"+applicationContext.getBean(beanDefinitionName));
}
}
}
我们添加两个类用于测试
@Component("myOrder")
public class Order {
@Value("xxx123")
private String id;
@Value("1000.0")
private Float price;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Float getPrice() {
return price;
}
public void setPrice(Float price) {
this.price = price;
}
@Override
public String toString() {
return "Order{" +
"id='" + id + ''' +
", price=" + price +
'}';
}
}
@Component
public class Account {
@Value("1")
private Integer id;
@Value("张三")
private String name;
@Value("22")
private Integer age;
@Autowired
private Order order;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + ''' +
", age=" + age +
", order=" + order +
'}';
}
}
测试结果如下



