Spring之丐版IOC实现
文章目录
大家好,我是Leo。Spring核心中依赖注入和IOC容器是非常常见的,用起来也是非常的顺手,只能说是真香,那如何实现一个丐版的SpringIOC呢?那今天我们就来讲如何实现一个乞丐版的IOC和通过注解进行依赖注入。
IOC控制反转
在开始之前,首先得先理解什么是IOC的控制反转,Spring 通过 IoC 容器来管理所有 Java 对象的实例化和初始化,控制对象与对象之间的依赖关系 。我们将由 IoC 容器管理的 Java 对象称为 Spring Bean,它与使用关键字 new 创建的 Java 对象没有任何区别。
说白了就是,IOC负责我们的饮食,我们要饭和菜,只需要从IOC容器获取就行。
那这样的作用也就非常明显了,解耦、方便
依赖注入
那依赖注入肯定是IOC的精髓所在,我们平常使用@Autowired注解等自动注入方式,用起来确实舒服。
那依赖注入常用方式有哪些呢?
构造函数的参数实现注入
基于set注入(静态工厂和动态工厂)
基于属性的注入
Bean的自动装配方式
可以在文件中设置自动装配(autowire)方式,支持的装配方式有
| 方式 | 描述 |
|---|---|
| no | 手动装配 |
| byName | 通过id的名字自动注入对象 |
| byType | 通过类型自动注入对象 |
| constructor | 通过构造方法自动注入对象 |
| autodectect | 完全交给Spring管理,按先Constructor后byType的顺序进行匹配 |
丐版IOC实现
BeanDefinition.java
Bean的定义类,主要是包括配置Bean定义的对应的实体,对应的构造方法和getset方法就省略了
public class BeanDefinition {
private String beanName;
private Class beanClass;
ResourceLoader.java
资源加载器,用来完成包的扫描和Bean的加载
public class ResourceLoader {
private static String rootPath;
private Map<String, BeanDefinition> beanDefinitionMap = new HashMap(16);
public Map<String, BeanDefinition> getResource(String basePackage) {
// com.zly
try {
// 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());
// 包扫描
try {
loadBean(new File(filePath));
} catch (Exception e) {
e.printStackTrace();
}
}
} catch (IOException e) {
e.printStackTrace();
}
return this.beanDefinitionMap;
}
private void loadBean(File file) throws Exception {
// 1. 判断是否是文件夹
if (file.isDirectory()) {
// 2. 获取文件夹的所有内容
File[] childFiles = file.listFiles();
// 3. 判断文件夹内为空,直接返回
if (childFiles == null || childFiles.length == 0) {
return;
}
// 4. 如果文件夹里面不为空,遍历文件夹所有的内容
for (File childFile : childFiles) {
// 4.1 遍历得到每个File对象,继续是文件夹,递归
if (childFile.isDirectory()) {
loadBean(childFile);
} else {
// 4.2 得到不是文件夹,是文件
// 4.3 得到包路径 和类名称
String pathWithClass = childFile.getAbsolutePath().substring(rootPath.length() - 1);
// 4.4 判断当前文件类型是否为class
if (pathWithClass.contains(".class")) {
// 4.5 是class类型,把\替换成. 把.class去掉
String allName = pathWithClass.replaceAll("\\\\", ".").replace(".class", "");
// 4.6 判断类上面是否有注解@Bean,放到map集合beanFactory
// 4.6.1 获取类的Class对象
Class<?> clazz = Class.forName(allName);
// 4.6.2 判断不是接口
if (!clazz.isInterface()) {
// 4.6.3 判断上面是否有@Bean注解
Bean annotation = clazz.getAnnotation(Bean.class);
if (annotation != null) {
// 4.6.4 实例化
String beanName = annotation.value();
if ("".equals(beanName)) {
beanName = allName;
}
if (clazz.getInterfaces().length > 0) {
System.out.println("正在加载【"+ clazz.getInterfaces()[0] +"】");
BeanDefinition beanDefinition = new BeanDefinition(beanName, clazz);
beanDefinitionMap.put(beanName, beanDefinition);
} else {
System.out.println("正在加载【"+ clazz.getInterfaces()[0]);
BeanDefinition beanDefinition = new BeanDefinition(beanName, clazz);
beanDefinitionMap.put(beanName, beanDefinition);
}
}
}
}
}
}
}
}
}
BeanRegister.java
用于向容器中注册一个Bean,在扫描后,我们的Bean主要是放在了beanDefinitionMap中,还没有进行加载和初始化,在获取中再进行加载到这个缓存Map中
public class BeanRegister {
private Map<String, Object> singletonMap = new HashMap<>(32);
public Object getSingletonBean(String beanName) {
return singletonMap.get(beanName);
}
public void registerSingletonBean(String beanName, Object bean) {
if (singletonMap.containsKey(beanName)) {
return;
}
singletonMap.put(beanName, bean);
}
}
Bean和DI的注解
这里的两个注解主要是用来标志这是一个Bean和进行依赖注入的
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {
String value() default "";
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Di {
}
BeanFactory.java
这里的对象工厂主要是我们最重要的一个类,在创建时,需要加载注册器和资源加载,在获取Bean中,需要进行依赖注入,并创建一个Bean
public class BeanFactory {
/**
* 创建一个map集合,放bean对象
*/
private Map<String , BeanDefinition> beanDefinitionMap = new HashMap<>();
private BeanRegister beanRegister;
public BeanFactory(String basePackage) {
beanRegister = new BeanRegister();
this.beanDefinitionMap = new ResourceLoader().getResource(basePackage);
}
public Object getBean(String beanName) {
// 从缓存中获取
Object bean = beanRegister.getSingletonBean(beanName);
if (bean != null) {
return bean;
}
// 创建bean
return createBean(beanDefinitionMap.get(beanName));
}
public Object getBean(Class<?> clazz) {
BeanDefinition beanDefinition = getBeanNameByType(clazz);
if (Objects.isNull(beanDefinition)) {
log.error("can not find {}", clazz);
return null;
} else {
return getBean(beanDefinition.getBeanName());
}
}
private Object createBean(BeanDefinition beanDefinition) {
try {
Object bean = beanDefinition.getBeanClass().getConstructor().newInstance();
beanRegister.registerSingletonBean(beanDefinition.getBeanName(), bean);
loadDi(beanDefinition.getBeanName());
return bean;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 属性注入
*/
private void loadDi(String beanName) {
// 1. 实例化对象都在beanFactory的map集合中,遍历
Object bean = this.beanRegister.getSingletonBean(beanName);
// 2. 获取map集合每个对象和属性
Class<?> objectClass = bean.getClass();
// 3. 遍历得到每个对象属性数组,得到每个属性
Field[] declaredFields = objectClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
// 4. 判断属性上面是否有@Di注解
Di annotation = declaredField.getAnnotation(Di.class);
if (annotation != null) {
declaredField.setAccessible(true);
// 如果私有属性,可以设置值
// 5. 如果有@Di注解,把对象进行注入
try {
BeanDefinition beanDefinition = getBeanNameByType(declaredField.getType());
if (Objects.isNull(beanDefinition)) {
declaredField.set(bean, null);
} else {
declaredField.set(bean, getBean(beanDefinition.getBeanName()));
}
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
}
private BeanDefinition getBeanNameByType(Class clazz) {
Set<Map.Entry<String, BeanDefinition>> entries = this.beanDefinitionMap.entrySet();
for (Map.Entry<String, BeanDefinition> entry : entries) {
if (entry.getValue().getBeanClass().equals(clazz)) {
return entry.getValue();
}
if (entry.getValue().getBeanClass().getInterfaces()[0].equals(clazz)) {
return entry.getValue();
}
}
return null;
}
}
ApplicationContext
我也模仿了Spring一样定义了一个ApplicationContext容器,获取Bean从这个容器获取,context容器再从BeanFactory中获取
public class AnnotationApplicationContext implements ApplicationContext {
/**
* 创建一个map集合,放bean对象
*/
private BeanFactory beanFactory;
@Override
public Object getBean(Class clazz) {
return beanFactory.getBean(clazz);
}
@Override
public Object getBean(String beanName) {
return beanFactory.getBean(beanName);
}
/**
* 设置包扫描规则
* 当前包及其子包,哪个类有@Bean注解,把这个类通过反射化
*/
public AnnotationApplicationContext(String basePackage) throws Exception {
this.beanFactory = getBeanFactory(basePackage);
}
private BeanFactory getBeanFactory(String baskPackage) {
return new BeanFactory(baskPackage);
}
}
测试,实现
@Bean
public class UserServiceImpl implements UserService {
@Di
private UserDao userDao;
@Override
public void add() {
System.out.println("service.........");
userDao.add();
}
}
public interface UserService {
void add();
}
@Bean
public class UserDaoImpl implements UserDao {
@Override
public void add() {
System.out.println("dao add...");
}
}
public interface UserDao {
void add();
}
public class UserTest {
public static void main(String[] args){
try {
ApplicationContext context = new AnnotationApplicationContext("com.zly");
UserService userService = (UserService)context.getBean(UserService.class);
userService.add();
} catch (Exception e) {
e.printStackTrace();
}
}
}
相信你看到这里,就可以很容易的理解了这个丐版的Spring就算完成了,希望可以对大家有帮助啦
Spring之丐版IOC实现的更多相关文章
- 简单实现Spring框架--注解版
自己写的Spring框架——简单实现IoC容器功能 前几天在网上看了篇帖子,是用xml的方式实现spring的ioc容器,觉得挺有意思的,这边自己试着用注解的形式造了一套轮子. 工程结构 codein ...
- spring.net中的IOC和DI-初使用
前面准备:下载spring.net并解压 下载地址:spring.net下载地址 Ioc:控制反转 DI:依赖注入 一.IOC(控制反转) 1.新建一个控制台程序springTest, ...
- spring 学习 AOP和IOC
自11开始接触三大框架,至今已俞5载, 当时风光无限的ssh,现在还在被广泛使用,并有扩大之势的只有spring了 spring主要特性,是广为使用的AOP(面向切面)和IOC(控制反转) 1.其中, ...
- spring源码浅析——IOC
=========================================== 原文链接: spring源码浅析--IOC 转载请注明出处! ======================= ...
- spring mvc注解版01
spring mvc是基于servlet实现的在spring mvc xml版中已经说过了,注解版相较于xml版更加简洁灵活. web项目的jar包: commons-logging-1.1.3.ja ...
- spring学习(01)之IOC
spring学习(01)之IOC IOC:控制反转——Spring通过一种称作控制反转(IOC)的技术促进了低耦合.当应用了IOC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创 ...
- spring源码分析---IOC(1)
我们都知道spring有2个最重要的概念,IOC(控制反转)和AOP(依赖注入).今天我就分享一下spring源码的IOC. IOC的定义:直观的来说,就是由spring来负责控制对象的生命周期和对象 ...
- Spring框架学习之IOC(二)
Spring框架学习之IOC(二) 接着上一篇的内容,下面开始IOC基于注解装配相关的内容 在 classpath 中扫描组件 <context:component-scan> 特定组件包 ...
- Spring框架学习之IOC(一)
Spring框架学习之IOC(一) 先前粗浅地学过Spring框架,但当时忙于考试及后期实习未将其记录,于是趁着最近还有几天的空闲时间,将其稍微整理一下,以备后期查看. Spring相关知识 spri ...
- spring之:XmlWebApplicationContext作为Spring Web应用的IoC容器,实例化和加载Bean的过程
它既是 DispatcherServlet 的 (WebApplicationContext)默认策略,又是 ContextLoaderListener 创建 root WebApplicationC ...
随机推荐
- Nginx版本。
Nginx官网提供了三个类型的版本Mainline version:Mainline 是 Nginx 目前主力在做的版本,可以说是开发版Stable version:最新稳定版,生产环境上建议使用的版 ...
- 入门级java开发环境的配置安装
为了能够在计算机上开发Java程序和运行Java程序,就需要在Windows操作系统上配置Java开发环境. 首先,安装JDK: 1.在Oracle官网上下载JavaSE: 2.在Download下载 ...
- Java基础学习——Scanner
import java.util.Scanner; public class HelloWord{ public static void main(String[] args){ Scanner s= ...
- 全新TI AM62xx系列核心板上市,小小身板蕴藏巨大势能!
2011年TI推出AM335x,成为了此后市场上最受欢迎的通用工业级ARM处理器,并广泛应用于工业HMI, 医疗电子,机器人,能源,汽车等领域.随着工业4.0的发展,HMI人机交互.工业工控.医疗等领 ...
- Unity ContentSizeFitter组件
Content Size Fitter组件,它可以动态改变物体的宽高,但它有一个非常需要注意的点就是,它不是即时刷新,是帧末刷新,这个特性如果没注意会出现一个问题 就是你拿到加了这个组件的宽高本不是你 ...
- windows server 2008 创建计划任务不能正常执行
- Oracle-登录的用户名和密码大小写敏感
Oracle-登录的用户名和密码大小写敏感
- 网络基础-OSI七层模型
什么是OSI模型 OSI模型(或 Open Systems Interconnection Model开放系统互连模型)是网络中使用的绝对基础模型.这个关键模型提供了一个框架,规定所有联网设备将如何发 ...
- python爬虫基础教程
爬虫介绍 爬虫就是程序,是从互联网中,各个网站上爬取数据(能浏览到的网页才可以爬),做数据清洗,入库 爬虫本质: 模拟http请求,获取数据,入库 网站/app > 抓包 我们日常使用的baid ...
- 声网 X Yalla:面对面不如线上见,中东年轻人最偏爱的语聊房是怎样“炼”成的?
"实时互动的本质是服务,而非功能."这是声网一直以来坚信的理念. 功能上线之后,服务才真正开始.实时互动的每一秒,甚至每一毫秒的体验都需要得到稳定.可靠的保证.而广大用户之所以能够 ...
