大家好,我是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实现的更多相关文章

  1. 简单实现Spring框架--注解版

    自己写的Spring框架——简单实现IoC容器功能 前几天在网上看了篇帖子,是用xml的方式实现spring的ioc容器,觉得挺有意思的,这边自己试着用注解的形式造了一套轮子. 工程结构 codein ...

  2. spring.net中的IOC和DI-初使用

    前面准备:下载spring.net并解压 下载地址:spring.net下载地址 Ioc:控制反转         DI:依赖注入 一.IOC(控制反转) 1.新建一个控制台程序springTest, ...

  3. spring 学习 AOP和IOC

    自11开始接触三大框架,至今已俞5载, 当时风光无限的ssh,现在还在被广泛使用,并有扩大之势的只有spring了 spring主要特性,是广为使用的AOP(面向切面)和IOC(控制反转) 1.其中, ...

  4. spring源码浅析——IOC

    =========================================== 原文链接: spring源码浅析--IOC   转载请注明出处! ======================= ...

  5. spring mvc注解版01

    spring mvc是基于servlet实现的在spring mvc xml版中已经说过了,注解版相较于xml版更加简洁灵活. web项目的jar包: commons-logging-1.1.3.ja ...

  6. spring学习(01)之IOC

    spring学习(01)之IOC IOC:控制反转——Spring通过一种称作控制反转(IOC)的技术促进了低耦合.当应用了IOC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创 ...

  7. spring源码分析---IOC(1)

    我们都知道spring有2个最重要的概念,IOC(控制反转)和AOP(依赖注入).今天我就分享一下spring源码的IOC. IOC的定义:直观的来说,就是由spring来负责控制对象的生命周期和对象 ...

  8. Spring框架学习之IOC(二)

    Spring框架学习之IOC(二) 接着上一篇的内容,下面开始IOC基于注解装配相关的内容 在 classpath 中扫描组件 <context:component-scan> 特定组件包 ...

  9. Spring框架学习之IOC(一)

    Spring框架学习之IOC(一) 先前粗浅地学过Spring框架,但当时忙于考试及后期实习未将其记录,于是趁着最近还有几天的空闲时间,将其稍微整理一下,以备后期查看. Spring相关知识 spri ...

  10. spring之:XmlWebApplicationContext作为Spring Web应用的IoC容器,实例化和加载Bean的过程

    它既是 DispatcherServlet 的 (WebApplicationContext)默认策略,又是 ContextLoaderListener 创建 root WebApplicationC ...

随机推荐

  1. Nginx版本。

    Nginx官网提供了三个类型的版本Mainline version:Mainline 是 Nginx 目前主力在做的版本,可以说是开发版Stable version:最新稳定版,生产环境上建议使用的版 ...

  2. 入门级java开发环境的配置安装

    为了能够在计算机上开发Java程序和运行Java程序,就需要在Windows操作系统上配置Java开发环境. 首先,安装JDK: 1.在Oracle官网上下载JavaSE: 2.在Download下载 ...

  3. Java基础学习——Scanner

    import java.util.Scanner; public class HelloWord{ public static void main(String[] args){ Scanner s= ...

  4. 全新TI AM62xx系列核心板上市,小小身板蕴藏巨大势能!

    2011年TI推出AM335x,成为了此后市场上最受欢迎的通用工业级ARM处理器,并广泛应用于工业HMI, 医疗电子,机器人,能源,汽车等领域.随着工业4.0的发展,HMI人机交互.工业工控.医疗等领 ...

  5. Unity ContentSizeFitter组件

    Content Size Fitter组件,它可以动态改变物体的宽高,但它有一个非常需要注意的点就是,它不是即时刷新,是帧末刷新,这个特性如果没注意会出现一个问题 就是你拿到加了这个组件的宽高本不是你 ...

  6. windows server 2008 创建计划任务不能正常执行

  7. Oracle-登录的用户名和密码大小写敏感

    Oracle-登录的用户名和密码大小写敏感

  8. 网络基础-OSI七层模型

    什么是OSI模型 OSI模型(或 Open Systems Interconnection Model开放系统互连模型)是网络中使用的绝对基础模型.这个关键模型提供了一个框架,规定所有联网设备将如何发 ...

  9. python爬虫基础教程

    爬虫介绍 爬虫就是程序,是从互联网中,各个网站上爬取数据(能浏览到的网页才可以爬),做数据清洗,入库 爬虫本质: 模拟http请求,获取数据,入库 网站/app > 抓包 我们日常使用的baid ...

  10. 声网 X Yalla:面对面不如线上见,中东年轻人最偏爱的语聊房是怎样“炼”成的?

    "实时互动的本质是服务,而非功能."这是声网一直以来坚信的理念. 功能上线之后,服务才真正开始.实时互动的每一秒,甚至每一毫秒的体验都需要得到稳定.可靠的保证.而广大用户之所以能够 ...