Simple implementation of Spring container (2) Encapsulate BeanDefinition object and put it into Map

Stage 2:

 // 1.编写自己的Spring容器,实现扫描包,得到bean的class对象.
 2.扫描将 bean 信息封装到 BeanDefinition对象,并放入到Map.

Idea:

1. Encapsulate the bean information into the BeanDefinition object, and then put it into the BeanDefinitionMap collection. The structure of the collection is probably
key[beanName]–value[beanDefintion]< /span> value--- ---->Corresponding to the encapsulated BeanDefintion object
key---------> corresponds to the specified name. If not specified, the first letter of the class will be lowercase as its name.

2. Because the scope of a bean may be a singleton or a prototype, Spring needs to scan the bean information and save it to the collection, so that when getBean() processes it according to the actual situation.

Implementation

1. Add a custom Scope annotation

package com.elf.spring.annotation;

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

/**
 * @author 45~
 * @version 1.0
 * Scope 可以指定一个Bean的作用范围[singleton,prototype]
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Scope {
    
    
    //通过value可以指定singleton,prototype
    String value() default "";
}

2. Add the @Scope multi-instance annotation to MonsterService.java

package com.elf.spring.component;
import com.elf.spring.annotation.Component;
import com.elf.spring.annotation.Scope;

/**
 * @author 45~
 * @version 1.0
 * 说明 MonsterService 是一个Servic
 */
@Component //把MonsterService注入到我们自己的spring容器中
@Scope(value = "prototype")
public class MonsterService {
    
    

}

3. Prepare to write a BeanDefinition.java under the ioc package to encapsulate and record Bean information.

package com.elf.spring.ioc;

/**
 * @author 45~
 * @version 1.0
 * BeanDefinition 用于封装和记录Bean的信息 [1.scope 2.存放bean对应的Class对象,反射可以生成对应的对象]
 *  2:因为将来getBean()时有可能是多实例,有可能是动态生成的,还要存放bean的class对象
 */
public class BeanDefinition {
    
    
    private String scope;
    private Class clazz;//存放bean的class对象

    public String getScope() {
    
    
        return scope;
    }

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

    public Class getClazz() {
    
    
        return clazz;
    }

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

    @Override
    public String toString() {
    
    
        return "BeanDefinition{" +
                "scope='" + scope + '\'' +
                ", clazz=" + clazz +
                '}';
    }
}

3. The pom.xml file introduces the tool class commons-lang under the jar package to complete the function of lowercase initial letters. Instead of using the StringUtils tool class that comes with springframework
Insert image description here

<?xml version="1.0" encoding="UTF-8"?>
<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.0</modelVersion>

    <groupId>com.elf</groupId>
    <artifactId>elf-myspring1207</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
        </dependency>
    </dependencies>

</project>

4. Container file, extract the method in the constructor and encapsulate it into a method, which can be called directly in the constructor to make the code concise.
Here the BeanDefinition object is generated and placed Enter the Map

添加内容1:
//定义属性BeanDefinitionMap -> 存放BeanDefinition对象(多例对象)
    private ConcurrentHashMap<String,BeanDefinition> beanDefinitionMap =
            new ConcurrentHashMap<>();
    //定义属性SingletonObjects -> 存放单例对象 (跟原生容器的名字保持一致)
    //因为将来存放单例池的时候肯定要指定单例对象是对应哪个Bean的,所以k用String来充当
    //存放单例对象的类型是不确定的,可能是Dog,Cat,或者其他的对象,所以用Object
    private ConcurrentHashMap<String,Object> singletonObjects =
            new ConcurrentHashMap<>();
添加内容2:
//先得到beanName(有可能通过经典4注解,例如Component注解的value值来指定)
                            //1.得到类上的Component注解,此时的clazz已经是当前bean的class对象,通过类加载器得到的 反射知识
                            Component componentAnnotation = cla.getDeclaredAnnotation(Component.class);
                            //2.得到配置的value
                            String beanName = componentAnnotation.value();
                            if("".equals(beanName)){
    
    //如果没有写value,空串
                                //将该类的类名首字母小写作为beanName
                                //StringUtils其实是在springframework的框架下面的类,而这里本身我就是要自己写所以不用

                                beanName = StringUtils.uncapitalize(className);
                            }
                            //3.将Bean的信息封装到BeanDefinition对象中,然后将其放入到BeanDefinitionMap集合中
                            BeanDefinition beanDefinition = new BeanDefinition();
                            //!!!多看看这里多理解
                            beanDefinition.setClazz(cla);//由类加载器通过反射得到对象,Class<?> cla = classLoader.loadClass(classFullName);
                            //4.获取Scope值
                            if (cla.isAnnotationPresent(Scope.class)){
    
    
                                //如果配置了Scope,就获取它配置的值
                                Scope scopeAnnotatiion = cla.getDeclaredAnnotation(Scope.class);
                                beanDefinition.setScope(scopeAnnotatiion.value());
                            }else{
    
    
                                //如果没有配置Scope,就以默认的值singleton
                                beanDefinition.setScope("singleton");
                            }
                            //将beanDefinitionMap对象放入Map
                            beanDefinitionMap.put(beanName,beanDefinition);
                        }else {
    
    
                            //如果该类没有使用了@Component注解,说明是一个Spring bean
                            System.out.println("这不是一个Spring bean" + cla + " 类名=" + className);
                        }

container file

package com.elf.spring.ioc;

import com.elf.spring.annotation.*;
import org.apache.commons.lang.StringUtils;

import java.io.File;
import java.net.URL;
import java.util.concurrent.ConcurrentHashMap;


/**
 * @author 45~
 * @version 1.0
 */
public class ElfSpringApplicationContext {
    
    
    //第一步,扫描包,得到bean的class对象,排除包下不是bean的,因此还没有放到容器中
    //因为现在写的spring容器比原先的基于注解的,要更加完善,所以不会直接把它放在ConcurrentHashMap
    private Class configClass;

    //定义属性BeanDefinitionMap -> 存放BeanDefinition对象
    private ConcurrentHashMap<String,BeanDefinition> beanDefinitionMap =
            new ConcurrentHashMap<>();
    //定义属性SingletonObjects -> 存放单例对象 (跟原生容器的名字保持一致)
    //因为将来存放单例池的时候肯定要指定单例对象是对应哪个Bean的,所以k用String来充当
    //存放单例对象的类型是不确定的,可能是Dog,Cat,或者其他的对象,所以用Object
    private ConcurrentHashMap<String,Object> singletonObjects =
            new ConcurrentHashMap<>();

    //构造器
    public ElfSpringApplicationContext(Class configClass) {
    
    
        beanDefinitionScan(configClass);//调用封装方法,简洁
        System.out.println("beanDefinitionMap=" + beanDefinitionMap);

    }//构造器结束
    //该方法完成对指定包的扫描,并将Bean信息封装到BeanDefinition对象,再放入到Map中
    public void beanDefinitionScan(Class configClass){
    
    
        this.configClass = configClass;

        /**获取要扫描的包:
         1.先得到ElfSpringConfig配置的 @ComponentScan(value= "com.elf.spring.component")
         2.通过 @ComponentScan的value => 即要扫描的包 **/
        ComponentScan componentScan =
                (ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);
        String path = componentScan.value();
        System.out.println("要扫描的包path=" + path);

        /**
         * 得到要扫描包下的所有资源(类.class)
         * 1.得到类的加载器 -> APP类加载器是可以拿到 target目录下的classes所有文件的
         * 2.通过类的加载器获取到要扫描的包的资源url => 类似一个路径
         * 3.将要加载的资源(.class)路径下的文件进行遍历 => io
         */
        ClassLoader classLoader = ElfSpringApplicationContext.class.getClassLoader();
        path = path.replace(".", "/"); // 把.替换成 /
        URL resource = classLoader.getResource(path);
        System.out.println("resource=" + resource);

        File file = new File(resource.getFile());
        if (file.isDirectory()) {
    
    
            File[] files = file.listFiles();
            for (File f : files) {
    
     //把所有的文件都取出来
                System.out.println("============================");
                System.out.println("f.getAbsolutePath()=" + f.getAbsolutePath());
                String fileAbsolutePath = f.getAbsolutePath();//到target的classes目录下了

                //这里只处理.class文件
                if (fileAbsolutePath.endsWith(".class")) {
    
    
                    //1.获取类名
                    String className = fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1,
                            fileAbsolutePath.indexOf(".class"));
                    //2.获取类的完整路径(全类名)
                    String classFullName = path.replace("/", ".") + "." + className;
                    System.out.println("classFullName=" + classFullName);
                    //3.判断该类是否需要注入,就看是不是有注解@Component @Service @Contoller @Re....
                    try {
    
    
                        Class<?> cla = classLoader.loadClass(classFullName);
                        if (cla.isAnnotationPresent(Component.class) ||
                                cla.isAnnotationPresent(Controller.class) ||
                                cla.isAnnotationPresent(Service.class) ||
                                cla.isAnnotationPresent(Repository.class)) {
    
    
                            //演示机制
                            //如果该类使用了@Component注解,说明是一个Spring bean
                            System.out.println("这是一个Spring bean=" + cla + " 类名=" + className);

                            //先得到beanName(有可能通过经典4注解,例如Component注解的value值来指定)
                            //1.得到类上的Component注解,此时的clazz已经是当前bean的class对象,通过类加载器得到的 反射知识
                            Component componentAnnotation = cla.getDeclaredAnnotation(Component.class);
                            //2.得到配置的value
                            String beanName = componentAnnotation.value();
                            if("".equals(beanName)){
    
    //如果没有写value,空串
                                //将该类的类名首字母小写作为beanName
                                //StringUtils其实是在springframework的框架下面的类,而这里本身我就是要自己写所以不用

                                beanName = StringUtils.uncapitalize(className);
                            }
                            //3.将Bean的信息封装到BeanDefinition对象中,然后将其放入到BeanDefinitionMap集合中
                            BeanDefinition beanDefinition = new BeanDefinition();
                            //!!!多看看这里多理解
                            beanDefinition.setClazz(cla);//由类加载器通过反射得到对象,Class<?> cla = classLoader.loadClass(classFullName);
                            //4.获取Scope值
                            if (cla.isAnnotationPresent(Scope.class)){
    
    
                                //如果配置了Scope,就获取它配置的值
                                Scope scopeAnnotatiion = cla.getDeclaredAnnotation(Scope.class);
                                beanDefinition.setScope(scopeAnnotatiion.value());
                            }else{
    
    
                                //如果没有配置Scope,就以默认的值singleton
                                beanDefinition.setScope("singleton");
                            }
                            //将beanDefinitionMap对象放入Map
                            beanDefinitionMap.put(beanName,beanDefinition);
                        }else {
    
    
                            //如果该类没有使用了@Component注解,说明是一个Spring bean
                            System.out.println("这不是一个Spring bean" + cla + " 类名=" + className);
                        }
                    } catch (Exception e) {
    
    
                        e.printStackTrace();
                    }
                }
            }//遍历文件for循环结束
            System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
        }
    }

    //编写放法返回容器中的对象
    public Object getBean(String name) {
    
    
        return null;
    }
}

operation result

beanDefinitionMap={
monsterService=BeanDefinition{scope=‘prototype’, clazz=class com.elf.spring.component.MonsterService},

monsterDao=BeanDefinition{scope='singleton', clazz=class com.elf.spring.component.MonsterDao}
}
ok a>
Insert image description here
There is a problem here: singleton and multiple instance objects are placed in beanDefinitionMap, and there is no singleton object in singletonObjects.

Guess you like

Origin blog.csdn.net/weixin_45036508/article/details/134862837