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 handlerMap = new HashMap();
@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 paramIndexMapping = new HashMap();
Annotation[][] pa = method.getParameterAnnotations();
//遍历
for (int i = 0;i[] parameterTypes = method.getParameterTypes();
for (int i = 0; i parameType = parameterTypes[i];
if(parameType == HttpServletRequest.class || parameType == HttpServletResponse.class){
paramIndexMapping.put(parameType.getName(),i);
}
}
//2、根据参数位置匹配参数名字,从url中取到参数名字对应的值
Object[] paramValues = new Object[parameterTypes.length];
Map params = req.getParameterMap();
//http://localhost/web/query.json?name=MC&name=TT
for (Map.Entry 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 factoryBeanInstanceCache = new HashMap();
//key 类名 value 反射的实例对象
private Map factoryBeanObjectCache = new HashMap();
private MCBeanDefinitionReader reader;
public MCApplicationContext(String... configLocations) {
//1、加载配置文件
reader = new MCBeanDefinitionReader(configLocations);
try {
//2、解析配置文件,把所有的配置信息封装成BeanDefinition对象
List beanDefinitions = reader.loadBeanDefinitions();
//3、所有的配置信息缓存起来
this.registry.addBeanDefinitionMap(beanDefinitions);
//4、加载非延迟加载的所有Bean
doLoadInstance();
} catch (Exception e) {
e.printStackTrace();
}
}
private void doLoadInstance(){
//循环调用getBean()
for (Map.Entry 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