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 控制反转) ...
随机推荐
- selenium 简单指南
1.1 下载selenium2.0的包 官方download包地址:http://code.google.com/p/selenium/downloads/list 官方User Guide: h ...
- [Erlang32]ibrowse流程及性能测试
1.简介 ibrowse是用erlang写的一个HTTP client.github地址:https://github.com/cmullaparthi/ibrowse 使用方法见项目的readme. ...
- Spring学习(五)——集成MyBatis
本篇我们将在上一篇http://www.cnblogs.com/wenjingu/p/3829209.html的Demo程序的基础上将 MyBatis 代码无缝地整合到 Spring 中. 数据库仍然 ...
- vim 命令全
1 简介 vim是文本编辑器.代码补完.编译及错误跳转等方便编程的功能特别丰富,在程序员中被广泛使用.下面讲述一些必要的基本命令,而掌握好这些命令,您就能够很容易将vim当作一个通用的万能编辑器来使用 ...
- Katalon Studio简单使用(一)
官网地址:https://www.katalon.com/ katalon 目前有两种产品 一个是studio 另外一个是katalon analytics,此处先来学习studio部分. 文章学习内 ...
- c#设计模式之代理模式(Proxy Pattern)
引言 代理这个词语,大家在现实世界已经频繁的接触过,例如火车站代理售票点,因为这些代理售票点的存在,我们不必要去火车站的售票处就可以查询或者取到火车票.代理点本身是没有能力生产车票的,我们在代理处享受 ...
- JS判断时特殊值与boolean类型的转换
扒开JQuery以及其他一些JS框架源码,常常能看到下面这样的判断,写惯了C#高级语言语法的我,一直以来没能系统的理解透这段代码. var test; //do something... if(tes ...
- 使用ubuntu搭建时间机器备份服务
如何在ubuntu下搭建时间备份服务 折腾了很久,终于可以了. 请严格按照下面的方式来操作. 真正明白问题的,可以按照自己的思路来. 我用的是ubnutu 16.04 安装配置netatalk sud ...
- IOS渠道追踪方式
本文来自网易云社区 作者:马军 IOS,安卓渠道追踪的差异 Google Play国内不可用,国内的安卓 App 分发,都是依托几十个不同的应用市场或发行渠道,如百度.360.腾讯等互联网企业以及小米 ...
- 水平居中和transform: translateY(-50%) 实现元素垂直居中效果
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...