Spring IOC源码解析

众所周知,Spring有很多优点,这些优点也让Spring彻底的让大家记住,这次我们来了解IOC底层源码的实现

什么是IOC?

每次我们了解一个东西的时候,我们都需要去理解它的含义,那么IOC到底是什么呢?

  • IOC(inversion of control):翻译过来就是 翻转控制,在java刚出来的时候创建对象,使用对象都是我们自己new出来,现在不同了,有人帮我们去创建并且帮我们管理,这种主动被动形式切换了,消除了大量的冗余代码,所以叫控制反转.
  • 依赖注入 : 依靠了动态代理以及反射,Spring获取Bean基本都是从map中获取,实际上是在程序初始化的时候,就已将bean实例化,我们注入在我们要使用的地方,只不过是一个复用,他们的实际引用地址是一样的.

写一个自己的IOC

  • 目标:帮用户把对象创建好放到一个容器里,用户可通过容器直接获取对象。

  • 达成目标所需要的条件:

    1. 哪些些对象需要IOC管理(创建Bean)。
    2. 通过什么方式告诉IOC这些对象是需要IOC管理的 (xml或注解)。
    3. 去哪些找这些对象(指定扫描的包)。

实现思路

  1. 标识:创建自定义的注解(带有我们自定义注解的类才进行对象的实例化和装配);
    @Component @Service @AutoWired UserDao,UserService

  2. 读取:需要读取用户配置文件;(用户可自定义哪些包需要进行扫描bean的装配);
    component-scan
    loadScanPath();

  3. 扫描:扫描文件进行类加载(根据用户的配置扫描对应的包加载对应包里面的class放到一个容器里面);
    beanClassMap
    loadBeanClass();

  4. 创建:把扫描出来的class中带有自定义注解的类和属性分别进行实例化和属性注入;
    beanMap
    registerBean();
    dependency();

  5. 封装:把实例化好的对象放到一个容器里并提供调用方法;
    ApplicationContext.getBean();

实现代码

第一步 创建普通的java项目

创建一个普通java项目即可

第二步 创建包结构

我们创建包结构(spring不用导入任何依赖)

在这里插入图片描述

第三步 模仿spring的几个注解

  1. 先写spring的autowired注解,我们起名为IocAutowired
package com.spring.ioc.annotation;

import java.lang.annotation.*;

@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
//模仿spring中的autowired
public @interface IocAutowired {
}
  1. 其次写spring的component注解,我们起名为IocCompont
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
//模仿spring的 compont注解
public @interface IocCompont {
     String value() default "";
}
  1. 最后我们写spring的 service注解,我们起名为IocService
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
//我们模仿spring的 service注解
public @interface IocService {
}

第四部 创建bean类,一个dao层类,一个service层类

创建bean类,一个dao层类,一个service层类,并分别使用我们定义好的注解.

UserDao类
package com.spring.ioc.bean;

import com.spring.ioc.annotation.IocCompont;

/**
 * @program: IOC
 * @description: dao层类
 * @author: ZhengZhe
 * @create: 2019-10-29 09:18
 */
@IocCompont
public class UserDao {
    void study(String name){
        System.out.println("这是我的昵称: "+name);
    }
}
UserService类
package com.spring.ioc.bean;

import com.spring.ioc.annotation.IocAutowired;
import com.spring.ioc.annotation.IocService;

/**
 * @program: IOC
 * @description: Service层类
 * @author: ZhengZhe
 * @create: 2019-10-29 09:19
 */
@IocService
public class UserService {
    @IocAutowired
    private UserDao userDao;
    //调用dao层的方法进行输出
    public void study(String name){
        userDao.study(name);
    }
}

第五步 写配置文件

创建名称为: application.properties的配置文件

component-scan=com.spring.ioc.bean

第六步 书写核心构建类IocApplicationContex

我们需要写一个核心类去调用资源文件中的xml或者properties文件,初始化被注解的类 .

package com.spring.ioc.core;

import com.spring.ioc.annotation.IocAutowired;
import com.spring.ioc.annotation.IocCompont;
import com.spring.ioc.annotation.IocService;

import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @program: IOC
 * @description: Ioc控制翻转核心类
 * @author: ZhengZhe
 * @create: 2019-10-29 09:22
 */
public class IocApplicationContex {

    //保存IOC容器bean
    private ConcurrentHashMap<String,Object> beanMap=new ConcurrentHashMap();

    //保存所有扫描出来的cass全路径名
    private Set<String> beanClass=new HashSet<String>();

    public Object getBean(String name){
        return beanMap.get(name);
    }


    public IocApplicationContex() {
        refresh();
    }

    private void refresh() {
        //定位扫描路径
        String scanPath=sanPath();
        //加载所有class
        loadClass(scanPath);
        //实例化ArdComponent  ArdService 注解的类
        registerBean();
        //对实例化对象的属性进行依赖注入
        depenency();

    }

    private void depenency() {
        try {
            //遍历所有bean
            for(Object bean:beanMap.values()){
                Class clazz=bean.getClass();
                Field[] fields=clazz.getDeclaredFields();//所有属性
                for(Field field:fields){
                    //验证属性是否带有ArdAutowired注解
                    if(field.isAnnotationPresent(IocAutowired.class)){
                        String className=  field.getType().getSimpleName();
                        String beanName=className.substring(0,1).toLowerCase()+className.substring(1,className.length());
                        Object dependencyBean= beanMap.get(beanName);
                        field.setAccessible(true);
                        field.set(bean,dependencyBean);//设置属性
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    private void registerBean() {
        try {
            for(String className:beanClass){
                Class clazz=Class.forName(className);
                if(clazz.isAnnotationPresent(IocCompont.class)||clazz.isAnnotationPresent(IocService.class)){
                    Object bean=clazz.newInstance();
                    //bean名称,类名首字母小写
                    String cname=clazz.getSimpleName();
                    String beanName=cname.substring(0,1).toLowerCase()+cname.substring(1,cname.length());
                    beanMap.put(beanName,bean);
                }

            }
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("实例化bean失败");
        }
    }

    private void loadClass(String scanPath) {
        try {
            String filePath= scanPath.replace(".","/");
            URL url=this.getClass().getClassLoader().getResource(filePath);
            File file=new File(url.toURI());
            doLoadClass(file,filePath);
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }

    }

    private void doLoadClass(File file,String scanPath) {
        //文件夹
        if(file.isDirectory()){
            File[] fileList=file.listFiles();
            for(File f:fileList){
                doLoadClass(f,scanPath+"/"+f.getName());
            }
        }

        //文件
        if(file.getName().indexOf(".class")>0){
            //文件夹格式转为包格式
            String packgePath=scanPath.replace("/",".");
            //包名+类名
            beanClass.add(packgePath.replace(".class",""));
        }
    }


    private String sanPath() {
        try {
            InputStream is=this.getClass().getClassLoader().getResourceAsStream("application.properties");
            Properties properties=new Properties();
            properties.load(is);
            return properties.get("component-scan").toString();
        }catch (Exception e){
            e.printStackTrace();
            System.out.println("加载配置文件错误");
        }
        return null;
    }
}

第七步 创建测试类测试

package com.spring.ioc;

import com.spring.ioc.bean.UserService;
import com.spring.ioc.core.IocApplicationContex;

/**
 * @program: IOC
 * @description: IOC测试类
 * @author: ZhengZhe
 * @create: 2019-10-29 09:29
 */
public class IocTest {
    public static void main(String[] args) {
        //创建核心类对象
        IocApplicationContex iocApplicationContex = new IocApplicationContex();
        //输入我们想要的对象名称,并强转
        UserService userService = (UserService) iocApplicationContex.getBean("userService");
        userService.study("小哲");
    }
}

第八步 查看输出结果

这是我的昵称: 小哲

IOC总结

在上面的项目中我们并没有进行所谓的spring依赖注入,我们使用我们自己创建的注解以及初始化核心类也能实现类似于spring的IOC效果,当然代码中肯定会有很多的bug和细节,现在只是简单介绍一下IOC的底层代码实现原理.希望大家多多指正

源代码(待补充)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值