内容简介:我们都知道 Spring 的两大核心是IOC 和 AOP。也有人认为是IOC、DI和AOP。这几项应该是所有的java后台都应该掌握的。所以为了加深对这几个概念的理解,本人根据自己的理解动手实现了一个简单的IOC。IOC:(Inversion of Control) 控制反转,是一种设计思想。包含了两方面:一、控制,二、反转。
我们都知道 Spring 的两大核心是IOC 和 AOP。也有人认为是IOC、DI和AOP。这几项应该是所有的 java 后台都应该掌握的。所以为了加深对这几个概念的理解,本人根据自己的理解动手实现了一个简单的IOC。
1.概念理解
IOC:(Inversion of Control) 控制反转,是一种设计思想。包含了两方面:一、控制,二、反转。 控制 指的是当前对象对内部成员的控制权。 反转 指的是这种控制权不由当前对象管理了,由其他类、第三方容器来管理。
DI:(Dependency Injection) 依赖注入,属性或组件之间的关系由容器去记录,由容器动态的去将某个依赖关系注入到某个组件中。
2. IOC原理
其实IOC容器就是一个大工厂,用以管理所有的对象以及依赖关系。原理就是通过Java的反射技术来实现的。通过反射可以获得类的所有信息(成员变量、类名等等)。下面是一个IOC的流程图:
3. 代码实现
在我的实现里,我通过模仿spring的注解的方式来描述类的关系以及bean的注入。通过maven来管理jar包。需要说明的是我通过reflections来帮助获得被自定义注解注释的类maven依赖如下
<dependency> <groupId>org.reflections</groupId> <artifactId>reflections</artifactId> <version>0.9.11</version> </dependency> 复制代码
项目最终目录结构
3.1 注解类
在代码中,有几个注解类,SimpleAutowire、SimpleBean、SimpleCompotent以及SimpleConfigure。他们是按照Spring的Bean、Autowire、Compotent、Configure的简单实现。目前SimpleIOC 只支持单例bean。可以根据自定义的beanName进行bean的生成以及注入,亦可以根据默认的类的全限定名进行bean的实例化以及注入。 以上几个注解类的代码如下:
package com.lichaobao.simpleioc.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @author lichaobao * @date 2019/4/18 * @QQ 1527563274 * 标记需要注入的属性 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface SimpleAutowire { String value() default ""; } 复制代码
package com.lichaobao.simpleioc.annotations; import java.lang.annotation.*; /** * @author lichaobao * @date 2019/4/18 * @QQ 1527563274 * 标记需要生成的单例bean */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Documented public @interface SimpleBean { String name() default ""; } 复制代码
package com.lichaobao.simpleioc.annotations; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * @author lichaobao * @date 2019/4/18 * @QQ 1527563274 * 标记需要生成的bean */ @Retention(RetentionPolicy.RUNTIME) public @interface SimpleCompotent { String value() default ""; } 复制代码
package com.lichaobao.simpleioc.annotations; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * @author lichaobao * @date 2019/4/18 * @QQ 1527563274 * 标记配置类 用于生成bean */ @Retention(RetentionPolicy.RUNTIME) public @interface SimpleConfigure { } 复制代码
我们可以看到,注解类实现都大同小异,其实注解类的主要作用就是为了标记,帮助我们来区分具体类的具体作用。具体我们要这些被注解的类去干什么,需要我们用代码去具体实现。
3.2 BeanDefinition
在贴出如何实现bean的初始化以及管理功能之前,我们先来说一下BeanDefinition这个类。
package com.lichaobao.simpleioc.factory; import java.util.HashMap; import java.util.Map; /** * @author lichaobao * @date 2019/4/18 * @QQ 1527563274 * 存放bean的数据结构 */ public class BeanDefinition { private Object bean; private String className; private Class beanClass; private Map<String,Object> propertyMap = new HashMap<String, Object>(); public Object getBean() { return bean; } public void setBean(Object bean) { this.bean = bean; } public String getClassName() { return className; } public void setClassName(String className) { this.className = className; try{ this.beanClass = Class.forName(className); }catch (Exception e){ e.printStackTrace(); } } public Class getBeanClass() { return beanClass; } public void setBeanClass(Class beanClass) { this.beanClass = beanClass; } public Map<String, Object> getPropertyMap() { return propertyMap; } public void setPropertyMap(Map<String, Object> propertyMap) { this.propertyMap = propertyMap; } } 复制代码
我们用BeanDefinition来描述或者说记录一个bean的信息,包括这个bean的实例化对象、这个bean的名称、这个bean所依赖的属性以及这个bean的class类。在spring 源码中,同样也是根据BeanDefinition这个数据结构来记录bean的信息。不过spring中的实现更为复杂。我们这里只是简单的对其进行一个实现。
3.2 根据SimpleConfigure与SimpleBean完成类似spring 配置类的功能
说完存储的数据结构,我们来说说如何去扫描被上面注解所标注的类,以及进行bean的初始化。前面提到,我们通过org.reflections这个包来完成自定义注解的扫描。使用方式很简单,我们只需要传入要扫描的包名就行了如下:
Reflections reflections = new Reflections(packageName); Set<Class<?>> classesList = reflections.getTypesAnnotatedWith(SimpleConfigure.class); 复制代码
现在扫描完配置类我们只需要将配置类里的被SimpleBean注释的方法拿出来,然后执行这些方法就能实现类似于spring的配置类的功能。 BeanFactory代码如下:
package com.lichaobao.simpleioc.factory; import com.lichaobao.simpleioc.annotations.SimpleBean; import com.lichaobao.simpleioc.annotations.SimpleConfigure; import org.reflections.Reflections; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.HashMap; import java.util.Map; import java.util.Set; /** * @author lichaobao * @date 2019/4/18 * @QQ 1527563274 */ public class BeanFactory { /** * 构造函数私有化 防止被调用 */ private BeanFactory(){ } /** * 单例模式 内部静态类 */ private static class BeanFactorySingle{ private static BeanFactory beanFactory = new BeanFactory(); } /** * 存放bean实例 */ private static Map<String,BeanDefinition> beanMap = new HashMap<String, BeanDefinition>(); /** * 扫描被SimpleConfigure 标记的类 找到其中被SimpleBean标记的方法,进行bean的初始化以及保存 */ public void init(String packageName) throws Exception { if(beanMap.size()==0){ Reflections reflections = new Reflections(packageName); Set<Class<?>> classesList = reflections.getTypesAnnotatedWith(SimpleConfigure.class); for (Class clazz:classesList){ Method[] methods = clazz.getDeclaredMethods(); for(Method method:methods){ if(method.isAnnotationPresent(SimpleBean.class)){ Type type = method.getGenericReturnType(); String className = type.toString().split(" ")[1]; SimpleBean simpleBean = method.getAnnotation(SimpleBean.class); Object o = clazz.newInstance(); Object bean = method.invoke(o); String beanName = "".equals(simpleBean.name())? className:simpleBean.name(); registry(beanName,className,bean,null); } } } } } /** * 获得 bean * @param beanName bean名称(bean的自定义名称(如果添加的话)默认为类的全限定名) * @return Object * @throws Exception Not found */ public Object getBean(String beanName)throws Exception{ BeanDefinition beanDefinition = beanMap.get(beanName); if(beanDefinition == null){ throw new Exception("No beans found name "+ beanName); } Object bean = beanDefinition.getBean(); if(bean == null){ bean = doCreate(beanDefinition); } return bean; } /** * 如果bean是空 则根据 beanDefinition中记录的bean结构 进行bean的初始化 * @param beanDefinition 存放bean结构 * @return Object * @throws Exception InstantiationException, IllegalAccessException */ private Object doCreate(BeanDefinition beanDefinition) throws Exception{ Object bean = beanDefinition.getBeanClass().newInstance(); bean = addProperty(bean,beanDefinition); return bean; } /** * 根据beanDefinition 中的记录 将属性的值赋给要生成的bean * @param bean bean * @param beanDefinition 存放bean数据结构 * @return object * @throws Exception NoSuchFieldException, SecurityException */ private Object addProperty(Object bean,BeanDefinition beanDefinition)throws Exception{ Map<String,Object> map = beanDefinition.getPropertyMap(); Field[] fields = beanDefinition.getBeanClass().getDeclaredFields(); for(Field field : fields){ Field declaredField = bean.getClass().getDeclaredField(field.getName()); declaredField.setAccessible(true); declaredField.set(bean,map.get(field.getName())); } return bean; } void registry(String beanName,String className,Object bean,Map<String,Object> map) throws Exception { BeanDefinition beanDefinition = new BeanDefinition(); beanDefinition.setClassName(className); beanDefinition.setBean(bean); beanDefinition.setPropertyMap(map); if(beanMap.containsKey(beanName)){ throw new Exception("重复注册bean"); } beanMap.put(beanName,beanDefinition); } public static BeanFactory getInstance(){ return BeanFactorySingle.beanFactory; } } 复制代码
3.3 依赖注入功能实现
上面的类说明了SimpleBean与SimpleConfigure的功能即完成配置类的功能,那DI我们是怎么实现的呢,接下来我们就要说说AutowireFactory这个类。在AutowireFactory中我们通过扫描所有被注解SimpleCompotent的类,根据上一步BeanFactory中实例化的bean注入到此类中的被SimpleAutowire注释的属性。完成被SimpleCompotent注释的类的bean的实例化。具体实现如下,同样也是用到反射的知识。
package com.lichaobao.simpleioc.factory; import com.lichaobao.simpleioc.annotations.SimpleAutowire; import com.lichaobao.simpleioc.annotations.SimpleCompotent; import org.reflections.Reflections; import java.lang.reflect.Field; import java.lang.reflect.Type; import java.util.HashMap; import java.util.Map; import java.util.Set; /** * @author lichaobao * @date 2019/4/18 * @QQ 1527563274 * 扫描所有被SimpleCompotent标记的类 * 根据其内部属性被SimpleAutowire标记的属性 * 从BeanFactory中找到相应的bean进行注入并进行 * bean的初始化。 */ public class AutowireFactory { /** * 扫描指定包名下的类 根据SimpleCompotent 以及SimpleAutowire进行注入 * @param packageName 包名 * @throws Exception Exception */ public void init(String packageName) throws Exception { Reflections reflections = new Reflections(packageName); Set<Class<?>> classesList = reflections.getTypesAnnotatedWith(SimpleCompotent.class); for(Class clazz:classesList){ Object bean = clazz.newInstance(); Field[] fields = clazz.getDeclaredFields(); Map<String,Object> fieldMap = new HashMap<String, Object>(); for(Field field :fields){ field.setAccessible(true); if(field.isAnnotationPresent(SimpleAutowire.class)){ SimpleAutowire simpleAutowire = field.getAnnotation(SimpleAutowire.class); Type type = field.getGenericType(); String beanName = "".equals(simpleAutowire.value()) ? type.toString().split(" ")[1]:simpleAutowire.value(); Object fieldBean = BeanFactory.getInstance().getBean(beanName); field.set(bean,fieldBean); fieldMap.put(field.getName(),fieldBean); }else{ fieldMap.put(field.getName(),field.get(bean)); } SimpleCompotent simpleCompotent = (SimpleCompotent)clazz.getAnnotation(SimpleCompotent.class); String beanName = "".equals(simpleCompotent.value()) ? clazz.getName():simpleCompotent.value(); BeanFactory.getInstance().registry(beanName,clazz.getName(),bean,fieldMap); } } } } 复制代码
4. 测试
- 首先我们新建一个类Test,代码如下:
package com.lichaobao.test; /** * @author lichaobao * @date 2019/4/18 * @QQ 1527563274 */ public class Test { private String a; public String getA() { return a; } public void setA(String a) { this.a = a; } public Test(String a) { this.a = a; } public void doSomething(){ System.out.println(a); } } 复制代码
- 新建一个配置类,用上SimpleConfigure以及SimpleBean完成对Test类的实例化
package com.lichaobao.test; import com.lichaobao.simpleioc.annotations.SimpleBean; import com.lichaobao.simpleioc.annotations.SimpleConfigure; /** * @author lichaobao * @date 2019/4/18 * @QQ 1527563274 */ @SimpleConfigure public class Configure { @SimpleBean public Test test(){ return new Test("我是配置类里生成的test"); } } 复制代码
- 新建一个类TestA 用上SimpleCompotent与SimpleAutowire来完成属性的依赖注入以及TestA类的实例化
package com.lichaobao.test; import com.lichaobao.simpleioc.annotations.SimpleAutowire; import com.lichaobao.simpleioc.annotations.SimpleCompotent; /** * @author lichaobao * @date 2019/4/18 * @QQ 1527563274 */ @SimpleCompotent public class TestA { @SimpleAutowire private Test test; void watch(){ System.out.println("======================"); System.out.println("我看看test属性被注入了没"); test.doSomething(); System.out.println("上面打印了,属实注入了呀"); } } 复制代码
- 测试类如下:
package com.lichaobao.test; import com.lichaobao.simpleioc.factory.AutowireFactory; import com.lichaobao.simpleioc.factory.BeanFactory; /** * @author lichaobao * @date 2019/4/18 * @QQ 1527563274 */ public class TestMain { public static void main(String[] args)throws Exception{ AutowireFactory autowireFactory = new AutowireFactory(); BeanFactory.getInstance().init("com.lichaobao"); autowireFactory.init("com.lichaobao"); Test test = (Test) BeanFactory.getInstance().getBean("com.lichaobao.test.Test"); TestA testA = (TestA) BeanFactory.getInstance().getBean("com.lichaobao.test.TestA"); test.doSomething(); testA.watch(); } } 复制代码
运行后打印
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 自己动手实现一个Promise
- 动手用CUDA实现CNN
- LeetCode146 动手实现LRU算法
- 自己动手实现神经网络分词模型
- 自己动手实现一个简单的React
- 动手造轮子:实现简单的 EventQueue
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。