目录
- IOC相关类
- BeanDefinitionReader
- BeanDefinition
- ApplicationContext
- BeanWrapper
- 简化后的DispatcherServlet
本章源码:https://github.com/MagicalPiggy/pig-spring/tree/master/pig-spring-2.0-ioc
IOC相关类
用一个类(300行)手写Spring框架中已经基本实现了一个Spring框架,但是所有功能都放在了PIGDispatcherServlet一个类中,十分臃肿,违背了单一职责原则,因此需要将其中的功能进行分离。
首先考虑分离 IOC部分,先了解一下Spring源码中,负责这部分的几个比较重要的类——
- ApplicationContext
上下文 - BeanDefinition
Bean的定义,保存了Bean的定义方式(从配置文件中获取) - BeanWrapper
Bean的包装,由于spring中的对象可能是原生对象,也可能是代理对象,因此需要一个统一的包装类用来保存真实的实例。 - BeanDefinitionReader
用来读取配置文件的工具类。
那么我们使用这几个类将IOC功能进行分离,新建类如下:
原servlet中需要分离的代码:
BeanDefinitionReader
工具类,负责加载配置文件、解析与扫描相关类
package com.zhu.framework.beans.support;
import com.zhu.framework.beans.config.PigBeanDefinition;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
/**
* 读取配置文件的工具类
*/
public class PigBeanDefinitionReader {
// 配置文件的Properties类
private Properties contextConfig = new Properties();
// 扫描到的类名(需要注册到ioc容器的class)
private List<String> registryBeanClasses = new ArrayList<String>();
public PigBeanDefinitionReader(String[] configLocations) {
// 1.读取配置文件
doLoadConfig(configLocations[0]);
// 2.扫描相关类
doScanner(contextConfig.getProperty("scanPackage"));
}
public List<PigBeanDefinition> loadBeanDefinitions() {
List<PigBeanDefinition> result = new ArrayList<PigBeanDefinition>();
try {
for (String className : registryBeanClasses) {
Class<?> beanClass = Class.forName(className);
// 如果扫描到的是一个接口则不处理
if (beanClass.isInterface()) {continue;}
// 将信息封装为PigBeanDefinition对象,并添加到列表中
result.add(doCreateBeanDefinition(toLowerFirstCase(beanClass.getSimpleName()), beanClass.getName()));
// 如果这个类实现了某个接口,也需要将接口名+类名封装存入
for (Class<?> i : beanClass.getInterfaces()) {
result.add(doCreateBeanDefinition(i.getName(), beanClass.getName()));
}
}
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
private PigBeanDefinition doCreateBeanDefinition(String factoryBeanName, String beanClassName) {
PigBeanDefinition pigBeanDefinition = new PigBeanDefinition();
pigBeanDefinition.setFactoryBeanName(factoryBeanName);
pigBeanDefinition.setBeanClassName(beanClassName);
return pigBeanDefinition;
}
private void doLoadConfig(String contextConfigLocation) {
// 直接从ClassPath下找spring的配置文件
// 相当于把application.properties文件读到了内存中(contextConfig这个对象)
InputStream is = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation.replaceAll("classpath:",""));
try {
contextConfig.load(is);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (null != is) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private void doScanner(String scanPackage) {
// 到ClassPath下找相关的.class文件
// scanPackage包路径对应一个文件夹
// 下面转为文件路径
URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.", "/"));
File classpath = new File(url.getFile());
for (File file : classpath.listFiles()) {
// 如果是文件夹,则递归遍历之
if (file.isDirectory()) {
doScanner(scanPackage + "." + file.getName());
} else {
if (!file.getName().endsWith(".class")) {continue;}
String className = scanPackage + "." + file.getName().replaceAll(".class", "");
// 可以在实例化阶段通过调用Class.forName(className)反射创建实例
registryBeanClasses.add(className);
}
}
}
private String toLowerFirstCase(String simpleName) {
char[] chars = simpleName.toCharArray();
chars[0] += 32;
return String.valueOf(chars);
}
}
BeanDefinition
用于保存bean的定义信息,两个属性factoryBeanName保存短类名,beanClassName保存全类名。
在上面的工具类中,加了相关注解的bean被扫描后,类名信息被保存在BeanDefinition中。所有bean的定义信息,最后保存在beanDefinitionMap中,这是一个伪容器,因为并没有保存bean的实例。
package com.zhu.framework.beans.config;
/**
* 保存bean的配置信息
*/
public class PigBeanDefinition {
// 类名
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;
}
}
ApplicationContext
负责在servlet初始化方法中,对bean进行实例化。
beanDefinitionMap中的所有bean将通过getBean()方法进行实例化与依赖注入。
package com.zhu.framework.context;
import com.zhu.framework.annotation.PIGAutowired;
import com.zhu.framework.annotation.PIGController;
import com.zhu.framework.annotation.PIGService;
import com.zhu.framework.beans.PigBeanWrapper;
import com.zhu.framework.beans.config.PigBeanDefinition;
import com.zhu.framework.beans.support.PigBeanDefinitionReader;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* IOC容器的主入口
*/
public class PigApplicationContext {
private String[] configLocations;
private PigBeanDefinitionReader reader;
private final Map<String, PigBeanDefinition> beanDefinitionMap = new HashMap<String, PigBeanDefinition>();
private Map<String, PigBeanWrapper> factoryBeanInstanceCache = new HashMap<String, PigBeanWrapper>();
// 单例bean的容器
private Map<String, Object> factoryBeanObjectCache = new HashMap<String, Object>();
public PigApplicationContext(String... configLocations) {
this.configLocations = configLocations;
try {
// 1.读取配置文件
reader = new PigBeanDefinitionReader(configLocations);
// 2.解析配置文件,将配置信息转为BeanDefinition对象
List<PigBeanDefinition> beanDefinitions = reader.loadBeanDefinitions();
// 3.把BeanDefinition对应实例注册到beanDefinitionMap key:beanName,value: beanDefinition对象
doRegisterBeanDefinition(beanDefinitions);
// 配置信息初始化阶段完成
// 4.完成依赖注入,时机为调用getBean()时
doAutowrited();
} catch (Exception e) {
e.printStackTrace();
}
}
public int getBeanDefinitionCount() {
return factoryBeanInstanceCache.size();
}
// 只处理非延时加载的情况
private void doAutowrited() {
for (Map.Entry<String, PigBeanDefinition> beanDefinitionEntry : this.beanDefinitionMap.entrySet()) {
String beanName = beanDefinitionEntry.getKey();
// 真正的注入需要在getBean方法中完成
// getBean方法:1.创建实例 2.依赖注入
getBean(beanName);
}
}
private void doRegisterBeanDefinition(List<PigBeanDefinition> beanDefinitions) throws Exception {
for (PigBeanDefinition beanDefinition : beanDefinitions) {
if (this.beanDefinitionMap.containsKey(beanDefinition.getFactoryBeanName())) {
throw new Exception("The " + beanDefinition.getFactoryBeanName() + " is exists!");
}
this.beanDefinitionMap.put(beanDefinition.getFactoryBeanName(), beanDefinition);
this.beanDefinitionMap.put(beanDefinition.getBeanClassName(), beanDefinition);
}
}
public Object getBean(Class beanClass) {
return getBean(beanClass.getName());
}
public Object getBean(String beanName) {
// 1.获取BeanDefinition配置信息
PigBeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);
// 2.反射进行实例化
Object instance = instantiateBean(beanName, beanDefinition);
// 3.将创建出来的实例包装为BeanWrapper对象
PigBeanWrapper beanWrapper = new PigBeanWrapper(instance);
// 4.把BeanWrapper对象存入真正的IoC容器中
this.factoryBeanInstanceCache.put(beanName, beanWrapper);
// 5.执行依赖注入
populateBean(beanName, beanDefinition, beanWrapper);
return this.factoryBeanInstanceCache.get(beanName).getWrapperInstance();
}
// 创建实例化对象
private Object instantiateBean(String beanName, PigBeanDefinition beanDefinition) {
// 全类名
String className = beanDefinition.getBeanClassName();
Object instance = null;
try {
Class<?> clazz = Class.forName(className);
instance = clazz.getDeclaredConstructor().newInstance();
// spring内部的容器不止一个
factoryBeanObjectCache.put(beanName, instance);
} catch (Exception e) {
e.printStackTrace();
}
return instance;
}
// 完成依赖注入
private void populateBean(String beanName, PigBeanDefinition beanDefinition, PigBeanWrapper beanWrapper) {
Object instance = beanWrapper.getWrapperInstance();
Class<?> clazz = beanWrapper.getWrapperClass();
// 只有加了注解的才进行依赖注入
// @Component
if (!(clazz.isAnnotationPresent(PIGController.class) || clazz.isAnnotationPresent(PIGService.class))) {
return;
}
// 拿到实例的所有字段(属性)
// 只能拿到public的属性
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
// 如果没有加Autowired注解则跳过
if (!field.isAnnotationPresent(PIGAutowired.class)) {
continue;
}
PIGAutowired autowired = field.getAnnotation(PIGAutowired.class);
String autowiredBeanName = autowired.value().trim();
// 若注解未指定值,则取其类型作为beanName
if ("".equals(autowiredBeanName)) {
autowiredBeanName = field.getType().getName();
}
// 暴力访问,保证所有访问类型都可注入成功
field.setAccessible(true);
try {
// 以下举例说明
// field相当于@PIGAutowired private IDemoService demoService (为DemoAction类中的一个字段)
// instance相当于DemoAction的实例
// this.factoryBeanInstanceCache.get(autowiredBeanName).getWrapperInstance()相当于从容器中拿到key为IDemoService(全类名)对应的实例,即demoService的实例
if (this.factoryBeanObjectCache.get(autowiredBeanName) == null) {
continue;
}
// 关键步骤
field.set(instance, this.factoryBeanInstanceCache.get(autowiredBeanName).getWrapperInstance());
} catch (IllegalAccessException e) {
e.printStackTrace();
continue;
}
}
}
public String[] getBeanDefinitionNames() {
return this.beanDefinitionMap.keySet().toArray(new String[this.beanDefinitionMap.size()]);
}
}
BeanWrapper
IOC容器实例的包装类,在getBean方法中,反射创建的实例被包装为BeanWrapper对象,然后存入到真正的IoC容器:factoryBeanInstanceCache中。
/**
* IOC容器实例的包装类
*/
public class PigBeanWrapper {
private Object wrapperInstance;
private Class<?> wrapperClass;
public PigBeanWrapper(Object wrapperInstance) {
this.wrapperClass = wrapperInstance.getClass();
this.wrapperInstance = wrapperInstance;
}
public Object getWrapperInstance() {
return wrapperInstance;
}
public Class<?> getWrapperClass() {
return wrapperClass;
}
}
简化后的DispatcherServlet
由于四个步骤中的代码被分离出去,现在的DispatcherServlet简洁了不少,只有MVC功能将在下一章继续拆分。
package com.zhu.framework.servlet;
import com.zhu.framework.annotation.*;
import com.zhu.framework.context.PigApplicationContext;
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.Method;
import java.util.*;
public class PIGDispatcherServlet extends HttpServlet {
// 初始化ioc容器
private Map<String, Object> ioc = new HashMap<String, Object>();
// RequestMapping映射关系
private Map<String, Method> handlerMapping = new HashMap<String, Method>();
private PigApplicationContext applicationContext;
@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 {
// 6. 根据URL完成方法的调度
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 {
// 获取URI:统一资源标识符 如/HttpStudy/demo1
String url = req.getRequestURI();
// 获取虚拟目录 如/HttpStudy
String contextPath = req.getContextPath();
// 将 /HttpStudy/demo1 替换为 /demo1
url = url.replaceAll(contextPath, "").replaceAll("/+", "/");
if (!this.handlerMapping.containsKey(url)) {
resp.getWriter().write("404 Not Found !!");
return;
}
Method method = this.handlerMapping.get(url);
// 获取请求中所有参数的map集合
Map<String,String[]> paramsMap = req.getParameterMap();
// 拿到方法的形参列表
Class<?>[] parameterTypes = method.getParameterTypes();
// 声明实参列表
Object[] paramValues = new Object[parameterTypes.length];
// 根据形参列表的配置 给实参列表赋值
for (int i = 0; i < parameterTypes.length; i++) {
Class paramterType = parameterTypes[i];
if (paramterType == HttpServletRequest.class) {
paramValues[i] = req;
continue;
} else if (paramterType == HttpServletResponse.class) {
paramValues[i] = resp;
continue;
} else if (paramterType == String.class) {
// 二维数组:一个方法有多个注解,一个注解有多个值
Annotation[][] pa = method.getParameterAnnotations();
for (int j = 0; j < pa.length; j++) {
for (Annotation annotation : pa[i]) {
if (annotation instanceof PIGRequestParam) {
// 从注解配置中取出参数名称
String paramName = ((PIGRequestParam) annotation).value();
if (!"".equals(paramName.trim())) {
// 从请求体中取出参数的值
String value = Arrays.toString(paramsMap.get(paramName))
.replaceAll("\\[|\\]","")
.replaceAll("\\s","");
paramValues[i] = value;
}
}
}
}
}
}
// 反射调用方法
// 第一个参数,Method所在的实例
// 第二个参数,Method的实参列表
method.invoke(applicationContext.getBean(method.getDeclaringClass()), paramValues);
}
// 初始化阶段
@Override
public void init(ServletConfig config) throws ServletException {
// ================== IoC =======================
applicationContext = new PigApplicationContext(config.getInitParameter("contextConfigLocation"));
// ================== MVC =======================
// 初始化HandlerMapping
doInitHandlerMapping();
System.out.println("PIG Spring framework is initialized!");
}
private void doInitHandlerMapping() {
if (applicationContext.getBeanDefinitionCount() == 0) {
return;
}
String [] beanNames = applicationContext.getBeanDefinitionNames();
for (String beanName : beanNames) {
Object instance = applicationContext.getBean(beanName);
Class<?> clazz = instance.getClass();
// 只针对加了Controller注解的类
if (!clazz.isAnnotationPresent(PIGController.class)) {
continue;
}
// 处理类上的url
String baseUrl = "";
if (clazz.isAnnotationPresent(PIGRequestMapping.class)) {
PIGRequestMapping requestMapping = clazz.getAnnotation(PIGRequestMapping.class);
baseUrl = requestMapping.value();
}
// 默认只获取public方法
for (Method method : clazz.getMethods()) {
// 只处理加了PIGRequestMapping注解的方法
if (!method.isAnnotationPresent(PIGRequestMapping.class)) {
continue;
}
// 拼接URL
PIGRequestMapping requestMapping = method.getAnnotation(PIGRequestMapping.class);
String url = ("/" + baseUrl + "/" + requestMapping.value()).replaceAll("/+","/");
// 存入map中
handlerMapping.put(url, method);
System.out.println("Mapped " + url +"," + method);
}
}
}
}