Spring18 - 手写 IoC - @Bean
创建注解类 @Bean
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {
}
创建扫描包以及子包、解析注解的 ApplicationContext接口
public interface ApplicationContext {
Object getBean(Class clazz);
}
在 ApplicationContext 接口的实现类 AnnotationApplicationContext 中完成有 @Bean 注解的类的实例化
AnnotationApplicationContext 的有参构造中,实现包扫描与实例化
public AnnotationApplicationContext(String basePackage){
try {
//传入参数格式:com.atguigu.xxx.xxx
//1 将 . 替换为 / 改成文件路径
String packagePath = basePackage.replaceAll("\\.", "\\/");
//2 获取包绝对路径
Enumeration<URL> urls =
Thread.currentThread().getContextClassLoader().getResources(packagePath);
while(urls.hasMoreElements()){
URL url = urls.nextElement();
String filePath = URLDecoder.decode(url.getFile(),"UTF-8");
//将共有的根路径截取,方便后续操作
rootPath = filePath.substring(0, filePath.length() - packagePath.length());
//包扫描
loadBean(new File(filePath));
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
- 因此,在 ApplicationContext 对象创建后(调用有参构造),可以直接通过 getBean 方法获取类的实例对象
具体的包扫描与注解解析操作封装在 loadBean 方法中,对传入的文件进行扫描,实现类的实例化
public void loadBean(File file) throws Exception {
//1 判断当前是否为文件夹
if (file.isDirectory()) {
//2 获取文件夹里面的内容
File[] childrenFiles = file.listFiles();
//3 判断文件夹里面为空,直接返回
if (childrenFiles == null || childrenFiles.length == 0) {
return;
} else {
//4 如果文件夹不为空,遍历文件夹所有内容
for (File childFile : childrenFiles) {
if (childFile.isDirectory()) {
//4.1 遍历得到每个File对象,继续判断,如果还是文件夹,递归直到非文件夹
loadBean(childFile);
} else {
//4.2 如果遍历得到的是文件
//4.2.1 通过字符串截取,得到包路径+类名称部分
String pathWithClass = childFile.getAbsolutePath().substring(rootPath.length() - 1);
//4.2.2 判断当前文件是否为 .class(编译后)
if (pathWithClass.contains(".class")) {
//4.2.3 如果是 .class,把路径 \ 替换为 . 把 .class去掉
//最终得到路径的格式:com.atguigu.service.UserServiceImpl 则可以使用反射进行实例化
String allName = pathWithClass.replaceAll("\\\\", ".").replace(".class", "");
//4.2.4 获取类的 Class 从而判断是否为接口
Class<?> clazz = Class.forName(allName);
//接口不需要实例化,如果不是接口再判断是否有 @Bean 注解
if (!clazz.isInterface()) {
//4.2.5 判断类上是否有 @Bean注解
Bean annotation = clazz.getAnnotation(Bean.class);
//4.2.6 如果有 @Bean注解则进行反射实例
if (annotation != null) {
Object instance = clazz.getConstructor().newInstance();
//5 将实例化后的对象存入 map集合 beanFactory中
//5.1 判断当前类是否有接口
if (clazz.getInterfaces().length > 0) {
//5.1.1 如果有接口,让接口的 class 作为 map 中的 key
beanFactory.put(clazz.getInterfaces()[0], instance);
} else {
//5.1.2 如果没有接口,让自己的 class 作为 map 中的 key
beanFactory.put(clazz, instance);
}
}
}
}
}
}
}
}
}
分类:
SSM / Spring6
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析