- 定义两个注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Component {
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface AutoWired {
}
- 定义几个测试类
@Component
public class UserMapper {
}
@Component
@Setter
@Getter
public class UserServiceV2 {
@AutoWired
private OrderService orderService;
private String name;
private Integer age;
public String address;
private void helloUser(){
System.out.println("helloUser...");
}
private void helloUser(String str){
System.out.println("helloUser..." + str);
}
}
@Setter
@Getter
@Component
public class OrderService {
private String orderNo = "123";
@AutoWired
private UserServiceV2 userServiceV2;
}
@Data
@Component
public class UserController {
@AutoWired
private UserServiceV2 userService;
}
- 容器代码
public class ApplicationContext {
private static Map<Class, Object> context = new ConcurrentHashMap<>();
public static void init(String basePackages) throws Exception{
String basePackageSperator = basePackages.replaceAll("\\.", Matcher.quoteReplacement(File.separator));
//获取到classpath
URL resource = Thread.currentThread().getContextClassLoader().getResource("");
File file = new File(resource.getFile());
String basePath = file.getAbsolutePath() + File.separator + basePackageSperator;
//从basePath下开始遍历class文件
File classFilePath = new File(basePath);
List<String> filePaths = new ArrayList<>();
getPath(classFilePath.getAbsolutePath(), filePaths, ".class");
//遍历这些class文件,创建标有@Component注解的对象
filePaths = filePaths.stream().map(item -> {
item = item = item.replace(classFilePath.getAbsolutePath() + File.separator, "");
item = item.replace(".class","");
return item;
}).collect(Collectors.toList());
doInitContext(basePackages,filePaths);
}
private static void doInitContext(String basePackages, List<String> filePaths) throws Exception {
for (String filePath : filePaths) {
Class<?> clazz = Class.forName(basePackages + "." + filePath);
if (clazz.isAnnotationPresent(Component.class)){
Object newInstance = clazz.newInstance();
context.put(clazz,newInstance);
}
}
context.keySet().stream().forEach(k -> {
Field[] declaredFields = k.getDeclaredFields();
for (Field declaredField : declaredFields) {
boolean present = declaredField.isAnnotationPresent(AutoWired.class);
if (present){
Class<?> type = declaredField.getType();
//从容器中找出该属性类型的实例
Object bean = getBean(type);
//将这个bean赋值给该对象的属性
Object instance = context.get(k);
declaredField.setAccessible(true);
try {
declaredField.set(instance,bean);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
});
}
public static <T> T getBean(Class<T> clazz){
return (T) context.get(clazz);
}
private static void getPath(String rootPath, List<String> filePaths, String suffix) {
File file = new File(rootPath);
Optional<File[]> files = Optional.ofNullable(file.listFiles(
(f) -> f.isDirectory() || f.getName().endsWith(suffix)
));
//找出该目录下所有符合后缀名的文件名以及目录,目录需要递归遍历
files.ifPresent(fs -> {
for (File f : fs) {
if (f.isDirectory()) {
getPath(f.getAbsolutePath(), filePaths, suffix);
} else {
filePaths.add(f.getAbsolutePath());
}
}
});
}
public static void main(String[] args) throws Exception {
ApplicationContext.init("com.example.demo.reflect.app");
ApplicationContext.context.keySet().stream().forEach(k -> {
System.out.println(context.get(k));
});
UserServiceV2 bean = ApplicationContext.getBean(UserServiceV2.class);
OrderService orderService = ApplicationContext.getBean(OrderService.class);
System.out.println("bean.getOrderService() == orderService = " + (bean.getOrderService() == orderService) );
}
}
核心思想就是从classpath目录下,拼接上给定的包名,整理成一个目录,然后遍历这个目录下的class文件,将文件路径格式替换为全类名格式,然后通过给定全限定类名,创建class实例对象。判断该class对象有没有被@Component注解修饰,有的话创建一个对象,存入容器,创建完成以后再继续遍历每个class对象的成员变量是否被@AutoWried修饰,如果有,则找到该成员变量类型的对象,为该class对象的该成员变量赋值。这整个过程中其实就是对象引用的来回传递。