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 控制反转) ...
随机推荐
- TFS文件编码检查机制和修改(Team Foundation Server 2013)
TFS的版本控制系统会自动按照下面的标准检测代码文件的编码格式: 1. 首先,如果代码文件包含了BOM部分,则使用BOM中制定的编码格式打开文档 什么是BOM (Byte order mark)? h ...
- 在linux中使用包管理器安装node.js
网上文章中,在linux下安装node.js都是使用源码编译,其实node的github上已经提供了各个系统下使用各自的包管理器(package manager)安装node.js的方法. 1. 在U ...
- Tempdb--临时对象缓存
SQL Server删除一个临时对象时,不移除该对象的条目,当再次使用时,就无须重新创建临时对象,SQL Server为临时对象缓存一个数据页和一个IAM页,并回收剩余页,如果临时表的大小超过8MB, ...
- solr特点三: boost(改变默认打分排序)
有时候默认的字段打分不能满足我们的需要,如我们想把关键词出现在标题中的优先显示. 测试于:Solr 4.5.1, Jdk 1.6.0_45, Tomcat 6.0.37 | CentOS 5.7 实现 ...
- vim命令以及gcc编译器的常用cmd
Gcc常用命令: -c 仅对源文件进行编译,不链接生成可执行文件.常用于查错和只生成目标文件. -o 经过gcc处理过后的结果保存在-o后面的文件中,可以是多种文件 ...
- MVC中获取所有按钮,并绑定事件!
<script> var btns = $('[id=addbtn]'); //不能直接使用#ID来获取,必须用[] //循环遍历所有的按钮,一个一个添加事件绑定 for (var i ...
- HTML5 Communication API
本文探讨用于构建实时(real-time)跨域(cross-origin)通信的两个重要模块:跨文档消息通讯和XMLHttpRequest Level 2.通过它们,我们可以构建引人注目的Web应用, ...
- OpenResty 最佳实践 (1)
此文已由作者汤晓静授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. OpenResty 发展起源 OpenResty(也称为 ngx_openresty)是一个全功能的 Web ...
- 【timeisprecious】【JavaScript 】JavaScript String 对象
JavaScript>String 对象 1 .From Runnob JavaScript String 对象(概览) JavaScript String 对象(教程)
- 【vim】正常模式下的一般操作
正常模式一般用于浏览文本,其实也就是通过键盘命令让光标在文本中跳来跳去,在任何模式下按一次或两次<Esc>会进入正常模式. 基本思想 vim对光标的定位操作非常精确和高效,这是它的一个非常 ...