简单手写Spring--启动、扫描、容器管理bean

1、思路

  • 1.1、Spring通过读取配置文件或者配置类获取需要Spring管理的Bean信息。
  • 1.2、通过配置类获取需要被扫描的配置类。
  • 1.3、通过ApplicationClassLoader加载所有被扫描到的类信息,获取需要被Spring控制的类信息,判断作用域或者其他Spring控制的属性,生成BeanDefinition集合信息,将BeanDefinition作为一个Spring上下文的一个Map类型属性存在上下文中。
  • 1.4、处理Spring上下文中的BeanDefinition信息,对BeanDefinition集合遍历,根据各种Spring控制属性和类信息反射生成Bean。若是默认的单例模式,初始化的时候就生成Spring的Bean对象,将该对象放入单例池Singleton集合中,该单例池也作为Spring的上下文一个Map类型属性存在上下文中。

2、实现

2.1、@ComponentScan 注解
  • Spring根据@ComponentScan扫描需要被Spring管理的包路径。
/**
 * Created on 2021/4/17
 *
 * @author kaer
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ComponentScan {

    String value() default "";
}
2.2、@Component注解
  • Spring根据@Component来识别需要Spring管理的Bean。
/**
 * Created on 2021/4/17
 *
 * @author kaer
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Component {

    String value();
}
2.3、@Scope注解
  • Spring的Bean的作用域标识.。
/**
 * Created on 2021/4/17
 *
 * @author kaer
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Scope {

    String value();
}

2.4、BeanDefinition
  • bean定义Model,有作用域和当前类信息等属性。
/**
 * Created on 2021/4/17
 *
 * @author kaer
 */
public class BeanDefinition {

    /**
     * bean作用域
     */
    private String scope;

    /**
     * 类对象
     */
    private Class clazz;

    public Class getClazz() {
        return clazz;
    }

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

    public String getScope() {
        return scope;
    }

    public void setScope(String scope) {
        this.scope = scope;
    }
}
2.5、SanApplicationContext
  • Spring的上下文Model与初始化步骤控制–扫描、BeanDefinition信息的生成、单例Bean创建与被单例池管理。Spring上下文主要有两个属性
    单例池:
    private Map<String, Object> singleTonBeanMap = new ConcurrentHashMap<>();
    Bean定义集合:
    private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
/**
 * Created on 2021/4/17
 *
 * @author linjianshan
 */
public class SanApplicationContext {

    private static final String SINGLETON = "singleton";
    private static final String PROTOTYPE = "prototype";

    private Class configClass;

    /**
     * 单例bean池
     */
    private Map<String, Object> singleTonBeanMap = new ConcurrentHashMap<>();

    /**
     * bena定义map
     */
    private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();


    public SanApplicationContext(Class configClass) {
        this.configClass = configClass;

        /**
         * 扫描类,初始化 beanDefinitio
         */
        scan(configClass);

        /**
         * 创建bean
         */
        createBean(beanDefinitionMap);


    }

    private void createBean(Map<String, BeanDefinition> beanDefinitionMap) {
        for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : beanDefinitionMap.entrySet()) {
            String beanName = beanDefinitionEntry.getKey();
            BeanDefinition beanDefinition = beanDefinitionEntry.getValue();

            Class clazz = beanDefinition.getClazz();
            try {
                Object o = clazz.getDeclaredConstructor().newInstance();
                singleTonBeanMap.put(beanName, o);
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
        }
    }

    private void scan(Class configClass) {
        //解析配置类
        //ComponentScan注解-->扫描路径-->扫描
        ComponentScan componentScanAnnotation = (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class);
        String path = componentScanAnnotation.value();
        System.out.println(path);
        path = path.replace(".", "/");
        ClassLoader classLoader = SanApplicationContext.class.getClassLoader();
        URL resource = classLoader.getResource(path);
        File file = new File(resource.getFile());
        if (file.isDirectory()) {
            File[] files = file.listFiles();

            for (File f : files) {
                String fileName = f.getAbsolutePath();
                System.out.println(fileName);
                if (fileName.endsWith(".class")) {

                    try {
                        String className = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".class"));
                        className = className.replace("\\", ".");
                        System.out.println(className);

                        Class<?> clazz = classLoader.loadClass(className);
                        if (clazz.isAnnotationPresent(Component.class)) {
          
                            Component componentAnnotation = clazz.getDeclaredAnnotation(Component.class);
                            String beanName = componentAnnotation.value();

                            BeanDefinition beanDefinition = new BeanDefinition();
                            beanDefinition.setClazz(clazz);
                            if (clazz.isAnnotationPresent(Scope.class)) {
                                Scope scopeAnnotation = clazz.getDeclaredAnnotation(Scope.class);
                                beanDefinition.setScope(scopeAnnotation.value());
                            } else {
                                //默认单例
                                beanDefinition.setScope(SINGLETON);
                            }

                            beanDefinitionMap.put(beanName, beanDefinition);

                        }

                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }


            }
        }
    }

    public Object getBean(String beanName) {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);

        if (beanDefinition == null) {
            //随便抛错
            throw new NullPointerException();
        }

        Object result = null;
        String scope = beanDefinition.getScope();
        if (SINGLETON.equals(scope)) {
            result = singleTonBeanMap.get(beanName);
        } else {
            Class clazz = beanDefinition.getClazz();
            try {
                result = clazz.getDeclaredConstructor().newInstance();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
        }
        return result;
    }
}
2.6、SpringConfig
  • Spring配置类,也可以使用配置文件的方式。配置类上使用@ComponentScan注解配置Spring扫描的包路径。
/**
 * Created on 2021/4/17
 *
 * @author kaer
 */
@ComponentScan("com.san.service")
public class SpringConfig {
}

3、测试

3.1、在SpringConfig配置的被扫描的包路径下建立需要被Spring管理的类
  • MemberService,标记出需要被Spirng管理,不自定义其作用域。
/**
 * Created on 2021/4/17
 *
 * @author kaer
 */
@Component("memberService")
public class MemberService {
}
  • AssetService,标记出需要被Spring管理,自定义作用域为原型。
/**
 * Created on 2021/4/18
 *
 * @author kaer
 */
@Component("assetService")
@Scope("prototype")
public class AssetService {
}
3.2、Test
/**
 * Created on 2021/4/17
 *
 * @author kaer
 */
public class Test {

    public static void main(String[] args) {
        SanApplicationContext sanApplicationContext = new SanApplicationContext(SpringConfig.class);
        System.out.println(sanApplicationContext.getBean("memberService"));
        System.out.println(sanApplicationContext.getBean("memberService"));
        System.out.println(sanApplicationContext.getBean("assetService"));
        System.out.println(sanApplicationContext.getBean("assetService"));
    }
}

3.3、结果分析
com.san.service.MemberService@1d44bcfa
com.san.service.MemberService@1d44bcfa
com.san.service.AssetService@266474c2
com.san.service.AssetService@6f94fa3e

结果如上,分析输出类的toString()方法可知:

  • 通过BeanName获取MemberService多个Bean时,结果显示同一个实例Bean,符合MemberService默认的作用域定义。
  • 通过BeanName获取AssetService多个Bean时,结果显示不是同一个Bean,符合AssetService自定义原型作用域。
4、总结
  • 实际Spring的启动、Bean的实例化过程还是有许多细节和步骤的,本文将其简化了,但其思想是一致的。首选就是扫描Spring管理的类获取Beanfinition的信息,然后将单例Bean先实例化存入单例池中,其他作用域再根据其他操作进行控制。比如原型模式作用域就是每次客户端获取Bean时,就获取对应的BeanDefinition信息再来通过反射的方式来实例化新的Bean返回。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值