Spring IoC常用注解手写实现

执行流程

1.    初始化Spring容器时传入配置类,通过配置类的@ComponentScan注解告知Spring需要扫描的包路径,不在扫描包路径下的@Component等注解修饰的Bean不会被IoC容器创建;
2.    从工程根目录/target/classes中获取全类名,使用类加载器加载全类名得到class对象;
3.    通过无参构造函数来实例化单例bean并放入单例池中,beanName默认是类名首字母小写;
4.    调用getBean方法时根据beanName从单例池中获取Bean。

代码(注解类忽略)

Address类

package com.wjq.bean;

import com.wjq.myspring.annotation.Component;

@Component("address")
public class Address {
    public String getAddressStr() {
        return "北京";
    }
}

User类

package com.wjq.bean;

import com.wjq.myspring.annotation.Autowired;
import com.wjq.myspring.annotation.Component;

@Component
public class User {
    @Autowired
    private Address address;

    public String getAddressStr() {
        return address.getAddressStr();
    }
}

ApplicationConfig类

package com.wjq.myspring;

import com.wjq.myspring.annotation.ComponentScan;

/**
 * 配置类,告知扫描的包路径
 */
@ComponentScan("com.wjq.bean")
public class ApplicationConfig {

}

ApplicationContext类

package com.wjq.myspring;

import com.wjq.myspring.annotation.Autowired;
import com.wjq.myspring.annotation.Component;
import com.wjq.myspring.annotation.ComponentScan;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Spring容器类
 */
public class ApplicationContext {
    private ConcurrentHashMap<String, Object> singletonPool = new ConcurrentHashMap<>();

    public ApplicationContext(Class<?> configClass) {
        // 解析配置类上的@ComponentScan注解,获取扫描的包路径com.wjq.bean
        String packagePath = configClass.getDeclaredAnnotation(ComponentScan.class).value();

        // 扫描并创建Bean
        scanAndCreateBean(packagePath);
    }

    private void scanAndCreateBean(String packagePath) {
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        URL url = loader.getResource(packagePath.replace(".", "/"));

        // filePath是com.wjq.bean下java文件编译后生成的class文件所在的绝对路径
        String filePath;
        try {
            filePath = URLDecoder.decode(url.getFile(), "UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }

        File file = new File(filePath);
        List<String> classNames = new ArrayList<>();
        // 从target/classes中获取全类名
        for (File childFile : file.listFiles()) {
            String childFilePath = childFile.getPath();
            if (childFilePath.endsWith(".class")) {
                // \\表示\
                childFilePath = childFilePath.substring(childFilePath.lastIndexOf("\\") + 1, childFilePath.lastIndexOf("."));
                // childFilePath结果是全类名com.wjq.bean.Address和com.wjq.bean.User
                classNames.add(packagePath + "." + childFilePath);
            }
        }

        for (String className : classNames) {
            try {
                Class<?> clazz = loader.loadClass(className);
                // 判断类上是否有@Component注解
                if (clazz.isAnnotationPresent(Component.class)) {
                    // 类名首字母小写作为beanName
                    String curClassName = className.substring(className.lastIndexOf('.') + 1);
                    String beanName = curClassName.substring(0, 1).toLowerCase() + curClassName.substring(1);

                    // 通过无参构造函数来创建bean
                    Object obj = clazz.getDeclaredConstructor().newInstance();

                    // 依赖注入
                    for (Field declaredField : clazz.getDeclaredFields()) {
                        // 判断成员变量是否有@Autowired注解
                        if (declaredField.isAnnotationPresent(Autowired.class)) {
                            // 根据beanName获取对应bean
                            Object fieldBean = getBean(declaredField.getName());
                            declaredField.setAccessible(true);
                            declaredField.set(obj, fieldBean);
                        }
                    }

                    singletonPool.put(beanName, obj);
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    public Object getBean(String beanName) {
        if (singletonPool.containsKey(beanName)) {
            return singletonPool.get(beanName);
        }

        throw new RuntimeException("bean not exist");
    }
}

Main类

import com.wjq.bean.User;
import com.wjq.myspring.ApplicationConfig;
import com.wjq.myspring.ApplicationContext;

public class Main {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ApplicationContext(ApplicationConfig.class);
        User user = (User) applicationContext.getBean("user");
        System.out.println(user.getAddressStr());
    }
}

执行结果

posted on 2022-08-14 20:05  王景迁  阅读(53)  评论(0编辑  收藏  举报

导航