手写Spring底层原理

一.创建maven项目

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二.创建对应的文件夹,分别是spring和nickel

在这里插入图片描述
说明:spring文件夹用于存放spring相关的文件,nickel用于存放相应的配置文件和相关被spring加载的bean对象。

三.创建对对应的文件

3.1创建CommponentScan注解文件

package com.spring;

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

/**
 * @author Nickel
 * @version 1.0
 * @date 2023/7/5 8:21
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface CommponentScan {
    String value() default "";
}

在这里插入图片描述
说明:该注解用于配置文件类上,用于spring应该扫描路径

3.2创建NickelAppConfig类

package com.spring;

/**
 * @author Nickel
 * @version 1.0
 * @date 2023/7/5 8:55
 */
@CommponentScan("com.nickel.service")
public class NickelAppConfig {
}

在这里插入图片描述

说明:该类用于配置Spring应该扫描的文件夹,通过CommponentScan注解里面的值来确定扫描的路径

3.3创建NickelAnnotationConfigApplicationContext

package com.spring;

/**
 * @author Nickel
 * @version 1.0
 * @date 2023/7/5 8:20
 */
public class NickelAnnotationConfigApplicationContext {

    private Class scanConfig;

    public NickelAnnotationConfigApplicationContext(Class scanConfig){
        this.scanConfig=scanConfig;
    }

    public Object getBean(String beanName){
        return null;
    }
}

在这里插入图片描述

说明:该类是spring加载容器,当spring加载时候,会加载该类,通过会加载所有Commponent注解的类,同时判断是单例加载还是多例加载,还有执行初始化相关的方法

3.4创建Commponent注解

package com.spring;

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

/**
 * @author Nickel
 * @version 1.0
 * @date 2023/7/5 8:21
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Commponent {
    String value() default "";
}

在这里插入图片描述
说明:注解用于扫描的类上,用于管理该类是否被spring进行管理,value值用于存放在spring加载的key值

3.5创建Scope注解

package com.spring;

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

/**
 * @author Nickel
 * @version 1.0
 * @date 2023/7/5 8:36
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Scope {
    String value() default "";
}

在这里插入图片描述
说明:Scope注解用于类上,用于说明改类是单例bean,还是多实例bean,当没有Scope注解和Scope的value值为singleton为单实例bean,当值为prototype为多实例bean

3.6创建UserService类

package com.nickel.service;

import com.spring.Commponent;
import com.spring.Scope;

/**
 * @author Nickel
 * @version 1.0
 * @date 2023/7/5 8:43
 */
@Commponent("userService")
@Scope("singleton")
public class UserService {

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

在这里插入图片描述

说明:改类有Commponent注解,表示会被spring加载,Scope的value值为singleton表示该类会被spring加载

3.7创建OrderService类

package com.nickel.service;


/**
 * @author Nickel
 * @version 1.0
 * @date 2023/7/5 8:44
 */
public class OrderService {
}

在这里插入图片描述
说明:该类没有Commponent注解,表示在spring加载的时候不会加载该类

四.Spring扫描的实现

4.1 在Test类中写对应的测试方法

package com.nickel;

import com.nickel.service.UserService;
import com.spring.NickelAnnotationConfigApplicationContext;

/**
 * @author Nickel
 * @version 1.0
 * @date 2023/7/5 8:16
 */
public class Test {
    public static void main(String[] args) {
        NickelAnnotationConfigApplicationContext context=new NickelAnnotationConfigApplicationContext(NickelAppConfig.class);
        UserService userService = (UserService)context.getBean("userService");

        userService.test();
    }
}

说明:该类目前未实现加载bean的方法,所以执行该方法会报错,实现后续的方法将会加载。

4.2 NickelAnnotationConfigApplicationContext获取扫描的路径

package com.spring;

import java.lang.annotation.Annotation;

/**
 * @author Nickel
 * @version 1.0
 * @date 2023/7/5 8:20
 */
public class NickelAnnotationConfigApplicationContext {

    private Class scanConfig;

    public NickelAnnotationConfigApplicationContext(Class scanConfig){
        this.scanConfig=scanConfig;

        //扫描逻辑
        if (scanConfig.isAnnotationPresent(CommponentScan.class)) {
            CommponentScan commponentScan =(CommponentScan)scanConfig.getAnnotation(CommponentScan.class);
            String path=commponentScan.value();

            System.out.println(path);
        }
    }

    public Object getBean(String beanName){
        return null;
    }
}

在这里插入图片描述

说明:获取扫描配置文件中,CommponentScan注解的value值,获取spring项目的扫描路径

4.3创建对象BeanDefinition

package com.spring;

/**
 * @author Nickel
 * @version 1.0
 * @date 2023/7/5 9:42
 */
public class BeanDefinition {
    private Class type;
    private String scope;
    private Boolean isLazy;

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

    public Boolean getLazy() {
        return isLazy;
    }

    public void setLazy(Boolean lazy) {
        isLazy = lazy;
    }
}

说明:该类用于存储被Conponent注解标识的类。

4.3 通过SpringLoader来加载该类

package com.spring;

import java.lang.annotation.Annotation;
import java.net.URL;

/**
 * @author Nickel
 * @version 1.0
 * @date 2023/7/5 8:20
 */
public class NickelAnnotationConfigApplicationContext {

    private Class scanConfig;

    public NickelAnnotationConfigApplicationContext(Class scanConfig){
        this.scanConfig=scanConfig;

        //扫描逻辑
        if (scanConfig.isAnnotationPresent(CommponentScan.class)) {
            CommponentScan commponentScan =(CommponentScan)scanConfig.getAnnotation(CommponentScan.class);
            String path=commponentScan.value();
            path=path.replace(".","/");
            System.out.println(path);
            
            
            //通过ApplicationContext来加载该类
            ClassLoader classLoader = NickelAnnotationConfigApplicationContext.class.getClassLoader();
            URL resource = classLoader.getResource(path);
        }
    }

    public Object getBean(String beanName){
        return null;
    }
}

在这里插入图片描述
说明:由于springLoader是通过相对路径来加载文件的,相对路径的文件路径为/./…这种形式,所以在加载的时候‘.’路径修改为‘/’路径。

4.4 判断该类是否有Component注解和Scope注解来走不同的逻辑

package com.spring;

import java.io.File;
import java.lang.annotation.Annotation;
import java.net.FileNameMap;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

/**
 * @author Nickel
 * @version 1.0
 * @date 2023/7/5 8:20
 */
public class NickelAnnotationConfigApplicationContext {

    private Class scanConfig;

    public NickelAnnotationConfigApplicationContext(Class scanConfig){
        this.scanConfig=scanConfig;

        //扫描逻辑
        if (scanConfig.isAnnotationPresent(CommponentScan.class)) {
            CommponentScan commponentScan =(CommponentScan)scanConfig.getAnnotation(CommponentScan.class);
            String path=commponentScan.value();
            path=path.replace(".","/");
            
            
            //通过ApplicationContext来加载该类
            ClassLoader classLoader = NickelAnnotationConfigApplicationContext.class.getClassLoader();
            URL resource = classLoader.getResource(path);

            File file=new File(resource.getPath());
            for (File listFile : file.listFiles()) {
                String absolutePath = listFile.getAbsolutePath();
                absolutePath=absolutePath.substring(absolutePath.indexOf("com"),absolutePath.indexOf(".class"));
                absolutePath=absolutePath.replace("\\",".");
                System.out.println(absolutePath);

                try {
                    Class<?> aClass = classLoader.loadClass(absolutePath);
                    if (aClass.isAnnotationPresent(Commponent.class)) {


                        //判断该类是单例加载还是多例加载,没有Scope注解表示多例
                        if (aClass.isAnnotationPresent(Scope.class)) {
                            Scope annotation = aClass.getAnnotation(Scope.class);
                            String value = annotation.value();
                            if(value.equals("singleton")){
                                //单例
                            }else{
                                //多例
                            }
                        }else{
                            //单例
                        }
                    }
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public Object getBean(String beanName){
        return null;
    }
}

在这里插入图片描述
说明:这里获取所有具有Commponent注解的类,这个类将会被spring加载,同时具有Scope注解值value为singleton将会被加载一次,其他的将会被加载多次。

4.4 提取扫描方法,创建beanDefinition对象,并把对象存放在beanDefinitionMap

package com.spring;

import java.io.File;
import java.lang.annotation.Annotation;
import java.net.FileNameMap;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

/**
 * @author Nickel
 * @version 1.0
 * @date 2023/7/5 8:20
 */
public class NickelAnnotationConfigApplicationContext {

    private Class scanConfig;
    private Map<String,BeanDefinition> beanDefinitionMap=new HashMap<>();

    public NickelAnnotationConfigApplicationContext(Class scanConfig){
        this.scanConfig=scanConfig;

        //扫描逻辑
        scan(scanConfig);
    }

    public Object getBean(String beanName){
        return null;
    }

    private void scan(Class scanConfig) {
        if (scanConfig.isAnnotationPresent(CommponentScan.class)) {
            CommponentScan commponentScan =(CommponentScan) scanConfig.getAnnotation(CommponentScan.class);
            String path=commponentScan.value();
            path=path.replace(".","/");


            //通过ApplicationContext来加载该类
            ClassLoader classLoader = NickelAnnotationConfigApplicationContext.class.getClassLoader();
            URL resource = classLoader.getResource(path);

            File file=new File(resource.getPath());
            for (File listFile : file.listFiles()) {
                String absolutePath = listFile.getAbsolutePath();
                absolutePath=absolutePath.substring(absolutePath.indexOf("com"),absolutePath.indexOf(".class"));
                absolutePath=absolutePath.replace("\\",".");
                System.out.println(absolutePath);

                try {
                    Class<?> aClass = classLoader.loadClass(absolutePath);
                    if (aClass.isAnnotationPresent(Commponent.class)) {
                        //获取beanName
                        Commponent commponentAnnotation = aClass.getAnnotation(Commponent.class);
                        String beanName=commponentAnnotation.value();


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

                        //判断该类是单例加载还是多例加载,没有Scope注解表示多例
                        if (aClass.isAnnotationPresent(Scope.class)) {
                            Scope annotation = aClass.getAnnotation(Scope.class);
                            String value = annotation.value();
                            beanDefinition.setScope(value);
                        }else{
                            beanDefinition.setScope("singleton");
                        }

                        beanDefinitionMap.put(beanName,beanDefinition);
                    }
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

在这里插入图片描述

4.5 创建bean方法,同时创建一个singletonObjects对象用于存所有单实例bean

package com.spring;

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.net.FileNameMap;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

/**
 * @author Nickel
 * @version 1.0
 * @date 2023/7/5 8:20
 */
public class NickelAnnotationConfigApplicationContext {

    private Class scanConfig;
    private Map<String,BeanDefinition> beanDefinitionMap=new HashMap<>();
    private Map<String,Object> singletonObjects=new HashMap<>();

    public NickelAnnotationConfigApplicationContext(Class scanConfig){
        this.scanConfig=scanConfig;

        //扫描逻辑
        scan(scanConfig);

        //创建bean
        for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
            String beanName=entry.getKey();
            BeanDefinition beanDefinition= entry.getValue();
            if (beanDefinition.getScope().equals("singleton")) {
                   Object singletonBean=createBean(beanName,beanDefinition);
                singletonObjects.put(beanName,singletonBean);
            }
        }

    }

    public Object getBean(String beanName){
        return null;
    }

    private Object createBean(String beanName,BeanDefinition beanDefinition){
        Class clazz=beanDefinition.getType();
        Object instance=null;
        try {
             instance = clazz.getConstructor().newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        return instance;
    }

    private void scan(Class scanConfig) {
        if (scanConfig.isAnnotationPresent(CommponentScan.class)) {
            CommponentScan commponentScan =(CommponentScan) scanConfig.getAnnotation(CommponentScan.class);
            String path=commponentScan.value();
            path=path.replace(".","/");


            //通过ApplicationContext来加载该类
            ClassLoader classLoader = NickelAnnotationConfigApplicationContext.class.getClassLoader();
            URL resource = classLoader.getResource(path);

            File file=new File(resource.getPath());
            for (File listFile : file.listFiles()) {
                String absolutePath = listFile.getAbsolutePath();
                absolutePath=absolutePath.substring(absolutePath.indexOf("com"),absolutePath.indexOf(".class"));
                absolutePath=absolutePath.replace("\\",".");
                System.out.println(absolutePath);

                try {
                    Class<?> aClass = classLoader.loadClass(absolutePath);
                    if (aClass.isAnnotationPresent(Commponent.class)) {
                        //获取beanName
                        Commponent commponentAnnotation = aClass.getAnnotation(Commponent.class);
                        String beanName=commponentAnnotation.value();


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

                        //判断该类是单例加载还是多例加载,没有Scope注解表示多例
                        if (aClass.isAnnotationPresent(Scope.class)) {
                            Scope annotation = aClass.getAnnotation(Scope.class);
                            String value = annotation.value();
                            beanDefinition.setScope(value);
                        }else{
                            beanDefinition.setScope("singleton");
                        }

                        beanDefinitionMap.put(beanName,beanDefinition);
                    }
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

在这里插入图片描述

4.6getBean方法修改

package com.spring;

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.net.FileNameMap;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

/**
 * @author Nickel
 * @version 1.0
 * @date 2023/7/5 8:20
 */
public class NickelAnnotationConfigApplicationContext {

    private Class scanConfig;
    private Map<String,BeanDefinition> beanDefinitionMap=new HashMap<>();
    private Map<String,Object> singletonObjects=new HashMap<>();

    public NickelAnnotationConfigApplicationContext(Class scanConfig){
        this.scanConfig=scanConfig;

        //扫描逻辑
        scan(scanConfig);

        //创建bean
        for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
            String beanName=entry.getKey();
            BeanDefinition beanDefinition= entry.getValue();
            if (beanDefinition.getScope().equals("singleton")) {
                Object singletonBean=createBean(beanName,beanDefinition);
                singletonObjects.put(beanName,singletonBean);
            }
        }

    }

    public Object getBean(String beanName){
        if (!beanDefinitionMap.containsKey(beanName)) {
           throw new NullPointerException();
        }

        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if(beanDefinition.getScope().equals("singleton")){
           Object singletonBean=singletonObjects.get(beanName);
           if(singletonBean==null){
               singletonBean=createBean(beanName,beanDefinition);
               singletonObjects.put(beanName,singletonBean);
           }
           return singletonBean;
        }else{
            return createBean(beanName,beanDefinition);
        }
    }

    private Object createBean(String beanName,BeanDefinition beanDefinition){
        Class clazz=beanDefinition.getType();
        Object instance=null;
        try {
             instance = clazz.getConstructor().newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        return instance;
    }

    private void scan(Class scanConfig) {
        if (scanConfig.isAnnotationPresent(CommponentScan.class)) {
            CommponentScan commponentScan =(CommponentScan) scanConfig.getAnnotation(CommponentScan.class);
            String path=commponentScan.value();
            path=path.replace(".","/");


            //通过ApplicationContext来加载该类
            ClassLoader classLoader = NickelAnnotationConfigApplicationContext.class.getClassLoader();
            URL resource = classLoader.getResource(path);

            File file=new File(resource.getPath());
            for (File listFile : file.listFiles()) {
                String absolutePath = listFile.getAbsolutePath();
                absolutePath=absolutePath.substring(absolutePath.indexOf("com"),absolutePath.indexOf(".class"));
                absolutePath=absolutePath.replace("\\",".");

                try {
                    Class<?> aClass = classLoader.loadClass(absolutePath);
                    if (aClass.isAnnotationPresent(Commponent.class)) {
                        //获取beanName
                        Commponent commponentAnnotation = aClass.getAnnotation(Commponent.class);
                        String beanName=commponentAnnotation.value();


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

                        //判断该类是单例加载还是多例加载,没有Scope注解表示多例
                        if (aClass.isAnnotationPresent(Scope.class)) {
                            Scope annotation = aClass.getAnnotation(Scope.class);
                            String value = annotation.value();
                            beanDefinition.setScope(value);
                        }else{
                            beanDefinition.setScope("singleton");
                        }

                        beanDefinitionMap.put(beanName,beanDefinition);
                    }
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

4.Test类修改,测试方法有效性

package com.nickel;


import com.spring.NickelAnnotationConfigApplicationContext;

/**
 * @author Nickel
 * @version 1.0
 * @date 2023/7/5 8:16
 */
public class Test {
    public static void main(String[] args) {
        NickelAnnotationConfigApplicationContext context=new NickelAnnotationConfigApplicationContext(NickelAppConfig.class);
        System.out.println(context.getBean("userService"));
        System.out.println(context.getBean("userService"));
    }
}

在这里插入图片描述

五.Spring实现初始化,初始化前、初始化后的方法

5.1初始化前方法实现

由于spring实现初始化的方法是实现接口InitializingBean,同时实现接口里面afterPropertiesSet方法

1.创建InitializingBean接口,在接口中添加afterPropertiesSet方法

package com.spring;

/**
 * @author Nickel
 * @version 1.0
 * @date 2023/7/5 15:03
 */
public interface InitializingBean {
    void afterPropertiesSet();
}

在这里插入图片描述

2.在创建bean的时候,判断方法是否实现InitializingBean接口,实现接口转化后实现初始化方法

package com.spring;

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.net.FileNameMap;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

/**
 * @author Nickel
 * @version 1.0
 * @date 2023/7/5 8:20
 */
public class NickelAnnotationConfigApplicationContext {

    private Class scanConfig;
    private Map<String,BeanDefinition> beanDefinitionMap=new HashMap<>();
    private Map<String,Object> singletonObjects=new HashMap<>();

    public NickelAnnotationConfigApplicationContext(Class scanConfig){
        this.scanConfig=scanConfig;

        //扫描逻辑
        scan(scanConfig);

        //创建bean
        for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
            String beanName=entry.getKey();
            BeanDefinition beanDefinition= entry.getValue();
            if (beanDefinition.getScope().equals("singleton")) {
                Object singletonBean=createBean(beanName,beanDefinition);
                singletonObjects.put(beanName,singletonBean);
            }
        }

    }

    public Object getBean(String beanName){
        if (!beanDefinitionMap.containsKey(beanName)) {
           throw new NullPointerException();
        }

        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if(beanDefinition.getScope().equals("singleton")){
           Object singletonBean=singletonObjects.get(beanName);
           if(singletonBean==null){
               singletonBean=createBean(beanName,beanDefinition);
               singletonObjects.put(beanName,singletonBean);
           }
           return singletonBean;
        }else{
            return createBean(beanName,beanDefinition);
        }
    }

    private Object createBean(String beanName,BeanDefinition beanDefinition){
        Class clazz=beanDefinition.getType();
        Object instance=null;
        try {
             instance = clazz.getConstructor().newInstance();

            if (instance instanceof InitializingBean) {
                ((InitializingBean)instance).afterPropertiesSet();
            }
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        return instance;
    }

    private void scan(Class scanConfig) {
        if (scanConfig.isAnnotationPresent(CommponentScan.class)) {
            CommponentScan commponentScan =(CommponentScan) scanConfig.getAnnotation(CommponentScan.class);
            String path=commponentScan.value();
            path=path.replace(".","/");


            //通过ApplicationContext来加载该类
            ClassLoader classLoader = NickelAnnotationConfigApplicationContext.class.getClassLoader();
            URL resource = classLoader.getResource(path);

            File file=new File(resource.getPath());
            for (File listFile : file.listFiles()) {
                String absolutePath = listFile.getAbsolutePath();
                absolutePath=absolutePath.substring(absolutePath.indexOf("com"),absolutePath.indexOf(".class"));
                absolutePath=absolutePath.replace("\\",".");

                try {
                    Class<?> aClass = classLoader.loadClass(absolutePath);
                    if (aClass.isAnnotationPresent(Commponent.class)) {
                        //获取beanName
                        Commponent commponentAnnotation = aClass.getAnnotation(Commponent.class);
                        String beanName=commponentAnnotation.value();


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

                        //判断该类是单例加载还是多例加载,没有Scope注解表示多例
                        if (aClass.isAnnotationPresent(Scope.class)) {
                            Scope annotation = aClass.getAnnotation(Scope.class);
                            String value = annotation.value();
                            beanDefinition.setScope(value);
                        }else{
                            beanDefinition.setScope("singleton");
                        }

                        beanDefinitionMap.put(beanName,beanDefinition);
                    }
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

在这里插入图片描述

3.Test测试类测试初始化的方法

package com.nickel;

import com.nickel.service.UserService;
import com.spring.NickelAnnotationConfigApplicationContext;

/**
 * @author Nickel
 * @version 1.0
 * @date 2023/7/5 8:16
 */
public class Test {
    public static void main(String[] args) {
        NickelAnnotationConfigApplicationContext context=new NickelAnnotationConfigApplicationContext(NickelAppConfig.class);
//        System.out.println(context.getBean("userService"));
//        System.out.println(context.getBean("userService"));
          UserService userService = (UserService)context.getBean("userService");
         userService.test();
    }
}

在这里插入图片描述

5.2实现初始化前的方法

1.创建BeanPostProcess接口,在接口定义两个方法postProcessBeforeInitialization和postProcessAfterInitialization

package com.spring;

/**
 * @author Nickel
 * @version 1.0
 * @date 2023/7/5 15:44
 */
public interface BeanPostProcess {
    default Object postProcessBeforeInitialization(Object bean, String beanName){
        return bean;
    }

    default Object postProcessAfterInitialization(Object bean, String beanName){
        return bean;
    }
}

2.定义一个方法NickelBeanPostProcess实现BeanPostProcess方法

package com.nickel.service;

import com.spring.BeanPostProcess;
import com.spring.Commponent;

/**
 * @author Nickel
 * @version 1.0
 * @date 2023/7/5 15:50
 */
@Commponent
public class NickelBeanPostProcss implements BeanPostProcess {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        return BeanPostProcess.super.postProcessBeforeInitialization(bean, beanName);
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        if(beanName.equals("userService")){
            System.out.println("userService初始化后");
        }
        return BeanPostProcess.super.postProcessAfterInitialization(bean, beanName);
    }
}

说明:由于该方法要被spring进行管理,那么必须要在改类上面添加Commponent注解,这样类才会被加载

3.改造扫描方法,使得BeanPostProcess能够被加载,创建beanPostProcessList存放所有的BeanPostProcess方法

package com.spring;

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.net.FileNameMap;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author Nickel
 * @version 1.0
 * @date 2023/7/5 8:20
 */
public class NickelAnnotationConfigApplicationContext {

    private Class scanConfig;
    private Map<String,BeanDefinition> beanDefinitionMap=new HashMap<>();
    private Map<String,Object> singletonObjects=new HashMap<>();
    private List<BeanPostProcess> beanPostProcessList=new ArrayList<>();

    public NickelAnnotationConfigApplicationContext(Class scanConfig){
        this.scanConfig=scanConfig;

        //扫描逻辑
        scan(scanConfig);

        //创建bean
        for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
            String beanName=entry.getKey();
            BeanDefinition beanDefinition= entry.getValue();
            if (beanDefinition.getScope().equals("singleton")) {
                Object singletonBean=createBean(beanName,beanDefinition);
                singletonObjects.put(beanName,singletonBean);
            }
        }

    }

    public Object getBean(String beanName){
        if (!beanDefinitionMap.containsKey(beanName)) {
           throw new NullPointerException();
        }

        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if(beanDefinition.getScope().equals("singleton")){
           Object singletonBean=singletonObjects.get(beanName);
           if(singletonBean==null){
               singletonBean=createBean(beanName,beanDefinition);
               singletonObjects.put(beanName,singletonBean);
           }
           return singletonBean;
        }else{
            return createBean(beanName,beanDefinition);
        }
    }

    private Object createBean(String beanName,BeanDefinition beanDefinition){
        Class clazz=beanDefinition.getType();
        Object instance=null;
        try {
             instance = clazz.getConstructor().newInstance();

            for (BeanPostProcess beanPostProcess : beanPostProcessList) {
                instance=beanPostProcess.postProcessBeforeInitialization(instance,beanName);
            }

            if (instance instanceof InitializingBean) {
                ((InitializingBean)instance).afterPropertiesSet();
            }

            for (BeanPostProcess beanPostProcess : beanPostProcessList) {
                instance=beanPostProcess.postProcessAfterInitialization(instance,beanName);
            }
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        return instance;
    }

    private void scan(Class scanConfig) {
        if (scanConfig.isAnnotationPresent(CommponentScan.class)) {
            CommponentScan commponentScan =(CommponentScan) scanConfig.getAnnotation(CommponentScan.class);
            String path=commponentScan.value();
            path=path.replace(".","/");


            //通过ApplicationContext来加载该类
            ClassLoader classLoader = NickelAnnotationConfigApplicationContext.class.getClassLoader();
            URL resource = classLoader.getResource(path);

            File file=new File(resource.getPath());
            for (File listFile : file.listFiles()) {
                String absolutePath = listFile.getAbsolutePath();
                absolutePath=absolutePath.substring(absolutePath.indexOf("com"),absolutePath.indexOf(".class"));
                absolutePath=absolutePath.replace("\\",".");

                try {
                    Class<?> aClass  = classLoader.loadClass(absolutePath);
                    if (aClass.isAnnotationPresent(Commponent.class)) {
                        //判断是否实现BeanPostProcess方法
                        if (BeanPostProcess.class.isAssignableFrom(aClass)) {
                            BeanPostProcess beanPostProcess = (BeanPostProcess) aClass.getConstructor().newInstance();
                            beanPostProcessList.add(beanPostProcess);
                        }else{
                            //获取beanName
                            Commponent commponentAnnotation = aClass.getAnnotation(Commponent.class);
                            String beanName=commponentAnnotation.value();


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

                            //判断该类是单例加载还是多例加载,没有Scope注解表示多例
                            if (aClass.isAnnotationPresent(Scope.class)) {
                                Scope annotation = aClass.getAnnotation(Scope.class);
                                String value = annotation.value();
                                beanDefinition.setScope(value);
                            }else{
                                beanDefinition.setScope("singleton");
                            }

                            beanDefinitionMap.put(beanName,beanDefinition);
                        }


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

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.NickelBeanPostProcessor里面实现切面逻辑

4.1创建UserInterface接口
package com.nickel.service;

/**
 * @author Nickel
 * @version 1.0
 * @date 2023/7/5 16:45
 */
public interface UserInterface {
    public void test();
}

在这里插入图片描述

4.2在UserService中实现接口
package com.nickel.service;

import com.spring.Commponent;
import com.spring.InitializingBean;
import com.spring.Scope;

/**
 * @author Nickel
 * @version 1.0
 * @date 2023/7/5 8:43
 */
@Commponent("userService")
@Scope("singleton")
public class UserService implements InitializingBean,UserInterface {

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

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

在这里插入图片描述

4.3在代理中配置相关切面的逻辑
package com.nickel.service;

import com.spring.BeanPostProcess;
import com.spring.Commponent;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @author Nickel
 * @version 1.0
 * @date 2023/7/5 15:50
 */
@Commponent
public class NickelBeanPostProcss implements BeanPostProcess {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        if(beanName.equals("userService")){
           //使用jdk动态代理
            Object proxyInstance = Proxy.newProxyInstance(NickelBeanPostProcss.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;
    }
}

在这里插入图片描述

4.4在测试类中进行测试
package com.nickel;

import com.nickel.service.UserInterface;
import com.nickel.service.UserService;
import com.spring.NickelAnnotationConfigApplicationContext;

/**
 * @author Nickel
 * @version 1.0
 * @date 2023/7/5 8:16
 */
public class Test {
    public static void main(String[] args) {
        NickelAnnotationConfigApplicationContext context=new NickelAnnotationConfigApplicationContext(NickelAppConfig.class);
//        System.out.println(context.getBean("userService"));
//        System.out.println(context.getBean("userService"));
        UserInterface userService = (UserInterface)context.getBean("userService");
         userService.test();
    }
}

在这里插入图片描述
说明:由于代理的对象是接口,所以此时返回的对象是接口对象,这是需要转化为接口。

五.Spring实现Autowired自动注入

5.1创建自动注入对象

package com.spring;

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

/**
 * @author Nickel
 * @version 1.0
 * @date 2023/7/5 8:21
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Autowired {

}

在这里插入图片描述

5.2在UserService创建注入对象

package com.nickel.service;

import com.spring.Autowired;
import com.spring.Commponent;
import com.spring.InitializingBean;
import com.spring.Scope;

/**
 * @author Nickel
 * @version 1.0
 * @date 2023/7/5 8:43
 */
@Commponent("userService")
@Scope("singleton")
public class UserService implements InitializingBean,UserInterface {

    @Autowired
    private OrderService orderService;

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

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

在这里插入图片描述

5.3在spring加载的时候,获取autowire注解

package com.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.FileNameMap;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author Nickel
 * @version 1.0
 * @date 2023/7/5 8:20
 */
public class NickelAnnotationConfigApplicationContext {

    private Class scanConfig;
    private Map<String,BeanDefinition> beanDefinitionMap=new HashMap<>();
    private Map<String,Object> singletonObjects=new HashMap<>();
    private List<BeanPostProcess> beanPostProcessList=new ArrayList<>();

    public NickelAnnotationConfigApplicationContext(Class scanConfig){
        this.scanConfig=scanConfig;

        //扫描逻辑
        scan(scanConfig);

        //创建bean
        for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
            String beanName=entry.getKey();
            BeanDefinition beanDefinition= entry.getValue();
            if (beanDefinition.getScope().equals("singleton")) {
                Object singletonBean=createBean(beanName,beanDefinition);
                singletonObjects.put(beanName,singletonBean);
            }
        }

    }

    public Object getBean(String beanName){
        if (!beanDefinitionMap.containsKey(beanName)) {
           throw new NullPointerException();
        }

        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if(beanDefinition.getScope().equals("singleton")){
           Object singletonBean=singletonObjects.get(beanName);
           if(singletonBean==null){
               singletonBean=createBean(beanName,beanDefinition);
               singletonObjects.put(beanName,singletonBean);
           }
           return singletonBean;
        }else{
            return createBean(beanName,beanDefinition);
        }
    }

    private Object createBean(String beanName,BeanDefinition beanDefinition){
        Class clazz=beanDefinition.getType();
        Object instance=null;
        try {
             instance = clazz.getConstructor().newInstance();

            for (Field field : clazz.getDeclaredFields()) {
                if (field.isAnnotationPresent(Autowired.class)) {
                    field.setAccessible(true);
                    //获取方法中类型的对象
                    Object object= Introspector.decapitalize(field.getType().getSimpleName());
                    field.set(instance,getBean(object.toString()));
                }
            }

            for (BeanPostProcess beanPostProcess : beanPostProcessList) {
                instance=beanPostProcess.postProcessBeforeInitialization(instance,beanName);
            }

            if (instance instanceof InitializingBean) {
                ((InitializingBean)instance).afterPropertiesSet();
            }

            for (BeanPostProcess beanPostProcess : beanPostProcessList) {
                instance=beanPostProcess.postProcessAfterInitialization(instance,beanName);
            }
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        return instance;
    }

    private void scan(Class scanConfig) {
        if (scanConfig.isAnnotationPresent(CommponentScan.class)) {
            CommponentScan commponentScan =(CommponentScan) scanConfig.getAnnotation(CommponentScan.class);
            String path=commponentScan.value();
            path=path.replace(".","/");


            //通过ApplicationContext来加载该类
            ClassLoader classLoader = NickelAnnotationConfigApplicationContext.class.getClassLoader();
            URL resource = classLoader.getResource(path);

            File file=new File(resource.getPath());
            for (File listFile : file.listFiles()) {
                String absolutePath = listFile.getAbsolutePath();
                absolutePath=absolutePath.substring(absolutePath.indexOf("com"),absolutePath.indexOf(".class"));
                absolutePath=absolutePath.replace("\\",".");

                try {
                    Class<?> aClass  = classLoader.loadClass(absolutePath);
                    if (aClass.isAnnotationPresent(Commponent.class)) {
                        //判断是否实现BeanPostProcess方法
                        if (BeanPostProcess.class.isAssignableFrom(aClass)) {
                            BeanPostProcess beanPostProcess = (BeanPostProcess) aClass.getConstructor().newInstance();
                            beanPostProcessList.add(beanPostProcess);
                        }else{
                            //获取beanName
                            Commponent commponentAnnotation = aClass.getAnnotation(Commponent.class);
                            String beanName=commponentAnnotation.value();


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

                            //判断该类是单例加载还是多例加载,没有Scope注解表示多例
                            if (aClass.isAnnotationPresent(Scope.class)) {
                                Scope annotation = aClass.getAnnotation(Scope.class);
                                String value = annotation.value();
                                beanDefinition.setScope(value);
                            }else{
                                beanDefinition.setScope("singleton");
                            }

                            beanDefinitionMap.put(beanName,beanDefinition);
                        }


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

在这里插入图片描述

六.Spring实现NickelValue注解上面的value直接赋值给属性

在这里插入图片描述

1.创建NickelValue注解对象

package com.nickel.service;

import com.spring.Autowired;
import com.spring.Commponent;
import com.spring.InitializingBean;
import com.spring.Scope;

/**
 * @author Nickel
 * @version 1.0
 * @date 2023/7/5 8:43
 */
@Commponent("userService")
@Scope("singleton")
public class UserService implements InitializingBean,UserInterface {

    @Autowired
    private OrderService orderService;

    @NickelValue("Nickel")
    private String name;

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

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

2.在UserService中使用NickelValue对象

package com.nickel.service;

import com.spring.Autowired;
import com.spring.Commponent;
import com.spring.InitializingBean;
import com.spring.Scope;

/**
 * @author Nickel
 * @version 1.0
 * @date 2023/7/5 8:43
 */
@Commponent("userService")
@Scope("singleton")
public class UserService implements InitializingBean,UserInterface {

    @Autowired
    private OrderService orderService;

    @NickelValue("Nickel")
    private String name;

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

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

在这里插入图片描述

3.定义实现接口BeanPostProcess的实现方法

package com.nickel.service;

import com.spring.BeanPostProcess;
import com.spring.Commponent;

import java.lang.reflect.Field;

/**
 * @author Nickel
 * @version 1.0
 * @date 2023/7/5 17:42
 */
@Commponent
public class NickelValueBeanProcess implements BeanPostProcess {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        for (Field field : bean.getClass().getDeclaredFields()) {
            if (field.isAnnotationPresent(NickelValue.class)) {
                field.setAccessible(true);
                try {
                    field.set(bean,field.getAnnotation(NickelValue.class).value());
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        return bean;
    }
}

说明:注意必须添加Commponent注解

4.执行test方法

在这里插入图片描述

七.bean对象中获取spring加载的beanName

7.1创建BeanNameAware接口

package com.nickel.service;

/**
 * @author Nickel
 * @version 1.0
 * @date 2023/7/5 17:53
 */
public interface BeanNameAware {
    void setBeanName(String name);
}

在这里插入图片描述

7.2UserService实现BeanNameAware接口

package com.nickel.service;

import com.spring.Autowired;
import com.spring.Commponent;
import com.spring.InitializingBean;
import com.spring.Scope;

/**
 * @author Nickel
 * @version 1.0
 * @date 2023/7/5 8:43
 */
@Commponent("userService")
@Scope("singleton")
public class UserService implements InitializingBean,UserInterface,BeanNameAware{

    @Autowired
    private OrderService orderService;

    @NickelValue("Nickel")
    private String name;

    private String beanName;

    public void test(){
        System.out.println("test");
        System.out.println(orderService);
        System.out.println(name);
        System.out.println("获取beanName的值"+beanName);
    }

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

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


在这里插入图片描述

7.3NickelAnnotationConfigApplicationContext实现这个接口的方法

package com.spring;

import com.nickel.service.BeanNameAware;
import com.nickel.service.NickelValue;

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.FileNameMap;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author Nickel
 * @version 1.0
 * @date 2023/7/5 8:20
 */
public class NickelAnnotationConfigApplicationContext {

    private Class scanConfig;
    private Map<String,BeanDefinition> beanDefinitionMap=new HashMap<>();
    private Map<String,Object> singletonObjects=new HashMap<>();
    private List<BeanPostProcess> beanPostProcessList=new ArrayList<>();

    public NickelAnnotationConfigApplicationContext(Class scanConfig){
        this.scanConfig=scanConfig;

        //扫描逻辑
        scan(scanConfig);

        //创建bean
        for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
            String beanName=entry.getKey();
            BeanDefinition beanDefinition= entry.getValue();
            if (beanDefinition.getScope().equals("singleton")) {
                Object singletonBean=createBean(beanName,beanDefinition);
                singletonObjects.put(beanName,singletonBean);
            }
        }

    }

    public Object getBean(String beanName){
        if (!beanDefinitionMap.containsKey(beanName)) {
           throw new NullPointerException();
        }

        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if(beanDefinition.getScope().equals("singleton")){
           Object singletonBean=singletonObjects.get(beanName);
           if(singletonBean==null){
               singletonBean=createBean(beanName,beanDefinition);
               singletonObjects.put(beanName,singletonBean);
           }
           return singletonBean;
        }else{
            return createBean(beanName,beanDefinition);
        }
    }

    private Object createBean(String beanName,BeanDefinition beanDefinition){
        Class clazz=beanDefinition.getType();
        Object instance=null;
        try {
             instance = clazz.getConstructor().newInstance();

            for (Field field : clazz.getDeclaredFields()) {
                if (field.isAnnotationPresent(Autowired.class)) {
                    field.setAccessible(true);
                    //获取方法中类型的对象
                    Object object= Introspector.decapitalize(field.getType().getSimpleName());
                    field.set(instance,getBean(object.toString()));
                }
            }

            for (BeanPostProcess beanPostProcess : beanPostProcessList) {
                instance=beanPostProcess.postProcessBeforeInitialization(instance,beanName);
            }

            if (instance instanceof InitializingBean) {
                ((InitializingBean)instance).afterPropertiesSet();
            }

            if (instance instanceof BeanNameAware) {
                ((BeanNameAware)instance).setBeanName(beanName);
            }

            for (BeanPostProcess beanPostProcess : beanPostProcessList) {
                instance=beanPostProcess.postProcessAfterInitialization(instance,beanName);
            }
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        return instance;
    }

    private void scan(Class scanConfig) {
        if (scanConfig.isAnnotationPresent(CommponentScan.class)) {
            CommponentScan commponentScan =(CommponentScan) scanConfig.getAnnotation(CommponentScan.class);
            String path=commponentScan.value();
            path=path.replace(".","/");


            //通过ApplicationContext来加载该类
            ClassLoader classLoader = NickelAnnotationConfigApplicationContext.class.getClassLoader();
            URL resource = classLoader.getResource(path);

            File file=new File(resource.getPath());
            for (File listFile : file.listFiles()) {
                String absolutePath = listFile.getAbsolutePath();
                absolutePath=absolutePath.substring(absolutePath.indexOf("com"),absolutePath.indexOf(".class"));
                absolutePath=absolutePath.replace("\\",".");

                try {
                    Class<?> aClass  = classLoader.loadClass(absolutePath);
                    if (aClass.isAnnotationPresent(Commponent.class)) {
                        //判断是否实现BeanPostProcess方法
                        if (BeanPostProcess.class.isAssignableFrom(aClass)) {
                            BeanPostProcess beanPostProcess = (BeanPostProcess) aClass.getConstructor().newInstance();
                            beanPostProcessList.add(beanPostProcess);
                        }else{
                            //获取beanName
                            Commponent commponentAnnotation = aClass.getAnnotation(Commponent.class);
                            String beanName=commponentAnnotation.value();


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

                            //判断该类是单例加载还是多例加载,没有Scope注解表示多例
                            if (aClass.isAnnotationPresent(Scope.class)) {
                                Scope annotation = aClass.getAnnotation(Scope.class);
                                String value = annotation.value();
                                beanDefinition.setScope(value);
                            }else{
                                beanDefinition.setScope("singleton");
                            }

                            beanDefinitionMap.put(beanName,beanDefinition);
                        }


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

在这里插入图片描述

7.4测试结果

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值