文章目录
- IOC控制反转
- 依赖注入
- Bean的自动装配方式
- 丐版IOC实现
- BeanDefinition.java
- ResourceLoader.java
- BeanRegister.java
- Bean和DI的注解
- BeanFactory.java
- ApplicationContext
- 测试,实现
- 在这里插入图片描述
大家好,我是Leo。Spring核心中依赖注入和IOC容器是非常常见的,用起来也是非常的顺手,只能说是真香,那如何实现一个丐版的SpringIOC呢?那今天我们就来讲如何实现一个乞丐版的IOC和通过注解进行依赖注入。
IOC控制反转
在开始之前,首先得先理解什么是IOC的控制反转,Spring 通过 IoC 容器来管理所有 Java 对象的实例化和初始化,控制对象与对象之间的依赖关系 。我们将由 IoC 容器管理的 Java 对象称为 Spring Bean,它与使用关键字 new 创建的 Java 对象没有任何区别。
说白了就是,IOC负责我们的饮食,我们要饭和菜,只需要从IOC容器获取就行。
那这样的作用也就非常明显了,解耦、方便
依赖注入
那依赖注入肯定是IOC的精髓所在,我们平常使用@Autowired注解等自动注入方式,用起来确实舒服。
那依赖注入常用方式有哪些呢?
-
构造函数的参数实现注入
-
基于set注入(静态工厂和动态工厂)
-
基于属性的注入
Bean的自动装配方式
可以在文件中设置自动装配(autowire)方式,支持的装配方式有
方式 | 描述 |
---|---|
no | 手动装配 |
byName | 通过id的名字自动注入对象 |
byType | 通过类型自动注入对象 |
constructor | 通过构造方法自动注入对象 |
autodectect | 完全交给Spring管理,按先Constructor后byType的顺序进行匹配 |
丐版IOC实现
BeanDefinition.java
Bean的定义类,主要是包括配置Bean定义的对应的实体,对应的构造方法和getset方法就省略了
public class BeanDefinition {private String beanName;private Class beanClass;
ResourceLoader.java
资源加载器,用来完成包的扫描和Bean的加载
public class ResourceLoader {private static String rootPath;private Map<String, BeanDefinition> beanDefinitionMap = new HashMap(16);public Map<String, BeanDefinition> getResource(String basePackage) {// com.zlytry {// 1. 把.换成/String packagePath = basePackage.replaceAll("\\.", "\\\\");// 2.获取包的绝对路径Enumeration<URL> urls =Thread.currentThread().getContextClassLoader().getResources(packagePath);while (urls.hasMoreElements()) {URL url = urls.nextElement();String filePath = URLDecoder.decode(url.getFile(), "utf-8");rootPath = filePath.substring(0, filePath.length() - packagePath.length());// 包扫描try {loadBean(new File(filePath));} catch (Exception e) {e.printStackTrace();}}} catch (IOException e) {e.printStackTrace();}return this.beanDefinitionMap;}private void loadBean(File file) throws Exception {// 1. 判断是否是文件夹if (file.isDirectory()) {// 2. 获取文件夹的所有内容File[] childFiles = file.listFiles();// 3. 判断文件夹内为空,直接返回if (childFiles == null || childFiles.length == 0) {return;}// 4. 如果文件夹里面不为空,遍历文件夹所有的内容for (File childFile : childFiles) {// 4.1 遍历得到每个File对象,继续是文件夹,递归if (childFile.isDirectory()) {loadBean(childFile);} else {// 4.2 得到不是文件夹,是文件// 4.3 得到包路径 和类名称String pathWithClass = childFile.getAbsolutePath().substring(rootPath.length() - 1);// 4.4 判断当前文件类型是否为classif (pathWithClass.contains(".class")) {// 4.5 是class类型,把\替换成. 把.class去掉String allName = pathWithClass.replaceAll("\\\\", ".").replace(".class", "");// 4.6 判断类上面是否有注解@Bean,放到map集合beanFactory// 4.6.1 获取类的Class对象Class<?> clazz = Class.forName(allName);// 4.6.2 判断不是接口if (!clazz.isInterface()) {// 4.6.3 判断上面是否有@Bean注解Bean annotation = clazz.getAnnotation(Bean.class);if (annotation != null) {// 4.6.4 实例化String beanName = annotation.value();if ("".equals(beanName)) {beanName = allName;}if (clazz.getInterfaces().length > 0) {System.out.println("正在加载【"+ clazz.getInterfaces()[0] +"】");BeanDefinition beanDefinition = new BeanDefinition(beanName, clazz);beanDefinitionMap.put(beanName, beanDefinition);} else {System.out.println("正在加载【"+ clazz.getInterfaces()[0]);BeanDefinition beanDefinition = new BeanDefinition(beanName, clazz);beanDefinitionMap.put(beanName, beanDefinition);}}}}}}}}}
BeanRegister.java
用于向容器中注册一个Bean,在扫描后,我们的Bean主要是放在了beanDefinitionMap中,还没有进行加载和初始化,在获取中再进行加载到这个缓存Map中
public class BeanRegister {private Map<String, Object> singletonMap = new HashMap<>(32);public Object getSingletonBean(String beanName) {return singletonMap.get(beanName);}public void registerSingletonBean(String beanName, Object bean) {if (singletonMap.containsKey(beanName)) {return;}singletonMap.put(beanName, bean);}}
Bean和DI的注解
这里的两个注解主要是用来标志这是一个Bean和进行依赖注入的
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {String value() default "";
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Di {
}
BeanFactory.java
这里的对象工厂主要是我们最重要的一个类,在创建时,需要加载注册器和资源加载,在获取Bean中,需要进行依赖注入,并创建一个Bean
public class BeanFactory {/*** 创建一个map集合,放bean对象*/private Map<String , BeanDefinition> beanDefinitionMap = new HashMap<>();private BeanRegister beanRegister;public BeanFactory(String basePackage) {beanRegister = new BeanRegister();this.beanDefinitionMap = new ResourceLoader().getResource(basePackage);}public Object getBean(String beanName) {// 从缓存中获取Object bean = beanRegister.getSingletonBean(beanName);if (bean != null) {return bean;}// 创建beanreturn createBean(beanDefinitionMap.get(beanName));}public Object getBean(Class<?> clazz) {BeanDefinition beanDefinition = getBeanNameByType(clazz);if (Objects.isNull(beanDefinition)) {log.error("can not find {}", clazz);return null;} else {return getBean(beanDefinition.getBeanName());}}private Object createBean(BeanDefinition beanDefinition) {try {Object bean = beanDefinition.getBeanClass().getConstructor().newInstance();beanRegister.registerSingletonBean(beanDefinition.getBeanName(), bean);loadDi(beanDefinition.getBeanName());return bean;} catch (Exception e) {e.printStackTrace();}return null;}/*** 属性注入*/private void loadDi(String beanName) {// 1. 实例化对象都在beanFactory的map集合中,遍历Object bean = this.beanRegister.getSingletonBean(beanName);// 2. 获取map集合每个对象和属性Class<?> objectClass = bean.getClass();// 3. 遍历得到每个对象属性数组,得到每个属性Field[] declaredFields = objectClass.getDeclaredFields();for (Field declaredField : declaredFields) {// 4. 判断属性上面是否有@Di注解Di annotation = declaredField.getAnnotation(Di.class);if (annotation != null) {declaredField.setAccessible(true);// 如果私有属性,可以设置值// 5. 如果有@Di注解,把对象进行注入try {BeanDefinition beanDefinition = getBeanNameByType(declaredField.getType());if (Objects.isNull(beanDefinition)) {declaredField.set(bean, null);} else {declaredField.set(bean, getBean(beanDefinition.getBeanName()));}} catch (IllegalAccessException e) {throw new RuntimeException(e);}}}}private BeanDefinition getBeanNameByType(Class clazz) {Set<Map.Entry<String, BeanDefinition>> entries = this.beanDefinitionMap.entrySet();for (Map.Entry<String, BeanDefinition> entry : entries) {if (entry.getValue().getBeanClass().equals(clazz)) {return entry.getValue();}if (entry.getValue().getBeanClass().getInterfaces()[0].equals(clazz)) {return entry.getValue();}}return null;}}
ApplicationContext
我也模仿了Spring一样定义了一个ApplicationContext容器,获取Bean从这个容器获取,context容器再从BeanFactory中获取
public class AnnotationApplicationContext implements ApplicationContext {/*** 创建一个map集合,放bean对象*/private BeanFactory beanFactory;@Overridepublic Object getBean(Class clazz) {return beanFactory.getBean(clazz);}@Overridepublic Object getBean(String beanName) {return beanFactory.getBean(beanName);}/*** 设置包扫描规则* 当前包及其子包,哪个类有@Bean注解,把这个类通过反射化*/public AnnotationApplicationContext(String basePackage) throws Exception {this.beanFactory = getBeanFactory(basePackage);}private BeanFactory getBeanFactory(String baskPackage) {return new BeanFactory(baskPackage);}}
测试,实现
@Bean
public class UserServiceImpl implements UserService {@Diprivate UserDao userDao;@Overridepublic void add() {System.out.println("service.........");userDao.add();}
}
public interface UserService {void add();
}
@Bean
public class UserDaoImpl implements UserDao {@Overridepublic void add() {System.out.println("dao add...");}
}
public interface UserDao {void add();
}
public class UserTest {public static void main(String[] args){try {ApplicationContext context = new AnnotationApplicationContext("com.zly");UserService userService = (UserService)context.getBean(UserService.class);userService.add();} catch (Exception e) {e.printStackTrace();}}
}
相信你看到这里,就可以很容易的理解了这个丐版的Spring就算完成了,希望可以对大家有帮助啦👍👍👍