手写一个最简单的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
手写一个最简单的IOC容器,从而了解spring的核心原理的更多相关文章
- 【spring】-- 手写一个最简单的IOC框架
1.什么是springIOC IOC就是把每一个bean(实体类)与bean(实体了)之间的关系交给第三方容器进行管理. 如果我们手写一个最最简单的IOC,最终效果是怎样呢? xml配置: <b ...
- IoC原理-使用反射/Emit来实现一个最简单的IoC容器
从Unity到Spring.Net,到Ninject,几年来陆陆续续用过几个IoC框架.虽然会用,但也没有一直仔细的研究过IoC实现的过程.最近花了点时间,下了Ninject的源码,研究了一番,颇有收 ...
- 【最简单IOC容器实现】实现一个最简单的IOC容器
前面DebugLZQ的两篇博文: 浅谈IOC--说清楚IOC是什么 IoC Container Benchmark - Performance comparison 在浅谈IOC--说清楚IOC是什么 ...
- 手写一个线程池,带你学习ThreadPoolExecutor线程池实现原理
摘要:从手写线程池开始,逐步的分析这些代码在Java的线程池中是如何实现的. 本文分享自华为云社区<手写线程池,对照学习ThreadPoolExecutor线程池实现原理!>,作者:小傅哥 ...
- 手把手教你手写一个最简单的 Spring Boot Starter
欢迎关注微信公众号:「Java之言」技术文章持续更新,请持续关注...... 第一时间学习最新技术文章 领取最新技术学习资料视频 最新互联网资讯和面试经验 何为 Starter ? 想必大家都使用过 ...
- 手写一个超简单的Vue
基本结构 这里我根据自己的理解模仿了Vue的单文件写法,通过给Vue.createApp传入参数再挂载元素来实现页面与数据的互动. 其中理解不免有错,希望大佬轻喷. 收集数据 这里将Vue.creat ...
- 手写一个最迷你的Web服务器
今天我们就仿照Tomcat服务器来手写一个最简单最迷你版的web服务器,仅供学习交流. 1. 在你windows系统盘的F盘下,创建一个文件夹webroot,用来存放前端代码. 2. 代码介绍: ( ...
- 比Spring简单的IoC容器
比Spring简单的IoC容器 Spring 虽然比起EJB轻量了许多,但是因为它需要兼容许多不同的类库,导致现在Spring还是相当的庞大的,动不动就上40MB的jar包, 而且想要理解Spring ...
- 利用SpringBoot+Logback手写一个简单的链路追踪
目录 一.实现原理 二.代码实战 三.测试 最近线上排查问题时候,发现请求太多导致日志错综复杂,没办法把用户在一次或多次请求的日志关联在一起,所以就利用SpringBoot+Logback手写了一个简 ...
随机推荐
- 【Azure 服务总线】详解Azure Service Bus SDK中接收消息时设置的maxConcurrentCalls,prefetchCount参数
(Azure Service Bus服务总线的两大类消息处理方式: 队列Queue和主题Topic) 问题描述 使用Service Bus作为企业消息代理,当有大量的数据堆积再Queue或Topic中 ...
- 攻防世界 reverse babymips
babymips XCTF 4th-QCTF-2018 mips,ida中想要反编译的化需要安装插件,这题并不复杂直接看mips汇编也没什么难度,这里我用了ghidra,直接可以查看反编译. 1 ...
- 攻防世界 reverse 新手练习区
1.re1 DUTCTF IDA shift+F12 查看字符串 DUTCTF{We1c0met0DUTCTF} 2.game ZSCTF zsctf{T9is_tOpic_1s_v5ry_int7r ...
- 生产中常用的获取IP地址方法的总结
从ifconfig命令的结果中筛选出除了lo网卡之外的所有IPv4地址 centos7 (1)ifconfig | awk '/inet / && !($2 ~ /^127/){pri ...
- Database | 浅谈Query Optimization (1)
综述 由于SQL是声明式语言(declarative),用户只告诉了DBMS想要获取什么,但没有指出如何计算.因此,DBMS需要将SQL语句转换成可执行的查询计划(Query Plan).但是对同样的 ...
- Dynamics CRM新加了组织后提示数据加密错误的解决方法
新加组织后登录报错如下: 这个是因为你新还原的组织原来绑定的加密GUID和现有的组织冲突导致的,所以需要重新为数据加密绑定一个GUID 解决办法:随机生成一个GUID 可以在https://guidg ...
- oo第二单元博客总结
P1 设计结构 三次作业的架构都没有较大的改动,基本上都是靠调度器接受输入的请求并放入队列,然后调度器根据不同的电梯的当前状态来把请求分配至不同电梯的请求队列中,最后电梯再根据自己的请求队列去运行.因 ...
- MySQL数据库高级四:工具拾遗(视图)
视图
- 数据库MySQL一
P252 1.MySQL 最为主要使用的数据库 my sequel 不容易查找数据 DB数据库 存储数据的仓库,它保存了一系列有组织的数据 DBMS数据库管理系统,数据库是通过DBMS创建和操作的容器 ...
- FastAPI项目实战:"异步"接口测试"平台"
apiAutoTestWeb 是什么? apiAutoTest接口自动化测试工具的可视化版本,将原本对用例的操作转移到Web页面之上 用什么实现? 接口自动化测试:大体上测试逻辑将采用apiAutoT ...