Code source manuscrit de Spring (version simplifiée)

la navigation: 

[Notes Java + Résumé de Stepping on the Pit] Bases Java + Avancé + JavaWeb + SSM + SpringBoot + St. Regis Takeaway + SpringCloud + Dark Horse Tourism + Guli Mall + Xuecheng Online + Chapitre avancé MySQL + Mode conception + Questions d'entretien Nioke

Code : https://wwmg.lanzouk.com/ictoK135ye2f

Table des matières

1. Préparation

Répertoire du projet 1.0

1.1 Cours dans le cadre du forfait Printemps

1.1.1 Classe de conteneur

1.1.2 Annotations @ComponentScan

1.1.3 Annotations @Component

1.1.4 Annotation @Autowired

1.1.5 Annotations @Scope

1.1.6 Classe de déclaration du bean : BeanDefinition

1.1.7 Interface de rappel du nom du bean : BeanNameAware

1.1.8 Interface d'initialisation : InitializingBean

1.1.9 Interface du post-processeur Bean : BeanPostProcessor 

1.2 Classes liées aux activités des utilisateurs  

1.2.1 Conteneur de création de classe de test

1.2.2 Classe de configurationAppConfig

1.2.3 Service utilisateur

1.2.4 OrderService, pour l'injection de dépendances 

1.2.5 Classe d'implémentation de l'interface du post-processeur Bean : MyBeanPostProcessor

2. Améliorer la classe conteneur : VinceApplicationContext

2.0 Créer un processus de bean

2.1 Mise en œuvre du code

2.1.1 Variables membres 

2.1.2 Méthode de construction, le paramètre est la classe de configuration

2.1.3 Méthode createBean() 

2.1.4 Méthode getBean()

3. Tous les codes


1. Préparation

Répertoire du projet 1.0

1.1 Cours dans le cadre du forfait Printemps

Cours sous le package com.vince.spring :

1.1.1 Classe de conteneur

 Par rapport à la manière de créer un conteneur Spring, ce que nous devons simuler est la classe AnnotationConfigApplicationContext.

  • ClassPathXmlApplicationContext : Il est nécessaire de transmettre un fichier de configuration XML pour indiquer à Spring de créer un conteneur Spring ApplicationContext selon la configuration spécifiée. Après la création, vous pouvez obtenir l'objet bean correspondant du conteneur via getBean.
  • AnnotationConfigApplicationContext : C'est aussi un conteneur, mais il est transmis sous la forme d' une classe de configuration Java . Vous pouvez ajouter @ComponentScan à la classe pour définir le chemin qui doit être analysé sous Spring, etc. Vous pouvez également ajouter un Bean au conteneur via la méthode @Bean (égale à la balise bean) et via cette annotation
public class Test {
	public static void main(String[] args) {
		ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring.xml");
		classPathXmlApplicationContext.getBean("user");

		AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
		
		SpringCodeApplicationContext applicationContext = new SpringCodeApplicationContext(AppConfig.class);
	}
}

En nous référant au framework Spring, nous pouvons voir que cette classe conteneur doit avoir une méthode de construction, des paramètres entrants et getBean()des méthodes.

paquet com.vince.spring; 

//模拟AnnotationConfigApplicationContext
public class VinceApplicationContext{

    private Class configClass;
//构造方法,参数是配置类的字节码文件
	public SpringCodeApplicationContext(Class configClass) {
        //1.赋值配置类成员变量
    	this.configClass = configClass;

		// 2.解析配置类
        // 通过反射,判断配置类字节码有没有注解@ComponentScan
		// 如果有,就根据@ComponentScan传入的扫描路径,扫描那个包下的Bean

        // 3.实例化bean

    }
//容器里拥有getBean()方法,获取Bean
    public Object getBean(String beanName){
		return null;
	}

1.1.2 Annotations @ComponentScan

paquet com.vince.spring; 

/**
 * 用于扫描指定包路径的Bean
 */
@Retention(RetentionPolicy.RUNTIME)//注解生命周期,不仅被保存到class文件中,jvm加载class文件之后,仍然存在
//@Target用来表示注解作用范围,超过这个作用范围,编译的时候就会报错。
@Target(ElementType.TYPE)//注解范围是:接口、类、枚举、注解
public @interface ComponentScan {

    String value() default "";

}

1.1.3 Annotations @Component

paquet com.vince.spring; 

/**
 * 注解为Bean
 */
@Retention(RetentionPolicy.RUNTIME)//注解生命周期,不仅被保存到class文件中,jvm加载class文件之后,仍然存在
@Target(ElementType.TYPE)//注解范围是:接口、类、枚举、注解
public @interface Component {

    String value() default "";

}

1.1.4 Annotation @Autowired

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

}

1.1.5 Annotations @Scope

@Retention(RetentionPolicy.RUNTIME)//注解生命周期,不仅被保存到class文件中,jvm加载class文件之后,仍然存在
@Target(ElementType.TYPE)//注解范围是:接口、类、枚举、注解
public @interface Scope {

    String value() default "";

}

1.1.6 Classe de déclaration du bean : BeanDefinition

BeanDefinition définit les métadonnées de configuration de Bean, notamment :

  • Nom de classe de Bean
  • Définissez le nom du bean parent, qu'il soit primaire,
  • Informations de configuration du comportement du bean, portée, mode de liaison automatique, rappel du cycle de vie, chargement différé, méthode initiale, méthode de destruction, etc.
  • Paramètres de dépendance entre beans, dépendances
  • Paramètres de construction, paramètres de propriété  
//Bean的声明类,用来描述Bean;里面定义的Bean的类型(Class格式)和作用域
public class BeanDefinition {

    private Class type;
    private String scope;

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

    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;
    }
}

1.1.7 Interface de rappel du nom du bean : BeanNameAware

//回调接口,主要setter方法给Bean的beanName变量赋值。Aware译为明白的,意识到的。
public interface BeanNameAware {

    public void setBeanName(String beanName);
}

1.1.8 Interface d'initialisation : InitializingBean

//初始化Bean的接口,有个方法,
public interface InitializingBean {

    //在属性填充后执行
    public void afterPropertiesSet();

}

1.1.9 Interface du post-processeur Bean : BeanPostProcessor 

Les deux méthodes de cette interface sont exécutées avant et après l'initialisation, et le proxy du JDK peut être utilisé via la classe d'implémentation 

//Bean后置处理器接口
public interface BeanPostProcessor {

    //在Bean 的初始化方法(如 @PostConstruct 注解的方法)被调用之前被自动调用
    public Object postProcessBeforeInitialization(String beanName,Object bean);

    //在 Bean 的初始化方法被调用之后被自动调用
    public Object postProcessAfterInitialization(String beanName,Object bean);

}

1.2 Classes liées aux activités des utilisateurs  

Cours sous le package com.vince.service : 

1.2.1 Conteneur de création de classe de test

paquet com.vince.spring; 

public class Test {
    public static void main(String[] args){
//模拟AnnotationConfigApplicationContext容器,构造参数传入配置类
        VinceApplicationContext applicationContext = new VinceApplicationContext(AppConfig.class);
        UserInterface userService = (UserInterface)applicationContext.getBean("userService");
        userService.test();
    }
}

  

1.2.2 Classe de configurationAppConfig

paquet com.vince.spring; 

// com/springCode/service/UserService.java
@ComponentScan("com.springCode.service")
public class AppConfig {
}

1.2.3 Service utilisateur

paquet com.vince.service; 

public interface UserInterface {

    public void test();

}
@Component
//@Scope("prototype") //多例
public class UserService implements BeanNameAware , InitializingBean ,UserInterface{

    @Autowired
    private OrderService orderService;

    private String beanName;

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

    @Override
    public void setBeanName(String beanName) {
        this.beanName = beanName;
    }

    @Override
    public void afterPropertiesSet() {
        System.out.println("初始化方法");
    }
}

1.2.4 OrderService, pour l'injection de dépendances 

@Component
//@Scope("prototype") //多例
public class OrderService {
    public void submitOrder(){
        System.out.println("下单。。。");
    }

}

1.2.5 Classe d'implémentation de l'interface du post-processeur Bean : MyBeanPostProcessor

//Bean后置处理器实现类,两个方法在初始化之前和之后执行,可以通过实现类使用JDK的Proxy.newProxyInstance();基于反射实现aop。
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(String beanName, Object bean) {
        if ("userService".equals(beanName)) {
            System.out.println("BeanPostProcessor实现类的postProcessBeforeInitialization()方法");
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(String beanName, Object bean) {
        if ("userService".equals(beanName)) {
            //代理对象,通过JDK的Proxy.newProxyInstance();基于反射实现aop。
            // 第一个参数类加载器,第二个参数接口的class对象,第三个参数重写invoke()的InvocationHandler实现类
            Object proxyInstance = Proxy.newProxyInstance(MyBeanPostProcessor.class.getClassLoader(),
                    bean.getClass().getInterfaces(), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    System.out.println("切面逻辑,BeanPostProcessor实现类的postProcessAfterInitialization()方法");
                    return method.invoke(bean,args);
                }
            });
            return proxyInstance;
        }
        return bean;
    }
}

2. Améliorer la classe conteneur : VinceApplicationContext

2.0 Créer un processus de bean

Annotez le constructeur de la classe conteneur pour créer le Bean :

  • Obtenez le chemin d'analyse du Bean : obtenez l'objet Class de la classe de configuration et obtenez le chemin d'analyse défini dans l'annotation @ComponentScan en fonction de la réflexion ;
  • Traverser le bean d'analyse : parcourir le fichier de bytecode de chaque classe dans le chemin d'analyse, le charger dans un objet de classe avec un chargeur de classe et juger les annotations telles que @Component en fonction de la réflexion, le cas échéant, cette classe est une classe Bean.
    • Stockez la liste BeanPostProcessor après l'instanciation : si la classe Bean est la classe d'implémentation de l'interface BeanPostProcessor, instanciez la classe Bean et stockez-la dans la variable membre List<BeanPostProcessor> ;
    • Créer et attribuer un objet BeanDefinition : créez un objet BeanDefinition et attribuez une valeur, attribuez la variable de type à l'objet de classe de la classe Bean et attribuez la variable de portée à la portée (acquérez la valeur d'annotation @Scope par réflexion). BeanDefinition stocke des méta-informations telles que les objets et les étendues Bean.
    • Ajouter des paires clé-valeur à beanDefinitionMap : ajoutez le nom du bean et son objet BeanDefinition correspondant à la variable membre de la classe conteneur beanDefinitionMap sous la forme de paires clé-valeur. beanDefinitionMap est thread-safe et est de type ConcurrentHashMap.
  • Parcourez beanDefinitionMap pour créer Bean : parcourez la clé de beanDefinitionMap, appelez la méthode createBean() pour créer Bean. La logique spécifique de la méthode createBean() :
    • Instanciation : Sur la base de la réflexion, déduire la méthode de construction et créer une instance selon l'objet Class de la classe Bean. S'il s'agit d'un singleton, il sera stocké dans le pool singleton. Cette méthode est également utilisée lors de l'instanciation de getBean(), elle est donc extraite en tant que méthode privée. Cette méthode détermine d'abord la portée du Bean. Pour une seule instance, elle est d'abord obtenue à partir du pool singleton en fonction du nom du bean. Si il n'y a pas de pool singleton, appelez createBean La méthode () crée un objet Bean de type Object en fonction du nom du Bean et de la BeanDefinition correspondante.
    • Remplissage d'attribut : en fonction de la réflexion, obtenez tous ses attributs en fonction de l'objet Class de la classe Bean, ouvrez la restriction d'accès des membres privés pour les attributs annotés avec @Autowired, etc., et remplissez l'objet Bean de l'attribut via le getBean ( nom ou classe). Méthode getBean() : déterminez la portée, s'il s'agit d'une instance unique, appelez createBean() et placez-la dans le pool singleton et revenez, s'il s'agit d'un prototype, appelez createBean() et revenez directement. Par souci de simplification, seul le cache de premier niveau est considéré ici, et les caches de deuxième et troisième niveaux ne sont pas pris en compte pour le moment.
    • Rappel Handle Aware : si l'instance du Bean implémente l'interface BeanNameAware (à en juger par instanceof), appelez la méthode setBeanName() réécrite par le Bean pour attribuer une valeur à la variable beanName de l'instance du Bean.
    • Exécutez toutes les méthodes de pré-initialisation de BeanPostProcessor : parcourez la liste BeanPostProcessor et exécutez la méthode postProcessBeforeInitialization() dans chaque objet BeanPostProcessor. Cette méthode peut implémenter un proxy dynamique via Proxy.newProxyInstance() du JDK pour renvoyer l'objet proxy de l'objet cible.
    • Initialisation : si l'instance du Bean implémente l'interface InitializingBean (à en juger par instanceof), appelez la méthode afterPropertiesSet() réécrite par le Bean pour traiter la logique d'initialisation. afterPropertiesSet se traduit par "une fois les propriétés renseignées"
    • Exécutez toutes les méthodes de post-initialisation de BeanPostProcessor : parcourez la liste BeanPostProcessor et exécutez la méthode postProcessAfterInitialization() dans chaque objet BeanPostProcessor. Cette méthode peut implémenter un proxy dynamique via Proxy.newProxyInstance() du JDK pour renvoyer l'objet proxy de l'objet cible.
    • Mettez-le dans le pool singleton : s'il s'agit d'un singleton, placez l'objet proxy de type Object Bean dans le pool singleton SingletonObjects.

2.1 Mise en œuvre du code

La classe VinceApplicationContext sous le package Spring  

2.1.1 Variables membres 

    private Class configClass;//配置类的Class对象

    //Bean名和它对应的BeanDefinition键值对;
    private ConcurrentHashMap<String,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

    //单例池;map存各Bean名所对应的Bean实例
    private ConcurrentHashMap<String,Object> singletonObjects = new ConcurrentHashMap<>();
    //存放扫描包路径下的所有BeanPostProcessor类
    private ArrayList<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();

2.1.2 Méthode de construction, le paramètre est la classe de configuration

//构造方法,参数是配置类的Class对象
    public VinceApplicationContext(Class configClass) {
        //1.赋值配置类成员变量
        this.configClass = configClass;
        // 2.通过反射,判断配置类Class对象有没有注解@ComponentScan;如果有,就解析ComponentScan对象
        // --->BeanDefinition -->beanDefinitionMap
        if (configClass.isAnnotationPresent(ComponentScan.class)) {
            //解析ComponentScan对象
            ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
//            获取ComponentScan对象的value,也就是用户传入的扫描路径。例如@ComponentScan("com.vince.service"),扫描这个包下的Bean
            String path = componentScanAnnotation.value();// com.vince.service
            // 将包名中的点号替换为斜杠
            path = path.replace(".","/");//com/vince/service
            // 获取容器类的类加载器
            ClassLoader classLoader = VinceApplicationContext.class.getClassLoader();
            // 类加载器获取扫描包的绝对路径
            URL resource = classLoader.getResource(path);//file:/D:/xxx/com/vince/service
            // 把URL绝对路径对象封装成File对象
            File file = new File(resource.getFile());
            // 如果File对象是目录,则遍历判断扫描目录下的文件是不是bean;
            if (file.isDirectory()) {
                File[] files = file.listFiles();
                //遍历“扫描路径”下所有文件中,找到Bean,创建对应BeanDefinition对象,并存到map里;
                // 判断Bean方式:基于反射,判断该字节码文件对应的类有没有@Component注解
                for (File f: files) {
                    String fileName = f.getAbsolutePath();
                    if (fileName.endsWith(".class")) {
                        //将文件名转为类名;D:\xxx\com\vince\service\AppConfig.class ----> AppConfig
                        //这里其实不应该写死成“com”,只是方便起见,其实应该用更复杂的逻辑截取全名为类名
                        String className = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".class"));
                        className = className.replace("\\",".");
                        try {
                            //根据类的全限定名加载类
                            Class<?> clazz = classLoader.loadClass(className);
                            //如果该类有@Component注解
                            if (clazz.isAnnotationPresent(Component.class)) {
                                System.out.println("容器类构造方法里,扫描@ComponentScan('xx'),发现xx路径下这个类是Bean:"+clazz.getName());
                                //a.如果@Component("类名")有设置类名,Bean名就是设置的这个类名
                                Component component = clazz.getAnnotation(Component.class);
                                String beanName = component.value();

                                //判断这个Bean类是不是BeanPostProcessor接口的实现类,如果是的话就通过反射创建实例,并放进处理器列表里。
                                //注意不能用instanceof,因为instanceof是实例与类的关系比较,isAssignableFrom是类和接口的关系比较
                                if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
                                    BeanPostProcessor instance = (BeanPostProcessor) clazz.newInstance();
                                    beanPostProcessorList.add(instance);
                                    System.out.println("这个Bean是BeanPostProcessor接口的实现类,需要加入list里,以供所有Bean初始化之前和之后增强");
                                }

                                //b.如果@Component没有设置类名,就将首字母小写后的Bean类名设为Bean名
                                if ("".equals(beanName)) {
                                    beanName = Introspector.decapitalize(clazz.getSimpleName());//工具类将字符串校验后首字母小写
                                }

                                // 创建一个BeanDefinition对象,用来描述Bean,里面赋值这个Bean的类型和作用域
                                BeanDefinition beanDefinition = new BeanDefinition();
                                //反射获取@Scope注解指定的作用域,赋值给BeanDefinition对象的scope变量
                                if (clazz.isAnnotationPresent(Scope.class)) {
                                    Scope scopeAnnotation = clazz.getAnnotation(Scope.class);
                                    beanDefinition.setScope(scopeAnnotation.value());
                                }else{
                                    beanDefinition.setScope("singleton");
                                }
                                //把这个Bean的Class对象赋值给BeanDefinition对象的type变量
                                beanDefinition.setType(clazz);
                                //把所有Bean名和它对应的BeanDefinition对象映射关系,存到map里统一管理
                                beanDefinitionMap.put(beanName,beanDefinition);
                                System.out.println("Bean名:"+beanName+",BeanDefinition对象:"+beanDefinition);
                            }
                        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }

        // 3.实例化bean,如果作用域是单例,则从单例池中取;取不到就创建新的Bean对象,并存入单例池
        for (String beanName:beanDefinitionMap.keySet()) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            if ("singleton".equals(beanDefinition.getScope())) {
                //实例化Bean
                Object bean = createBean(beanName,beanDefinition);
                singletonObjects.put(beanName,bean);
            }
        }
    }

2.1.3 Méthode createBean() 

    //根据Bean名和BeanDefinition对象实例化Bean的方法;私有,只供本类内部调用
    //BeanDefinition对象的成员变量type就是Bean的类型(Class格式)
    private Object createBean(String beanName,BeanDefinition beanDefinition){
//        获取Bean的Class格式的类型
        Class clazz = beanDefinition.getType();
        try {
            //1.基于反射创建Bean的实例;
            Object instance = clazz.getConstructor().newInstance();
            //2.依赖注入
            //遍历Bean类Class对象的所有属性;
            for (Field f : clazz.getDeclaredFields()) {
                //找出有@Autowired注解的属性,给这些属性进行填充
                if (f.isAnnotationPresent(Autowired.class)) {
                    //破除 private 修饰符访问限制;这样就可以访问注入的这个依赖里面的私有成员
                    f.setAccessible(true);
                    //给该属性填充属性名对应的Bean对象。
                    // 该属性通过getBean能获得Bean,因为上面构造方法里已经扫描了所有的Bean,并创建了BeanDefinition对象加入到“声明map”里了。
                    //void set(Object obj, Object value)
                    //obj 表示要设置属性值的对象;value 表示要为该成员变量设置的新值。
                    f.set(instance,getBean(f.getName()));
                }
            }

            // 3.Aware回调。如果Bean实例实现了BeanNameAware接口,就调用重写的setter方法,给Bean实例的beanName变量赋值
            if (instance instanceof BeanNameAware) {
                ((BeanNameAware) instance).setBeanName(beanName);
            }

            // 遍历后置处理器列表,执行后置处理器的before方法,在Bean 的初始化方法(如 @PostConstruct 注解的方法)被调用之前被自动调用
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                instance = beanPostProcessor.postProcessBeforeInitialization(beanName,instance);
            }

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

            //Bean后置处理器接口的after方法,在 Bean 的初始化方法被调用之后被自动调用
            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;
    }

2.1.4 Méthode getBean()

//容器类的getBean()方法,根据作用域获取Bean
    public Object getBean(String beanName){
        //从map里获取BeanDefinition对象
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (beanDefinition == null) {
            //如果BeanDefinition对象为空,说明这个bean没有创建成功,抛异常;
            throw new NullPointerException();
        }else{
            //如果BeanDefinition对象不为空,则判断作用域后获取Bean
            String scope = beanDefinition.getScope();
            if ("singleton".equals(scope)) {
                //如果是单例则从单例池根据Bean名取Bean;
                Object bean= singletonObjects.get(beanName);
                if (bean == null) {
                    //如果单例池查到的是null,则新创建Bean,再给单例池赋值
                    bean = createBean(beanName, beanDefinition);
                    singletonObjects.put(beanName,bean);
                }
                //返回Bean
                return bean;
            }else{
                // 多例,每次直接创建新的
                return createBean(beanName, beanDefinition);
            }
        }
    }

3. Tous les codes

https://gitee.com/vincewm/spring-master

Je suppose que tu aimes

Origine blog.csdn.net/qq_40991313/article/details/130864833
conseillé
Classement