spring之IOC模拟实现
使用Spring框架已经有很长时间了,一直没有仔细的想过框架的设计思想是什么样的,底层到底是怎么实现的,这几天调试程序看了些源码,写下来做个记录。由于Spring框架博大精深,个人理解的难免有不正确的地方,希望看到的朋友可以指正,不胜感激。
一 什么是IOC
IOC是Inversion of Control的缩写,即就是所谓的控制反转,它是一种设计思想,主要包含IOC和DI两方面的内容。
IOC:不使用spring框架的时候,当我们需要一个java类对象时,我们需要手动的去创建管理这些对象。使用spring时,我们只把所有的对象同意存放在IOC容器中,要用某个类对象的时候不再需要我们去创建并管理它,完全交给spring做统一管理。
DI:依赖注入,在类加载时从外网里执行,遇到需要的对象时直接去IOC容器中获取,然后赋值给当前注入的类。
二 模拟实现
我们先创建一个项目。要把对象交给容器管理,我们就需要告诉它去管理哪些对象,所以在classpath下创建一个application.properties文件,并配置要交给容器的文件。如下表示我们要把basePackage路径(包含子目录)下的class文件都交给容器管理。
basePackage=org.wl.test.demo
创建注解类,用来标记需要被加载到IOC容器中
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller { String value() default ""; }
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired { String value() default ""; }
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Service { String value() default ""; }
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Qualifier { String value() default ""; }
创建controller和接口类及接口的实现类,并加入对应的注解
@Controller
public class UserController { @Autowired
private UserService userService; /**
* 接口可能会有多个实现类,所以在注入接口时必须用Autowired或者Qualifier指定具体的实现类
*/
@Autowired
@Qualifier("userService2")
private IUserService userService2; public void save(){
UserInfo user = new UserInfo();
userService.saveUser(user); userService2.saveUser(user);
} }
public interface IUserService { /**
* 保存用户信息
* @param user
*/
void saveUser(UserInfo user); }
@Service
public class UserService implements IUserService {
@Override
public void saveUser(UserInfo user) {
System.out.println("实现类1 保存用户信息到数据库 " + user.toString());
}
}
@Service
public class UserService2 implements IUserService {
@Override
public void saveUser(UserInfo user) {
System.out.println("实现类2 保存用户信息到数据库 " + user.toString());
}
}
创建一个ApplicationContext类,在此类完成相关的方法
public class ApplicationContext { public ApplicationContext() { } }
解析配置文件中的配置信息,获取要扫描的包路径
public String getBasePackage(String fileName) throws IOException {
String basePackage;
Properties prop = new Properties();
InputStream in = this.getClass().getClassLoader().getResourceAsStream(fileName);
prop.load(in);
basePackage = prop.getProperty("basePackage");
return basePackage;
}
定义一个集合,存放在指定的扫描包及其子包中扫描到的class文件
/**
* 初始化一个集合,存放扫描到的class对象
*/
private List<Class<?>> classList = Collections.synchronizedList(new ArrayList<>());
/**
* 跟据基础包名读取包及子包中的类对象
* @param basePackage
*/
public void scanClasses(String basePackage){
if(basePackage == null || "".equals(basePackage)){return;} doScan(basePackage);
System.out.println(classList);
}
private void doScan(String basePackage) {
String path = basePackage.replaceAll("\\.","/");
URL url = this.getClass().getClassLoader().getResource(path);
File file = new File(url.getFile());
file.listFiles(new FileFilter() {
@Override
public boolean accept(File childFile) {
String fileName = childFile.getName();
if(childFile.isDirectory()){
//当前文件是目录,递归 扫描下级子目录下的class文件
doScan(basePackage + "." + fileName);
}else{
if(fileName.endsWith(".class")){
String className = basePackage + "." + fileName.replace(".class", "");
try {
Class<?> clazz = this.getClass().getClassLoader().loadClass(className);
classList.add(clazz);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
return false;
}
});
}
将扫描到的class对象中的使用了自定义的注解的类实例,存放到容器中
/**
* 初始化map 存放别名与对象实例
*/
private Map<String, Object> aliasInstanceMap = new HashMap<>();
/**
* 完成别名与实例的映射
*/
public void buildAliasInstanceMap(String basePackage) throws Exception { if(classList.size() == 0){return;} for(Class<?> clazz : classList){
if (clazz.isAnnotationPresent(Controller.class) || clazz.isAnnotationPresent(Service.class)
|| clazz.isAnnotationPresent(Autowired.class)) {
String alias = getAlias(clazz);
Object obj = aliasInstanceMap.get(alias); //如果别名实例映射关系已经存在,则给出提示
if(obj != null){
throw new Exception("alias is exist!");
}else{
aliasInstanceMap.put(alias, clazz.newInstance());
}
}
} System.out.println(aliasInstanceMap);
}
/**
* 获取对象的别名,如果注解中配置了别名,别使用配置的别名,否则默认使用类名首字母小写
* @param clazz
* @return
*/
public String getAlias(Class<?> clazz){
String alias = "";
Controller controller = clazz.getAnnotation(Controller.class);
if(controller != null){
alias = controller.value();
}
Service service = clazz.getAnnotation(Service.class);
if (service != null) {
alias = service.value();
}
Autowired autowired = clazz.getAnnotation(Autowired.class);
if(autowired != null){
alias = autowired.value();
} //注解中没有配置别名
if("".equals(alias)){
String simpleName = clazz.getSimpleName();
alias = simpleName.substring(0, 1).toLowerCase() + simpleName.substring(1);
}
return alias;
}
扫描类,根据类属性中使用了Autowired的类别名,从IOC容器中取得对应的实例赋值给属性类,就是完成依赖注入,
/**
* 属性对象的注入
*/
public void doAutowired(){
if (aliasInstanceMap.size() == 0) {
return;
} aliasInstanceMap.forEach((k, v)->{ Field[] fields = v.getClass().getDeclaredFields(); for(Field field : fields){
if (field.isAnnotationPresent(Autowired.class)) {
String alias = ""; Autowired autowired = field.getAnnotation(Autowired.class);
if(autowired != null){
//注入的对象是接口时,由于不知道接口有几个实现类,所以就必须在Autowired或者Qualifier上指定要注解的具体的实现类
if(!"".equals(autowired.value())){
alias = autowired.value();
}else{
Qualifier qualifier = field.getAnnotation(Qualifier.class);
if(qualifier != null){
alias = qualifier.value();
}
}
} if ("".equals(alias)) {
alias = getAlias(field.getType());
} Object instance = null;
if(!"".equals(alias)){
instance = aliasInstanceMap.get(alias);
} field.setAccessible(true); try {
field.set(v, instance);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
} }
}); }
至此我们就实现了IOC和DI,做测试
public static void main(String[] args) throws Exception {
String fileName = "application.properties";
ApplicationContext context = new ApplicationContext();
String basePackage = context.getBasePackage(fileName );
context.scanClasses(basePackage);
context.buildAliasInstanceMap(basePackage);
context.doAutowired();
//测试
UserController controller = (UserController) context.getBean("userController");
controller.save();
}
/**
* 根据beanName 获取
* @param beanName
* @return
*/
public Object getBean(String beanName){
return aliasInstanceMap.get(beanName);
}
执行main方法后,可以在控制台看到两个接口实现类打印的内容
spring之IOC模拟实现的更多相关文章
- Spring的Ioc模拟实现
关于IOC:我们讲个故事吧! 有一个厨师,他在做一道菜的时候需要某种调味料(bean),可是他正好没有那瓶调味料(bean),这个时候他就必须去制作一瓶调味料(bean)出来.(这就像我们平时需要 ...
- 简单理解Spring之IOC和AOP及代码示例
Spring是一个开源框架,主要实现两件事,IOC(控制反转)和AOP(面向切面编程). IOC 控制反转,也可以称为依赖倒置. 所谓依赖,从程序的角度看,就是比如A要调用B的方法,那么A就依赖于B, ...
- [原]容器学习(一):动手模拟spring的IoC
介绍 学习经典框架的实现原理以及设计模式在其实际中的运用,是非常有必要的,可以让我们更好进行面向对象. 本篇文章就来模拟Spring的IOC功能,明白原理后,可以更好的使用它,进而为进行面向对象提供一 ...
- 容器学习(一):动手模拟spring的IoC
介绍 学习经典框架的实现原理以及设计模式在事实上际中的运用,是很有必要的,能够让我们更好进行面向对象. 本篇文章就来模拟Spring的IOC功能.明确原理后,能够更好的使用它,进而为进行面向对象提供一 ...
- JAVA模拟Spring实现IoC过程(附源码)
前言:本人大四学生,第一次写博客,如果有写得不好的地方,请大家多多指正 一.IoC(Inversion of Control)反转控制 传统开发都是需要对象就new,但这样做有几个问题: 效率低下,创 ...
- 自己动手模拟spring的IOC
我们这里是模拟spring,主要模拟spring中的IOC功能,所以在此我们一样要在service层中定义dao的实例,当然不用new出来,我们就通过spring的IOC把这里的dao层注入进来.不要 ...
- spring IOC 模拟实现
IOC即inverse of control 控制反转 以前对象之间的引用是通过new来调用实现,有了Spring IOC,我们可以把对象之间的引用交给他来管理,这样就把控制权交给了Spring,所以 ...
- Spring中IOC和AOP的详细解释
我们是在使用Spring框架的过程中,其实就是为了使用IOC,依赖注入,和AOP,面向切面编程,这两个是Spring的灵魂. 主要用到的设计模式有工厂模式和代理模式. IOC就是典型的工厂模式,通过s ...
- Spring 实践 -IoC
Spring 实践 标签: Java与设计模式 Spring简介 Spring是分层的JavaSE/EE Full-Stack轻量级开源框架.以IoC(Inverse of Control 控制反转) ...
随机推荐
- TL-WN725N v2.0 树莓派驱动
TL-WN725N 1.0版Pi是可以直接识别的,但是后来TL-WN725N又出了个2.0版的,要用第三方驱动. 安装步骤如下: wget https://dl.dropboxusercontent. ...
- 基于Extjs的web表单设计器 第二节——表单控件设计
这一节介绍表单设计器的常用控件的设计. 在前面两章节的附图中我已经给出了表单控件的两大分类:区域控件.常用控件.这里对每个分类以及分类所包含的控件的作用进行一一的介绍,因为它们很重要,是表单设计器的基 ...
- pycharm中安装可以贴图片的Markdown插件
方法一:(测试成功) 先安装官方推荐的Markdown support插件,再安装Paste images into MarkDown 如果Paste images into MarkDown插件在线 ...
- Volo.Abp.EntityFrameworkCore.MySQL 使用
创建新项目 打开 https://cn.abp.io/Templates ,任意选择一个项目类型,然后创建项目,我这里创建了一个Web Api 解压项目,还原Nuget,项目目录如下: 首先我们来查看 ...
- how to trace the error log
Executed as user: WTC\Ebw.Admin. Transaction (Process ID 95) was deadlocked on lock resources with a ...
- docker-runc not installed on system 问题
问题描述: docker运行镜像的时候,报错如下: 1 2 3 [root@etcd1 k8s]# docker run -it registry.helloworld.com/test/atsd:l ...
- “全栈2019”Java异常第二十一章:finally不被执行的情况
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java异 ...
- PHP和JS中全局变量和局部变量
一,PHP中全局变量和局部变量 php与C++中对全局变量和局部变量定义类似,全局变量:函数外定义的变量,在全局通用:局部变量:在函数内定义的变量,只在函数内有效.PHP中变量范围跨越了include ...
- php全局变量漏洞 $GLOBALS
在Discuz代码中有这么一段: if (isset($_REQUEST[‘GLOBALS’]) OR isset($_FILES[‘GLOBALS’])) { exit(‘Request tain ...
- php 对比两个数组中的值是否相等
$a = ['1','2','4','3'];//提交答案 $b = ['2','1','3'];//正确答案 $state = $this->diffArray($b, $a); echo ' ...