动手实现Spring的IOC功能(基于注解)

动手实现Spring的IOC功能(基于注解)

文件结构

设计技术

主要设计两大技能点:反射,单例模式

实现过程

首先从启动spring开始,启动spring需要一个ApplicationContext的类,这个类中传入一个配置类,这个配置类主要是说明了包扫描的路径

创建一个配备了包扫描路径的类,扫描com/ssm/service下面的文件

AppConfig文件

@IComponentScan("com.ssm.service")
public class AppConfig {
}

还需要实现@IComponentScan的注解

import java.lang.annotation.*;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface IComponentScan {
    String value() default "";
}

另外再写两个Service,Service中需要三类注解,所有需要自己先实现这三类注解:@IComponent注解,@IAutowired注解和@Scope注解

实现@IComponent注解,主要作用是标注一个类为spring容器的bean,等下我需要讲这个类实例化

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

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface IComponent {
    String value() default "";
}

实现@IAutowired注解,主要是在类中引入xxxservice实例

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

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.CONSTRUCTOR,ElementType.METHOD})
//可用在字段和构造方法和普通方法上面
public @interface IAutowired {
}

实现@Scope注解,我主要实现它的两种方式

singleton单例模式:全局有且仅有一个实例,默认为单例模式

prototype原型模式:每次获取bean的时候都会有一个新实例

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

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Scope {
    String value() default "singleton";
}

UserService文件如下

@IComponent("userService")
public class UserService {
}

OrderService文件如下

先不用关注实现的接口InitializingBean和BeanNameAware,主要是OrderService中使用到@IAutowired注解拿到UserService对象

import com.spring.*;

/**
 * @author ssm
 */
@IComponent("orderService")
@Scope("prototype")
public class OrderService implements InitializingBean, BeanNameAware {

    @IAutowired
    private UserService userService;

    //这个beanName主要想要实现spring能够自动将OrderService这个Bean的名字(即Component里面的名字)赋值给这个beanName
    private String beanName;

    public void test(){
        //这里测试是看输出的这个userservice是否为空,如果不为空就说明我们写的依赖注入功能实现了
        System.out.println(userService);
    }

    @Override
    public void afterPropertiesSet() {
        //bean在生成的过程当中会调用这个方法
        System.out.println("初始化");
    }

    @Override
    public void setBeanName(String beanName) {
        this.beanName = beanName;
        //测试是否在生成bean时候自动赋值了beanName
        System.out.println("beanName:  "+beanName);
    }
}

最重要的部分是ApplicationContext的内容

我在代码中做了详细的注释,主要就是:

扫描配置类,通过配置类上面的IComponent注解得到扫描的路径,扫描这个路径下的所有文件并且得到每个文件的Class对象,讲Class对象存入List,从List中一个一个解析类,这里的解析类主要是看类上面是否有IComponent注解,解析得到每个类的相关信息(BeanName,BeanClass,Scope),将每个类的相关信息存入beanDefinitionMap,beanDefinitionMap是map结构,key是BeanName,value是BeanDefinition,BeanDefinition是每个Bean相关的信息

生成bean实例化,主要使用了反射调用构造方法,即beanClass.getDeclaredConstructor()生成实例,用beanClass.getDeclaredFields()来判断类中的成员变量上是否有@IAutowired注解,如果有该注解则去调用getBean(beanName)方法,该方法从beanDefinitionMap中取bean的信息来生成bean对象,getBean方法中拿到Bean实例后将其赋值给字段上加了@IAutowired注解的对象

import com.ssm.service.UserService;

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author ssm
 */
public class SSMApplicationContext {

    //主要保存了bean的信息,比如保存了class,scope,beanName
    private ConcurrentHashMap<String ,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
    //单例池,主要存着实例化出来的单例对象
    private ConcurrentHashMap<String,Object> singletonObjects = new ConcurrentHashMap<>();

    private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();
    public SSMApplicationContext(Class configClass){
        //构造方法主要是思考spring启动的时候需要做什么?
        //1:扫描类
        //2:创建bean
        //       思考:spring启动是所有bean都要创建吗?主要创建哪种类型的bean呢?
        //             是非懒加载的单例!启动spring的时候就创建bean
        //2:第二个步骤概括就是要生成单例bean,并且要把生成的bean放入单例池中

        //扫描到类之后要干什么?解析这个类,具体解析些什么信息,比如有component注解

        //扫描配置文件下的类,得到所有类对象
        List<Class> classList = scan(configClass);
        for (Class clazz : classList) {
            //一个一个解析类,将所有类对象的基本信息存入beanDefinition
            //ConcurrentHashMap<String ,BeanDefinition> beanDefinitionMap,key是beanName,value是BeanDefinition(主要是scope和beanClass)
            BeanDefinition beanDefinition = new BeanDefinition();
            beanDefinition.setBeanClass(clazz);

            IComponent component = (IComponent) clazz.getAnnotation(IComponent.class);
            String beanName = component.value();

            if (clazz.isAnnotationPresent(Scope.class)){
                Scope scope = (Scope) clazz.getAnnotation(Scope.class);
                beanDefinition.setScope(scope.value());
            }else {
                beanDefinition.setScope("singleton");
            }

            //判断扫描IComponent的类是不是实现了beanPostProcessor
            if (BeanPostProcessor.class.isAssignableFrom(clazz)){
                //判断clazz是否是BeanPostProcessor的实现类/子类
                try {
                    BeanPostProcessor bpp = (BeanPostProcessor) clazz.getDeclaredConstructor().newInstance();
                    beanPostProcessorList.add(bpp);
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                }
            }
            beanDefinitionMap.put(beanName,beanDefinition);
        }

        for (String beanName: beanDefinitionMap.keySet()){
            //beanDefinitionMap里面存着bean实例化的基本信息
             BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
             if (beanDefinition.getScope().equals("singleton")){
                 //生成这个bean(和bean的生命周期有关,如下)
                 Object bean = createBean(beanName,beanDefinition);
                 singletonObjects.put(beanName,bean);//把创建出来的bean放入单例池中保存起来
             }
        }
    }

    private Object createBean(String beanName, BeanDefinition beanDefinition) {
        //生成这个bean(和bean的生命周期有关,如下)
        Class beanClass = beanDefinition.getBeanClass();
        try {
            //实例化(用class调用构造方法来进行实例化)
            Object bean = beanClass.getDeclaredConstructor().newInstance();
            //填充属性
            Field[] fields = beanClass.getDeclaredFields();//思考:类中的什么属性是需要填充的
            for (Field field : fields) {
                if (field.isAnnotationPresent(IAutowired.class)){
                    //在字段上判断是否加了Autowired注解
                    //思考:存在加了Autowired注解的字段,那我填充属性,拿什么东西填充呢?填充对象是?
                    //OrderService类中userService加了注解,那我应该拿一个UserService的对象去给这个加了注解的userservice赋值
//                    UserService userService = (UserService) getBean(field.getName());
                    Object userService = getBean(field.getName());

                    field.setAccessible(true);//反射中必须设置true才可以通过反射访问字段,才能给实例化赋值
                    field.set(bean,userService);//用userService给这个实例化的bean赋值
                }
            }
            //Aware
            if (bean instanceof BeanNameAware){
                //bean是否实现了BeanNameAwaren接口
                //实现了则将bean进行强制类型转化
                ((BeanNameAware)bean).setBeanName(beanName);
            }

            //...可实现程序员定义的逻辑
            //可以实现多个BeanPostProcessor,所以循环
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                bean = beanPostProcessor.postProcessBeforeInitialization(bean,beanName);
            }

            //初始化(instanceof只能针对实例来写,不能用于针对类来写)
            if (bean instanceof InitializingBean){
                //bean是否实现了InitializingBean接口
                //实现了则将bean进行强制类型转化
                ((InitializingBean)bean).afterPropertiesSet();
            }

            //...可实现程序员定义的逻辑
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                bean = beanPostProcessor.postProcessAfterInitialization(bean,beanName);
            }
            return bean;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return null;
    }


    public Object getBean(String beanName){
        //还需要提供一个getBean方法,返回值是Object
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (beanDefinition.getScope().equals("prototype")){
            return createBean(beanName,beanDefinition);
        }else {
            //单例生成,从单例池中拿对象
            Object bean = singletonObjects.get(beanName);
            if (bean == null){
                Object o = createBean(beanName,beanDefinition);
                singletonObjects.put(beanName,o);
                return o;
            }
            return bean;
        }
    }


    private List<Class> scan(Class configClass) {
        List<Class> classList = new ArrayList<>();

        //扫描类,主要是为了得到扫描的路径
        //如何得到扫描路径,先拿到Annocation注解,因为我们知道是IComponentScan注解,
        // 所以这里进行了强转,然后调用value得到了扫描的路径
        IComponentScan iComponentScan = (IComponentScan) configClass.getAnnotation(IComponentScan.class);
        String scanPath = iComponentScan.value();
//        System.out.println(scanPath);  com.ssm.service
        //其实扫描是为了扫描目录,但scanPath只是得到了路径,真的目录的格式应该是com/ssm/service,所以需要转换scanPath
        scanPath = scanPath.replace(".","/");//粗暴转为目录格式,替换.为/

        //思考:如何扫描类(使用类加载器,调用类加载器上的getResource来获得)
        ClassLoader classLoader =  SSMApplicationContext.class.getClassLoader();
        URL resource = classLoader.getResource(scanPath);
//        System.out.println(resource);
        //resource在没有转为文件目录的时候输出是null,转为文件目录以后输出是 file:/C:/Users/SSM/Desktop/exercise/my_spring/target/classes/com/ssm/service

        File file = new File(resource.getFile());
        File[] files = file.listFiles();//扫描目录下所有的文件,什么格式都会扫描进来

        for (File f : files) {
            //System.out.println(f);
            //C:\Users\SSM\Desktop\exercise\my_spring\target\classes\com\ssm\service\OrderService.class
            //C:\Users\SSM\Desktop\exercise\my_spring\target\classes\com\ssm\service\UserService.class
            String absolutePath = f.getAbsolutePath();
            absolutePath = absolutePath.substring(absolutePath.indexOf("com"),absolutePath.indexOf(".class"));

            //System.out.println(absolutePath);
            //再次把斜线变成点
            absolutePath = absolutePath.replace("\\",".");//com.ssm.service.OrderService
            try {
                Class<?> clazz = classLoader.loadClass(absolutePath);//加载类
                classList.add(clazz);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
        return classList;
    }
}

BeanDefinition文件如下:主要用来存储bean信息的

public class BeanDefinition {
    private String scope;
    private Class beanClass;

    public String getScope() {
        return scope;
    }

    public void setScope(String scope) {
        this.scope = scope;
    }

    public Class getBeanClass() {
        return beanClass;
    }

    public void setBeanClass(Class beanClass) {
        this.beanClass = beanClass;
    }
}

在上面的ApplicationContext中有以下代码单独摘出来如下:

//Aware
if (bean instanceof BeanNameAware){
    //bean是否实现了BeanNameAwaren接口
    //实现了则将bean进行强制类型转化
    ((BeanNameAware)bean).setBeanName(beanName);
}

//...可实现程序员定义的逻辑
//可以实现多个BeanPostProcessor,所以循环
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
    bean = beanPostProcessor.postProcessBeforeInitialization(bean,beanName);
    System.out.println(bean+"------------------------beanPostProcessor");
}

//初始化(instanceof只能针对实例来写,不能用于针对类来写)
if (bean instanceof InitializingBean){
    //bean是否实现了InitializingBean接口
    //实现了则将bean进行强制类型转化
    ((InitializingBean)bean).afterPropertiesSet();
}

//...可实现程序员定义的逻辑
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
    bean = beanPostProcessor.postProcessAfterInitialization(bean,beanName);
    System.out.println(bean+"------------------------beanPostProcessor");
}

这里参考bean的生命周期,这里主要实现了Aware接口,bean初始化,BeanPostProcessor接口,这部分代码省略

最后附上代码:https://gitee.com/vampire-boom/spring-ioc

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值