[Professor麦]自研实现Spring-IOC

SpringIOC基础理论

Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想"别找我们,我们找你”;即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。

依赖注入和依赖查询实现IOC思想的其中的几种方式

IOC的职责:松耦合、"别找我们,我们找你”

  1. 依赖查询,一句话总结就是,主动去寻找相关的依赖类,用户是拉取
  2. 依赖注入,由容器主动推送
  3. 依赖查找是主动或手动的依赖查找方式,通常需要依赖容器或标准API 实现。而依赖注入则是手动或自动依赖绑定的方式,无需依赖特定的容器和 API
  4. https://www.cnblogs.com/joemsu/p/7688307.html 构造器和setter注入的比较

自研实现SpringIOC

前期准备

因为本次自研框架仅实现注解功能,所以就需要定义一些注解

@Target(ElementType.TYPE)  //在类上
@Retention(RetentionPolicy.RUNTIME)  // 要用到反射
public @interface Component {
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Repository {
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Service {
}

/**
 * Autowired目前仅支持成员变量注入
 *
 */
@Target(ElementType.FIELD) // 仅作用在成员变量上
@Retention(RetentionPolicy.RUNTIME) // 需要保留到程序运行时
public @interface Autowired {
    String value () default "";
}

IOC-资源定位模块

@Slf4j
public class ClassUtil {

    public static final String FILE_PROTOCOL = "file";

    /**
     * 获取包下类集合
     *
     * @parampackageName包名
     * @return 类集合
     */
    public static Set<Class<?>> extractPackageClass(String packageName){
        //1.获取到类的加载器。
        ClassLoader classLoader = getClassLoader();
        //2.通过类加载器获取到加载的资源
        // 因为获取出来的文件夹都是/,所以要将/转成包的形式
        URL url = classLoader.getResource(packageName.replace(".", "/"));
        if (url == null){
            log.warn("unable to retrieve anything from package: " + packageName);
            return  null;
        }
        //3.依据不同的资源类型,采用不同的方式获取资源的集合
        Set<Class<?>> classSet = null;
        //过滤出文件类型的资源
        if (url.getProtocol().equalsIgnoreCase(FILE_PROTOCOL)){
            // 如果他是file协议
            classSet = new HashSet<Class<?>>();
            // 拿到包的绝对路径(url.getPath()获取绝对路径)
            File packageDirectory = new File(url.getPath());
            extractClassFile(classSet, packageDirectory, packageName);
        }
        //TODO 此处可以加入针对其他类型资源的处理

        return classSet;
    }
    /**
     * 递归获取目标package里面的所有class文件(包括子package里的class文件)
     *
     * @param emptyClassSet 装载目标类的集合
     * @param fileSource    文件或者目录
     * @param packageName   包名
     * @return 类集合
     */
    /**
     * 递归获取目标package里面的所有class文件(包括子package里的class文件)
     *
     * @param emptyClassSet 装载目标类的集合
     * @param fileSource    文件或者目录
     * @param packageName   包名
     * @return 类集合
     */
    private static void extractClassFile(Set<Class<?>> emptyClassSet, File fileSource, String packageName) {

        if(!fileSource.isDirectory()){
            // 如果不是目录
            return;
        }
        //如果是一个文件夹,则调用其listFiles方法获取文件夹下的文件或文件夹
        File[] files = fileSource.listFiles(new FileFilter() {
            @Override
            public boolean accept(File file) {
                if(file.isDirectory()){
                    return true;
                } else{
                    //获取文件的绝对值路径
                    String absoluteFilePath = file.getAbsolutePath();
                    if(absoluteFilePath.endsWith(".class")){
                        //若是class文件,则直接加载
                        addToClassSet(absoluteFilePath);
                    }
                }
                return false;
            }
            //根据class文件的绝对值路径,获取并生成class对象,并放入classSet中
            private void addToClassSet(String absoluteFilePath) {
                //1.从class文件的绝对值路径里提取出包含了package的类名
                //如/Users/baidu/imooc/springframework/sampleframework/target/classes/com/imooc/entity/dto/MainPageInfoDTO.class
                //需要弄成cn.mjz.entity.dto.MainPageInfoDTO
                absoluteFilePath = absoluteFilePath.replace(File.separator, ".");
                String className = absoluteFilePath.substring(absoluteFilePath.indexOf(packageName));
                className = className.substring(0, className.lastIndexOf("."));
                //2.通过反射机制获取对应的Class对象并加入到classSet里
                Class targetClass = loadClass(className);
                emptyClassSet.add(targetClass);
            }
        });
        if(files != null){
            for(File f : files){
                //递归调用,提取类里面的文件
                extractClassFile(emptyClassSet, f, packageName);
            }
        }
    }
    /**
     * 获取Class对象
     *
     * @param className class全名=package + 类名
     * @return Class
     */
    public static Class<?> loadClass(String className){
        try {
            return Class.forName(className);
        } catch (ClassNotFoundException e) {
            log.error("load class error:", e);
            throw new RuntimeException(e);
        }
    }
    /**
     * 实例化class
     *
     * @param clazz Class
     * @param <T>   class的类型
     * @param accessible   是否支持创建出私有class对象的实例
     * @return 类的实例化
     */
    public static <T> T newInstance(Class<?> clazz, boolean accessible){
        try {
            Constructor constructor = clazz.getDeclaredConstructor();
            constructor.setAccessible(accessible);
            return (T)constructor.newInstance();
        } catch (Exception e) {
            log.error("newInstance error", e);
            throw new RuntimeException(e);
        }
    }
    /**
     * 获取classLoader
     *
     * @return 当前ClassLoader
     */
    public static  ClassLoader getClassLoader(){
        return Thread.currentThread().getContextClassLoader();
    }
    /**
     * 设置类的属性值
     *
     * @param field      成员变量
     * @param target     类实例
     * @param value      成员变量的值
     * @param accessible 是否允许设置私有属性,这个是为了可以访问私有成员变量,所以我们的依赖注入对象是私有成员变量也不怕拿不到
     */
    public static void setField(Field field, Object target, Object value, boolean accessible){
        field.setAccessible(accessible);
        try {
            // 将该类的实现类下的子实现类注入到该实现类中,这个就是依赖注入
            field.set(target, value);
        } catch (IllegalAccessException e) {
            log.error("setField error", e);
            throw new RuntimeException(e);
        }
    }
    
    // 测试
    public static void main(String[] args) {
        extractPackageClass("cn.mjz.entity");
        File[] files = null;
        // 对于foreache方法一定要做空指针判断,不然就会有空指针异常
        for(File f : files){
            System.out.printf("haha");
        }
        files.getClass().getClassLoader();

    }
}

IOC-装载、注册模块

  1. 定义基本的成员变量

    /**
     * 存放所有被配置标记的目标对象的Map
     */
    private final Map<Class<?>, Object> beanMap = new ConcurrentHashMap();
    /**
     * 加载bean的注解列表
     */
    private static final List<Class<? extends Annotation>> BEAN_ANNOTATION
            = Arrays.asList(Component.class, Controller.class, Service.class, Repository.class, Aspect.class);
    
  2. 单例加载Bean容器的实现

    /**
     * 获取Bean容器实例
     *
     * @return BeanContainer
     */
    public static BeanContainer getInstance() {
        return ContainerHolder.HOLDER.instance;
    }
    
    private enum ContainerHolder {
        HOLDER;
        private BeanContainer instance;
    
        ContainerHolder() {
            instance = new BeanContainer ();
        }
    }
    
  3. 扫描加载所有Bean

    /**
     * 扫描加载所有Bean
     *
     * @param packageName 包名
     */
    public synchronized void loadBeans(String packageName) {
        //判断bean容器是否被加载过
        if (isLoaded()) {
            log.warn("BeanContainer has been loaded.");
            return;
        }
        // 提取所有的该包下的class文件信息
        Set<Class<?>> classSet = ClassUtil.extractPackageClass(packageName);
        if (ValidationUtil.isEmpty(classSet)) {
            log.warn("extract nothing from packageName" + packageName);
            return;
        }
        for (Class<?> clazz : classSet) {
            for (Class<? extends Annotation> annotation : BEAN_ANNOTATION) {
                //如果类上面标记了定义的注解
                if (clazz.isAnnotationPresent(annotation)) {
                    //将目标类本身作为键,目标类的实例作为值,放入到beanMap中
                    beanMap.put(clazz, ClassUtil.newInstance(clazz, true));
                }
            }
        }
        loaded = true;
    }
    

IOC-加载 Bean模块

/**
 * 提供依赖注入的服务
 */
@Slf4j
public class DependencyInjector {
    /**
     * Bean容器
     */
    private BeanContainer beanContainer;
    public DependencyInjector(){
        beanContainer = BeanContainer.getInstance();
    }
    /**
     * 执行Ioc
     */
    public void doIoc(){
        if(ValidationUtil.isEmpty(beanContainer.getClasses())){
            log.warn("empty classset in BeanContainer");
            return;
        }
        //1.遍历Bean容器中所有的Class对象
        for(Class<?> clazz : beanContainer.getClasses()){
            //2.遍历Class对象的所有成员变量
            Field[] fields = clazz.getDeclaredFields();
            if (ValidationUtil.isEmpty(fields)){
                continue;
            }
            for(Field field : fields){
                //3.找出被Autowired标记的成员变量
                if(field.isAnnotationPresent(Autowired.class)){
                    Autowired autowired = field.getAnnotation(Autowired.class);
                    String autowiredValue = autowired.value();
                    //4.获取这些成员变量的类型
                    Class<?> fieldClass = field.getType();
                    //5.获取这些成员变量的类型在容器里对应的实例
                    Object fieldValue = getFieldInstance(fieldClass, autowiredValue);
                    if(fieldValue == null){
                        throw new RuntimeException("unable to inject relevant type,target fieldClass is:" + fieldClass.getName() + " autowiredValue is : " + autowiredValue);
                    } else {
                        //6.通过反射将对应的成员变量实例注入到成员变量所在类的实例里
                        Object targetBean =  beanContainer.getBean(clazz);
                        ClassUtil.setField(field, targetBean, fieldValue, true);
                    }
                }
            }
        }


    }
    /**
     * 根据Class在beanContainer里获取其实例或者实现类
     */
    private Object getFieldInstance(Class<?> fieldClass, String autowiredValue) {
        Object fieldValue = beanContainer.getBean(fieldClass);
        if (fieldValue != null){
            return fieldValue;
        } else {
            Class<?> implementedClass = getImplementedClass(fieldClass, autowiredValue);
            if(implementedClass != null){
                return beanContainer.getBean(implementedClass);
            } else {
                return null;
            }
        }
    }
    /**
     * 获取接口的实现类
     */
    private Class<?> getImplementedClass(Class<?> fieldClass, String autowiredValue) {
        // 通过接口或者父类获取实现类或者子类的Class集合,不包括其本身
        Set<Class<?>> classSet =  beanContainer.getClassesBySuper(fieldClass);
        if(!ValidationUtil.isEmpty(classSet)){
            if(ValidationUtil.isEmpty(autowiredValue)){
                if(classSet.size() == 1){
                    return classSet.iterator().next();
                } else {
                    //如果多于两个实现类且用户未指定其中一个实现类,则抛出异常
                    throw new RuntimeException("multiple implemented classes for " + fieldClass.getName() + " please set @Autowired's value to pick one");
                }
            } else {
                for(Class<?> clazz : classSet){
                    if(autowiredValue.equals(clazz.getSimpleName())){
                        return clazz;
                    }
                }
            }
        }
        return null;
    }
}

总结

至此,springIOC的核心已经简单实现完成,其实springIOC的核心就是我上面提到的那几个模块,为了让大家深入了解spring的实现,本人也做了一个脑图,是对springIOC的核心的总结。

链接:

附上以上代码的gitee地址:https://gitee.com/oreo_team/self_developed_spring

http://naotu.baidu.com/file/752e733a3704d8793c23f1ebefe73745?token=ef8cc354de57e935

http://naotu.baidu.com/file/59f1cea147ffad7dd064bf51b775f656?token=9369c18878d626fe

SpringIOC-Resource

个人唠叨

好了,这么快又到了个人唠叨环节了,虽然本期文章很短,但是是以一种不同的方式展示spring源码,也算是对上期个人唠叨的实现,大家可以参考参考学习学习。

本周的吐槽

本周,是没有像上周这么潇洒了,还能学习新知识,本来想继续肝完spring-AOP和netty源码的,结果被机器学习和软件工程给大作业给硬生生要了,所以,本周并没有如期完成上周的计划。

一些经验总结

再来说说机器学习吧,连续搞了3天的机器学习,没有认真听过一节课的我(哈哈哈,不要被老师看到)从一个小白变成一个会基本特征工程编码的"小白",其实,这里重点不是机器学习,重点在于,如何快速上手一门新技术(仅限小白,如果你是大佬,当我没说)

  1. 首先你要对这门新技术有一个简单的认识,在这个阶段,我是不建议大家做实战的,首先要了解清楚概念,相信很多人都会推荐你上官网看相关的资源,其实,对于一个小白来说,一来就看官网,毫无疑问是一种劝退。那其实我并没有上官网,而是搜索一些别人总结的资料来看,了解一下,让我知道一个大概。
  2. 然后,你就可以上手实战了,那要怎么实战了,这里建议你找一个视频来跟着视频一起敲代码,敲完之后再去了解每一行代码的含义,这里视频的难度不建议过高,好像我一开始,并没有直接看班上老师的实战,而是直接看了网上一些评价比较高的实战,那简直就是劝退啊!看得我迷迷糊糊!!!!然后我才去翻班上老师的实战,跟着一起敲代码,最后理解每一行代码的含义
  3. 到这里,基本也已经是可以着手准备你要做的东西,就是一开始你是为了什么而学习这个新技术。比如我:是为了期末大作业而去做这个项目,我这时候就要开始了,然而,这个过程是最痛苦的,你会经历各种怀疑人生,因为虽然你理解一些基础概念,但是这每个概念你串联起来就不会了(哈哈哈哈,是不是贼真实),所以这个时候,弄懂项目的开发流程是很重要的,比如我数据挖掘的流程是特征预处理,然后再合并特征表,然后再进行模型训练,我做的那块是模型训练,然后再深入了解模型训练的流程,所以在上手项目或者实战新技术的时候,最终的是先了解主线,再去搞细节(好像我上面的spring解析一样也是采用先了解主线再深入细节的方法),所以我这里的主线就是模型训练,然后模型训练的主线就是,把合并的特征标表分为测试集和验证集,放到模型中进行训练和计算出auc,最对测试集进行预测。那这个流程其实是涉及到很多细节的,比如,表的合并的空指怎么处理,怎么把预测值放回到表中,怎么进行预测等等,那这些细节就要带有目的性的到网上找答案。
  4. 好了,做到这里,你基本上就已经算是入门了。

下周计划

我这时候就要开始了,然而,这个过程是最痛苦的,你会经历各种怀疑人生,因为虽然你理解一些基础概念,但是这每个概念你串联起来就不会了(哈哈哈哈,是不是贼真实),所以这个时候,弄懂项目的开发流程是很重要的,比如我数据挖掘的流程是特征预处理,然后再合并特征表,然后再进行模型训练,我做的那块是模型训练,然后再深入了解模型训练的流程,所以在上手项目或者实战新技术的时候,最终的是先了解主线,再去搞细节(好像我上面的spring解析一样也是采用先了解主线再深入细节的方法),所以我这里的主线就是模型训练,然后模型训练的主线就是,把合并的特征标表分为测试集和验证集,放到模型中进行训练和计算出auc,最对测试集进行预测。那这个流程其实是涉及到很多细节的,比如,表的合并的空指怎么处理,怎么把预测值放回到表中,怎么进行预测等等,那这些细节就要带有目的性的到网上找答案。
4. 好了,做到这里,你基本上就已经算是入门了。

下周计划

下周比较特殊,是答辩周,很多课都要结课了,不过主要任务还是有的:搞完spring-AOP和搞完Netty源码,有时间的话,就要开始刷算法题了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring-IOCSpring框架的核心部分之一,它是一种设计模式,全称为Inversion of Control(控制反转)。它通过将对象的创建、依赖关系的管理和对象的生命周期交给Spring容器来实现,从而降低了组件之间的耦合度,提高了代码的可重用性和可维护性。Spring-IOC实现主要依靠Spring容器,Spring容器是Spring框架的核心,它负责创建、管理和装配Bean对象,其中Bean是Spring框架中最基本的组件。 Spring-IOC实现主要有两种方式:BeanFactory和ApplicationContext。其中,BeanFactory是Spring-IOC的基本实现,而ApplicationContext是BeanFactory的子接口,提供了更多高级特性。ApplicationContext是Spring框架中最常用的IOC容器,它除了提供BeanFactory的所有功能外,还提供了更多的企业级特性,例如AOP、事务管理、国际化、事件传播等。 下面是一个简单的Spring-IOC的例子,假设我们有一个UserService接口和一个UserServiceImpl实现类,我们可以通过Spring-IOC容器来创建和管理UserServiceImpl对象: 1.定义UserService接口和UserServiceImpl实现类 ```java public interface UserService { void addUser(User user); } @Service public class UserServiceImpl implements UserService { @Override public void addUser(User user) { // 添加用户的具体实现 } } ``` 2.在Spring配置文件中配置UserService实例 ```xml <bean id="userService" class="com.example.service.UserServiceImpl"/> ``` 3.在代码中获取UserService实例并使用 ```java ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); UserService userService = context.getBean("userService", UserService.class); User user = new User(); userService.addUser(user); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值