用过Spring框架的人一定都知道Spring的依赖注入控制反转;通俗的讲就是负责实例化对象 和 管理对象间的依赖 实现解耦。

我们来对比两段代码:

UserController{
UserService userService = new UserService();
userService.insert(user);
} UserController{
@Autowwired
UserService userService;
userService.insert(user);
}

乍一看好像没什么区别,好像都是一样的。在controller里面创建了一个service对象然后调用它里面的方法。但是换个角度想想, 如果还有2个,3个,甚至n个类需要用到这个service呢,那它岂不是要被创建n次,这样就会极大的浪费资源,分分钟就内存溢出了。

企业开发案例:

我们只需要在xml配置文件里面指定配置参数,然后在类上加上spring注解它就能帮我们管理对象了,那它又是怎么实现的呢? 答案是: xml解析 + 反射 + 工厂模式

手写IOC容器,演示它的加载过程:

1. 模仿spring,我们也定义一个注解

@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface MyService { } @Target({ TYPE, FIELD, METHOD })
@Retention(RUNTIME)
public @interface MyAutowired { }

2. 像使用spring注解一样,使用我们刚才的注解

@MyService
public class OrderService { public void add() {
System.out.println("Order....add()");
}
} @MyService
public class UserService { @MyAutowired
private OrderService orderService; public void add() {
orderService.add();
System.out.println("User....add()");
}
}

3. 演示容器加载核心代码

/**
* SpringIOC容器实现过程
*/
public class MyClassPathXmlApplicationContext {
// 1. 指定扫描包的范围
private String packageName ; // Spring的bean容器 (容器启动时初始化所有bean,默认单例) key:beanName(默认类名小写) value:bean对象
private ConcurrentHashMap<String, Object> beans; public MyClassPathXmlApplicationContext(String packageName) throws Exception {
// 类加载时初始化这些参数
this.packageName = packageName;
this.beans = new ConcurrentHashMap<String, Object>();
initBeans();// 初始化所有类
initEntryField();// 初始化所有类的属性
} /**
* 初始化bean(IOC控制反转)
*/
public void initBeans() throws Exception {
// 2. 通过java反射扫描指定包下面所有类的class地址
List<Class<?>> classes = ClassUtil.getClasses(packageName);
ConcurrentHashMap<String, Object> classExistAnnotation = this.findClassExistAnnotation(classes);
if(classExistAnnotation == null || classExistAnnotation.isEmpty()) throw new Exception("cannot find bean");
} /**
* 判断类上面是否存在bean的注解
*/
private ConcurrentHashMap<String, Object> findClassExistAnnotation(List<Class<?>> classes) throws InstantiationException, IllegalAccessException {
for(Class<?> classInfo : classes) {
// 3. 获取指定路径中有ioc注解的类
MyService annotation = classInfo.getAnnotation(MyService.class);
if(annotation!=null) {
String className = classInfo.getSimpleName();// 获取类名称
// 4. 将类名首字母小写,获取bean名称。
String beanaName = StringUtil.toLowerCaseFirstOne(className);
// 5. 反射初始化对象
Object object = classInfo.newInstance();
// 6. 将bean对象存入spring容器中
beans.put(beanaName, object);
}
}
return beans;// 对象初始化完成
}
/**
* 初始化属性
*/
private void initEntryField() throws Exception {
// 7. 遍历spring容器中所有的bean对象
for (Entry<String, Object> entry : beans.entrySet()) {
Object bean = entry.getValue();
this.attriAssign(bean);
} }
/**
* 依赖注入实现原理(DI依赖注入)
*/
public void attriAssign(Object object) throws Exception {
Class<? extends Object> classInfo = object.getClass();
// 8. 使用反射机制,获取当前类的所有属性值
Field[] fields = classInfo.getDeclaredFields();
for(Field field : fields) {
// 9. 获取有@MyAutowired注解的属性
MyAutowired myAutowired = field.getAnnotation(MyAutowired.class);
if(myAutowired != null) {
// 10. 获取属性名称
String beanName = field.getName();
// 11. 默认使用属性名称,查找bean容器对象
Object bean = this.getBean(beanName);
if(bean != null) {
field.setAccessible(true);// 允许访问私有属性
// 12. 将得到的bean对象赋值给当前对象的属性上。(bean名称=属性名称)
field.set(object, bean);
}
}
}
}
/**
* 通过bean名称去spring容器里面获取bean对象
*/
public Object getBean(String beanName) throws Exception {
if(StringUtils.isEmpty(beanName)) throw new Exception("beans.factory.BeanCreationException");
Object object = beans.get(beanName);
return object;
}
/**
* 测试
*/
public static void main(String[] args) throws Exception {
MyClassPathXmlApplicationContext applicationContext = new MyClassPathXmlApplicationContext("com.wulei.service");
UserService userService = (UserService)applicationContext.getBean("userService");
userService.add();
}
}

依赖注入:比如userController里面的userService属性加上bean注解,在类被加载时通过反射获取service的对象,并且赋值给该属性,这就叫做依赖注入。
控制反转:controller引用service的实例,不需要通过new来创建,将创建对象的责任转移给spring容器,这就叫反转; ioc容器实现对象的创建,以及外部资源的获取(其他类的属性和方法),这就叫控制

100行代码撸完SpringIOC容器的更多相关文章

  1. 【编程教室】PONG - 100行代码写一个弹球游戏

    大家好,欢迎来到 Crossin的编程教室 ! 今天跟大家讲一讲:如何做游戏 游戏的主题是弹球游戏<PONG>,它是史上第一款街机游戏.因此选它作为我这个游戏开发系列的第一期主题. 游戏引 ...

  2. 100行代码实现HarmonyOS“画图”应用,eTS开发走起!

    本期我们给大家带来的是"画图"应用开发者Rick的分享,希望能给你的HarmonyOS开发之旅带来启发~ 介绍 2021年的华为开发者大会(HDC2021)上,HarmonyOS ...

  3. 【转】100行代码实现最简单的基于FFMPEG+SDL的视频播放器

    FFMPEG工程浩大,可以参考的书籍又不是很多,因此很多刚学习FFMPEG的人常常感觉到无从下手.我刚接触FFMPEG的时候也感觉不知从何学起. 因此我把自己做项目过程中实现的一个非常简单的视频播放器 ...

  4. 100行代码实现现代版Router

      原文:http://www.html-js.com/article/JavaScript-version-100-lines-of-code-to-achieve-a-modern-version ...

  5. 用JavaCV改写“100行代码实现最简单的基于FFMPEG+SDL的视频播放器 ”

    FFMPEG的文档少,JavaCV的文档就更少了.从网上找到这篇100行代码实现最简单的基于FFMPEG+SDL的视频播放器.地址是http://blog.csdn.net/leixiaohua102 ...

  6. 100行代码实现最简单的基于FFMPEG+SDL的视频播放器(SDL1.x)【转】

    转自:http://blog.csdn.net/leixiaohua1020/article/details/8652605 版权声明:本文为博主原创文章,未经博主允许不得转载.   目录(?)[-] ...

  7. 100行代码让您学会JavaScript原生的Proxy设计模式

    面向对象设计里的设计模式之Proxy(代理)模式,相信很多朋友已经很熟悉了.比如我之前写过代理模式在Java中实现的两篇文章: Java代理设计模式(Proxy)的四种具体实现:静态代理和动态代理 J ...

  8. GuiLite 1.2 发布(希望通过这100+行代码来揭示:GuiLite的初始化,界面元素Layout,及消息映射的过程)

    经过开发群的长期验证,我们发现:即使代码只有5千多行,也不意味着能够轻松弄懂代码意图.痛定思痛,我们发现:虽然每个函数都很简单(平均长度约为30行),可以逐个击破:但各个函数之间如何协作,却很难说明清 ...

  9. 100 行代码实现的 JavaScript MVC 样式框架

    介绍 使用过 JavaScript框架(如 AngularJS, Backbone 或者Ember)的人都很熟悉在UI(用户界面,前端)中mvc的工作机理.这些框架实现了MVC,使得在一个单页面中实现 ...

随机推荐

  1. SpringCloud 入门知识篇

    SpringCloud 入门 springcloud 学习 7天学会springcloud 教程 https://www.cnblogs.com/skyblog/category/738524.htm ...

  2. Linux vi/vim and linux yum 命令

    Linux vi/vim 所有的 Unix Like 系统都会内建 vi 文书编辑器,其他的文书编辑器则不一定会存在. 但是目前我们使用比较多的是 vim 编辑器. vim 具有程序编辑的能力,可以主 ...

  3. Min_25筛初级应用:求$[1,n]$内质数个数

    代码 #include <bits/stdc++.h> #define rin(i,a,b) for(int i=(a);i<=(b);++i) #define irin(i,a,b ...

  4. 双重Iterator 报错!!!!

    List list = new ArrayList(); list.add(new String[]{"0","s1","0038",&qu ...

  5. sqli-labs(9)

    基于时间的GET单引号盲注 0x01爱之初试探 在我们注入了SQL代码之后,存在以下两种情况: 如果注入的SQL代码不影响后台[数据库]的正常功能执行,那么Web应用的页面显示正确(原始页面). 如果 ...

  6. 函数式接口和Lambda表达式

    函数式接口(一般标有@FunctionalInterface)就是只定义一个抽象方法的接口. 一个接口,如果满足函数式接口的定义,那么即使不标注为 @FunctionalInterface, 编译器依 ...

  7. java Map类

    实现类 类型区别 HashMap 最常用的Map,它根据键的HashCode 值存储数据,根据键可以直接获取它的值,具有很快的访问速度.HashMap最多只允许一条记录的键为Null(多条会覆盖);允 ...

  8. 一个蒟蒻的解题过程记录——洛谷P1003 铺地毯

    这到题算是我“火线回归”后码的第一道题,病好了心情不错,发篇博客分享一下 目录: ·题目描述 ·题目分析 ·解题思路 ·代码实现 ·总结 ·题目描述: 为了准备一场特殊的颁奖典礼,组织者在会场的一片矩 ...

  9. 远程桌面 虚拟打印 到本地打印机(虚拟化 终端 远程接入 RemoteApp)

    使用远程桌面或remoteapp进行打印时,若需使用本地的打印机,需要通过重定向方式,但本地打印机如果五花八门比较杂,那给服务器安装打印机驱动很麻烦. 其实可以借助虚拟打印机简化操作,省去给服务器安装 ...

  10. CSS3——分组和嵌套 尺寸 display显示 position定位 overflow float浮动

    分组和嵌套  分组选择器 ——————>   嵌套选择器 能适用于选择器内部的选择器的样式 p{ }: 为所有 p 元素指定一个样式. .marked{ }: 为所有 class="m ...