spring IOC和DI 理解

IOC(控制反转)和DI(依赖注入)是spring中的重要组成部分,下面是个人的一些理解,不代表官方。

1、IOC是什么?

IOC:全名是Inversion of Controller 中文解释是控制反转,不是什么技术,是一种设计思想。在java开发中IOC意味着你设计好的对象交给容器控制,而不是传统的在你对象内部直接控制。如何理解好IOC那?理解IOC的关键是要明确:“谁控制谁”、“控制什么”、为何是反转(有反转就应该有正转)哪些方面反转了,我们来分析一下:

1)谁控制谁,控制什么:传统的java SE程序设计,我们直接在对象内部new进行创建对象,是程序主动去创建对象,而IOC有专门的容器来创建这些对象,即由IOC容器来控制对象的创建;谁控制谁?当然是IOC容器控制对象,控制什么?主要是控制外部资源的获取(不只是对象包括比如文件等)。

2)为何是反转,那方面反转了:有反转就有正转,传统应用程序是由我们自己在对象中直接获取依赖对象,也就是正转;而反转是由容器来帮忙创建和注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取反转了。

用图例说明一下,传统程序设计如下图1,都是主动去创建相关对象然后再组合起来:

        

当有了IoC/DI的容器后,在客户端类中不再主动去创建这些对象了,如图2所示

 

 

2、IOC(控制反转)做什么用?

IOC不是一种技术,是一种设计思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由程序内部主动创建对象依赖,从而导致类与类之间高耦合,难于测试;有了IOC容器后,把创建和查找依赖对象的控制权交给了容器,由容器注入组合对象,所以对象和对象之间是松耦合的,这样利于测试,也利于功能复用,更重要的是使得程序的整个体系结构变的非常灵活。

其实IoC对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在IoC/DI思想中,应用程序就变成被动的了,被动的等待IoC容器来创建并注入它所需要的资源了。
IoC很好的体现了面向对象设计法则之一—— 好莱坞法则:“别找我们,我们找你”;即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。

3、DI是什么?
 DI:Dependency Injection ,即依赖注入:是组件之间依赖关系由容器在运行期决定,形象的说,由容器动态的将某个依赖关系注入到组件之中。

4、DI(依赖注入)有什么用

依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。

理解DI的关键:谁依赖谁,为什么需要依赖,谁注入谁,注入了什么?

1)谁依赖谁:当然是应用程序依赖IOC容器

2)为什么需要依赖:应用程序需要IOC容器来提供对象需要的外部资源

3)谁注入谁:IOC容器注入应用程序的某个对象,应用程序依赖的对象

4)注入了什么:注入某个对象依赖的外部资源(包括对象,资源,常来数据)

IoC和DI什么关系呢?其实它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以2004年大师级人物Martin Fowler又给出了一个新的名字:“依赖注入”,相对IoC 而言,“依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”。
 

5、怎么用?

整体如图:

 

代码如下:

1、Servelt转ApplicationContext:(1)获取applicationContext上下文(2)初始化handlerMapper

package com.zmc.spring.framework.webmvc.servlet;

import com.zmc.spring.framework.annotation.MCController;
import com.zmc.spring.framework.annotation.MCRequestMapping;
import com.zmc.spring.framework.annotation.MCRequestParam;
import com.zmc.spring.framework.context.MCApplicationContext;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * @author zhangmc
 * @create 2021/11/9 17:01
 */
public class MCDispatcherServlet extends HttpServlet{

    //IOC容器的访问上下文
    private MCApplicationContext mcApplicationContext = null;

    //控制端Controller方法和url 的对应关系
    public Map<String,Method> handlerMap = new HashMap<String, Method>();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req,HttpServletResponse resp) throws ServletException,IOException{
        try {
            doDispatch(req,resp);
        }catch (Exception e){
            e.printStackTrace();
            resp.getWriter().write("500 Exception,Detail: " + Arrays.toString(e.getStackTrace()));
        }
    }

    private void doDispatch(HttpServletRequest req,HttpServletResponse resp) throws Exception{
        String url = req.getRequestURI();
        String contextPath = req.getContextPath();

        url = url.replaceAll(contextPath,"").replaceAll("/+","/");
        //判断路径是否存在
        if(!this.handlerMap.containsKey(url)){
            resp.getWriter().write("404 Not Find!!!");
            return ;
        }
        
        //获取方法
        Method method = this.handlerMap.get(url);
        
        //1、先把形参的位置和参数的名字建立映射关系,并且缓存下来
        Map<String,Integer> paramIndexMapping = new HashMap<String, Integer>();

        Annotation[][] pa = method.getParameterAnnotations();
        //遍历
        for (int i = 0;i<pa.length;i++){
            for (Annotation an : pa[i]){
                //判断是否是MCRequestParam
                if(an instanceof MCRequestParam){
                    //强转
                    String paramName = ((MCRequestParam)an).value();
                    //过滤""
                    if(!"".equals(paramName.trim())){
                        paramIndexMapping.put(paramName,i);
                    }
                }
            }
        }

        Class<?>[] parameterTypes = method.getParameterTypes();
        for (int i = 0; i<parameterTypes.length;i++){
            Class<?> parameType = parameterTypes[i];
            if(parameType == HttpServletRequest.class || parameType == HttpServletResponse.class){
                paramIndexMapping.put(parameType.getName(),i);
            }
        }

        //2、根据参数位置匹配参数名字,从url中取到参数名字对应的值
        Object[] paramValues = new Object[parameterTypes.length];

        Map<String,String[]> params = req.getParameterMap();

        //http://localhost/web/query.json?name=MC&name=TT
        for (Map.Entry<String,String[]> entry : params.entrySet()){
            String value = Arrays.toString(entry.getValue())
                    .replaceAll("\\[|\\]","")
                    .replaceAll("\\s","");

            if(!paramIndexMapping.containsKey(entry.getKey())){
                continue;
            }

            int i = paramIndexMapping.get(entry.getKey());

            //涉及到类型强制转换
            paramValues[i] = value;
        }

        if(paramIndexMapping.containsKey(HttpServletRequest.class.getName())){
            int i = paramIndexMapping.get(HttpServletRequest.class.getName());
            paramValues[i] =  req;
        }

        if(paramIndexMapping.containsKey(HttpServletResponse.class.getName())){
            int i = paramIndexMapping.get(HttpServletResponse.class.getName());
            paramValues[i] = resp;
        }
        
        String beanName =toLowerFirstCase(method.getDeclaringClass().getSimpleName());
        //3、组成动态实际参数列表,传给反射调用
        method.invoke(mcApplicationContext.getBean(beanName),paramValues);
        

    }

    @Override
    public void init(ServletConfig config) throws ServletException{

        mcApplicationContext = new MCApplicationContext(config.getInitParameter("contextConfigLocation"));

        //========MVC功能=======
        //初始化handlerMapper
        doInitHandlerMapper();

        System.out.println("MC spring framework is init.");
    }

    //初始化controller方法和url路径
    private void doInitHandlerMapper(){
        if(this.mcApplicationContext.getBeanDefinitionCount() == 0){
            return;
        }

        for (String beanName : this.mcApplicationContext.getBeanDefinitionNames()){
            Object instance = mcApplicationContext.getBean(beanName);
            Class<?> clazz = instance.getClass();
            //判断类是否是Controller注解
            if(!clazz.isAnnotationPresent(MCController.class)){
                continue;
            }

            String baseUrl = "";
            if(clazz.isAnnotationPresent(MCRequestMapping.class)){
                MCRequestMapping mcRequestMapping = clazz.getAnnotation(MCRequestMapping.class);
                baseUrl = mcRequestMapping.value();
            }

            if(null == clazz.getMethods()){
                continue;
            }

            //取类中的方法
            for (Method method : clazz.getMethods()){
                //取方法注解 判断是否是requestMapping
                if(!method.isAnnotationPresent(MCRequestMapping.class)){
                    continue;
                }
                MCRequestMapping requestMapping = method.getAnnotation(MCRequestMapping.class);

                String requestUrl = ("/"+baseUrl+"/"+requestMapping.value()).replaceAll("/+","/");

                handlerMap.put(requestUrl,method);

                System.out.println("Mappred :" + requestUrl + "------>"+method);
            }


        }

    }

    private String toLowerFirstCase(String simpleName){
        char[] chars = simpleName.toCharArray();
        chars[0]+=32; //利用了ASCII码,大写字母和小写字母相差32这个规律
        return String.valueOf(chars);
    }

}

2、ApplicationContext 上下文对象

 

package com.zmc.spring.framework.context;

import com.zmc.spring.framework.annotation.MCAutowried;
import com.zmc.spring.framework.annotation.MCController;
import com.zmc.spring.framework.annotation.MCService;
import com.zmc.spring.framework.beans.MCBeanWrapper;
import com.zmc.spring.framework.beans.config.MCBeanDefinition;
import com.zmc.spring.framework.beans.support.MCBeanDefinitionReader;
import com.zmc.spring.framework.beans.support.MCDefaultListableBeanFactory;
import com.zmc.spring.framework.core.MCBeanFactory;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author zhangmc
 * @create 2021/11/9 18:10
 */
public class MCApplicationContext implements MCBeanFactory{

    private MCDefaultListableBeanFactory registry = new MCDefaultListableBeanFactory();

    //三级缓存(终极缓存) key beanName(类名) value BeanWrapper包装类
    private Map<String,MCBeanWrapper> factoryBeanInstanceCache = new HashMap<String, MCBeanWrapper>();

    //key 类名 value 反射的实例对象
    private Map<String,Object> factoryBeanObjectCache = new HashMap<String, Object>();

    private MCBeanDefinitionReader reader;

    public MCApplicationContext(String... configLocations) {

        //1、加载配置文件
        reader = new MCBeanDefinitionReader(configLocations);

        try {
            //2、解析配置文件,把所有的配置信息封装成BeanDefinition对象
            List<MCBeanDefinition> beanDefinitions = reader.loadBeanDefinitions();
            //3、所有的配置信息缓存起来
            this.registry.addBeanDefinitionMap(beanDefinitions);
            //4、加载非延迟加载的所有Bean
            doLoadInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void doLoadInstance(){
        //循环调用getBean()
        for (Map.Entry<String,MCBeanDefinition> entry : this.registry.beanDefinitionMap.entrySet()){
            String beanName = entry.getKey();
            if(!entry.getValue().isLazyInit()){
                getBean(beanName);
            }
        }
    }

//    @Override
    public Object getBean(Class className) {
        return getBean(className.getName());
    }

    //从IOC容器中获取一个Bean对象
//    @Override
    public Object getBean(String beanName) {
        //1、先拿到BeanDefinition对象
        MCBeanDefinition beanDefinition = registry.beanDefinitionMap.get(beanName);
        if(beanDefinition == null){
            return null;
        }
        //2、反射实例化对象
        Object instance = instantiateBean(beanName,beanDefinition);

        //3、将返回的对象封装到BeanWrapper
        MCBeanWrapper beanWrapper = new MCBeanWrapper(instance);

        //4、执行依赖注入
        populateBean(beanName,beanDefinition,beanWrapper);

        //5、保存到IOC容器中
        this.factoryBeanInstanceCache.put(beanName,beanWrapper);

        return beanWrapper.getWrapperInstance();
    }

    //反射实例化对象
    private Object instantiateBean(String beanName,MCBeanDefinition beanDefinition){
        String className = beanDefinition.getBeanClassName();
        Object instance = null;
        try {
            //jvm查找 并加载指定的类
            Class<?> clazz = Class.forName(className);
            //通过反射得到实例化对象
            instance = clazz.newInstance();

            //如果是代理对象,触发AOP的逻辑

            this.factoryBeanObjectCache.put(beanName,instance);

        } catch (Exception e) {
            e.printStackTrace();
        }

        return instance;
    }

    private void populateBean(String beanName,MCBeanDefinition beanDefinition,MCBeanWrapper beanWrapper){
        //获取实例化对象
        Object instance = beanWrapper.getWrapperInstance();
        //获取加载类
        Class<?> clazz = beanWrapper.getWrapperClass();
        //判断是否是Controller和Service的注解
        if(!(clazz.isAnnotationPresent(MCController.class) || clazz.isAnnotationPresent(MCService.class))){
            return;
        }
        //遍历 忽略字段的修饰符 不管你是 private / protected / public / default
        for (Field filed : clazz.getDeclaredFields()){
            //过滤非Autowired注解的
            if(!filed.isAnnotationPresent(MCAutowried.class)){
                continue;
            }
            MCAutowried  autowried= filed.getAnnotation(MCAutowried.class);
            String autowriedBeanName = autowried.value().trim();
            if("".equals(autowriedBeanName)){
                autowriedBeanName = filed.getType().getName();
            }

            //代码反射面前就是裸奔
            //强制访问 强吻
            filed.setAccessible(true);

            try {
                if(this.factoryBeanInstanceCache.get(autowriedBeanName) == null){
                    continue;
                }

                //相当于 demo.action.MyAction.
                filed.set(instance,this.factoryBeanInstanceCache.get(autowriedBeanName).getWrapperInstance());
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }

    public int getBeanDefinitionCount(){
        return this.registry.beanDefinitionMap.size();
    }

    public String[] getBeanDefinitionNames(){
        return this.registry.beanDefinitionMap.keySet().toArray(new String[0]);
    }
}

    

参考:浅谈对Spring IOC以及DI的理解_luoyepiaoxue2014的博客-CSDN博客_ioc和di

  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值