自己实现IOC容器,java代码实现简易版IOC容器,IOC容器实现的步骤分解
一、需求
实现一个简易的IOC容器,管理Bean,从IOC容器的BeanFactory中获取实例,从而取代自己new实例的做法。
二、实现步骤分析

三、具体代码实现
自定义注解类 MyComponent 和 MyAutowired:
package MyIOCAndMyAop.Annotations; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; @Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyComponent { }
package MyIOCAndMyAop.Annotations; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; @Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAutowired { }
MyIOC容器的实现:自己实现简单的IOC容器,来管理bean:BeanFactory<String, Object>,String为全类名,Object为通过类加载器加载进来的Class对象反射创建的bean。
package MyIOCAndMyAop; import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import MyIOCAndMyAop.Annotations.MyAutowired;
import MyIOCAndMyAop.Annotations.MyComponent; public class MyIOC { // beanFactory 要声明为类变量,因为它不能被GC回收:
private static HashMap<String, Object> beanFactory = new HashMap<>(); /**
* 随着MyIOC类被加载到内存进行初始化,就会执行其静态代码块
* @param args
*/
static {
init();
} /**
* 获取BeanFactory
* @return
*/
public static HashMap<String, Object> getBeanFactory(){
return beanFactory;
} /**
* 根据全类名更新BeanFactory中的bean
* @param typeName
* @param proxyInstance
*/
public static void updateBeanFromBeanFactory(String typeName, Object proxyInstance) {
beanFactory.put(typeName, proxyInstance);
} /**
* 通过全类名获得对应的实例
* @param completeClassName
* @return
*/
public static Object getBean(String completeClassName) {
return beanFactory.get(completeClassName);
} public static void init() {
HashMap<String, Class> loadedClazzList;//<全类名, Class对象>
try {
//1.加载指定的类
File file = new File("C:\\workplace\\test\\bin");//!!!这里写死了路径不合适,可以做改进
loadedClazzList = loadAllClazz(file); //2.实例化并放入IOC容器中:对于那些有注解的类,做实例化
newInstance(loadedClazzList); // 3.完成依赖注入
autoWired(); // 4.测试:找到beanFactory中的某个bean,并执行其某个方法 ===> 这里有个问题,只能执行指定的方法,所以beanFactory中的所有bean都得有这个方法,这里先这么做了,但这明显不合理。
// test();
} catch (Exception e) {
e.printStackTrace();
}
} public static void test() throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
for(Map.Entry<String, Object> entry : beanFactory.entrySet()) {
// System.out.println(entry.getKey() + " ---> ");
Method method = entry.getValue().getClass().getMethod("test");
method.invoke(entry.getValue());
}
} /**
* 对BeanFactory中管理的所有bean完成依赖注入。
* 交给IOC容器管理的类,需要注入成员变量,如果该成员变量是自定义的类,该类也是需要交给IOC容器管理的。
* @throws IllegalAccessException
* @throws IllegalArgumentException
* @throws MalformedURLException
* @throws ClassNotFoundException
*/
public static void autoWired() throws IllegalArgumentException, IllegalAccessException, ClassNotFoundException, MalformedURLException {
for(Map.Entry<String, Object> entry : beanFactory.entrySet()) {
Field[] fields = entry.getValue().getClass().getDeclaredFields();//!!!getFields():只能获取到运行时类中及其父类中声明为public的属性;getDeclaredFields():获取运行时类本身声明的所有的属性
for(Field field : fields) {
Annotation[] annotations = field.getAnnotations();
for(int i = 0; i < annotations.length; i++) {
if(annotations[i].annotationType() == MyAutowired.class) {
//从beanFactory中找到相应的bean,赋值给该成员变量,以完成依赖注入。
Object object = beanFactory.get(field.getType().getTypeName());
// System.out.println(field.getType().getTypeName());//MyIOCAndMyAop.bean.Student
//通过Field(反射)为成员变量赋值:
field.setAccessible(true);
field.set(entry.getValue(), object);
}
}
}
}
} /**
* 实例化: 放到loadedClazzlist集合中的Class对象都是需要做实例化的(加了@MyComponent注解的类)
*/
public static void newInstance(HashMap<String, Class> loadedClazzList) throws InstantiationException, IllegalAccessException, ClassNotFoundException, MalformedURLException {
for(Map.Entry<String, Class> entry : loadedClazzList.entrySet()) {
beanFactory.put(entry.getKey(), entry.getValue().newInstance());
}
} /**
* 加载指定路径下的类。
* 类加载:javase/src/classLoader/a01helloworld/A03GetExtClassLoader
* @return 类加载器加载进来的指定路径下的所有Class对象
* @throws IllegalAccessException
* @throws InstantiationException
*/
public static HashMap<String, Class> loadAllClazz(File file) throws ClassNotFoundException, MalformedURLException, InstantiationException, IllegalAccessException {
//用于存放类加载器加载进来的Class对象<全类名, Class对象>
HashMap<String, Class> loadedClazzList = new HashMap<>(); URL[] urls = new URL[]{file.toURI().toURL()};
URLClassLoader classLoader = new URLClassLoader(urls); ArrayList<String> allCompleteClassName = getAllCompleteClassName(file); for(String element : allCompleteClassName) {
Class<?> clazz = classLoader.loadClass(element);
Annotation[] annotations = clazz.getAnnotations();// !!!拿到Class对象的时候,就进行筛选出有注解的Class再放到容器中,而不是把指定路径下的所有类都加载进来。
for(int i = 0; i < annotations.length; i++) {
if(annotations[i].annotationType() == MyComponent.class) {
loadedClazzList.put(element, clazz);//得到各个类对象了
}
}
}
return loadedClazzList;
} /**
* 得到allNeedLoadClassFiles中所有要加载的class文件的全类名
*/
private static ArrayList<String> getAllCompleteClassName(File file) {
// 所有要加载的class的全类名,如:classLoader.a02myclassloader.bean.Bean
ArrayList<String> completeClassNames = new ArrayList<>();
// 用于存放指定路径下所有要加载的class文件
ArrayList<File> allNeedLoadClassFiles = new ArrayList<File>(); getAllNeedLoadClassFile(file, allNeedLoadClassFiles); for (File element : allNeedLoadClassFiles) {
String filePath = element.getPath().replace("\\", "."); if(filePath.endsWith(".class")) {
//filePath.indexOf("bin.")+4:"bin."之后。filePath.lastIndexOf(".class"):".class"之前,该方法是从后往前找,性能更高。
String completeClassName = filePath.substring(filePath.indexOf("bin.")+4, filePath.lastIndexOf(".class"));
completeClassNames.add(completeClassName);
}
}
return completeClassNames;
} /**
* 通过递归获取指定路径下所有要加载的class文件
* 递归:javase/src/recursion/A_PrintFolder
* @param file
*/
private static ArrayList<File> getAllNeedLoadClassFile(File file, ArrayList<File> allNeedLoadClassFiles) {
if(!file.exists()) {//!!!这里要多一层判断
return allNeedLoadClassFiles;
} if (file.isDirectory()) {//是文件夹
File[] listFiles = file.listFiles();
for (File element : listFiles) {
getAllNeedLoadClassFile(element, allNeedLoadClassFiles);
}
} else {//是文件
allNeedLoadClassFiles.add(file);
}
return allNeedLoadClassFiles;
}
}
自己实现AOP 1.0版本,含步骤分解:https://www.cnblogs.com/laipimei/p/11137250.html
自己实现SpringAOP 2.0版本,含实现步骤分解:https://www.cnblogs.com/laipimei/p/11163377.html
自己实现IOC容器,java代码实现简易版IOC容器,IOC容器实现的步骤分解的更多相关文章
- [java代码库]-简易计算器(第二种)
[java代码库]-简易计算器(第二种) 第二种方案:在程序中不使用if/switch……case等语句,完成计算器功能. <html> <head> <title> ...
- Java 语言实现简易版扫码登录
基本介绍 相信大家对二维码都不陌生,生活中到处充斥着扫码登录的场景,如登录网页版微信.支付宝等.最近学习了一下扫码登录的原理,感觉蛮有趣的,于是自己实现了一个简易版扫码登录的 Demo,以此记录一下学 ...
- 纯css爱心代码-最近超级火的打火机与公主裙中的爱心代码(简易版)
theme: cyanosis 最近打火机与公主裙中的爱心代码超级火,看着特别心动,让俺用css来写个简易版!!! 先看效果: 代码拆解: 主要是分为3大部分 分子颗粒 爱心 动画 代码实现: 分子颗 ...
- 10行代码实现简易版的Promise
实现之前,我们先看看Promise的调用 const src = 'https://img-ph-mirror.nosdn.127.net/sLP6rNBbQhy0OXFNYD9XIA==/79910 ...
- [java代码库]-简易计算器(第一种)
简易计算器(效果如图所示) 第一种方案:采用Javascript+html完成计算器,支持+-*/,结果显示不允许使用input输入域(可以考虑使用<span>) <html> ...
- JAVA实现二叉树(简易版--实现了二叉树的各种遍历)
1,个人感觉二叉树的实现主要还是如何构造一颗二叉树.构造二叉树函数的设计方法多种多样,本例采用 addNode 方法实现.以下程序通过定义内部类来表示二叉树的结点,然后再实现了二叉树这种数据结构的一些 ...
- Jenkins日常运维笔记-重启数据覆盖问题、迁移、基于java代码发版(maven构建)
之前在公司机房部署了一套jenkins环境,现需要迁移至IDC机房服务器上,迁移过程中记录了一些细节:1)jenkins默认的主目录放在当前用户家目录路径下的.jenkins目录中.如jenkins使 ...
- Java与Scala的两种简易版连接池
Java版简易版连接池: import java.sql.Connection; import java.sql.DriverManager; import java.util.LinkedList; ...
- 从 Java 代码逆向工程生成 UML 类图和序列图
from:http://blog.itpub.net/14780914/viewspace-588975/ 本文面向于那些软件架构师,设计师和开发人员,他们想使用 IBM® Rational® Sof ...
随机推荐
- android L 关机流程图
下面是简单的流程图,从Java到kernel层. ShutdownThread.java文件 stop playing music,因为后面可能要playing shutdown music. 代码如 ...
- 简单易用的MongoDB
从我第一次听到Nosql这个概念到如今已经走过4个年头了,但仍然没有具体的去做过相应的实践.最近获得一段学习休息时间,购买了Nosql技术实践一书,正在慢慢的学习.在主流观点中,Nosql大体分为4类 ...
- Win10《芒果TV》商店版更新v3.2.0:全新播放体验,跟着爸爸,想去哪就去哪
喜迎十一月黑五大促,跟着爸爸,想去哪就去哪,<芒果TV>UWP版迅速更新v3.2.0版,全新播放页华丽蜕变,新增互动评论.猜你喜欢.宽窄屏适配.多窗体模式切换. 芒果TV UWP V3.2 ...
- C语言实现的CRC16/CCITT-FALSE校验码函数
要求:输入字符串“00 AA FF CC AA 01 00” 得到校验码“79B1” 方法1: // ConsoleApplication1.cpp: 定义控制台应用程序的入口点. // #inclu ...
- C#获取字符串宽度像素
通过Graphics对象的MeasureString方法可以获取字符串的大小,如下: Graphics graphics = CreateGraphics(); SizeF sizeF = graph ...
- UWP入门(七)--SplitView详解与页面跳转
原文:UWP入门(七)--SplitView详解与页面跳转 官方文档,逼着自己用英文看,UWP开发离不开官方文档 1. SplitView 拆分视图控件 拆分视图控件具有一个可展开/可折叠的窗格和一个 ...
- Linux7下配置Nginx站点.
今天闲来无事,把服务器重新配置了一下,作为开发者,实际上很多人都是长时间不去配置服务器的,所以曾经很多东西又忘掉了差不多. 特在此分享一下配置成功后的配置文件内容. 其实配置后的文件内容很简单,也没有 ...
- Nginx多种负载均衡策略搭建
背景介绍 上篇介绍了利用Nginx反向代理实现负载均衡,本文详细讲述Nginx下的几种负载均衡策略. 轮询 轮询,顾名思义,就是轮流请求,基于上篇文章的介绍,我们将负载均衡策略聚焦于default.c ...
- kubernetes实战篇之Dashboard的访问权限限制
系列目录 前面我们的示例中,我们创建的ServiceAccount是与cluster-admin 绑定的,这个用户默认有最高的权限,实际生产环境中,往往需要对不同运维人员赋预不同的权限.而根据实际情况 ...
- Electron为文件浏览器创建图标(三)
在前面的文章中,请看之前文章,我们已经完成了使用 electron做文件浏览器这么一个应用,现在我们需要为应用创建图标操作.为应用创建图标以后,我们就可以从计算机中与其他应用区分开来,如果我们自己会做 ...