学习手写Spring

 Spring容器的初步实现

  • 构造方法
    • 使用componentScan注解扫描对应路径下的类,判断哪些需要装配到容器,并装配;
    • 使用beanMap封装  <bean的名字,对应的bean属性(哪个类 和 单例/多例)>
    • 对装配到容器中的单例对象调用createBean 进行创建
    • 并加入到单例bean池中(singlotenMap)
  • getBean方法
    • 通过输入 bean的名字 来获取对应的bean对象;
    • 如果是单例,从单例池中取;
    • 如果是多例,则调用 createBean 创建bean对象;
package com.aguang.spring;

import java.beans.Introspector;
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.concurrent.ConcurrentHashMap;

/*
 * spring容器
 */
public class AguangApplicationContext {

    private Class configClass;

    //存放bean对象的名字,以及对应的bean对象属性
    private ConcurrentHashMap<String, BeanDefinition> beanMap = new ConcurrentHashMap<>();
    private ConcurrentHashMap<String, Object> singletonMap = new ConcurrentHashMap<>();

    //构造方法;传入一个配置类:扫描bean对象的配置类
    public AguangApplicationContext(Class configClass) {
        this.configClass = configClass;

        //第一步:扫描bean对象--需要扫描 .Class文件
        //首先判断传入的类是否是 可以有扫描功能的类,即是否有ComponentScan注解
        if (configClass.isAnnotationPresent(ComponentScan.class)) {
            //获取类路径---类路径是ComponentScan注解中的属性
            //获得ComponentScan注解对象
            ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
            //得到了相对路径;  com.aguang.service,此路径下的文件是,原代码,需要的是  .Class文件
            String path = componentScanAnnotation.value();

            //对相对路径进行格式上的调整
            path = path.replace(".", "/");

            //通过类加载器,获取对应的.Class文件
            //获取类加载器对象
            ClassLoader classLoader = AguangApplicationContext.class.getClassLoader();
            //通过类加载器,以及相对路径path,可以获取到对应的.class文件的路径
            URL resource = classLoader.getResource(path);
            //获得对应的文件
            File file = new File(resource.getFile());

            //判断该文件是否是一个目录
            if (file.isDirectory()) {
                //取出其中的文件
                File[] files = file.listFiles();
                //遍历每一个文件,即遍历每一个类判断哪个是 Bean对象,哪个有component注解;需要使用反射拿到这个类
                for (File f : files) {
                    String fileName = f.getAbsolutePath();

                    //判断文件是否是.class文件
                    if (fileName.endsWith(".class")) {
                        //使用反射拿到这个类; 反射需要类名
                        String className = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".class"));

                        //对路径进行调整
                        className = className.replace("\\", ".");

                        try {
                            //获得对应的类对象
                            Class<?> clazz = classLoader.loadClass(className);
                            //判断是否包含Component注解,即是否是 Bean对象
                            if (clazz.isAnnotationPresent(Component.class)) {
                                //获取对应bean对象的名字
                                Component component = clazz.getAnnotation(Component.class);
                                String beanName = component.value();

                                if (beanName.equals("")) {
                                    //如果没有传入beanName,默认值为类的首字母小写
                                    beanName = Introspector.decapitalize(clazz.getSimpleName());
                                }

                                //定义bean对象的属性
                                BeanDefinition beanDefinition = new BeanDefinition();
                                //设置bean的类型
                                beanDefinition.setType(clazz);
                                //设置是多例,还是单例
                                if (clazz.isAnnotationPresent(Scope.class)) {
                                    Scope scope = clazz.getAnnotation(Scope.class);
                                    //将获取到的scope类型,设置到bean属性中
                                    beanDefinition.setScope(scope.value());
                                } else {
                                    //默认生成单例bean
                                    beanDefinition.setScope("singleton");
                                }
                                beanMap.put(beanName, beanDefinition);
                            }


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

            }


        }
        //第二部:实例化bean;将单例bean放入单例bean池中;
        //遍历beanMap集合
        for (String beanName : beanMap.keySet()) {
            BeanDefinition beanDefinition = beanMap.get(beanName);
            //判断是否是单例bean
            if (beanDefinition.getScope().equals("singleton")) {
                //直接创建单例bean(因为只需要创建一次,则在构造方法中就创建出来)
                Object bean = creatBean(beanName, beanDefinition);

                //将单例bean放入单例bean池中
                singletonMap.put(beanName, bean);
            }
        }

    }

    private Object creatBean(String beanName, BeanDefinition beanDefinition) {
        Class clazz = beanDefinition.getType();


        try {
            Object instance = clazz.getConstructor().newInstance();

            //依赖注入(简单版--依靠名字)
            //遍历类的属性值,判断是否有需要 注入bean 的属性值,如果有需要的,则注入
            for (Field field : clazz.getDeclaredFields()) {//getDeclaredFields():获得某个类的所有声明的字段,即包括public、private和proteced,但是不包括父类的申明字段。
                if (field.isAnnotationPresent(Autowired.class)) {
                    //值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查
                    field.setAccessible(true);
                    //将clazz类的属性,设置为对应的bean对象,即使用属性名字来获取bean对象;
                    field.set(instance,getBean(field.getName()));
                }
            }
            return instance;

        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return null;
    }

    /*
     * 从容器中获取bean对象
     */
    public Object getBean(String beanName) {

        //通过beanMap,输入bean的名字,获取对应的bean属性
        BeanDefinition beanDefinition = beanMap.get(beanName);

        if (beanDefinition == null) {
            throw new RuntimeException();
        } else {
            //beanMap中存在该beanName
            String scope = beanDefinition.getScope();
            //如果是单例bean,则从单例bean池中获取
            if (scope.equals("singleton")) {
                Object bean = singletonMap.get(beanName);
                //当前bean内包含
                if (bean == null) {
                    bean = creatBean(beanName, beanDefinition);
                    singletonMap.put(beanName, bean);
                }
                return bean;
            } else {
                //多例(只考虑单例和多例)
                //创建一个bean对象,并返回
                return creatBean(beanName, beanDefinition);
            }
        }
    }
}

相关注解

@component

将类标记为 bean对象,可以被扫描到,并将该类添加到spring容器中

  • 属性
    • bean的名字;如果未指定,默认为类名首字母小写;对应代码在spring容器中构造方法中实现;使用了Introspector.decapitalize(clazz.getSimpleName()); 
package com.aguang.spring;

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

/*
* 将类声明为bean对象,即需要装配到spring容器中
*/
@Retention(RetentionPolicy.RUNTIME)  //注解的生效时间;RetentionPolicy.RUNTIME —> 运行时生效
@Target(ElementType.TYPE)  //该ComponentScan注解;只能写在什么位置,ElementType.TYPE -> 只能写在类上
public @interface Component {

    //注解的属性:指定bean的名字,默认值为 ""
    String value() default "";
}

@componentScan

扫描对应路径下的文件,装配到spring容器中

  • 属性
    • 路径:spring容器会根据该路径找到对应的 .class文件的路径,进行bean对象的装配
package com.aguang.spring;

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

/*
* 从扫描路径扫描 需要装配 的类,自动装配到spring容器中
*/
@Retention(RetentionPolicy.RUNTIME)  //注解的生效时间;RetentionPolicy.RUNTIME —> 运行时生效
@Target(ElementType.TYPE)  //该ComponentScan注解;只能写在什么位置,ElementType.TYPE -> 只能写在类上
public @interface ComponentScan {

    //注解的属性:指定扫描路径,默认值为 ""
    String value() default "";
}

@Scope

对bean对象是单例还是多例,进行控制

  • 属性
    • 类型:多例 / 单例 ;spring容器会对单例bean在构造方法中就 加入到单例bean池中
package com.aguang.spring;

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

/*
* 将类声明为bean对象,即需要装配到spring容器中
*/
@Retention(RetentionPolicy.RUNTIME)  //注解的生效时间;RetentionPolicy.RUNTIME —> 运行时生效
@Target(ElementType.TYPE)  //该ComponentScan注解;只能写在什么位置,ElementType.TYPE -> 只能写在类上
public @interface Scope {

    //注解的属性:指定是bean的类型, ;默认值为 ""
    //多例bean ---->  prototype
    //单例bean ---->  singleton
    String value() default "";
}

@Autowired

作用在属性值上,将该属性的bean对象注入,

  • 在spring容器的creatBean方法中,实现了简单的依赖注入功能;通过反射判断当前类中的属性值是否有被@Autowired注解标记;如果有则通过getBean方法通过 属性值的名称注入;
package com.aguang.spring;

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

/*
* 将类声明为bean对象,即需要装配到spring容器中
*/
@Retention(RetentionPolicy.RUNTIME)  //注解的生效时间;RetentionPolicy.RUNTIME —> 运行时生效
@Target(ElementType.FIELD)  //该ComponentScan注解;只能写在什么位置,ElementType.FIELD -> 写在属性值上
public @interface Autowired {

}
  • createBean中的部分代码
Class clazz = beanDefinition.getType();

        try {
            Object instance = clazz.getConstructor().newInstance();

            //依赖注入(简单版--依靠名字)
            //遍历类的属性值,判断是否有需要 注入bean 的属性值,如果有需要的,则注入
            for (Field field : clazz.getDeclaredFields()) {//getDeclaredFields():获得某个类的所有声明的字段,即包括public、private和proteced,但是不包括父类的申明字段。
                if (field.isAnnotationPresent(Autowired.class)) {
                    //值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查
                    field.setAccessible(true);
                    //将clazz类的属性,设置为对应的bean对象,即使用属性名字来获取bean对象;
                    field.set(instance,getBean(field.getName()));
                }
            }
            return instance;

        } 

测试方法

Test类

  • 通过 Appconfig类 输入扫描路径 ,创建一个spring容器
package com.aguang.service;

import com.aguang.spring.AguangApplicationContext;

/*
* 测试类
*/
public class Test {
    public static void main(String[] args) {

        //创建了spring容器;使用了config的方式
        AguangApplicationContext applicationContext = new AguangApplicationContext(AppConfig.class);

        //使用容器中的 getBean 方法 传入bean对象名字,获取一个bean对象(类型由该名字对应的对象决定);
        /*
        System.out.println(applicationContext.getBean("orderService"));
        System.out.println(applicationContext.getBean("userService"));
        System.out.println(applicationContext.getBean("userService"));*/
        UserService userService = (UserService) applicationContext.getBean("userService");
        System.out.println(userService);
        /*OrderService orderService = (OrderService) applicationContext.getBean("orderService");
        System.out.println(orderService);*/

        //userService.test();
    }
}

Appconfig类

定义一个扫描配置类

package com.aguang.service;

import com.aguang.spring.ComponentScan;

/*
* 配置类
* 扫描的功能:ComponentScan,通过扫描路径,将需要装配的 类,自动装配到 spring 容器中
*/

//扫描com.aguang.service路径下的类
@ComponentScan("com.aguang.service")
public class AppConfig {
}

UserService类

package com.aguang.service;

/*
* Bean对象(默认单例bean)
* 对于多例bean,使用到才创建
*/

import com.aguang.spring.Autowired;
import com.aguang.spring.Component;
import com.aguang.spring.Scope;

//通过Component注解声明的bean对象(名字为userService)
@Component("userService")
//@Scope("prototype")
public class UserService {

    @Autowired
    private OrderService orderService;

    public void test(){
        System.out.println(orderService);
    }
}

OrderService类

package com.aguang.service;

import com.aguang.spring.Autowired;
import com.aguang.spring.Component;

@Component
public class OrderService {

    //@Autowired
    private UserService userService;

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值