自己实现IoC容器
- 定义必要的组件注解
@CustomAutowired
,@CustomService
,@CustomComponent
,@CustomRepository
,@CustomComponentScan
- 定义容器契约
CustomBeanFactory
- 定义容器类上下文
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