首页 文章详情

手写一个最简单的IOC容器,从而了解spring的核心原理

java1234 | 458 2021-05-20 23:03 0 0 0
UniSMS (合一短信)

点击上方蓝色字体,选择“标星公众号”

优质文章,第一时间送达

  作者 |  追极

来源 |  urlify.cn/fqyEJv

76套java从入门到精通实战课程分享

从事开发工作多年,spring源码没有特意去看过。但是相关技术原理倒是背了不少,毕竟面试的那关还是得过啊! 正所谓面试造火箭,工作拧螺丝。下面实现一个最简单的ioc容器,供大家参考。

1.最终结果

 

 

2.涉及相关技术

(1) jdk动态代理

(2) java反射

 

3.源代码

(1)包扫描工具类

package com.hdwang.ioc.core.utils;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.HashSet;
import java.util.Set;

/**
 * 类工具
 */
public class ClassUtils {

    /**
     * 获取某包下所有类
     *
     * @param packageName 包名
     * @param isRecursion 是否遍历子包
     * @return 类的完整名称
     */
    public static Set<String> getClassName(String packageName, boolean isRecursion) {
        Set<String> classNames = new HashSet<>();
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        String packagePath = packageName.replace(".""/");
        URL url = loader.getResource(packagePath);
        String filePath = null;
        try {
            filePath = URLDecoder.decode(url.getPath(), "utf-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        if (filePath != null) {
            classNames = getClassNameFromDir(filePath, packageName, isRecursion);
        }
        return classNames;
    }

    /**
     * 从项目文件获取某包下有类
     *
     * @param filePath    文件路径
     * @param isRecursion 是否遍历子包
     * @return 类的完整名称
     */
    private static Set<String> getClassNameFromDir(String filePath, String packageName, boolean isRecursion) {
        Set<String> className = new HashSet<>();
        File file = new File(filePath);
        File[] files = file.listFiles();
        for (File childFile : files) {

            if (childFile.isDirectory()) {
                if (isRecursion) {
                    className.addAll(getClassNameFromDir(childFile.getPath(), packageName + "." + childFile.getName(), isRecursion));
                }
            } else {
                String fileName = childFile.getName();
                if (fileName.endsWith(".class") && !fileName.contains("$")) {
                    className.add(packageName + "." + fileName.replace(".class"""));
                }
            }
        }
        return className;
    }


}

(2)字符串工具类

package com.hdwang.ioc.core.utils;

/**
 * 字符串工具类
 */
public class StringUtils {

    /**
     * 判断字符串是否空白
     *
     * @param str 字符串
     * @return 字符串是否空白
     */
    public static boolean isBlank(String str) {
        return str == null || str.trim().isEmpty();
    }

    /**
     * 判断字符串是否非空白
     *
     * @param str 字符串
     * @return 字符串是否非空白
     */
    public static boolean isNotBlank(String str) {
        return !isBlank(str);
    }
}

(3) Bean对象注解

package com.hdwang.ioc.core.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


/**
 * Bean对象注解
 * 待存入ioc容器的相关对象,声明在具体的实现类上
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.TYPE})
public @interface MyBean {

    /**
     * 待存入ioc容器的Bean名称
     *
     * @return Bean名称
     */
    String value() default "";
}

(4) 自动注入注解

package com.hdwang.ioc.core.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 自动注入注解
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.FIELD})
public @interface AutoInject {

    /**
     * 注入的bean名称,为空时根据类型注入
     *
     * @return Bean名称
     */
    String value() default "";
}

(5) Bean信息对象

package com.hdwang.ioc.core;

/**
 * Bean类信息
 */
public class BeanInfo {

    /**
     * Bean类的类型
     */
    private Class clasz;

    /**
     * 保存在ioc容器中的Bean名称
     */
    private String beanName;

    /**
     * 保存在ioc容器中的Bean类型
     */
    private Class beanType;

    /**
     * 保存在ioc容器中的bean对象实例
     */
    private Object bean;

    /**
     * 保存在ioc容器中的bean的代理对象实例
     */
    private Object proxyBean;

    public Class getClasz() {
        return clasz;
    }

    public void setClasz(Class clasz) {
        this.clasz = clasz;
    }

    public String getBeanName() {
        return beanName;
    }

    public void setBeanName(String beanName) {
        this.beanName = beanName;
    }

    public Class getBeanType() {
        return beanType;
    }

    public void setBeanType(Class beanType) {
        this.beanType = beanType;
    }

    public Object getBean() {
        return bean;
    }

    public void setBean(Object bean) {
        this.bean = bean;
    }

    public Object getProxyBean() {
        return proxyBean;
    }

    public void setProxyBean(Object proxyBean) {
        this.proxyBean = proxyBean;
    }
}

(6) 上下文对象

package com.hdwang.ioc.core;

import java.util.HashMap;
import java.util.Map;

/**
 * 上下文对象
 * 用于保存应用运行中的信息
 */
public class Context {

    /**
     * 根据Bean名称存储Bean的Map对象
     */
    private Map<String, Object> nameBeanMap = new HashMap<>();

    /**
     * 根据Bean类型存储Bean的Map对象
     */
    private Map<Class, Object> typeBeanMap = new HashMap<>();

    public Object getBean(String beanName) {
        return nameBeanMap.get(beanName);
    }

    public Object getBean(Class clasz) {
        return typeBeanMap.get(clasz);
    }

    public void putBean(String beanName, Object bean) {
        nameBeanMap.put(beanName, bean);
    }

    public void putBean(Class beanType, Object bean) {
        typeBeanMap.put(beanType, bean);
    }
}

(7) Bean的代理对象

package com.hdwang.ioc.core;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * Bean的代理对象
 * 使用jdk动态代理原理实现对java对象的代理,必须依赖接口
 */
public class BeanProxy implements InvocationHandler {

    /**
     * 被代理的bean对象
     */
    private Object bean;
    
    public BeanProxy(Object bean) {
        this.bean = bean;
    }

    /**
     * 调用目标bean的相关方法
     *
     * @param proxy  代理对象
     * @param method 方法
     * @param args   参数
     * @return 方法返回值
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before call method: " + method.getName());
        Object result = method.invoke(bean, args);
        System.out.println("after call method: " + method.getName());
        return result;
    }
}

(8) Bean工厂类(ioc容器类)

package com.hdwang.ioc.core;

import com.hdwang.ioc.core.annotation.AutoInject;
import com.hdwang.ioc.core.annotation.MyBean;
import com.hdwang.ioc.core.utils.ClassUtils;
import com.hdwang.ioc.core.utils.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * Bean工厂
 */
public class BeanFactory {

    /**
     * 基础包路径
     */
    private String basePackage;

    /**
     * 上下文对象
     */
    private Context context = new Context();

    /**
     * 工厂构造器
     *
     * @param basePackage 基础包路径
     */
    public BeanFactory(String basePackage) {
        this.basePackage = basePackage;
        init();
    }

    /**
     * 工厂初始化
     */
    private void init() {
        //扫描包和加载bean到ioc容器
        List<BeanInfo> myBeanList = scanPackageAndLoadBeans();

        //给bean注入依赖对象
        injectBeans(myBeanList);
    }

    /**
     * 扫描包和加载bean到ioc容器
     *
     * @return 加载进ioc容器中的相关Bean信息
     */
    private List<BeanInfo> scanPackageAndLoadBeans() {
        List<BeanInfo> myBeanList = new ArrayList<>();

        //找到包下所有类
        Set<String> classNames = ClassUtils.getClassName(basePackage, true);
        for (String className : classNames) {
            try {
                //查找类
                Class clasz = Class.forName(className);

                //判断类上是否存在MyBean注解
                if (clasz.isAnnotationPresent(MyBean.class)) {
                    //获取类上的MyBean注解
                    MyBean myBeanAnnotation = (MyBean) clasz.getAnnotation(MyBean.class);
                    //获取注解值,即Bean名称
                    String beanName = myBeanAnnotation.value();
                    //获取类继承的相关接口
                    Class[] interfaces = clasz.getInterfaces();
                    //判断类是否可以采用jdk动态代理(有接口方可进jdk动态代理,创建代理对象)
                    boolean canJdkProxyBean = interfaces != null && interfaces.length > 0;

                    //获取待注入ioc容器的Bean的类型
                    Class beanType = getBeanType(clasz, canJdkProxyBean);

                    //实例化当前类,生成bean实例
                    Object bean = clasz.newInstance();
                    Object iocBean = bean;
                    if (canJdkProxyBean) {
                        //可以使用jdk动态代理,则创建代理对象,代理此Bean
                        Object proxyBean = this.createBeanProxy(bean);
                        iocBean = proxyBean;
                    }
                    //保存生成的bean到ioc容器
                    if (StringUtils.isNotBlank(beanName)) {
                        context.putBean(beanName, iocBean);
                    }
                    context.putBean(beanType, iocBean);

                    //暂存Bean信息
                    BeanInfo beanInfo = new BeanInfo();
                    beanInfo.setClasz(clasz);
                    beanInfo.setBeanName(beanName);
                    beanInfo.setBeanType(beanType);
                    beanInfo.setBean(bean);
                    beanInfo.setProxyBean(canJdkProxyBean ? iocBean : null);
                    myBeanList.add(beanInfo);
                }
            } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
                System.out.println("加载bean异常");
                e.printStackTrace();
            }

        }
        return myBeanList;
    }

    /**
     * 给相关Bean注入依赖的Bean
     *
     * @param myBeanList 注入到ioc容器中的所有的Bean
     */
    private void injectBeans(List<BeanInfo> myBeanList) {
        for (BeanInfo myBeanInfo : myBeanList) {
            Class beanClass = myBeanInfo.getClasz();
            Object bean = myBeanInfo.getBean();

            //查找Bean的声明的所有字段
            Field[] fields = beanClass.getDeclaredFields();
            for (Field field : fields) {
                //判断字段上是否有AutoInject注解
                if (field.isAnnotationPresent(AutoInject.class)) {
                    //查找待注入的bean
                    AutoInject autoInjectAnnotation = field.getAnnotation(AutoInject.class);
                    //获取注解的值,即待注入的Bean名称
                    String injectBeanName = autoInjectAnnotation.value();
                    //获取字段的类型,即待注入的Bean类型
                    Class injectBeanType = field.getType();
                    Object proxyBean = null;

                    //从查找ioc容器中查找待注入的Bean对象
                    if (StringUtils.isNotBlank(injectBeanName)) {
                        //Bean名称不为空,则根据名称查找Bean
                        proxyBean = context.getBean(injectBeanName);
                    } else {
                        //Bean名称为空,则根据Bean类型查找Bean
                        proxyBean = context.getBean(injectBeanType);
                    }

                    //设置当前字段可访问
                    field.setAccessible(true);
                    try {
                        //将找到的Bean注入到当前字段上
                        field.set(bean, proxyBean);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    /**
     * 获取待注入到ioc容器中的Bean类型
     *
     * @param clasz           Bean类型
     * @param canJdkProxyBean 是否可以使用jdk动态代理
     * @return 注入到ioc容器中的Bean类型
     */
    private Class getBeanType(Class clasz, boolean canJdkProxyBean) {
        Class beanType = null;
        if (canJdkProxyBean) {
            //可以使用jdk动态代理,则bean类型取bean的接口类型
            beanType = clasz.getInterfaces()[0];
        } else {
            //不可以使用jdk动态代理,bean类型就取当前类类型
            beanType = clasz;
        }
        return beanType;
    }

    /**
     * 根据Bean名称获取Bean对象
     *
     * @param beanName Bean名称
     * @param <T>      Bean类型
     * @return ioc容器中的Bean, 找不到返回null
     */
    public <T> T getBean(String beanName) {
        return (T) context.getBean(beanName);
    }

    /**
     * 根据Bean类型获取Bean对象
     *
     * @param clasz 注入到ioc容器中的Bean类型
     * @param <T>   Bean类型
     * @return ioc容器中的Bean, 找不到返回null
     */
    public <T> T getBean(Class clasz) {
        return (T) context.getBean(clasz);
    }

    /**
     * 创建代理bean
     *
     * @param bean 当前Bean对象
     * @return Bean的代理对象
     */
    private Object createBeanProxy(Object bean) {
        InvocationHandler invocationHandler = new BeanProxy(bean);
        Object proxyBean = Proxy.newProxyInstance(bean.getClass().getClassLoader(),
                bean.getClass().getInterfaces(), invocationHandler);
        return proxyBean;
    }


}

4.示例代码

(1) User模型

package com.hdwang.ioc.example.model;

public class User {

    private Long id;

    private String name;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '
}';
    }
}

(2) UserService

package com.hdwang.ioc.example.service;

import com.hdwang.ioc.example.model.User;

public interface UserService {

   User getUserById(Long id);
}

(3) UserServiceImpl

package com.hdwang.ioc.example.service;

import com.hdwang.ioc.core.annotation.MyBean;
import com.hdwang.ioc.example.model.User;

@MyBean("userService")
public class UserServiceImpl implements UserService {

    @Override
    public User getUserById(Long id) {
        User user = new User();
        if (id == 1) {
            user.setId(id);
            user.setName("张三");
        } else if (id == 2) {
            user.setId(id);
            user.setName("李四");
        }
        return user;
    }
}

(4) UserController

package com.hdwang.ioc.example.controller;

import com.hdwang.ioc.core.annotation.AutoInject;
import com.hdwang.ioc.core.annotation.MyBean;
import com.hdwang.ioc.example.model.User;
import com.hdwang.ioc.example.service.UserService;

@MyBean("userController")
public class UserController {

    @AutoInject
    UserService userService;

    public User getUserById(Long id) {
        return userService.getUserById(id);
    }
}

(5) 主函数

package com.hdwang.ioc.example;

import com.hdwang.ioc.core.BeanFactory;
import com.hdwang.ioc.example.controller.UserController;
import com.hdwang.ioc.example.model.User;

/**
 * 程序启动类
 */
public class Main {

    /**
     * 主函数入口
     *
     * @param args 入参
     */
    public static void main(String[] args) {
        //定义要扫描的包名
        String basePackage = "com.hdwang.ioc.example";

        //初始化Bean工厂
        BeanFactory beanFactory = new BeanFactory(basePackage);

        //获取指定的Bean
        UserController userController = beanFactory.getBean(UserController.class);

        //调用Bean中的方法
        User user = userController.getUserById(1L);
        System.out.println(user);
    }
}

5.运行结果

before call method: getUserById
after call method: getUserById
User{id=1, name='张三'}

6.总结说明

ioc的实现,主要是用到了java的反射技术,和动态代理无关,代理对象可以实现一些增强的功能,所以人们常常称spring的bean的代理类为增强类!哈哈。。。

 

7.附录

项目源码:https://github.com/hdwang123/iocdemo

 






粉丝福利:Java从入门到入土学习路线图

👇👇👇

👆长按上方微信二维码 2 秒


感谢点赞支持下哈 

good-icon 0
favorite-icon 0
收藏
回复数量: 0
    暂无评论~~
    Ctrl+Enter