众所周知,Spring有很多优点,这些优点也让Spring彻底的让大家记住,这次我们来了解IOC底层源码的实现
什么是IOC?
每次我们了解一个东西的时候,我们都需要去理解它的含义,那么IOC到底是什么呢?
- IOC(inversion of control):翻译过来就是 翻转控制,在java刚出来的时候创建对象,使用对象都是我们自己new出来,现在不同了,有人帮我们去创建并且帮我们管理,这种主动被动形式切换了,消除了大量的冗余代码,所以叫控制反转.
- 依赖注入 : 依靠了动态代理以及反射,Spring获取Bean基本都是从map中获取,实际上是在程序初始化的时候,就已将bean实例化,我们注入在我们要使用的地方,只不过是一个复用,他们的实际引用地址是一样的.
写一个自己的IOC
-
目标:帮用户把对象创建好放到一个容器里,用户可通过容器直接获取对象。
-
达成目标所需要的条件:
- 哪些些对象需要IOC管理(创建Bean)。
- 通过什么方式告诉IOC这些对象是需要IOC管理的 (xml或注解)。
- 去哪些找这些对象(指定扫描的包)。
实现思路
-
标识:创建自定义的注解(带有我们自定义注解的类才进行对象的实例化和装配);
@Component @Service @AutoWired UserDao,UserService -
读取:需要读取用户配置文件;(用户可自定义哪些包需要进行扫描bean的装配);
component-scan
loadScanPath(); -
扫描:扫描文件进行类加载(根据用户的配置扫描对应的包加载对应包里面的class放到一个容器里面);
beanClassMap
loadBeanClass(); -
创建:把扫描出来的class中带有自定义注解的类和属性分别进行实例化和属性注入;
beanMap
registerBean();
dependency(); -
封装:把实例化好的对象放到一个容器里并提供调用方法;
ApplicationContext.getBean();
实现代码
第一步 创建普通的java项目
创建一个普通java项目即可
第二步 创建包结构
我们创建包结构(spring不用导入任何依赖)
第三步 模仿spring的几个注解
- 先写spring的autowired注解,我们起名为IocAutowired
package com.spring.ioc.annotation;
import java.lang.annotation.*;
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
//模仿spring中的autowired
public @interface IocAutowired {
}
- 其次写spring的component注解,我们起名为IocCompont
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
//模仿spring的 compont注解
public @interface IocCompont {
String value() default "";
}
- 最后我们写spring的 service注解,我们起名为IocService
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
//我们模仿spring的 service注解
public @interface IocService {
}
第四部 创建bean类,一个dao层类,一个service层类
创建bean类,一个dao层类,一个service层类,并分别使用我们定义好的注解.
UserDao类
package com.spring.ioc.bean;
import com.spring.ioc.annotation.IocCompont;
/**
* @program: IOC
* @description: dao层类
* @author: ZhengZhe
* @create: 2019-10-29 09:18
*/
@IocCompont
public class UserDao {
void study(String name){
System.out.println("这是我的昵称: "+name);
}
}
UserService类
package com.spring.ioc.bean;
import com.spring.ioc.annotation.IocAutowired;
import com.spring.ioc.annotation.IocService;
/**
* @program: IOC
* @description: Service层类
* @author: ZhengZhe
* @create: 2019-10-29 09:19
*/
@IocService
public class UserService {
@IocAutowired
private UserDao userDao;
//调用dao层的方法进行输出
public void study(String name){
userDao.study(name);
}
}
第五步 写配置文件
创建名称为: application.properties的配置文件
component-scan=com.spring.ioc.bean
第六步 书写核心构建类IocApplicationContex
我们需要写一个核心类去调用资源文件中的xml或者properties文件,初始化被注解的类 .
package com.spring.ioc.core;
import com.spring.ioc.annotation.IocAutowired;
import com.spring.ioc.annotation.IocCompont;
import com.spring.ioc.annotation.IocService;
import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* @program: IOC
* @description: Ioc控制翻转核心类
* @author: ZhengZhe
* @create: 2019-10-29 09:22
*/
public class IocApplicationContex {
//保存IOC容器bean
private ConcurrentHashMap<String,Object> beanMap=new ConcurrentHashMap();
//保存所有扫描出来的cass全路径名
private Set<String> beanClass=new HashSet<String>();
public Object getBean(String name){
return beanMap.get(name);
}
public IocApplicationContex() {
refresh();
}
private void refresh() {
//定位扫描路径
String scanPath=sanPath();
//加载所有class
loadClass(scanPath);
//实例化ArdComponent ArdService 注解的类
registerBean();
//对实例化对象的属性进行依赖注入
depenency();
}
private void depenency() {
try {
//遍历所有bean
for(Object bean:beanMap.values()){
Class clazz=bean.getClass();
Field[] fields=clazz.getDeclaredFields();//所有属性
for(Field field:fields){
//验证属性是否带有ArdAutowired注解
if(field.isAnnotationPresent(IocAutowired.class)){
String className= field.getType().getSimpleName();
String beanName=className.substring(0,1).toLowerCase()+className.substring(1,className.length());
Object dependencyBean= beanMap.get(beanName);
field.setAccessible(true);
field.set(bean,dependencyBean);//设置属性
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void registerBean() {
try {
for(String className:beanClass){
Class clazz=Class.forName(className);
if(clazz.isAnnotationPresent(IocCompont.class)||clazz.isAnnotationPresent(IocService.class)){
Object bean=clazz.newInstance();
//bean名称,类名首字母小写
String cname=clazz.getSimpleName();
String beanName=cname.substring(0,1).toLowerCase()+cname.substring(1,cname.length());
beanMap.put(beanName,bean);
}
}
} catch (Exception e) {
e.printStackTrace();
System.out.println("实例化bean失败");
}
}
private void loadClass(String scanPath) {
try {
String filePath= scanPath.replace(".","/");
URL url=this.getClass().getClassLoader().getResource(filePath);
File file=new File(url.toURI());
doLoadClass(file,filePath);
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
private void doLoadClass(File file,String scanPath) {
//文件夹
if(file.isDirectory()){
File[] fileList=file.listFiles();
for(File f:fileList){
doLoadClass(f,scanPath+"/"+f.getName());
}
}
//文件
if(file.getName().indexOf(".class")>0){
//文件夹格式转为包格式
String packgePath=scanPath.replace("/",".");
//包名+类名
beanClass.add(packgePath.replace(".class",""));
}
}
private String sanPath() {
try {
InputStream is=this.getClass().getClassLoader().getResourceAsStream("application.properties");
Properties properties=new Properties();
properties.load(is);
return properties.get("component-scan").toString();
}catch (Exception e){
e.printStackTrace();
System.out.println("加载配置文件错误");
}
return null;
}
}
第七步 创建测试类测试
package com.spring.ioc;
import com.spring.ioc.bean.UserService;
import com.spring.ioc.core.IocApplicationContex;
/**
* @program: IOC
* @description: IOC测试类
* @author: ZhengZhe
* @create: 2019-10-29 09:29
*/
public class IocTest {
public static void main(String[] args) {
//创建核心类对象
IocApplicationContex iocApplicationContex = new IocApplicationContex();
//输入我们想要的对象名称,并强转
UserService userService = (UserService) iocApplicationContex.getBean("userService");
userService.study("小哲");
}
}
第八步 查看输出结果
这是我的昵称: 小哲
IOC总结
在上面的项目中我们并没有进行所谓的spring依赖注入,我们使用我们自己创建的注解以及初始化核心类也能实现类似于spring的IOC效果,当然代码中肯定会有很多的bug和细节,现在只是简单介绍一下IOC的底层代码实现原理.希望大家多多指正