Spring框架进阶Spring V2.0 IOC与DI
Posted 烟锁迷城
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring框架进阶Spring V2.0 IOC与DI相关的知识,希望对你有一定的参考价值。
1、IOC的本质
Map 容器:IOC容器的本质就是一个Map
ApplicationContext 上下文:持有BeanFactory的引用
BeanFactory 工厂:负责从容器中获取Bean
BeanDefinitionReader 解析器:负责解析全部的配置文件
Definition 元信息配置:xml,yml,annotation,properties,为了适配各种不同的配置文件,Spring采用顶层设计BeanDefinition来保存配置信息。
Bean 实例:反射实例化Bean。这个Bean的来源可能是原生Bean,可能是代理Bean,无论是哪一种Bean,都会被BeanWrapper进行装饰(装饰器模式),它持有Bean的引用。
BeanWrapper 装饰器:缓存到IOC容器中,持有Bean的引用
通过ApplicationContext调用getBean()方法来获得各种BeanFactory ,通过BeanDefinitionReader读取配置文件信息,根据BeanDefinition创建Bean,得到的Bean无论是原生对象还是代理对象,都交给BeanWrapper进行装饰,所以getBean()方法,最后拿到的就是BeanWrapper对象。
2、从Servlet到ApplicationContext
在上一篇文章中,以MyDispatchServlet为基准实现了一个简单的Spring框架流程:
- 读取配置文件
- 根据包地址扫描文件
- 初始化IOC容器,实例化Bean,加载至IOC中
- 依赖注入
- 匹配URL和方法,存放到HandlerMapping
1·4步骤,都是IOC与DI的操作,都是需要转化到ApplicationContext之中的步骤。
新的步骤为:
- 读取配置文件,扫描对应的包下的文件
- 获取到所有配置信息
- 将所有配置信息保存
- 加载所有非延迟加载Bean
执行加载Bean的方法就是Spring框架的核心方法,getBean方法,其步骤为:
- 根据BeanName获取配置信息
- 实例化Instance
- 将Instance封装到BeanWrapper中
- 对Instance进行依赖注入
- 将Instance加载到IOC中
3、与Spring源码的结合
为了实现功能,需要结合Spring源码进行一些分析。
BeanFactory是核心,它具有一个关键的方法,getBean。
其中ApplicationContext类,DefaultListableBeanFactory类都继承了BeanFactory。
DefaultListableBeanFactory的作用是保存了所有的配信息:beanDefinitionMap,三级缓存(即IOC的终极缓存):factoryBeanInstanceCache,所有Bean的类备份:factoryBeanObjectCache,并且实现了getBean方法
ApplicationContext持有DefaultListableBeanFactory的引用,因此可以通过使用DefaultListableBeanFactory的一些数据完成方法。
BeanDefinitionReader类是专门用来读取配置文件和扫描包下文件。
BeanDefinition用来保存bean的名字:factoryBeanName,bean的全类名:beanClassName
BeanWrapper用来封装实例化的bean,最后保存到IOC中,其中包括实例对象:wrappedInstance,类对象:wrappedClass。
了解到这些主要的类,就可以开始进行代码的编写了。
4、实现过程
4.1、ApplicationContext部分
4.1.1、对象工厂
/**
* 创建对象的工厂
*/
public interface MyBeanFactory
Object getBean(Class beanClass);
Object getBean(String beanName);
4.1.2、读取配置文件并扫描包
在ApplicationContext的构造函数中,执行BeanDefinitionReader的创建以加载配置文件
public class MyApplicationContext implements MyBeanFactory
//持有引用
private MyDefaultListableBeanFactory registry = new MyDefaultListableBeanFactory();
//配置文件读取器
private MyBeanDefinitionReader reader = null;
public MyApplicationContext(String... configLocations)
//1、加载配置文件
reader = new MyBeanDefinitionReader(configLocations);
BeanDefinitionReader类拥有Properties和registryBeanClasses,这两个容器不必赘述,在上一篇中已经出现过
public class MyBeanDefinitionReader
//配置文件信息
private Properties contextConfig = new Properties();
//注册文件名集合
private List<String> registryBeanClasses = new ArrayList<String>();
public MyBeanDefinitionReader(String... locations)
//1、加载配置文件
doLoadConfig(locations[0]);
//2、扫描相关的类
doScanner(contextConfig.getProperty("scanPackage"));
这两个方法的实现也和上一篇文章中的实现方式并无差异。
private void doLoadConfig(String configLocation)
InputStream stream = this.getClass().getClassLoader().getResourceAsStream(configLocation.replaceAll("classpath:", ""));
try
contextConfig.load(stream);
catch (IOException e)
e.printStackTrace();
finally
if (stream != null)
try
stream.close();
catch (IOException e)
e.printStackTrace();
private void doScanner(String scanPackage)
URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\\\.", "/"));
File aFile = new File(url.getFile());
for (File file : aFile.listFiles())
String fileName = file.getName();
if (file.isDirectory())
doScanner(scanPackage + "." + fileName);
if (!fileName.endsWith(".class"))
continue;
String className = scanPackage + "." + fileName.replaceAll(".class", "");
registryBeanClasses.add(className);
4.1.3、获取到所有配置信息
首先需要创建配置类BeanDefinition,里面具有
bean的名字:factoryBeanName
bean的全类名:beanClassName
是否延迟加载:getLazyInit()
public class MyBeanDefinition
//beanName
private String factoryBeanName;
//全类名
private String beanClassName;
public String getFactoryBeanName()
return factoryBeanName;
public void setFactoryBeanName(String factoryBeanName)
this.factoryBeanName = factoryBeanName;
public String getBeanClassName()
return beanClassName;
public void setBeanClassName(String beanClassName)
this.beanClassName = beanClassName;
//是否延时加载
public boolean isLazyInit()
return false;
在ApplicationContext文件中,增加一个步骤:
public class MyApplicationContext implements MyBeanFactory
//持有引用
private MyDefaultListableBeanFactory registry = new MyDefaultListableBeanFactory();
//配置文件读取器
private MyBeanDefinitionReader reader = null;
public MyApplicationContext(String... configLocations)
//1、加载配置文件
reader = new MyBeanDefinitionReader(configLocations);
try
//2、解析配置文件,将配置信息封装成BeanDefinition对象
List<MyBeanDefinition> beanDefinitions = reader.loadBeanDefinitions();
catch (Exception e)
e.printStackTrace();
可以发现,这里的方法loadBeanDefinition类似于上一章的实例化过程,只不过这次保存的是bean对象的bean名与bean全名。
public List<MyBeanDefinition> loadBeanDefinitions()
List<MyBeanDefinition> result = new ArrayList<MyBeanDefinition>();
try
for (String beanName : registryBeanClasses)
Class<?> aClass = Class.forName(beanName);
if (aClass.isInterface())
continue;
result.add(createBeanDefinition(toLowerFirstCase(aClass.getSimpleName()), aClass.getName()));
for (Class<?> anInterface : aClass.getInterfaces())
result.add(createBeanDefinition(anInterface.getName(), aClass.getName()));
catch (Exception e)
e.printStackTrace();
return result;
private MyBeanDefinition createBeanDefinition(String factoryBeanName, String beanClassName)
MyBeanDefinition beanDefinition = new MyBeanDefinition();
beanDefinition.setFactoryBeanName(factoryBeanName);
beanDefinition.setBeanClassName(beanClassName);
return beanDefinition;
private String toLowerFirstCase(String simpleName)
char[] chars = simpleName.toCharArray();
chars[0] += 32;
return String.valueOf(chars);
4.1.4、保存配置信息至DefaultListableBeanDefinition
在ApplicationContext中继续添加步骤:
public class MyApplicationContext implements MyBeanFactory
//持有引用
private MyDefaultListableBeanFactory registry = new MyDefaultListableBeanFactory();
//配置文件读取器
private MyBeanDefinitionReader reader = null;
public MyApplicationContext(String... configLocations)
//1、加载配置文件
reader = new MyBeanDefinitionReader(configLocations);
try
//2、解析配置文件,将配置信息封装成BeanDefinition对象
List<MyBeanDefinition> beanDefinitions = reader.loadBeanDefinitions();
//3、把所有配置信息缓存
this.registry.doRegistryBeanDefinition(beanDefinitions);
catch (Exception e)
e.printStackTrace();
方法doRegistryBeanDefinition,简单理解就是进行配置信息的注册,即将BeanDefinition列表转移到BeanDefinitionMap之中,其key为factoryBeanName,value为BeanDefinition。
public class MyDefaultListableBeanFactory implements MyBeanFactory
//保存所有的配置信息
public Map<String, MyBeanDefinition> beanDefinitionMap = new HashMap<String, MyBeanDefinition>();
//三级缓存,也是终极缓存
private Map<String, MyBeanWrapper> factoryBeanInstanceCache = new HashMap<String, MyBeanWrapper>();
private Map<String, Object> factoryBeanObjectCache = new HashMap<String, Object>();
public void doRegistryBeanDefinition(List<MyBeanDefinition> beanDefinitions) throws Exception
for (MyBeanDefinition beanDefinition : beanDefinitions)
if (beanDefinitionMap.containsKey(beanDefinition.getFactoryBeanName()))
throw new Exception("已经存在" + beanDefinition.getFactoryBeanName());
beanDefinitionMap.put(beanDefinition.getFactoryBeanName(), beanDefinition);
4.1.5、加载非延时Bean
在ApplicationContext中,继续添加步骤:
public class MyApplicationContext implements MyBeanFactory
//持有引用
private MyDefaultListableBeanFactory registry = new MyDefaultListableBeanFactory();
//配置文件读取器
private MyBeanDefinitionReader reader = null;
public MyApplicationContext(String... configLocations)
//1、加载配置文件
reader = new MyBeanDefinitionReader(configLocations);
try
//2、解析配置文件,将配置信息封装成BeanDefinition对象
List<MyBeanDefinition> beanDefinitions = reader.loadBeanDefinitions();
//3、把所有配置信息缓存
this.registry.doRegistryBeanDefinition(beanDefinitions);
//4、加载所有非延时加载的bean
doLoadInstance();
catch (Exception e)
e.printStackTrace();
根据从DefaultListableBeanDefinition中获取的配置文件信息,可以以循环的方式完成bean的加载,使用的方法也是getBean。这里的getBean可以使用ApplicationContext的方法,因为继承了BeanFactory。
但是DefaultListableBeanDefinition也继承了BeanFactory,而且其中还包含三级缓存和Bean的实例保存,且ApplicationContext持有DefaultListableBeanDefinition的引用,所以Spring源码里的getBean是DefaultListableBeanDefinition实现的,ApplicationContext只需要引用其方法即可。
private void doLoadInstance()
//循环调用getBean方法
for (Map.Entry<String, MyBeanDefinition> entry : this.registry.beanDefinitionMap.entrySet())
String beanName = entry.getKey();
//是否延时加载,如果非延时,则立刻加载
if (!entry.getValue().isLazyInit())
getBean(beanName);
4.2、DefaultListableBeanDefinition部分
其实DefaultListableBeanDefinition部分只剩下getBean的实现了。
根据上述过的步骤,进行编写
4.2.1、获取配置信息
DefaultListableBeanDefinition类的步骤:
public class MyDefaultListableBeanFactory implements MyBeanFactory
//保存所有的配置信息
public Map<String, MyBeanDefinition> beanDefinitionMap = new HashMap<String, MyBeanDefinition>();
//三级缓存,也是终极缓存
private Map<String, MyBeanWrapper> factoryBeanInstanceCache = new HashMap<String, MyBeanWrapper>();
private Map<String, Object> factoryBeanObjectCache = new HashMap<String, Object>();
@Override
public Object getBean(String beanName)
//1、先拿到对应的beanDefinition配置信息
MyBeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);
return null;
4.2.2、实例化类
添加DefaultListableBeanDefinition类的步骤:
public class MyDefaultListableBeanFactory implements MyBeanFactory
//保存所有的配置信息
public Map<String, MyBeanDefinition> beanDefinitionMap = new HashMap<String, MyBeanDefinition>();
//三级缓存,也是终极缓存
private Map<String, MyBeanWrapper> factoryBeanInstanceCache = new HashMap<String, MyBeanWrapper>();
private Map<String, Object> factoryBeanObjectCache = new HashMap<String, Object>();
@Override
public Object getBean(String beanName)
//1、先拿到对应的beanDefinition配置信息
MyBeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);
//2、反射实例化对象
Object instance = instantiateBean(beanName, beanDefinition);
return null;
方法instantiateBean调用反射进行实例化,并将实例化内容保存到factoryBeanObjectCache
private Object instantiateBean(String beanName, MyBeanDefinition beanDefinition)
String beanClassName = beanDefinition.getBeanClassName();
Object instance = null;
try
Class<?> aClass = Class.forName(beanClassName);
instance = aClass.newInstance();
//如果是代理对象,将触发AOP代理
this.factoryBeanObjectCache.put(beanName, instance);
catch (Exception e)
e.printStackTrace();
return instance;
4.2.3、封装BeanWrapper
添加DefaultListableBeanDefinition类的步骤:
public class MyDefaultListableBeanFactory implements MyBeanFactory
//保存所有的配置信息
public Map<String, MyBeanDefinition> beanDefinitionMap = new HashMap<String, MyBeanDefinition>();
//三级缓存,也是终极缓存
private Map<String, MyBeanWrapper> factoryBeanInstanceCache = new HashMap<String, MyBeanWrapper>();
private Map<String, Object> factoryBeanObjectCache = new HashMap<String, Object>();
@Override
public Object getBean(String beanName)
//1、先拿到对应的beanDefinition配置信息
MyBeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);
//2、反射实例化对象
Object instance = instantiateBean(beanName, beanDefinition);
//3、将返回的对象封装成BeanWrapper
MyBeanWrapper beanWrapper = new MyBeanWrapper(instance);
return null;
创建BeanWrapper,需要两个属性
实例化的类:wrappedInstance
实例化类的Class:wrappedClass
public class MyBeanWrapper
private Object wrappedInstance;
private Class<?> wrappedClass;
public MyBeanWrapper(Object instance)
this.wrappedInstance = instance;
this.wrappedClass = instance.getClass();
public Object getWrappedInstance()
return this.wrappedInstance;
public Class<?> getWrappedClass()
return this.wrappedClass;
只需要构造函数进行初始化即可
4.2.4、依赖注入
添加DefaultListableBeanDefinition类的步骤:
public class MyDefaultListableBeanFactory implements MyBeanFactory
//保存所有的配置信息
public Map<String, MyBeanDefinition> beanDefinitionMap = new HashMap<String, MyBeanDefinition>();
//三级缓存,也是终极缓存
private Map<String, MyBeanWrapper> factoryBeanInstanceCache = new HashMap<String, MyBeanWrapper>();
private Map<String, Object> factoryBeanObjectCache = new HashMap<String, Object>();
@Override
public Object getBean(String beanName)
//1、先拿到对应的beanDefinition配置信息
MyBeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);
//2、反射实例化对象
Object instance = instantiateBean(beanName, beanDefinition);
//3、将返回的对象封装成BeanWrapper
MyBeanWrapper beanWrapper = new MyBeanWrapper(instance);
//4、执行依赖注入
populateBean(beanName, beanDefinition, beanWrapper);
return null;
方法populateBean生成bean,这个方法和之前的依赖注入并无太大区别,无需赘述
private void populateBean(String beanName, MyBeanDefinition beanDefinition, MyBeanWrapper beanWrapper)
Object instance = beanWrapper.getWrappedInstance();
Class<?> clazz = instance.getClass();
if (!(clazz.isAnnotationPresent(MyController.class) || clazz.isAnnotationPresent(MyService.class)))
return;
for (Field field : clazz.getDeclaredFields())
if (!field.isAnnotationPresent(MyAutowired.class))
continue;
MyAutowired autowired = field.getAnnotation(MyAutowired.class);
String autowiredBeanName = field.getType().getName();
if (!"".equals(autowired.value()))
autowiredBeanName = autowired.value().trim();
field.setAccessible(true);
try
if (this.factoryBeanInstanceCache.get(autowiredBeanName) == null)
continue;
field.set(instance, this.factoryBeanInstanceCache.get(autowiredBeanName).getWrappedInstance());
catch (IllegalAccessException e)
e.printStackTrace();
4.2.5、IOC装载
添加DefaultListableBeanDefinition类的步骤:
public class MyDefaultListableBeanFactory implements MyBeanFactory
//保存所有的配置信息
public Map<String, MyBeanDefinition> beanDefinitionMap = new HashMap<String, MyBeanDefinition>();
//三级缓存,也是终极缓存
private Map<String, MyBeanWrapper> factoryBeanInstanceCache = new HashMap<String, MyBeanWrapper>();
private Map<String, Object> factoryBeanObjectCache = new HashMap<String, Object>();
@Override
public Object getBean(String beanName)
//1、先拿到对应的beanDefinition配置信息
MyBeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);
//2、反射实例化对象
Object instance = instantiateBean(beanName, beanDefinition);
//3、将返回的对象封装成BeanWrapper
MyBeanWrapper beanWrapper = new MyBeanWrapper(instance);
//4、执行依赖注入
populateBean(beanName, beanDefinition, beanWrapper);
//5、保存到IOC容器中
this.factoryBeanInstanceCache.put(beanName, beanWrapper);
return beanWrapper.getWrappedInstance();
这里的步骤就是将最后的封装结果放入IOC,即三级缓存factoryBeanInstanceCache中。
4.3、DispatchServlet的部分
完成ApplicationContext的构建之后,就可以去掉那些不必要的步骤,以ApplicationContext进行代替。
public class MyDispatchServlet extends HttpServlet
private Map<String, Method> handlerMapping = new HashMap<String, Method>();
private MyApplicationContext myApplicationContext = null;
@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();
@Override
public void init(ServletConfig config) throws ServletException
myApplicationContext = new MyApplicationContext(config.getInitParameter("contextConfigLocation"));
doHandlerMapping();
没有了之前的IOC容器,很多判断就失去了依据,那么就需要从ApplicationContext中获取到有关于容器的内容
增加方法getBeanDefinitionCount,获取配置文件长度
增加方法getBeanDefinitionNames,获取配置文件的key,即bean的名字
public class MyApplicationContext implements MyBeanFactory
//持有引用
private MyDefaultListableBeanFactory registry = new MyDefaultListableBeanFactory();
//配置文件读取器
private MyBeanDefinitionReader reader = null;
public MyApplicationContext(String... configLocations)
//1、加载配置文件
reader = new MyBeanDefinitionReader(configLocations);
try
//2、解析配置文件,将配置信息封装成BeanDefinition对象
List<MyBeanDefinition> beanDefinitions = reader.loadBeanDefinitions();
//3、把所有配置信息缓存
this.registry.doRegistryBeanDefinition(beanDefinitions);
//4、加载所有非延时加载的bean
doLoadInstance();
catch (Exception e)
e.printStackTrace();
private void doLoadInstance()
//循环调用getBean方法
for (Map.Entry<String, MyBeanDefinition> entry : this.registry.beanDefinitionMap.entrySet())
String beanName = entry.getKey();
//是否延时加载,如果非延时,则立刻加载
if (!entry.getValue().isLazyInit())
getBean(beanName);
@Override
public Object getBean(Class beanClass)
return this.getBean(beanClass.getName());
@Override
public Object getBean(String beanName)
return this.registry.getBean(beanName);
public int getBeanDefinitionCount()
return this.registry.beanDefinitionMap.size();
public String[] getBeanDefinitionNames()
return this.registry.beanDefinitionMap.keySet().toArray(new String[0]);
这样剩余的判断就有依据了
public class MyDispatchServlet extends HttpServlet
private Map<String, Method> handlerMapping = new HashMap<String, Method>();
private MyApplicationContext myApplicationContext = null;
@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();
@Override
public void init(ServletConfig config) throws ServletException
myApplicationContext = new MyApplicationContext(config.getInitParameter("contextConfigLocation"));
doHandlerMapping();
private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws InvocationTargetException, IllegalAccessException, IOException
String url = req.getRequestURI();
String contextPath = req.getContextPath();
url = url.replaceAll(contextPath, "").replaceAll("/+", "/");
if (!this.handlerMapping.containsKey(url))
resp.getWriter().write("404 not found");
return;
Method method = handlerMapping.get(url);
Map<String, Integer> paramMapping = new HashMap<String, Integer>();
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
for (int i = 0; i < parameterAnnotations.length; i++)
for (Annotation annotation : parameterAnnotations[i])
if (annotation instanceof MyRequestParam)
String value = ((MyRequestParam) annotation).value();
if (!"".equals(value))
paramMapping.put(value, i);
Class<?>[] parameterTypes = method.getParameterTypes();
for (int i = 0; i < parameterTypes.length; i++)
Class<?> parameterType = parameterTypes[i];
if (HttpServletRequest.class == parameterType || HttpServletResponse.class == parameterType)
paramMapping.put(parameterType.getName(), i);
Object[] paramValues = new Object[paramMapping.size()];
Map<String, String[]> params = req.getParameterMap();
for (Map.Entry<String, String[]> param : params.entrySet())
String value = Arrays.toString(param.getValue())
.replaceAll("\\\\[|\\\\]", "")
.replaceAll("\\\\s", "");
if (!paramMapping.containsKey(param.getKey()))
continue;
paramValues[paramMapping.get(param.getKey())] = value;
if (paramMapping.containsKey(HttpServletRequest.class.getName()))
int index = paramMapping.get(HttpServletRequest.class.getName());
paramValues[index] = req;
if (paramMapping.containsKey(HttpServletResponse.class.getName()))
int index = paramMapping.get(HttpServletResponse.class.getName());
paramValues[index] = resp;
String beanName = toLowerFirstCase(method.getDeclaringClass().getSimpleName());
Object obj = myApplicationContext.getBean(beanName);
method.invoke(obj, paramValues);
private void doHandlerMapping()
if (myApplicationContext.getBeanDefinitionCount() == 0)
return;
try
for (String beanName : myApplicationContext.getBeanDefinitionNames())
Object instance = myApplicationContext.getBean(beanName);
Class aClass = instance.getClass();
if (!aClass.isAnnotationPresent(MyController.class))
continue;
String url = "";
if (aClass.isAnnotationPresent(MyRequestMapping.class))
MyRequestMapping myRequestMapping = (MyRequestMapping) aClass.getAnnotation(MyRequestMapping.class);
url = "/" + myRequestMapping.value();
for (Method method : aClass.getMethods())
if (!method.isAnnotationPresent(MyRequestMapping.class))
continue;
MyRequestMapping myRequestMapping = (MyRequestMapping) method.getAnnotation(MyRequestMapping.class);
handlerMapping.put((url + "/" + myRequestMapping.value()).replaceAll("/+", "/"), method);
catch (Exception e)
e.printStackTrace();
private String toLowerFirstCase(String fileName)
char[] chars = fileName.toCharArray();
chars[0] += 32;
return String.valueOf(chars);
全部完成后,执行,访问:http://localhost:8080/user/name?name=lily
以上是关于Spring框架进阶Spring V2.0 IOC与DI的主要内容,如果未能解决你的问题,请参考以下文章