Spring之手写IoC
|字数总计:2.6k|阅读时长:13分钟|阅读量:
1、原理-手写IoC
我们都知道,Spring框架的IOC是基于Java反射机制实现的,下面我们先回顾一下java反射。
1.1、回顾Java反射
Java
反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为Java
语言的反射机制。简单来说,反射机制指的是程序在运行时能够获取自身的信息。
要想解剖一个类,必须先要获取到该类的Class对象。而剖析一个类或用反射解决具体的问题就是使用相关API(1)java.lang.Class(2)java.lang.reflect,所以,Class对象是反射的根源。
自定义类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| package com.atguigu.reflect;
public class Car {
private String name; private int age; private String color;
public Car() { }
public Car(String name, int age, String color) { this.name = name; this.age = age; this.color = color; }
private void run() { System.out.println("私有方法-run....."); }
public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getColor() { return color; } public void setColor(String color) { this.color = color; }
@Override public String toString() { return "Car{" + "name='" + name + '\'' + ", age=" + age + ", color='" + color + '\'' + '}'; } }
|
编写测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
| package com.atguigu.reflect;
import org.junit.jupiter.api.Test; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method;
public class TestCar {
@Test public void test01() throws Exception { Class clazz1 = Car.class;
Class clazz2 = new Car().getClass();
Class clazz3 = Class.forName("com.atguigu.reflect.Car");
Car car = (Car)clazz3.getConstructor().newInstance(); System.out.println(car); }
@Test public void test02() throws Exception { Class clazz = Car.class;
Constructor[] constructors = clazz.getDeclaredConstructors(); for (Constructor c:constructors) { System.out.println("方法名称:"+c.getName()+" 参数个数:"+c.getParameterCount()); }
Constructor c2 = clazz.getDeclaredConstructor(String.class, int.class, String.class); c2.setAccessible(true); Car car2 = (Car)c2.newInstance("捷达", 15, "白色"); System.out.println(car2); }
@Test public void test03() throws Exception { Class clazz = Car.class; Car car = (Car)clazz.getDeclaredConstructor().newInstance(); Field[] fields = clazz.getDeclaredFields(); for (Field field:fields) { if(field.getName().equals("name")) { field.setAccessible(true); field.set(car,"五菱宏光"); System.out.println(car); } System.out.println(field.getName()); } }
@Test public void test04() throws Exception { Car car = new Car("奔驰",10,"黑色"); Class clazz = car.getClass(); Method[] methods = clazz.getMethods(); for (Method m1:methods) { if(m1.getName().equals("toString")) { String invoke = (String)m1.invoke(car); } }
Method[] methodsAll = clazz.getDeclaredMethods(); for (Method m:methodsAll) { if(m.getName().equals("run")) { m.setAccessible(true); m.invoke(car); } } } }
|
1.2、实现Spring的IoC
我们知道,IoC(控制反转)和DI(依赖注入)是Spring里面核心的东西,那么,我们如何自己手写出这样的代码呢?下面我们就一步一步写出Spring框架最核心的部分。
①搭建子模块
搭建模块:guigu-spring,搭建方式如其他spring子模块
②准备测试需要的bean
添加依赖
1 2 3 4 5 6 7 8
| <dependencies> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>5.3.1</version> </dependency> </dependencies>
|
创建UserDao接口
1 2 3 4 5 6
| package com.atguigu.spring6.test.dao;
public interface UserDao {
public void print(); }
|
创建UserDaoImpl实现
1 2 3 4 5 6 7 8 9 10 11 12
| package com.atguigu.spring6.test.dao.impl;
import com.atguigu.spring.dao.UserDao;
public class UserDaoImpl implements UserDao {
@Override public void print() { System.out.println("Dao层执行结束"); } }
|
创建UserService接口
1 2 3 4 5 6
| package com.atguigu.spring6.test.service;
public interface UserService {
public void out(); }
|
创建UserServiceImpl实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package com.atguigu.spring.test.service.impl;
import com.atguigu.spring.core.annotation.Bean; import com.atguigu.spring.service.UserService;
@Bean public class UserServiceImpl implements UserService {
@Override public void out() { System.out.println("Service层执行结束"); } }
|
③定义注解
我们通过注解的形式加载bean与实现依赖注入
bean注解
1 2 3 4 5 6 7 8 9 10 11
| package com.atguigu.spring.core.annotation;
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Bean { }
|
依赖注入注解
1 2 3 4 5 6 7 8 9 10 11
| package com.atguigu.spring.core.annotation;
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;
@Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface Di { }
|
说明:上面两个注解可以随意取名
④定义bean容器接口
1 2 3 4 5 6
| package com.atguigu.spring.core;
public interface ApplicationContext {
Object getBean(Class clazz); }
|
⑤编写注解bean容器接口实现
AnnotationApplicationContext基于注解扫描bean
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package com.atguigu.spring.core;
import java.util.HashMap;
public class AnnotationApplicationContext implements ApplicationContext {
private HashMap<Class, Object> beanFactory = new HashMap<>();
@Override public Object getBean(Class clazz) { return beanFactory.get(clazz); }
public AnnotationApplicationContext(String basePackage) { } }
|
⑥编写扫描bean逻辑
我们通过构造方法传入包的base路径,扫描被@Bean注解的java对象,完整代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
| package com.atguigu.spring.core;
import com.atguigu.spring.core.annotation.Bean;
import java.io.File; import java.util.HashMap;
public class AnnotationApplicationContext implements ApplicationContext {
private HashMap<Class, Object> beanFactory = new HashMap<>(); private static String rootPath;
@Override public Object getBean(Class clazz) { return beanFactory.get(clazz); }
public AnnotationApplicationContext(String basePackage) { try { String packageDirName = basePackage.replaceAll("\\.", "\\\\"); Enumeration<URL> dirs =Thread.currentThread().getContextClassLoader().getResources(packageDirName); while (dirs.hasMoreElements()) { URL url = dirs.nextElement(); String filePath = URLDecoder.decode(url.getFile(),"utf-8"); rootPath = filePath.substring(0, filePath.length()-packageDirName.length()); loadBean(new File(filePath)); }
} catch (Exception e) { throw new RuntimeException(e); } }
private void loadBean(File fileParent) { if (fileParent.isDirectory()) { File[] childrenFiles = fileParent.listFiles(); if(childrenFiles == null || childrenFiles.length == 0){ return; } for (File child : childrenFiles) { if (child.isDirectory()) { loadBean(child); } else { String pathWithClass = child.getAbsolutePath().substring(rootPath.length() - 1); if (pathWithClass.contains(".class")) { String fullName = pathWithClass.replaceAll("\\\\", ".").replace(".class", ""); try { Class<?> aClass = Class.forName(fullName); if(!aClass.isInterface()){ Bean annotation = aClass.getAnnotation(Bean.class); if(annotation != null){ Object instance = aClass.newInstance(); if(aClass.getInterfaces().length > 0) { System.out.println("正在加载【"+ aClass.getInterfaces()[0] +"】,实例对象是:" + instance.getClass().getName()); beanFactory.put(aClass.getInterfaces()[0], instance); }else{ System.out.println("正在加载【"+ aClass.getName() +"】,实例对象是:" + instance.getClass().getName()); beanFactory.put(aClass, instance); } } } } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) { e.printStackTrace(); } } } } } }
}
|
⑦java类标识Bean注解
1 2
| @Bean public class UserServiceImpl implements UserService
|
1 2
| @Bean public class UserDaoImpl implements UserDao
|
⑧测试Bean加载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package com.atguigu.spring;
import com.atguigu.spring.core.AnnotationApplicationContext; import com.atguigu.spring.core.ApplicationContext; import com.atguigu.spring.test.service.UserService; import org.junit.jupiter.api.Test;
public class SpringIocTest {
@Test public void testIoc() { ApplicationContext applicationContext = new AnnotationApplicationContext("com.atguigu.spring.test"); UserService userService = (UserService)applicationContext.getBean(UserService.class); userService.out(); System.out.println("run success"); } }
|
控制台打印测试
⑨依赖注入
只要userDao.print();调用成功,说明就注入成功
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package com.atguigu.spring.test.service.impl;
import com.atguigu.spring.core.annotation.Bean; import com.atguigu.spring.core.annotation.Di; import com.atguigu.spring.dao.UserDao; import com.atguigu.spring.service.UserService;
@Bean public class UserServiceImpl implements UserService {
@Di private UserDao userDao;
@Override public void out() { userDao.print(); System.out.println("Service层执行结束"); } }
|
执行第八步:报错了,说明当前userDao是个空对象
⑩依赖注入实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
| package com.atguigu.spring.core;
import com.atguigu.spring.core.annotation.Bean; import com.atguigu.spring.core.annotation.Di;
import java.io.File; import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map;
public class AnnotationApplicationContext implements ApplicationContext {
private HashMap<Class, Object> beanFactory = new HashMap<>(); private static String rootPath;
@Override public Object getBean(Class clazz) { return beanFactory.get(clazz); }
public AnnotationApplicationContext(String basePackage) { try { String packageDirName = basePackage.replaceAll("\\.", "\\\\"); Enumeration<URL> dirs =Thread.currentThread().getContextClassLoader().getResources(packageDirName); while (dirs.hasMoreElements()) { URL url = dirs.nextElement(); String filePath = URLDecoder.decode(url.getFile(),"utf-8"); rootPath = filePath.substring(0, filePath.length()-packageDirName.length()); loadBean(new File(filePath)); }
} catch (Exception e) { throw new RuntimeException(e); } loadDi(); } private void loadBean(File fileParent) { if (fileParent.isDirectory()) { File[] childrenFiles = fileParent.listFiles(); if(childrenFiles == null || childrenFiles.length == 0){ return; } for (File child : childrenFiles) { if (child.isDirectory()) { loadBean(child); } else { String pathWithClass = child.getAbsolutePath().substring(rootPath.length() - 1); if (pathWithClass.contains(".class")) { String fullName = pathWithClass.replaceAll("\\\\", ".").replace(".class", ""); try { Class<?> aClass = Class.forName(fullName); if(!aClass.isInterface()){ Bean annotation = aClass.getAnnotation(Bean.class); if(annotation != null){ Object instance = aClass.newInstance(); if(aClass.getInterfaces().length > 0) { System.out.println("正在加载【"+ aClass.getInterfaces()[0] +"】,实例对象是:" + instance.getClass().getName()); beanFactory.put(aClass.getInterfaces()[0], instance); }else{ System.out.println("正在加载【"+ aClass.getName() +"】,实例对象是:" + instance.getClass().getName()); beanFactory.put(aClass, instance); } } } } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) { e.printStackTrace(); } } } } } }
private void loadDi() { for(Map.Entry<Class,Object> entry : beanFactory.entrySet()){ Object obj = entry.getValue(); Class<?> aClass = obj.getClass(); Field[] declaredFields = aClass.getDeclaredFields(); for (Field field : declaredFields){ Di annotation = field.getAnnotation(Di.class); if( annotation != null ){ field.setAccessible(true); try { System.out.println("正在给【"+obj.getClass().getName()+"】属性【" + field.getName() + "】注入值【"+ beanFactory.get(field.getType()).getClass().getName() +"】"); field.set(obj,beanFactory.get(field.getType())); } catch (IllegalAccessException e) { e.printStackTrace(); } } } } }
}
|
执行第八步:执行成功,依赖注入成功