-
手写一个最简单的IOC容器,从而了解spring的核心原理
从事开发工作多年,spring源码没有特意去看过。但是相关技术原理倒是背了不少,毕竟面试的那关还是得过啊! 正所谓面试造火箭,工作拧螺丝。下面实现一个最简单的ioc容器,供大家参考。
1.最终结果
2.涉及相关技术
(1) jdk动态代理
(2) java反射
3.源代码
(1)包扫描工具类
package com.hdwang.ioc.core.utils; import java.io.File; import java.io.UnsupportedEncodingException; import java.net.URL; import java.net.URLDecoder; import java.util.HashSet; import java.util.Set; /** * 类工具 */ public class ClassUtils { /** * 获取某包下所有类 * * @param packageName 包名 * @param isRecursion 是否遍历子包 * @return 类的完整名称 */ public static Set<String> getClassName(String packageName, boolean isRecursion) { Set<String> classNames = new HashSet<>(); ClassLoader loader = Thread.currentThread().getContextClassLoader(); String packagePath = packageName.replace(".", "/"); URL url = loader.getResource(packagePath); String filePath = null; try { filePath = URLDecoder.decode(url.getPath(), "utf-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } if (filePath != null) { classNames = getClassNameFromDir(filePath, packageName, isRecursion); } return classNames; } /** * 从项目文件获取某包下有类 * * @param filePath 文件路径 * @param isRecursion 是否遍历子包 * @return 类的完整名称 */ private static Set<String> getClassNameFromDir(String filePath, String packageName, boolean isRecursion) { Set<String> className = new HashSet<>(); File file = new File(filePath); File[] files = file.listFiles(); for (File childFile : files) { if (childFile.isDirectory()) { if (isRecursion) { className.addAll(getClassNameFromDir(childFile.getPath(), packageName + "." + childFile.getName(), isRecursion)); } } else { String fileName = childFile.getName(); if (fileName.endsWith(".class") && !fileName.contains("$")) { className.add(packageName + "." + fileName.replace(".class", "")); } } } return className; } }
(2)字符串工具类
package com.hdwang.ioc.core.utils; /** * 字符串工具类 */ public class StringUtils { /** * 判断字符串是否空白 * * @param str 字符串 * @return 字符串是否空白 */ public static boolean isBlank(String str) { return str == null || str.trim().isEmpty(); } /** * 判断字符串是否非空白 * * @param str 字符串 * @return 字符串是否非空白 */ public static boolean isNotBlank(String str) { return !isBlank(str); } }
(3) Bean对象注解
package com.hdwang.ioc.core.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Bean对象注解 * 待存入ioc容器的相关对象,声明在具体的实现类上 */ @Retention(RetentionPolicy.RUNTIME) @Target(value = {ElementType.TYPE}) public @interface MyBean { /** * 待存入ioc容器的Bean名称 * * @return Bean名称 */ String value() default ""; }
(4) 自动注入注解
package com.hdwang.ioc.core.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 自动注入注解 */ @Retention(RetentionPolicy.RUNTIME) @Target(value = {ElementType.FIELD}) public @interface AutoInject { /** * 注入的bean名称,为空时根据类型注入 * * @return Bean名称 */ String value() default ""; }
(5) Bean信息对象
package com.hdwang.ioc.core; /** * Bean类信息 */ public class BeanInfo { /** * Bean类的类型 */ private Class clasz; /** * 保存在ioc容器中的Bean名称 */ private String beanName; /** * 保存在ioc容器中的Bean类型 */ private Class beanType; /** * 保存在ioc容器中的bean对象实例 */ private Object bean; /** * 保存在ioc容器中的bean的代理对象实例 */ private Object proxyBean; public Class getClasz() { return clasz; } public void setClasz(Class clasz) { this.clasz = clasz; } public String getBeanName() { return beanName; } public void setBeanName(String beanName) { this.beanName = beanName; } public Class getBeanType() { return beanType; } public void setBeanType(Class beanType) { this.beanType = beanType; } public Object getBean() { return bean; } public void setBean(Object bean) { this.bean = bean; } public Object getProxyBean() { return proxyBean; } public void setProxyBean(Object proxyBean) { this.proxyBean = proxyBean; } }
(6) 上下文对象
package com.hdwang.ioc.core; import java.util.HashMap; import java.util.Map; /** * 上下文对象 * 用于保存应用运行中的信息 */ public class Context { /** * 根据Bean名称存储Bean的Map对象 */ private Map<String, Object> nameBeanMap = new HashMap<>(); /** * 根据Bean类型存储Bean的Map对象 */ private Map<Class, Object> typeBeanMap = new HashMap<>(); public Object getBean(String beanName) { return nameBeanMap.get(beanName); } public Object getBean(Class clasz) { return typeBeanMap.get(clasz); } public void putBean(String beanName, Object bean) { nameBeanMap.put(beanName, bean); } public void putBean(Class beanType, Object bean) { typeBeanMap.put(beanType, bean); } }
(7) Bean的代理对象
package com.hdwang.ioc.core; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * Bean的代理对象 * 使用jdk动态代理原理实现对java对象的代理,必须依赖接口 */ public class BeanProxy implements InvocationHandler { /** * 被代理的bean对象 */ private Object bean; public BeanProxy(Object bean) { this.bean = bean; } /** * 调用目标bean的相关方法 * * @param proxy 代理对象 * @param method 方法 * @param args 参数 * @return 方法返回值 * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before call method: " + method.getName()); Object result = method.invoke(bean, args); System.out.println("after call method: " + method.getName()); return result; } }
(8) Bean工厂类(ioc容器类)
package com.hdwang.ioc.core; import com.hdwang.ioc.core.annotation.AutoInject; import com.hdwang.ioc.core.annotation.MyBean; import com.hdwang.ioc.core.utils.ClassUtils; import com.hdwang.ioc.core.utils.StringUtils; import java.lang.reflect.Field; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.List; import java.util.Set; /** * Bean工厂 */ public class BeanFactory { /** * 基础包路径 */ private String basePackage; /** * 上下文对象 */ private Context context = new Context(); /** * 工厂构造器 * * @param basePackage 基础包路径 */ public BeanFactory(String basePackage) { this.basePackage = basePackage; init(); } /** * 工厂初始化 */ private void init() { //扫描包和加载bean到ioc容器 List<BeanInfo> myBeanList = scanPackageAndLoadBeans(); //给bean注入依赖对象 injectBeans(myBeanList); } /** * 扫描包和加载bean到ioc容器 * * @return 加载进ioc容器中的相关Bean信息 */ private List<BeanInfo> scanPackageAndLoadBeans() { List<BeanInfo> myBeanList = new ArrayList<>(); //找到包下所有类 Set<String> classNames = ClassUtils.getClassName(basePackage, true); for (String className : classNames) { try { //查找类 Class clasz = Class.forName(className); //判断类上是否存在MyBean注解 if (clasz.isAnnotationPresent(MyBean.class)) { //获取类上的MyBean注解 MyBean myBeanAnnotation = (MyBean) clasz.getAnnotation(MyBean.class); //获取注解值,即Bean名称 String beanName = myBeanAnnotation.value(); //获取类继承的相关接口 Class[] interfaces = clasz.getInterfaces(); //判断类是否可以采用jdk动态代理(有接口方可进jdk动态代理,创建代理对象) boolean canJdkProxyBean = interfaces != null && interfaces.length > 0; //获取待注入ioc容器的Bean的类型 Class beanType = getBeanType(clasz, canJdkProxyBean); //实例化当前类,生成bean实例 Object bean = clasz.newInstance(); Object iocBean = bean; if (canJdkProxyBean) { //可以使用jdk动态代理,则创建代理对象,代理此Bean Object proxyBean = this.createBeanProxy(bean); iocBean = proxyBean; } //保存生成的bean到ioc容器 if (StringUtils.isNotBlank(beanName)) { context.putBean(beanName, iocBean); } context.putBean(beanType, iocBean); //暂存Bean信息 BeanInfo beanInfo = new BeanInfo(); beanInfo.setClasz(clasz); beanInfo.setBeanName(beanName); beanInfo.setBeanType(beanType); beanInfo.setBean(bean); beanInfo.setProxyBean(canJdkProxyBean ? iocBean : null); myBeanList.add(beanInfo); } } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { System.out.println("加载bean异常"); e.printStackTrace(); } } return myBeanList; } /** * 给相关Bean注入依赖的Bean * * @param myBeanList 注入到ioc容器中的所有的Bean */ private void injectBeans(List<BeanInfo> myBeanList) { for (BeanInfo myBeanInfo : myBeanList) { Class beanClass = myBeanInfo.getClasz(); Object bean = myBeanInfo.getBean(); //查找Bean的声明的所有字段 Field[] fields = beanClass.getDeclaredFields(); for (Field field : fields) { //判断字段上是否有AutoInject注解 if (field.isAnnotationPresent(AutoInject.class)) { //查找待注入的bean AutoInject autoInjectAnnotation = field.getAnnotation(AutoInject.class); //获取注解的值,即待注入的Bean名称 String injectBeanName = autoInjectAnnotation.value(); //获取字段的类型,即待注入的Bean类型 Class injectBeanType = field.getType(); Object proxyBean = null; //从查找ioc容器中查找待注入的Bean对象 if (StringUtils.isNotBlank(injectBeanName)) { //Bean名称不为空,则根据名称查找Bean proxyBean = context.getBean(injectBeanName); } else { //Bean名称为空,则根据Bean类型查找Bean proxyBean = context.getBean(injectBeanType); } //设置当前字段可访问 field.setAccessible(true); try { //将找到的Bean注入到当前字段上 field.set(bean, proxyBean); } catch (IllegalAccessException e) { e.printStackTrace(); } } } } } /** * 获取待注入到ioc容器中的Bean类型 * * @param clasz Bean类型 * @param canJdkProxyBean 是否可以使用jdk动态代理 * @return 注入到ioc容器中的Bean类型 */ private Class getBeanType(Class clasz, boolean canJdkProxyBean) { Class beanType = null; if (canJdkProxyBean) { //可以使用jdk动态代理,则bean类型取bean的接口类型 beanType = clasz.getInterfaces()[0]; } else { //不可以使用jdk动态代理,bean类型就取当前类类型 beanType = clasz; } return beanType; } /** * 根据Bean名称获取Bean对象 * * @param beanName Bean名称 * @param <T> Bean类型 * @return ioc容器中的Bean, 找不到返回null */ public <T> T getBean(String beanName) { return (T) context.getBean(beanName); } /** * 根据Bean类型获取Bean对象 * * @param clasz 注入到ioc容器中的Bean类型 * @param <T> Bean类型 * @return ioc容器中的Bean, 找不到返回null */ public <T> T getBean(Class clasz) { return (T) context.getBean(clasz); } /** * 创建代理bean * * @param bean 当前Bean对象 * @return Bean的代理对象 */ private Object createBeanProxy(Object bean) { InvocationHandler invocationHandler = new BeanProxy(bean); Object proxyBean = Proxy.newProxyInstance(bean.getClass().getClassLoader(), bean.getClass().getInterfaces(), invocationHandler); return proxyBean; } }
4.示例代码
(1) User模型
package com.hdwang.ioc.example.model; public class User { private Long id; private String name; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + '}'; } }
(2) UserService
package com.hdwang.ioc.example.service; import com.hdwang.ioc.example.model.User; public interface UserService { User getUserById(Long id); }
(3) UserServiceImpl
package com.hdwang.ioc.example.service; import com.hdwang.ioc.core.annotation.MyBean; import com.hdwang.ioc.example.model.User; @MyBean("userService") public class UserServiceImpl implements UserService { @Override public User getUserById(Long id) { User user = new User(); if (id == 1) { user.setId(id); user.setName("张三"); } else if (id == 2) { user.setId(id); user.setName("李四"); } return user; } }
(4) UserController
package com.hdwang.ioc.example.controller; import com.hdwang.ioc.core.annotation.AutoInject; import com.hdwang.ioc.core.annotation.MyBean; import com.hdwang.ioc.example.model.User; import com.hdwang.ioc.example.service.UserService; @MyBean("userController") public class UserController { @AutoInject UserService userService; public User getUserById(Long id) { return userService.getUserById(id); } }
(5) 主函数
package com.hdwang.ioc.example; import com.hdwang.ioc.core.BeanFactory; import com.hdwang.ioc.example.controller.UserController; import com.hdwang.ioc.example.model.User; /** * 程序启动类 */ public class Main { /** * 主函数入口 * * @param args 入参 */ public static void main(String[] args) { //定义要扫描的包名 String basePackage = "com.hdwang.ioc.example"; //初始化Bean工厂 BeanFactory beanFactory = new BeanFactory(basePackage); //获取指定的Bean UserController userController = beanFactory.getBean(UserController.class); //调用Bean中的方法 User user = userController.getUserById(1L); System.out.println(user); } }
5.运行结果
before call method: getUserById
after call method: getUserById
User{id=1, name='张三'}
6.总结说明
ioc的实现,主要是用到了java的反射技术,和动态代理无关,代理对象可以实现一些增强的功能,所以人们常常称spring的bean的代理类为增强类!哈哈。。。
7.附录
项目源码:https://github.com/hdwang123/iocdemo
原文:https://www.cnblogs.com/hdwang/p/14774421.html
最新更新
python爬虫及其可视化
使用python爬取豆瓣电影短评评论内容
nodejs爬虫
Python正则表达式完全指南
爬取豆瓣Top250图书数据
shp 地图文件批量添加字段
爬虫小试牛刀(爬取学校通知公告)
【python基础】函数-初识函数
【python基础】函数-返回值
HTTP请求:requests模块基础使用必知必会
SQL SERVER中递归
2个场景实例讲解GaussDB(DWS)基表统计信息估
常用的 SQL Server 关键字及其含义
动手分析SQL Server中的事务中使用的锁
openGauss内核分析:SQL by pass & 经典执行
一招教你如何高效批量导入与更新数据
天天写SQL,这些神奇的特性你知道吗?
openGauss内核分析:执行计划生成
[IM002]Navicat ODBC驱动器管理器 未发现数据
初入Sql Server 之 存储过程的简单使用
uniapp/H5 获取手机桌面壁纸 (静态壁纸)
[前端] DNS解析与优化
为什么在js中需要添加addEventListener()?
JS模块化系统
js通过Object.defineProperty() 定义和控制对象
这是目前我见过最好的跨域解决方案!
减少回流与重绘
减少回流与重绘
如何使用KrpanoToolJS在浏览器切图
performance.now() 与 Date.now() 对比