自己实现spring(一) —— ioc容器实现

前言:加深对spring的理解,把spring ioc的大致流程抽出来,自己动手撸一个简洁版的

  • 新建一个maven工程,这里使用idea工具
    自己实现spring(一) —— ioc容器实现_第1张图片
    pom文件只依赖一个servlet

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0modelVersion>
    <groupId>com.fandaygroupId>
    <artifactId>springartifactId>
    <version>1.0-SNAPSHOTversion>
    <dependencies>
        <dependency>
            <groupId>javax.servletgroupId>
            <artifactId>javax.servlet-apiartifactId>
            <version>3.0.1version>
            <scope>providedscope>
        dependency>
    dependencies>
project>

项目结构:

  • ioc spring的bean工厂,完成定位 注册 初始化 注入等过程
  • mvc spring的web层mvc框架,负责请求的分发
  • demo 用于测试框架使用

编写接口定义

  • ApplicationContext主要定义获取bean的方法,有根据id获取的,也有获取所有bean的
package com.fanday.ioc;
import java.util.Map;

public interface ApplicationContext {
    /**
     * 根据id获取bean
     * @param id
     * @return
     */
    Object getBean(String id);

    /**
     * 根据id获取特定类型的bean,完成强转
     * @param id
     * @param clazz
     * @param 
     * @return
     */
    T getBean(String id,Class clazz);

    /**
     * 获取工厂内的所有bean集合
     * @return
     */
    Map getBeans();
}
  • BeanRegister定义向工厂注册bean 和BeanDefinition的方法
package com.fanday.ioc;
import com.fanday.ioc.support.BeanDefinition;

import java.util.List;

public interface BeanRegister {
    /**
     * 向工厂内注册BeanDefinition
     * @param bds
     */
    void registBeanDefinition(List bds);

    /**
     * 向工厂内注册bean实例对象
     * @param id
     * @param instance
     */
    void registInstanceMapping(String id,Object instance);
}

自己实现spring(一) —— ioc容器实现_第2张图片

support包主要是一些默认的工厂实现,annotation包是一些需要的注解

package com.fanday.ioc.annotation;
import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)//运行时注解
@Target(ElementType.TYPE)//作用在类上面
@Documented
public @interface Controller {
    String value() default "";
}
package com.fanday.ioc.annotation;

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface Component {
    String value() default "";
}
package com.fanday.ioc.annotation;

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)//作用在字段上面
@Documented
public @interface Autowire {
    String value() default "";
}
package com.fanday.ioc.annotation;

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})//作用在类和方法 仿照springmvc的套路来
@Documented
public @interface RequestMapping {
    String value() default "";
}
package com.fanday.ioc.annotation;

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)//作用在方法的参数上面
@Documented
public @interface RequestParam {
    String value() default "";
}

resources下新建一个applicationContext.properties文件用于配置,容器启动时要扫描的包
scanPackage=com.fanday.demo

AnnotationApplicationContext 容器的具体实现

package com.fanday.ioc.support;

import com.fanday.ioc.ApplicationContext;
import com.fanday.ioc.BeanRegister;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

public class AnnotationApplicationContext implements ApplicationContext,BeanRegister {
    private Map instanceMapping = new ConcurrentHashMap();

    //保存所有bean的信息,主要包含bean的类型  id等信息
    private List beanDefinitions = new ArrayList();
    //配置文件的config,这里为了简单我们使用properties文件
    private Properties config = new Properties();

    public AnnotationApplicationContext(String location){
        InputStream is = null;
        try{

            //1、定位
            is = this.getClass().getClassLoader().getResourceAsStream(location);

            //2、载入
            config.load(is);

            //3、注册
            register();

            //4、实例化
            createBean();

            //5、注入
            populate();

        }catch(Exception e){
            e.printStackTrace();
        }finally {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 调用具体委派的注入类进行注入
     */
    private void populate() {
        Populator populator = new Populator();
        populator.populate(instanceMapping);
    }

    /**
     * 调用具体的创建对象创建bean
     */
    private void createBean() {
        BeanCreater creater = new BeanCreater(this);
        creater.create(beanDefinitions);
    }

    /**
     * 调用具体的注册对象注册bean信息
     */
    private void register() {
        BeanDefinitionParser parser = new BeanDefinitionParser(this);
        parser.parse(config);
    }

    public Object getBean(String id) {
        return instanceMapping.get(id);
    }
    public Properties getConfig() {
        return this.config;
    }

    public  T getBean(String id, Class clazz) {
        return (T)instanceMapping.get(id);
    }

    public Map getBeans() {
        return instanceMapping;
    }

    public void registBeanDefinition(List bds) {
        beanDefinitions.addAll(bds);
    }

    public void registInstanceMapping(String id, Object instance) {
        instanceMapping.put(id,instance);
    }

}

BeanDefinitionParser完成扫描包下bean信息的解析注册

package com.fanday.ioc.support;

import com.fanday.ioc.BeanRegister;

import java.io.File;
import java.net.URL;
import java.util.List;
import java.util.Properties;

public class BeanDefinitionParser {
    //配置的扫描包的key
    public static final String SCAN_PACKAGE = "scanPackage";
    //容器注册对象
    private BeanRegister register;
    public BeanDefinitionParser(BeanRegister register){
        this.register = register;
    }

    public void parse(Properties properties){
        //获取要扫描的包
        String packageName = properties.getProperty(SCAN_PACKAGE);
        //执行注册
        doRegister(packageName);
    }


    private void doRegister(String packageName) {
        //获取此包名下的绝对路径
        URL url = getClass().getClassLoader().getResource("./"+packageName.replaceAll("\\.","/"));
        File dir = new File(url.getFile());
        //循环遍历  递归找到所有的java文件
        for (File file:dir.listFiles()){
            if(file.isDirectory()){
                //文件夹-->递归继续执行
                doRegister(packageName+"."+file.getName());
            }else {
                //处理文件名来获取类名  运行时获取到的是class文件
                String className = packageName+"."+file.getName().replaceAll(".class","").trim();
                //调用BeanDefinitionGenerator.generate(className)方法,来处理
                //1.类带有容器要处理的注解,则解析id生成BeanDefinition集合返回
                //2.不带有需要处理的注解   直接返回null
                List definitions = BeanDefinitionGenerator.generate(className);
                if(definitions == null)continue;
                //调用容器的注册方法来完成bean信息的注册
                this.register.registBeanDefinition(definitions);
            }
        }

    }
}

BeanDefinitionGenerator根据具体的类名来完成BeanDefinition的生成

package com.fanday.ioc.support;

import com.fanday.ioc.annotation.Component;
import com.fanday.ioc.annotation.Controller;

import java.util.ArrayList;
import java.util.List;

public class BeanDefinitionGenerator {

    public static List generate(String className){
        try {
            Class clazz = Class.forName(className);
            String[] ids = generateIds(clazz);
            if(ids==null)return null;
            List list = new ArrayList();
            for (String id:ids){
                list.add(new BeanDefinition(id,clazz));
            }
            return list;
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 生成id数组
     * 1.带有@Controller 注解但是注解value没给值,@Controller一般没有
     * 接口定义,用类的全名作为id返回ids长度为1 
     * 2.@Component 没有value  获取所有的实现的接口,接口名为id,返货ids数组
     * 长度是实现的接口个数
     * 3.@Component 有value 返回id=value
     * 4.不带容器要实例化的注解  null
     */
    private static String[] generateIds(Class clazz) {
        String[] ids = null;
        if (clazz.isAnnotationPresent(Controller.class)) {
            ids = new String[]{clazz.getName()};
        } else if (clazz.isAnnotationPresent(Component.class)) {
            Component component = (Component) clazz.getAnnotation(Component.class);
            String value = component.value();
            if (!"".equals(value)) {
                ids = new String[]{value};
            } else {
                Class[] interfaces = clazz.getInterfaces();
                ids = new String[interfaces.length];
                //如果这个类实现了接口,就用接口的类型作为id
                for (int i = 0; i < interfaces.length; i++){
                    ids[i] = interfaces[i].getName();
                }
                return ids;
            }
        }
        return ids;
    }

}
package com.fanday.ioc.support;

public class BeanDefinition{
    private String id;
    private Class clazz;
    public BeanDefinition(String id, Class clazz){
        this.id = id;
        this.clazz = clazz;
    }

    public String getId() {
        return id;
    }

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

    public Class getClazz() {
        return clazz;
    }

    public void setClazz(Class clazz) {
        this.clazz = clazz;
    }

    public Object getInstance(){
        try {
            return clazz.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

BeanCreater比较简单,创建bean并且添加到容器工厂

package com.fanday.ioc.support;

import com.fanday.ioc.BeanRegister;

import java.io.File;
import java.net.URL;
import java.util.List;
import java.util.Properties;

public class BeanCreater {

    private BeanRegister register;
    public BeanCreater(BeanRegister register){
        this.register = register;
    }

    public void create(List bds){
        for (BeanDefinition bd:bds){
            doCreate(bd);
        }
    }

    private void doCreate(BeanDefinition bd) {
        Object instance = bd.getInstance();
        this.register.registInstanceMapping(bd.getId(),instance);
    }
}

bean都初始化完成了,接下来进行bean之间的依赖注入

package com.fanday.ioc.support;

import java.lang.reflect.Field;
import java.util.Map;
import com.fanday.ioc.annotation.Autowire;

public class Populator {

    public Populator(){
    }

    public void populate(Map instanceMapping){
        //首先要判断ioc容器中有没有东西
        if(instanceMapping.isEmpty())return;

        //循环遍历每一个容器中得对象
        for (Map.Entry entry:instanceMapping.entrySet()){
            //获取对象的字段
            Field[] fields = entry.getValue().getClass().getDeclaredFields();
            for (Field field:fields){
                if(!field.isAnnotationPresent(Autowire.class))continue;
                Autowire autowire = field.getAnnotation(Autowire.class);
                //后去字段要注入的id value  为空则按类名  接口名自动注入
                String id = autowire.value();
                if("".equals(id))id = field.getType().getName();
                field.setAccessible(true);
                try {
                    //反射注入
                    field.set(entry.getValue(),instanceMapping.get(id));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

至此 ioc的整个流程大致完成,我们来编写测试

自己实现spring(一) —— ioc容器实现_第3张图片
自己实现spring(一) —— ioc容器实现_第4张图片

自己实现spring(一) —— ioc容器实现_第5张图片

ok,ioc容器实现已基本完成,下次一起来撸mvc

你可能感兴趣的:(spring)