简单手写Spring

Spring总体功能为扫描类文件,构成BeanDefinition,然后生成代理对象,对这些对象进行管理。

表面上看:

从日常spring使用上可以看出,

首先需要一个启动容器类:生成容器类对象是会需要一个参数(配置类或者配置文件),然后有个常用方法getBean();

然后是一些常用的注解:

@ComponentScan:根据定义的扫描路径,把符合扫描规则的类装配到spring容器

@Component:用于标记一个类为组件

@Scope:表明类的作用域,如singleton(单例)、prototype(多例);Web 作用域(reqeust、session、globalsession),自定义作用域。

@AutoWired:对类成员变量、方法及构造函数进行标注,让 spring 完成 bean 自动装配的工作

所以可先准备这些类。

具体步骤:

1、先创建一个容器类,包含一个参数,表示配置类,一个方法getBean();

public class ApplicationContext {

    private Class configClass;

    public ApplicationContext(Class configClass) {

        this.configClass = configClass;

    }

    public Object getBean(String beanName){

       return null;

    }
}

2、然后是注解的声明,简单写表示意思即可

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ComponentScan {
    //扫描路径
    String value() default "";
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Component {
    //bean取名字用
    String value() default "";
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Scope {
    //bean作用域
    String value() default "";
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Autowired {

}

3、spring启动后会进行扫描,会根据配置的扫描路径,将符合扫描规则的类装配到spring容器中,因此,spring容器的构造方法中会进行扫描。我这里是配置类上加@ComponentScan("com.xxx.ccc"),所以如下这样写。

public class ApplicationContext {

    private Class configClass;

    public ApplicationContext(Class configClass) {

        this.configClass = configClass;

        //扫描 
        //扫描路径是java文件对应的class文件的路径,不是配置类上的声明的
        if(configClass.isAnnotationPresent(ComponentScan.class)){ //判断是否有..注解

            ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
            String path = componentScanAnnotation.value(); //这个时候还不是扫描路径,只是注解上配的java文件包路径 com.xxx.ccc

            //扫描路径在编译路径-classpath里可找到,所以这里做字符串转换
            path = path.replace(".", "/");

            ClassLoader classLoader = ApplicationContext.class.getClassLoader();
            URL resource = classLoader.getResource(path); // 获取class所在文件资源file:/E:/Project/xxxSpring/out/production/xxxSpring/com/xxx/service

            File file = new File(resource.getFile()); //E:\Project\xxxSpring\out\production\xxxSpring\com\xxx\service
            if(file.isDirectory()){
                File[] files = file.listFiles();
                for (File f : files) {
                    String fileName = f.getAbsolutePath();//长这样 E:\Project\xxxSpring\out\production\xxxSpring\com\xxx\service\AppConfig.class

                    if (fileName.endsWith(".class")) {

                        String className = fileName.substring(fileName.indexOf("com"),fileName.indexOf(".class"));
                        className = className.replace("\\", ".");

                        try {
                            //判断是不是一个bean  -->看是不是有Component注解
                            Class<?> aClass = classLoader.loadClass(className); //参数需要这样的  com.xxx.service.UserService,所以做上边的字符串转换

                            if (aClass.isAnnotationPresent(Component.class)) {
                            
                            }

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

        }


    }

    public Object getBean(String beanName){

       return null;

    }
}

 4、扫描完成后,就该生成对象了。但是考虑到spring中bean的作用域问题,以及在getBean()中不可能根据bean名字就知道从哪里拿bean对象的问题,所以下一步不是直接创建对象,而是创建包含类信息的对象。这时@Scope注解和BeanDefinition类就出现了,BeanDefinition:类的定义

/**
*至少两个参数吧
*/
public class BeanDefinition {

    private Class type;    //bean是哪个类的
    private String scope;  //bean作用域

    public Class getType() {
        return type;
    }

    public void setType(Class type) {
        this.type = type;
    }

    public String getScope() {
        return scope;
    }

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

因此继续补充代码,ApplicationContext中新增参数Map类型变量beanDefinitionMap,key为bean的名字,value为BeanDefinition对象,存储BeanDefinition的信息。这之后我们在getBean()时,就可以通过这个map来获取bean的信息,从而可以知道从里获取bean对象。

public class ApplicationContext {

    private Class configClass;

    public ApplicationContext(Class configClass) {

        this.configClass = configClass;

        private ConcurrentHashMap<String ,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

        //扫描 
        //扫描路径是java文件对应的class文件的路径,不是配置类上的声明的
        if(configClass.isAnnotationPresent(ComponentScan.class)){ //判断是否有..注解

            ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
            String path = componentScanAnnotation.value(); //这个时候还不是扫描路径,只是注解上配的java文件包路径 com.xxx.ccc

            //扫描路径在编译路径-classpath里可找到,所以这里做字符串转换
            path = path.replace(".", "/");

            ClassLoader classLoader = ApplicationContext.class.getClassLoader();
            URL resource = classLoader.getResource(path); // 获取class所在文件资源file:/E:/Project/xxxSpring/out/production/xxxSpring/com/xxx/service

            File file = new File(resource.getFile()); //E:\Project\xxxSpring\out\production\xxxSpring\com\xxx\service
            if(file.isDirectory()){
                File[] files = file.listFiles();
                for (File f : files) {
                    String fileName = f.getAbsolutePath();//长这样 E:\Project\xxxSpring\out\production\xxxSpring\com\xxx\service\AppConfig.class

                    if (fileName.endsWith(".class")) {

                        String className = fileName.substring(fileName.indexOf("com"),fileName.indexOf(".class"));
                        className = className.replace("\\", ".");

                        try {
                            //判断是不是一个bean  -->看是不是有Component注解
                            Class<?> aClass = classLoader.loadClass(className); //参数需要这样的  com.xxx.service.UserService,所以做上边的字符串转换

                            if (aClass.isAnnotationPresent(Component.class)) {
                                 Component component = aClass.getAnnotation(Component.class);
                                String beanName = component.value();

                                if(beanName.equals("")){
                                    beanName = Introspector.decapitalize(aClass.getSimpleName()); //将类名首字母小写
                                }

                                BeanDefinition beanDefinition = new BeanDefinition();
                                beanDefinition.setType(aClass);

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

                                beanDefinitionMap.put(beanName,beanDefinition);
                            }

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

        }


    }

    public Object getBean(String beanName){

        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);

        if (beanDefinition == null) {
            throw new NullPointerException();
        } else {
            String scope = beanDefinition.getScope();

            if (scope.equals("singleton")) {
               
            } else {
                //多例
                
            }
        }
       return null;

    }
}

5、这时就可以开始创建bean对象了。补充代码

ApplicationContext中新增参数Map类型变量singleObjects,key为bean的名字,value为bean对象

新增方法 createBean(String beanName , BeanDefinition beanDefinition) 用于专门创建bean。

补充getBean代码

public class ApplicationContext {

    private Class configClass;

    public ApplicationContext(Class configClass) {

        this.configClass = configClass;

        private ConcurrentHashMap<String ,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
        private ConcurrentHashMap<String ,Object> singleObjects = new ConcurrentHashMap<>();

        //扫描 
        //扫描路径是java文件对应的class文件的路径,不是配置类上的声明的
        if(configClass.isAnnotationPresent(ComponentScan.class)){ //判断是否有..注解

            ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
            String path = componentScanAnnotation.value(); //这个时候还不是扫描路径,只是注解上配的java文件包路径 com.xxx.ccc

            //扫描路径在编译路径-classpath里可找到,所以这里做字符串转换
            path = path.replace(".", "/");

            ClassLoader classLoader = ApplicationContext.class.getClassLoader();
            URL resource = classLoader.getResource(path); // 获取class所在文件资源file:/E:/Project/xxxSpring/out/production/xxxSpring/com/xxx/service

            File file = new File(resource.getFile()); //E:\Project\xxxSpring\out\production\xxxSpring\com\xxx\service
            if(file.isDirectory()){
                File[] files = file.listFiles();
                for (File f : files) {
                    String fileName = f.getAbsolutePath();//长这样 E:\Project\xxxSpring\out\production\xxxSpring\com\xxx\service\AppConfig.class

                    if (fileName.endsWith(".class")) {

                        String className = fileName.substring(fileName.indexOf("com"),fileName.indexOf(".class"));
                        className = className.replace("\\", ".");

                        try {
                            //判断是不是一个bean  -->看是不是有Component注解
                            Class<?> aClass = classLoader.loadClass(className); //参数需要这样的  com.xxx.service.UserService,所以做上边的字符串转换

                            if (aClass.isAnnotationPresent(Component.class)) {
                                 Component component = aClass.getAnnotation(Component.class);
                                String beanName = component.value();

                                if(beanName.equals("")){
                                    beanName = Introspector.decapitalize(aClass.getSimpleName()); //将类名首字母小写
                                }

                                BeanDefinition beanDefinition = new BeanDefinition();
                                beanDefinition.setType(aClass);

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

                                beanDefinitionMap.put(beanName,beanDefinition);
                            }

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

        }
    //实例化单例bean
        for (String beanName : beanDefinitionMap.keySet()) {

            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);

            if (beanDefinition.getScope().equals("singleton")) {
                Object bean = createBean(beanName, beanDefinition);
                singleObjects.put(beanName,bean);
            }
        }


    }

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

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

            return instance;

        } 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){

        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);

        if (beanDefinition == null) {
            throw new NullPointerException();
        } else {
            String scope = beanDefinition.getScope();

           if (scope.equals("singleton")) {
                Object bean = singleObjects.get(beanName);
                if (bean == null) {
                    bean  = createBean(beanName, beanDefinition);
                    singleObjects.put(beanName,bean);
                }
                return bean;
            } else {
                //多例
                return createBean(beanName,beanDefinition);
            }
        }

    }
}

6、完善createBean()方法。

spring在创建bean时,有些变量加了@AutoWired,就需要依赖注入。

spring还提供了回调的功能,用于比如说告诉对象他自己的beanName。

        回调功能需要使用Aware接口,需要回调功能的类需要实现Aware接口

spring还提供了初始化功能,用于比如说在创建时查询数据库信息给变量赋值。

        初始化功能需要用的Initial接口,需要初始化功能的类需要实现Initial接口。

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

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

             //简单版依赖注入
            for (Field f : clazz.getDeclaredFields()) {
                if (f.isAnnotationPresent(Autowired.class)) {
                    f.setAccessible(true);
                    f.set(instance,getBean(f.getName()));
                }
            }

            //Aware回调
            if (instance instanceof BeanNameAware) {
                ((BeanNameAware) instance).setBeanName(beanName);
            }

            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                instance = beanPostProcessor.postProcessBeforeInitialization(beanName,instance);
            }

            //初始化
            if (instance instanceof InitializingBean) {
                ((InitializingBean) instance).afterPropertiesSet();
            }

            //初始化后 AOP
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                instance = beanPostProcessor.postProcessAfterInitialization(beanName,instance);
            }

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

7、spring还有一个重要内容是AOP。这个部分在bean初始化后进行。

这里spring提供了一个接口 BeanPostProcessor,其中包含方法

public Object postProcessBeforeInitialization(String beanName,Object bean);
public Object postProcessAfterInitialization(String beanName,Object bean);

分别在bean初始化前后对生成的bean进行操作,初始化后的操作方法中可对bean进行aop。

BeanPostProcessor接口声明

public interface BeanPostProcessor {

    public Object postProcessBeforeInitialization(String beanName,Object bean);
    public Object postProcessAfterInitialization(String beanName,Object bean);
}

创建类实现BeanPostProcessor。

@Component
public class xxxBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(String beanName, Object bean) {
        System.out.println("初始化前");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(String beanName, Object bean) {
        System.out.println("初始化后");

        if (beanName.equals("userService")) {
            //AOP生成代理对象
            Object proxyInstance = Proxy.newProxyInstance(LskApplicationContext.class.getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    System.out.println("代理逻辑--切面逻辑");
                    return method.invoke(bean,args);
                }
            });

            return proxyInstance;
        }

        return bean;
    }
}

修改容器类的扫描和bean生成代码。

新增数组专门存放 BeanPostProcessor 派生类

private ArrayList<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();

扫描时就把扫描到的 BeanPostProcessor 派生类加入beanPostProcessorList

创建bean时,遍历beanPostProcessorList,对相应的类进行AOP生成代理对象

public class ApplicationContext {

    private Class configClass;

    public ApplicationContext(Class configClass) {

        this.configClass = configClass;

        private ConcurrentHashMap<String ,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
        private ConcurrentHashMap<String ,Object> singleObjects = new ConcurrentHashMap<>();
        private ArrayList<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();

        //扫描 
        //扫描路径是java文件对应的class文件的路径,不是配置类上的声明的
        if(configClass.isAnnotationPresent(ComponentScan.class)){ //判断是否有..注解

            ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
            String path = componentScanAnnotation.value(); //这个时候还不是扫描路径,只是注解上配的java文件包路径 com.xxx.ccc

            //扫描路径在编译路径-classpath里可找到,所以这里做字符串转换
            path = path.replace(".", "/");

            ClassLoader classLoader = ApplicationContext.class.getClassLoader();
            URL resource = classLoader.getResource(path); // 获取class所在文件资源file:/E:/Project/xxxSpring/out/production/xxxSpring/com/xxx/service

            File file = new File(resource.getFile()); //E:\Project\xxxSpring\out\production\xxxSpring\com\xxx\service
            if(file.isDirectory()){
                File[] files = file.listFiles();
                for (File f : files) {
                    String fileName = f.getAbsolutePath();//长这样 E:\Project\xxxSpring\out\production\xxxSpring\com\xxx\service\AppConfig.class

                    if (fileName.endsWith(".class")) {

                        String className = fileName.substring(fileName.indexOf("com"),fileName.indexOf(".class"));
                        className = className.replace("\\", ".");

                        try {
                            //判断是不是一个bean  -->看是不是有Component注解
                            Class<?> aClass = classLoader.loadClass(className); //参数需要这样的  com.xxx.service.UserService,所以做上边的字符串转换

                            if (aClass.isAnnotationPresent(Component.class)) {

                                 if (BeanPostProcessor.class.isAssignableFrom(aClass)) { //判断类是否是由前者派生的
                                    BeanPostProcessor instance = (BeanPostProcessor) aClass.newInstance();
                                    beanPostProcessorList.add(instance);
                                }

                                 Component component = aClass.getAnnotation(Component.class);
                                String beanName = component.value();

                                if(beanName.equals("")){
                                    beanName = Introspector.decapitalize(aClass.getSimpleName()); //将类名首字母小写
                                }

                                BeanDefinition beanDefinition = new BeanDefinition();
                                beanDefinition.setType(aClass);

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

                                beanDefinitionMap.put(beanName,beanDefinition);
                            }

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

        }
    //实例化单例bean
        for (String beanName : beanDefinitionMap.keySet()) {

            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);

            if (beanDefinition.getScope().equals("singleton")) {
                Object bean = createBean(beanName, beanDefinition);
                singleObjects.put(beanName,bean);
            }
        }


    }

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

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

            //简单版依赖注入
            for (Field f : clazz.getDeclaredFields()) {
                if (f.isAnnotationPresent(Autowired.class)) {
                    f.setAccessible(true);
                    f.set(instance,getBean(f.getName()));
                }
            }

            //Aware回调
            if (instance instanceof BeanNameAware) {
                ((BeanNameAware) instance).setBeanName(beanName);
            }

            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                instance = beanPostProcessor.postProcessBeforeInitialization(beanName,instance);
            }

            //初始化
            if (instance instanceof InitializingBean) {
                ((InitializingBean) instance).afterPropertiesSet();
            }

            //初始化后 AOP
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                instance = beanPostProcessor.postProcessAfterInitialization(beanName,instance);
            }

            return instance;

        } 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){

        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);

        if (beanDefinition == null) {
            throw new NullPointerException();
        } else {
            String scope = beanDefinition.getScope();

           if (scope.equals("singleton")) {
                Object bean = singleObjects.get(beanName);
                if (bean == null) {
                    bean  = createBean(beanName, beanDefinition);
                    singleObjects.put(beanName,bean);
                }
                return bean;
            } else {
                //多例
                return createBean(beanName,beanDefinition);
            }
        }

    }
}

到此,基本就模拟结束spring的基本功能。

我是跟着这个视频细看敲打了一遍。

【这是我见过最好的Spring高级底层原理源码教程,大家觉得呢?(2022最新版)】 https://www.bilibili.com/video/BV1tR4y1F75R/?p=7&share_source=copy_web&vd_source=800dd7f74ab854b348d03ba8a1c229cd

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值