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