实现简单的spring IOC容器和依赖注入功能

介绍:  最近看了一些别人的关于spring ioc/aop的博客,在别人基础上,实现以下spring ioc/aop简易的原理,在此感谢别人的分享.

使用java注解,反射机制,完成spring IOC容器和依赖注入的功能,模仿注解   Controller/Service/Component/Repository/Autowired的使用以及原理实现

项目的结构截图如下,后续完善AOP等功能

实现简单的spring IOC容器和依赖注入功能

自定义的注解有:

@MyAutowired

package com.hf.aopdemo.ioc.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @Description: 自定义依赖注入注解
 * @Date: 2019/4/8
 * @Auther: 
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface MyAutowired {
    String value() default "";
}

@MyService

package com.hf.aopdemo.ioc.annotation;

import java.lang.annotation.*;

/**
 * @Description: 自定义service注解
 * @Date: 2019/4/8
 * @Auther:
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface MyService {
    String value() default "";
}

@MyController

package com.hf.aopdemo.ioc.annotation;

import java.lang.annotation.*;

/**
 * @Description: 自定义controller注解
 * @Date: 2019/4/8
 * @Auther:
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface MyController {

    String value() default "";
}

@MyComponent

package com.hf.aopdemo.ioc.annotation;

import java.lang.annotation.*;

/**
 * @Description: 自定义component
 * @Date: 2019/4/8
 * @Auther:
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface MyComponent {
    String value() default "";
}

@MyRepository

package com.hf.aopdemo.ioc.annotation;

import java.lang.annotation.*;

/**
 * @Description: 自定义MyRepository注解
 * @Date: 2019/4/9
 * @Auther:
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
public @interface MyRepository {
    String value() default "";
}

自定义加载ioc容器初始化过程中的业务异常类:

package com.hf.aopdemo.ioc.exception;

/**
 * @Description: 自定义加载ioc bean异常
 * @Date: 2019/4/8
 * @Auther: 
 */
public class InitBeanException extends RuntimeException {

    public InitBeanException(String msg){
        super(msg);
    }

    public  InitBeanException(Throwable e){
        super(e);
    }
}

下面的就是核心代码了:

1.定义获取Bean对象的接口: MyBeanContext

package com.hf.aopdemo.ioc.context;

/**
 * @Description:
 * @Date: 2019/4/8
 * @Auther:
 */
public interface MyBeanContext {
    /**
     * 从ioc容器中根据名称获取对应的bean对象
     * @param beanName
     * @return
     */
    public Object getBean(String beanName);
}
2.具体加载初始化IOC/依赖注入的逻辑类:  这是一个抽象类
package com.hf.aopdemo.ioc.context;

import com.hf.aopdemo.ioc.annotation.*;
import com.hf.aopdemo.ioc.exception.InitBeanException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.*;

/**
 * @Description: 加载bean的系列逻辑
 * @Date: 2019/4/8
 * @Auther:
 */
@Slf4j
public abstract class AbsBeanContext implements MyBeanContext{



    /**
     * 定义IOC容器 对应的是map: key:类名称简写(首字母小写) --  value:创建的对象
     */
    private final Map<String,Object> IOCMap = new HashMap<>();

    /**
     * 包扫描路径集合
     */
    private final List<String> scanPaths = new ArrayList<>();


    /**
     * 获取自定义IOC容器中的对象
     * @param beanName
     * @return
     */
    @Override
    public Object getBean(String beanName) {
        return IOCMap.get(beanName);
    }


    /**
     * 获取包的路径
     * @param scanPackage
     */
    public void scanPackage(String scanPackage){
        if(StringUtils.isEmpty(scanPackage)){
            throw new InitBeanException("包名称不能为空");
        }
        List<String> packageNames = new ArrayList<>();
        //判断传入是否为多个包名
        if(scanPackage.contains(",")){
            packageNames = Arrays.asList(scanPackage.split(","));
        }else{
            packageNames.add(scanPackage);
        }
        packageNames.stream().forEach(packageName -> {
            //获取包对应的统一资源路径
            URL url = Thread.currentThread().getContextClassLoader().getResource(packageName.replaceAll("\\.", "/"));
            //转换为file处理
            File file = new File(url.getFile());
            //遍历改目录下面的所有文件/文件夹
            Arrays.stream(file.listFiles()).forEach(var -> {
                if(var.isDirectory()){
                    scanPackage(packageName + "." + var.getName());
                }else{
                    scanPaths.add(packageName + "." + var.getName().replaceAll(".class",""));
                }
            });
        });

    }

    /**
     * 初始化IOC容器
     */
    public void initIOC(){
        this.scanPaths.stream().forEach(var -> {
            try {
                Class<?> clzz = Class.forName(var);
                //IOC容器的key值,对应的是类名的首字母小写,如果有接口,就获取接口的名称
                String key = "";
                //判断作用在controller上的注解,
                //TODO 暂时先不考虑MyComponent作用在sevrice/mapper层
                if(clzz.isAnnotationPresent(MyComponent.class) || clzz.isAnnotationPresent(MyController.class)){
                    //获取注解上面的值
                    MyController controller = clzz.getAnnotation(MyController.class);
                    MyComponent component = clzz.getAnnotation(MyComponent.class);
                    if(null != controller){
                       //类似spring 的存储到ioc,首字母是小写的
                        key = firstCharToLower(controller.value());
                    }else{
                        key = firstCharToLower(component.value());
                    }
                 if(StringUtils.isEmpty(key)){
                     //类似spring 给定默认值
                    key = firstCharToLower(clzz.getSimpleName());
                 }
              //存储到ioc容器,key:类名称简写(首字母小写) --  value:创建的对象
                    IOCMap.put(key,clzz.newInstance());
                }else if(clzz.isAnnotationPresent(MyService.class)){
                    //这个是打上了MyService注解的,需要获取到实现的接口
                    MyService service = clzz.getAnnotation(MyService.class);
                    key = service.value();
                    this.handlerAnnocation(key,clzz);
                }else if(clzz.isAnnotationPresent(MyRepository.class)){
                    MyRepository repository = clzz.getAnnotation(MyRepository.class);
                    key = repository.value();
                    this.handlerAnnocation(key,clzz);
                }else{
                    log.error(clzz.getSimpleName() + ":没有找到自定义的注解....." );
                    return;
                }
            } catch (Exception e) {
               log.error("不能通过路径创建class字节码对象....");
               throw new InitBeanException(e);
            }
        });
    }


    /**
     * 针对MyRepository注解/MyService注解的IOC容器初始化处理
     * @param key
     * @param clzz
     */
    private void handlerAnnocation(String key,Class<?> clzz){
        if(StringUtils.isEmpty(key)){
            //用户没有给注解打上指定值,那么久去指定默认值
            //获取实现接口
            Class<?>[] interfaces = clzz.getInterfaces();
            /**
             * 判断是否有上级接口,如果木有就直接存入改类的名称作为key;  如果有就获取到接口的名称作为key存入IOCMap
             */
            if(!CollectionUtils.isEmpty(Arrays.asList(interfaces))){
                Arrays.stream(interfaces).forEach(var1 -> {
                    try {
                        IOCMap.put(firstCharToLower(var1.getSimpleName()),clzz.newInstance());
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                });
            }else{
                //没有实现接口或者没有给定注解值
                //给定默认值
                key = firstCharToLower(clzz.getSimpleName());
            }
        }
        if(!StringUtils.isEmpty(key)){
            //加入IOCMap
            try {
                IOCMap.put(key,clzz.newInstance());
            } catch (InstantiationException | IllegalAccessException var2) {
               log.error("<handlerAnnocation> can not newInstance.....:" + clzz.getSimpleName(),var2.getMessage(),var2);
            }
        }
    }

    /**
     * @Autowired依赖注入,只做针对字段属性的注入
     */
    public void autowiredInjection(){
        this.IOCMap.entrySet().stream().forEach(map -> {
            //取出对象的类型
            Class<?> clzz = map.getValue().getClass();
            //获取所有的字段
            Field[] fields = clzz.getDeclaredFields();
            //判断是否有MyAutowired注解
            Arrays.stream(fields).filter((Field field) -> field.isAnnotationPresent(MyAutowired.class)).forEach(var -> {
                //依赖注入
                var.setAccessible(true);
                //获取到注解值
                String beanName = var.getAnnotation(MyAutowired.class).value();
                if(StringUtils.isEmpty(beanName)){
                    //获取字段的类型,给定默认值
                    beanName = firstCharToLower(var.getType().getSimpleName());
                }
                try {
                //给对象设置字段值,也就是给依赖注入设置对象
                    var.set(map.getValue(),IOCMap.get(beanName));
                } catch (IllegalAccessException e) {
                   log.error("set field " + var.getName() + " error...",e.getMessage(),e);
                }
            });
        });
    }



    /**
     * 字符串首字母转换为小写
     * @param source
     * @return
     */
    public String firstCharToLower(String source){
        String result = "";
        if(!StringUtils.isEmpty(source)){
            StringBuilder builder = new StringBuilder();
            result = builder.append(source.substring(0,1).toLowerCase()).append(source.substring(1)).toString();
        }
        return result;
    }

}
3.真正的初始化IOC容器的类,该类用于完成IOC初始化和自动依赖注入
package com.hf.aopdemo.ioc.context;

import lombok.extern.slf4j.Slf4j;

/**
 * @Description:
 * @Date: 2019/4/9
 * @Auther:
 */
@Slf4j
public class MyBaseContext extends AbsBeanContext{

    /**
     * 包扫描常量
     */
    public String SCAN_PACKAGE = null;
    /**
     * 是否开启aop配置
     */
    public boolean ENABLE_AOP = false;

    /**
     * 创建对象,默认加载bean系列初始化
     */
    public MyBaseContext(String SCAN_PACKAGE,boolean ENABLE_AOP){
        this.ENABLE_AOP = ENABLE_AOP;
        this.SCAN_PACKAGE = SCAN_PACKAGE;
        //扫包处理,获取到路径
        this.scanPackage(this.SCAN_PACKAGE);
        //初始化IOC容器
        this.initIOC();
        //MyAutowired依赖注入
        this.autowiredInjection();
        log.info("初始化IOCMap完成.......");
    }
}

下面我们就做测试了:

新建controller,打上自定义注解

package com.hf.aopdemo.controller;

import com.hf.aopdemo.ioc.annotation.MyAutowired;
import com.hf.aopdemo.ioc.annotation.MyController;
import com.hf.aopdemo.service.UserService;

/**
 * @Description:
 * @Date: 2019/4/9
 * @Auther: 
 */
@MyController
public class UserController {

    @MyAutowired
    private UserService userService;

    public String sayHello(){
        String msg = userService.sayHello("world");
        return msg;
    }

}

新建service,打上自定义注解:

package com.hf.aopdemo.service;

/**
 * @Description:
 * @Date: 2019/4/9
 * @Auther: 
 */
public interface UserService {
    String sayHello(String msg);
}

实现类如下:

package com.hf.aopdemo.service.impl;

import com.hf.aopdemo.ioc.annotation.MyService;
import com.hf.aopdemo.service.UserService;

/**
 * @Description:
 * @Date: 2019/4/9
 * @Auther:
 */
@MyService
public class UserServiceImpl implements UserService {

    @Override
    public String sayHello(String msg) {
       return new StringBuilder().append("hello,").append(msg).toString();
    }
}

测试类:

package com.hf.aopdemo.ioc;

import com.hf.aopdemo.controller.UserController;
import com.hf.aopdemo.ioc.context.MyBaseContext;
import org.junit.Test;

/**
 * @Description:
 * @Date: 2019/4/9
 * @Auther: 
 */
public class IOCTest {

    @Test
    public void tt(){
        //指定包名称
        String scanPakage = "com.hf.aopdemo.service,com.hf.aopdemo.controller";
        MyBaseContext context = new MyBaseContext(scanPakage, false);
        UserController controller = (UserController) context.getBean("userController");
        System.out.println(controller.sayHello());

    }
}

运行可以看到测试结果:

实现简单的spring IOC容器和依赖注入功能