我们都知道 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();
}
}
复制代码
运行后打印