自己实现IoC容器

自己实现IoC容器

  1. 定义必要的组件注解 @CustomAutowired, @CustomService, @CustomComponent, @CustomRepository, @CustomComponentScan
  2. 定义容器契约CustomBeanFactory
  3. 定义容器类上下文CustomAnnotationConfigApplicationContext实现CustomBeanFactory

定义必要的组件注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
public @interface CustomAutowired {
    boolean required() default true;
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface CustomComponent {
    String value() default  "";
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface CustomRepository {
    String value() default  "";
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface CustomService {
    String value() default  "";
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface CustomComponentScan {
    String[] value() default {};
}

定义容器契约

public interface CustomBeanFactory {

    /**
     * get bean by name
     * @param name
     * @return
     * @param <T>
     */
    <T extends Object> T getBean(String name);

    /**
     * get bean by class
     * @param clazz
     * @return
     * @param <T>
     */
    <T> T getBean(Class<?> clazz);

    /**
     * check whether container's has the bean
     * @param name
     * @return
     */
    boolean containsBean(String name);

}

通过Map缓存已生成的Bean

/**
 * Cache of singleton objects: bean name to bean instance.
 * same as spring's DefaultSingletonBeanRegistry#singletonObjects
 * (K, V) => (${componentName}, ${component.class})
 * eg (helloComponent,pr.iceworld.mockspring6ioc.simulatespringioc.component.HelloComponent.class)
 */
private Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
/**
 * Set of registered singletons, containing the bean names in registration order.
 * same as spring's DefaultSingletonBeanRegistry#registeredSingletons
 */
private Set<String> registeredSingletons = new LinkedHashSet<>(256);

容器类完成契约实现

@Override
public <T extends Object> T getBean(String name) {
    Object obj = singletonObjects.get(name);
    return null == obj ? null : (T) obj;
}

@Override
public <T> T getBean(Class<?> clazz) {
    return (T) singletonObjects.get(getBeanName(clazz));
}

private String getBeanName(Class<?> clazz) {
    return convertClazzNameAsRegular(clazz.getSimpleName());
}

@Override
public boolean containsBean(String name) {
    return singletonObjects.containsKey(name);
}

完整容器类完成契约实现

public class CustomAnnotationConfigApplicationContext implements CustomConfigurableApplicationContext {

    /**
     * Cache of singleton objects: bean name to bean instance.
     * same as spring's DefaultSingletonBeanRegistry#singletonObjects
     * (K, V) => (${componentName}, ${component.class})
     * eg (helloComponent,pr.iceworld.mockspring6ioc.simulatespringioc.component.HelloComponent.class)
     */
    private Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
    /**
     * Set of registered singletons, containing the bean names in registration order.
     * same as spring's DefaultSingletonBeanRegistry#registeredSingletons
     */
    private Set<String> registeredSingletons = new LinkedHashSet<>(256);

    public CustomAnnotationConfigApplicationContext(Class<?> configClazz) {
        init(configClazz);
    }

    @Override
    public <T extends Object> T getBean(String name) {
        Object obj = singletonObjects.get(name);
        return null == obj ? null : (T) obj;
    }

    @Override
    public <T> T getBean(Class<?> clazz) {
        return (T) singletonObjects.get(getBeanName(clazz));
    }

    private String getBeanName(Class<?> clazz) {
        return convertClazzNameAsRegular(clazz.getSimpleName());
    }

    @Override
    public boolean containsBean(String name) {
        return singletonObjects.containsKey(name);
    }

    private Class<?> configClazz;
    public void refresh()  {
        //invokeBeanFactoryPostProcessors();
        parseBean();
        finishBeanFactoryInitialization();
    }

    private void init(Class<?> configClazz) {
        this.configClazz = configClazz;
        refresh();
    }

    private void finishBeanFactoryInitialization() {
        try {
            registerBean();
            doDependenceInjection();
        } catch (Exception e) {
            throw new CustomRuntimeException(e);
        }
    }

    private void parseBean() {
        String[] scanBeanPackages = getScanBeanPackages();
        for (String scanBeanPath: scanBeanPaths()) {
            for (String scanBeanPackage: scanBeanPackages) {
                loadBeanClass(scanBeanPath, scanBeanPackage, true);
            }
        }
    }

    private String[] getScanBeanPackages() {
        return getCustomComponentScan().value();
    }

    private CustomComponentScan getCustomComponentScan() {
        CustomComponentScan customComponentScan = configClazz.getAnnotation(CustomComponentScan.class);
        if (null == customComponentScan) {
            throw new CustomRuntimeException("No package is specific.");
        }
        return customComponentScan;
    }

    protected String[] scanBeanPaths() {
        return new String[] { configClazz.getClassLoader().getResource("").getPath() };
    }

    /**
     *
     * @param path
     * @param packageName
     * @param first 路径解析标记
     */
    private void loadBeanClass(String path, String packageName, boolean first) {
        String newPath = first ? path + packageName.replace(".", "/") : path;
        File parent = new File(newPath);
        if (parent != null) {
            for (File child : parent.listFiles()) {
                if (child.isDirectory()) {
                    loadBeanClass0(child, packageName);
                } else {
                    addClassNames(child, packageName);
                }
            }
        }
    }

    private void loadBeanClass0(File file, String packageName) {
        loadBeanClass(file.getPath(), packageName + "." + file.getName(), false);
    }

    private void addClassNames(File file, String packageName) {
        if (file.getName().indexOf(".class") > 0) {
            registeredSingletons.add(getClassName(file.getName(), packageName));
        }
    }

    private String getClassName(String filename, String packageName) {
        return packageName + "." + filename.replace(".class", "");
    }

    private void registerBean() throws Exception {
        if (CollectionUtils.isEmpty(registeredSingletons))  return;
        for (String className : registeredSingletons) {
            Class clazz = Class.forName(className);
            Annotation[] annotations = clazz.getAnnotations();

            for (Annotation annotation: annotations) {
                registerBean0(annotation, clazz);
            }
        }
    }

    /**
     * 获取 注解组件类
     * @param annotation
     * @param clazz
     * @throws Exception
     */
    private void registerBean0(Annotation annotation, Class clazz) throws Exception {
        String beanName = null;
        boolean matched = false;
        if (annotation instanceof CustomComponent component) {
            component = (CustomComponent) clazz.getAnnotation(CustomComponent.class);
            if (null != component) {
                beanName = component.value();
                matched = true;
            }
        } else if (annotation instanceof CustomRepository component) {
            component = (CustomRepository) clazz.getAnnotation(CustomRepository.class);
            if (null != component) {
                beanName = component.value();
                matched = true;
            }
        } else if (annotation instanceof CustomService component) {
            component = (CustomService) clazz.getAnnotation(CustomService.class);
            if (null != component) {
                beanName = component.value();
                matched = true;
            }
        }

        if (matched) {
            beanName = getBeanName0(beanName, clazz);
            if (null != beanName) {
                singletonObjects.put(beanName, clazz.getDeclaredConstructor().newInstance());
            }
        }
    }

    private void doDependenceInjection() throws Exception {
        if (singletonObjects.isEmpty()) return;
        for (Object obj : singletonObjects.values()) {
            doInjection(obj);
        }
    }

    private void doInjection(Object targetObj) throws Exception {
        Field[] fields = targetObj.getClass().getDeclaredFields();
        if (ArrayUtils.isNotEmpty(fields)) {
            for (Field field : fields) {
                if (null != field.getAnnotation(CustomAutowired.class)) {
                    doInjection0(field, targetObj);
                }
            }
        }
    }

    private void doInjection0(Field field, Object specificObj) throws Exception {
        String beanName = getBeanName(field.getType());
        if (!singletonObjects.containsKey(beanName)) {
            singletonObjects.put(beanName, field.getType().getDeclaredConstructor().newInstance());
        }
        field.setAccessible(true);
        field.set(specificObj, singletonObjects.get(beanName));
        doInjection(singletonObjects.get(beanName));
    }

    private String getBeanName0(String beanName, Class<?> clazz) {
        return StringUtils.isEmpty(beanName)
                ? convertClazzNameAsRegular(clazz.getSimpleName())
                : beanName;
    }

    public static String convertClazzNameAsRegular(String clazzName) {
        return convert1stCharacter2LowerCase(clazzName);
    }

    public static String convert1stCharacter2LowerCase(String clazzName) {
        if (null != clazzName) {
            return Character.toLowerCase(clazzName.charAt(0)) + clazzName.substring(1);
        }
        return null;
    }
}
public interface CustomApplicationContext extends CustomBeanFactory {

}

public interface CustomConfigurableApplicationContext extends CustomApplicationContext {

    void refresh();
}
@CustomRepository
public class HelloRepository {

    public void sayHello(String name) {
        System.out.println("Hello - " + name);
    }
}
@CustomComponent("HelloAlias")
public class HelloAliasComponent {

}
@CustomComponent
public class HelloComponent {

    @CustomAutowired
    HelloRepository helloRepository;

    public void sayHello(String name) {
        helloRepository.sayHello(name);
    }
}
@CustomService
public class HelloService {

    @CustomAutowired
    HelloComponent helloComponent;

    public void sayHello(String name) {
        helloComponent.sayHello(name);
    }
}

扫描包路径

@CustomComponentScan("pr.iceworld.mockspring6ioc.simulatespringioc")
public class ProjectConfig {

}
public class SimulateSpringIocApplication {

    public static void main(String[] args) {
        CustomBeanFactory customBeanFactory = new CustomAnnotationConfigApplicationContext(ProjectConfig.class);
        HelloService helloService = customBeanFactory.getBean(HelloService.class);
        System.out.println("contain Bean: HelloAlias is true => " + customBeanFactory.containsBean("HelloAlias"));
        System.out.println("contain Bean: helloAlias is false => " + customBeanFactory.containsBean("helloAlias"));
        System.out.println("contain Bean: helloComponent is true => " + customBeanFactory.containsBean("helloComponent"));
        helloService.sayHello("Fernando");
    }
}
contain Bean: HelloAlias is true => true
contain Bean: helloAlias is false => false
contain Bean: helloComponent is true => true
Hello - Fernando
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值