建议从下文开始阅读。
Spring源码 v1.0 —— 简陋版
从servlet 到 ApplicationContext
在上一个简陋版本中,了解到SpringMVC的入口是在DispatcherServlet。我们在init()方法中完成了IOC容器的初始化。
但是在我们日常使用Spring的过程中,见的最多的却是ApplicationContext,似乎Spring托管的所有实例Bean都可以通过调用getBean方法来获得。那么ApplicationContext又从何而来?从Spring源码中我们可以逐步分析,DispatacherServlet的类图如下:
DispatcherServlet 继承了FrameworkServlet,FrameworkServlet继承了HttpServletBean,HttpServletBean继承了HttpServlet。在HttpServletBean的init()方法中调用了FrameworkServlet的initServletBean方法,在initServletBean()方法中初始化WebAppicationContext实例。在initServletBean()方法中调用了DispatcherServlet重写的onRefresh()方法。在DispatcherServlet的onRefresh()方法中调用了initStrategies()方法,初始化SpringMVC的九大组件。
其实,上面复杂的调用关系,我们可以简单的得出一个结论:就是在Servlet的init()方法中初始化了IOC容器和SpringMVC所依赖的九大组件。
三个重要的类
环境搭建
在初始环境上进行改进,将1.0版本代码复制到新工程即可。
该工程将1.0版本进行优化,通过applicationContext来进行IOC容器的初始化及DI依赖注入。
最顶层工厂接口
首先从最顶层开始实现,创建相关接口
public interface DemoBeanFactory {
Object getBean(Class beanClass);
Object getBean(String beanName);
}
beans(配置封装)模块
public class DemoBeanDefinition {
/**
* 是否懒加载
* @return
*/
public boolean isLazyInit() {
return false;
}
private String factoryBeanName;//beanName
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 class DemoBeanWrapper {
// 存储 实例
private Object wrapperInstance;
// 存储 类
private Class<?> wrappedClass;
public DemoBeanWrapper(Object instance) {
this.wrapperInstance = instance ;
this.wrappedClass = instance.getClass();
}
public Object getWrappedInstance(){
return this.wrapperInstance;
}
public Class<?> getWrappedClass(){
return this.wrappedClass;
}
}
context(IOC容器)模块
该类为核心类,从构造方法开始查看代码。
是spring继BeanFactory之外的另一个核心接口或容器,允许容器通过应用程序上下文环境创建、获取、管理bean。
public class DemoApplicationContext implements DemoBeanFactory {
private DemoDefaultListableBeanFactory registry = new DemoDefaultListableBeanFactory();
//读取配置文件
private DemoBeanDefinitionReader reader;
// 三级缓存 (终结缓存) 通用的IOC容器
private Map<String, DemoBeanWrapper> factoryBeanInstanceCache = new HashMap<>();
//单例的IOC容器缓存
private Map<String, Object> factoryObjectInstanceCache = new HashMap<>();
//在servlet中实例化,传入配置信息
public DemoApplicationContext(String... configLocations) throws Exception {
//1.加载配置文件
reader = new DemoBeanDefinitionReader(configLocations);
//2.解析配置文件,将所有的配置信息封装为BeanDefinition对象
List<DemoBeanDefinition> beanDefinitions = reader.loadBeanDefinitions();
//3.缓存所有配置信息
this.registry.doRegisBeanDefinition(beanDefinitions);
//4.加载非延时加载的所有bean
doLoadInstance();
}
private void doLoadInstance() {
//循环调用getBean方法
for (Map.Entry<String, DemoBeanDefinition> entry : this.registry.beanDefinitionMap.entrySet()) {
String beanName = entry.getKey();
//只处理非延时加载的情况
if (!entry.getValue().isLazyInit()) {
getBean(beanName);
}
}
}
@Override
public Object getBean(Class beanClass) {
return getBean(beanClass.getName());
}
//从IOC容器中获得一个Bean对象 核心
@Override
public Object getBean(String beanName) {
//1.先拿到BeanDefinition配置信息
DemoBeanDefinition beanDefinition = registry.beanDefinitionMap.get(beanName);
//2.反射实例化对象
Object instance = instantiateBean(beanName, beanDefinition);
//3.将返回的bean对象 封装成 BeanWrapper
DemoBeanWrapper beanWrapper = new DemoBeanWrapper(instance);
//4.执行依赖注入
populateBean(beanName, beanDefinition, beanWrapper);
//5.保存到IOC容器中
this.factoryBeanInstanceCache.put(beanName, beanWrapper);
return beanWrapper.getWrappedInstance();
}
// 依赖注入
private void populateBean(String beanName, DemoBeanDefinition beanDefinition, DemoBeanWrapper beanWrapper) {
Object instance = beanWrapper.getWrappedInstance();
Class<?> clazz = beanWrapper.getWrappedClass();
if (!(clazz.isAnnotationPresent(DemoController.class) || clazz.isAnnotationPresent(DemoService.class))) {
return;
}
//忽略字段的修饰符
for (Field field : clazz.getDeclaredFields()) {
if (!field.isAnnotationPresent(DemoAutowired.class)) {
continue;
}
DemoAutowired autowired = field.getAnnotation(DemoAutowired.class);
String autowiredBeanName = autowired.value().trim();
if ("".equals(autowiredBeanName)) {
autowiredBeanName = field.getType().getName();
}
//强制访问
field.setAccessible(true);
try {
if (this.factoryBeanInstanceCache.get(autowiredBeanName) == null) {
continue;
}
// 相当于 demoAction.demoService = ioc.get("com.demo.service.IDemoService");
field.set(instance, this.factoryBeanInstanceCache.get(autowiredBeanName).getWrappedInstance());
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
//反射实例化对象
private Object instantiateBean(String beanName, DemoBeanDefinition beanDefinition) {
String className = beanDefinition.getBeanClassName();
Object instance = null;
try {
Class<?> clazz = Class.forName(className);
instance = clazz.newInstance();
//如果是代理对象,则触发AOP的逻辑
this.factoryObjectInstanceCache.put(beanName, instance);
} catch (Exception e) {
e.printStackTrace();
}
return instance;
}
public int getBeanDefinitionCount() {
return this.registry.beanDefinitionMap.size();
}
public String[] getBeanDefinitionNames() {
return this.registry.beanDefinitionMap.keySet().toArray(new String[0]);
}
}
该类为BeanFactory下的另一实现类,用来实现Bean的list集合操作,在本工程中用来缓存配置信息。
public class DemoDefaultListableBeanFactory implements DemoBeanFactory {
public Map<String, DemoBeanDefinition> beanDefinitionMap = new HashMap<>();
@Override
public Object getBean(Class beanClass) {
return null;
}
@Override
public Object getBean(String beanName) {
return null;
}
//缓存所有配置信息
public void doRegisBeanDefinition(List<DemoBeanDefinition> beanDefinitions) throws Exception {
for (DemoBeanDefinition beanDefinition : beanDefinitions) {
if (this.beanDefinitionMap.containsKey(beanDefinition.getFactoryBeanName())) {
throw new Exception("The" + beanDefinition.getFactoryBeanName() + "is Exists!!!");
}
this.beanDefinitionMap.put(beanDefinition.getFactoryBeanName(), beanDefinition);
}
}
}
读取配置文件
该类的作用是读取 Spring 配置文件中的内容,将其转换为 IoC 容器内部的数据结构:BeanDefinition。
public class DemoBeanDefinitionReader {
//保存用户篇配置好的配置文件
private Properties contextConfig = new Properties();
//缓存从包路径下扫描的全类名,需要被注册的BeanClass
private List<String> registryBeanClasses = new ArrayList<String>();
public DemoBeanDefinitionReader(String... locations) {
//1.加载配置文件
doLoadConfig(locations[0]);
//2.扫描相关的类
doScanner(contextConfig.getProperty("scanPackage"));
}
//对配置信息进行封装
public List<DemoBeanDefinition> loadBeanDefinitions() {
List<DemoBeanDefinition> result = new ArrayList<>();
try {
for (String className : registryBeanClasses) {
Class<?> beanClass = Class.forName(className);
//beanClass本身是接口的话不做处理
if (beanClass.isInterface()) {
continue;
}
//1.默认类名首字母小写的情况
result.add(doCreateBeanDefinition(toLowerFirstCase(beanClass.getSimpleName()), beanClass.getName()));
//2.如果是接口,就用实现类
for (Class<?> i : beanClass.getInterfaces()) {
result.add(doCreateBeanDefinition(i.getName(), beanClass.getName()));
}
}
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
private DemoBeanDefinition doCreateBeanDefinition(String factoryBeanName, String factoryClassName) {
DemoBeanDefinition beanDefinition = new DemoBeanDefinition();
beanDefinition.setBeanClassName(factoryClassName);
beanDefinition.setFactoryBeanName(factoryBeanName);
return beanDefinition;
}
//根据contextConfigLocation的路径去classpath下找到的对应的文件
private void doLoadConfig(String contextConfigLocation) {
InputStream is = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
try {
contextConfig.load(is);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (null != is) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
// 扫描 classpath下符合包路劲规则所有的class文件
private void doScanner(String 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 {
// 不是class文件则跳过
if (!file.getName().endsWith(".class")) {
continue;
}
// 包名.类型 比如: com.demo.DemoAction
String className = (scanPackage + "." + file.getName().replace(".class", ""));
// 实例化 需要用到class.forName(className);
registryBeanClasses.add(className);
}
}
}
private String toLowerFirstCase(String simpleName) {
char[] chars = simpleName.toCharArray();
chars[0] += 32; //利用ascll码,大小写字母之间相差32
return String.valueOf(chars);
}
}
servlet修改
和V1版本相似,只不过在init()方法中调用applicationContext上下文类,将读取配置文件,ioc容器,依赖注入等功能交给上下文处理。
public class DemoDispatchServlet extends HttpServlet {
//保存controller 中 URL和Method的对应关系
private Map<String, Method> handlerMapping = new HashMap<String, Method>();
// IOC 容器的访问上下文
private DemoApplicationContext applicationContext = 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 {
//6.根据url委派给具体的调用方法
try {
doDispatcher(req, resp);
} catch (Exception e) {
e.printStackTrace();
resp.getWriter().write("500 Exception,Detail: " + Arrays.toString(e.getStackTrace()));
}
}
private void doDispatcher(HttpServletRequest req, HttpServletResponse resp) throws Exception {
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 = this.handlerMapping.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 annotation : pa[i]) {
if (annotation instanceof DemoRequestParam) {
String paramName = ((DemoRequestParam) annotation).value();
if (!"".equals(paramName.trim())) {
paramIndexMapping.put(paramName, i);
}
}
}
}
Class<?>[] paramTypes = method.getParameterTypes();
for (int i = 0; i < paramTypes.length; i++) {
Class<?> type = paramTypes[i];
if (type == HttpServletRequest.class || type == HttpServletResponse.class) {
paramIndexMapping.put(type.getName(), i);
}
}
// 2.根据参数位置匹配参数名字,从url中取到参数名字对应的值
Object[] paramValues = new Object[paramTypes.length];
//http://localhost/demo/query?name=Tom&name=Tomcat&name=Mic
Map<String, String[]> params = req.getParameterMap();
for (Map.Entry<String, String[]> param : params.entrySet()) {
String value = Arrays.toString(param.getValue())
.replaceAll("\\[|\\]", "")
.replaceAll("\\s", "");
if (!paramIndexMapping.containsKey(param.getKey())) {
continue;
}
int index = paramIndexMapping.get(param.getKey());
//设计到类型强制转换
paramValues[index] = value;
}
if (paramIndexMapping.containsKey(HttpServletRequest.class.getName())) {
int index = paramIndexMapping.get(HttpServletRequest.class.getName());
paramValues[index] = req;
}
if (paramIndexMapping.containsKey(HttpServletResponse.class.getName())) {
int index = paramIndexMapping.get(HttpServletResponse.class.getName());
paramValues[index] = resp;
}
String beanName = toLowerFirstCase(method.getDeclaringClass().getSimpleName());
// 3.组成动态参数列表 ,传给反射调用。
method.invoke(applicationContext.getBean(beanName), paramValues);
}
@Override
public void init(ServletConfig config) throws ServletException {
try {
applicationContext = new DemoApplicationContext(config.getInitParameter("contextConfigLocation"));
} catch (Exception e) {
e.printStackTrace();
}
//============MVC功能===============
//5.初始化HandleMapping
doInitHandleMapping();
System.out.println("Spring framework is init.");
}
private void doInitHandleMapping() {
if (applicationContext.getBeanDefinitionCount() == 0) {
return;
}
for (String beanName : this.applicationContext.getBeanDefinitionNames()) {
Object instance = applicationContext.getBean(beanName);
Class<?> clazz = instance.getClass();
if (!clazz.isAnnotationPresent(DemoController.class)) {
continue;
}
//处理controller类添加地址注解
String baseUrl = "";
if (clazz.isAnnotationPresent(DemoRequestMapping.class)) {
DemoRequestMapping requestMapping = clazz.getAnnotation(DemoRequestMapping.class);
baseUrl = requestMapping.value();
}
//只迭代public方法
for (Method method : clazz.getMethods()) {
if (!method.isAnnotationPresent(DemoRequestMapping.class)) {
continue;
}
DemoRequestMapping requestMapping = method.getAnnotation(DemoRequestMapping.class);
// /demo/query 使用正则表达处理 多个斜杠问题
String url = ("/" + baseUrl + "/" + requestMapping.value()).replaceAll("/+", "/");
handlerMapping.put(url, method);
System.out.println("Mapped : " + url + "--->" + method);
}
}
}
private String toLowerFirstCase(String simpleName) {
char[] chars = simpleName.toCharArray();
chars[0] += 32; //利用ascll码,大小写字母之间相差32
return String.valueOf(chars);
}
}
测试
按照V1.0版本测试即可。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/16856.html