这是SpringMVC体系的最后一篇文章了。时至今日,只有一个字,就是‘妙’。
经过最开始的简单手写,到整个SpringMVC的源码分析。本篇的内容就在之前的简单版上升个级,使之更加的契合源码。
预先准备
之前的手写简单源码,获取手写简单版
因为是在简单版代码上做升级,就只贴几个关键点了
升级版构造
- 初始化配置,从配置中获取需要扫描的包,扫描对应的class文件
- 解析路径下的class对象,将beanName和className保存起来
- 存储到注册信息Map,配置阶段结束,接下来就是依赖注入阶段
- 从注册Map中拿到BeanDefinition信息,实例化对象(统一走getBean),AOP切面在此完成代理类和代码织入
- 保存到IOC容器,存在class对象和实例化对象
- 对加了Autowired注解的声明使用反射进行注入
- 对被代理的原始类如果也有声明的,也进行注入
- 对延时加载的对象,在获取对象时统一走getBean()获取
- MVC DispatcherServlet init初始化直接调用IOC、DI和上下文
- 初始化9大组件(mapping放到初始化阶段完成,从IOC拿到实例化对象,包含RequestMapping注解的,取出url和方法,保存对应关系原始类对象、方法对象、reqUrl)
- 调用阶段,通过url获取原始类的对象和方法,使用invoke反射调用,非页面的通过write()返回
- 页面渲染,读取对应的html并替换约定字符传参,返回给网页
1.ApplicationContext容器
- 注册阶段分离,实例化阶段统一用源码中的getBean()。
- 依赖注入阶段,检查AOP切面配置
public class TestApplicationContext {
//保存application.properties配置文件中的内容
public TBeanDefinitionReader reader;
//注册信息保存
Map<String, TBeanDefinition> beanDefinitionMap = new HashMap<String, TBeanDefinition>();
//传说中的IOC容器 beanName -> TBeanWarpper 设计
public Map<String, TBeanWarpper> iocCacheMap = new HashMap<String, TBeanWarpper>();
//beanName -> 实例化的类对象
public Map<String, Object> iocObjectCacheMap = new HashMap<String, Object>();
//被代理对象的原始对象 最后需要反射注入
public List<TBeanWarpper> targetCacheList = new ArrayList<TBeanWarpper>();
public TestApplicationContext(String... classpaths) {
init(classpaths);
}
public void init(String... classpaths) {
try {
//1.读取配置文件 加载配置 && 扫描对应的类
this.reader = new TBeanDefinitionReader(classpaths);
//2.解析类路径下的class对象(不做实例化) 把beanName和className保存起来
List<TBeanDefinition> beanDefinitions = this.reader.loadBeanDefinitions();
//3.存储到注册信息map ioc到这里就完成了 接下来就是依赖注入阶段了
doRegistBeanDefinition(beanDefinitions);
//4.依赖注入
doAutowired();
System.out.println("application is init");
} catch (Exception e) {
e.printStackTrace();
}
}
private void doAutowired() {
//配置阶段完成 真正开始实例化对象
for (Map.Entry<String, TBeanDefinition> entry : this.beanDefinitionMap.entrySet()) {
String beanName = entry.getKey();
getBean(beanName);
}
//被代理的原始类 如果有声明 也需要注入
for (TBeanWarpper warpper : this.targetCacheList) {
populateBean(null, null, warpper);
}
}
//开始Bean的实例化 和 DI依赖注入
public Object getBean(String beanName) {
//1.从缓存中拿到TBeanDefinition信息
TBeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);
//2.实例化对象
Object instance = instanceBean(beanDefinition);
//3.封装成TBeanWarpper对象 存储class对象 和 实例化对象
TBeanWarpper beanWarpper = new TBeanWarpper(instance);
//4.保存到IOC容器
iocCacheMap.put(beanName, beanWarpper);
//5.执行依赖注入
populateBean(beanName, beanDefinition, beanWarpper);
return instance;
}
//实例化 && AOP在此完成
private Object instanceBean(TBeanDefinition beanDefinition) {
String beanName = beanDefinition.getFactoryBeanName();
if (beanDefinition.getInterfaceImplBeanName() != null) { //接口取实现类的beanName 保持单列
beanName = beanDefinition.getInterfaceImplBeanName();
}
String beanClassName = beanDefinition.getBeanClassName();
Object instance = null;
try {
//先从实例化对象容器中取
if (this.iocObjectCacheMap.containsKey(beanName)) {
instance = this.iocObjectCacheMap.get(beanName);
} else {
Class<?> clazz = Class.forName(beanClassName);
//如果满足AOP切面条件 此处应该生成代理对象,那么最后调用处invoke应该自己实现,里面需要保存原有的实例对象
instance = checkAopCase(clazz);
//beanName和beanClassName都存一份 保持单例
this.iocObjectCacheMap.put(beanName, instance);
this.iocObjectCacheMap.put(beanClassName, instance);
}
} catch (Exception e) {
e.printStackTrace();
}
return instance;
}
private Object checkAopCase(Class<?> clazz) throws Exception {
Object instance = clazz.newInstance();
//获取切面切点 正则匹配全类名 匹配上就生成代理对象
Properties config = this.reader.getConfig();
String pointCut = config.getProperty("pointCut");
String regxMethodStr = pointCut.replaceAll("\\.", "\\\\.")
.replaceAll("\\\\.\\*", ".*")
.replaceAll("\\(", "\\\\(")
.replaceAll("\\)", "\\\\)");
//类名是 class com.xxx.demo.service.impl.HelloServiceImp 所以匹配类名做如下处理
String regxClassStr = regxMethodStr.substring(0, regxMethodStr.lastIndexOf("\\("));
Pattern classPattern = Pattern.compile("class " + regxClassStr.substring(regxClassStr.lastIndexOf(" ") + 1));
if (classPattern.matcher(instance.getClass().toString()).matches()) {
TAopConfig aopConfig = new TAopConfig();
aopConfig.setPointCut(pointCut);
aopConfig.setAspectClass(config.getProperty("aspectClass"));
aopConfig.setAspectBefore(config.getProperty("aspectBefore"));
aopConfig.setAspectAfter(config.getProperty("aspectAfter"));
aopConfig.setAspectAfterThrowing(config.getProperty("aspectAfterThrowing"));
TAdvisedSupport advisedSupport = new TAdvisedSupport(aopConfig);
//匹配方法的正则
Pattern methodPattern = Pattern.compile(regxMethodStr);
advisedSupport.setMethodPattern(methodPattern);
advisedSupport.setTarget(instance);
advisedSupport.setTargetClass(clazz);
//被代理的保留原始实例化对象 如果有声明其他类 需要注入
Object target = instance;
this.targetCacheList.add(new TBeanWarpper(target));
//匹配上的生成代理对象
instance = new TJDKDynamicAopProxy(advisedSupport).getProxy();
}
return instance;
}
private void populateBean(String beanName, TBeanDefinition beanDefinition, TBeanWarpper beanWarpper) {
Class<?> clazz = beanWarpper.getWarpperClass();
Object instance = beanWarpper.getWarpperInstance();
//1.前面加了注解的的类已经实例化完成 这里判断需要注入的声明即可
//获取类里面所有的声明对象 如果有TAutowired注解 反射赋值
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (!field.isAnnotationPresent(TAutowired.class)) continue;
//TAutowired这里加了个value 可以自定义beanName 等同于Qualifier("xxx")
TAutowired autowired = field.getAnnotation(TAutowired.class);
String autowireNname = field.getType().getName();
if (!"".equals(autowired.value())) {
autowireNname = autowired.value();
}
//延时加载 或者循环依赖 导致IOC容器还没有实例化的 在下次调用才会初始化
if (!this.iocCacheMap.containsKey(autowireNname)) continue;
field.setAccessible(true);
try {
field.set(instance, this.iocCacheMap.get(autowireNname).getWarpperInstance());
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
private void doRegistBeanDefinition(List<TBeanDefinition> beanDefinitions) throws Exception {
for (TBeanDefinition beanDefinition : beanDefinitions) {
if (this.beanDefinitionMap.containsKey(beanDefinition.getFactoryBeanName())) {
throw new Exception("Create bean Exception ! The bean " + beanDefinition.getFactoryBeanName() + " is exists!!!");
}
this.beanDefinitionMap.put(beanDefinition.getFactoryBeanName(), beanDefinition);
//类名全路径也保存一份
this.beanDefinitionMap.put(beanDefinition.getBeanClassName(), beanDefinition);
}
}
public Object getBean(Class<?> clazz) {
return this.getBean(clazz.getName());
}
public Map<String, TBeanDefinition> getBeanDefinitionMap() {
return this.beanDefinitionMap;
}
public Properties getConfig() {
return this.reader.getConfig();
}
2.初始化类注册信息 TBeanDefinitionReader
//保存扫描的所有的类名
private List<String> beanDefinitionNames = new ArrayList<String>();
private Properties contextConfig = new Properties();
public TBeanDefinitionReader(String[] classpaths) {
//初始化配置
doLoadConfig(classpaths[0]);
//扫描对应的类
doScanner(contextConfig.getProperty("packscanner"));
}
private void doScanner(String packscanner) {
URL url = this.getClass().getResource("/" + packscanner.replaceAll("\\.", "/"));
File file = new File(url.getPath());
for (File f : file.listFiles()) {
if (f.isDirectory()) {
//com.xxx.service -> com.xxx.service.action
doScanner(packscanner + "." + f.getName());
} else {
if (!f.getName().endsWith(".class")) continue;
this.beanDefinitionNames.add(packscanner + "." + f.getName().replaceAll("\\.class", ""));
}
}
}
private void doLoadConfig(String packetName) {
InputStream in = this.getClass().getClassLoader()
.getResourceAsStream(packetName.replace("classPath:", ""));
try {
contextConfig.load(in);
} catch (IOException e) {
e.printStackTrace();
}
}
public List<TBeanDefinition> loadBeanDefinitions() {
List<TBeanDefinition> beanDefinitions = new ArrayList<TBeanDefinition>();
for (String className : this.beanDefinitionNames) {
try {
//这里只解析成class 不做实例化
//存储 beanName 和className
Class<?> clazz = Class.forName(className);
//需要注册的类 应该是加了注解的类 接口要排除掉
//只有加了注解的类才需要实例化 TController TService
if (!(clazz.isAnnotationPresent(TController.class)
|| clazz.isAnnotationPresent(TService.class)) || clazz.isInterface())
continue;
String beanName = toFristLowerCase(clazz.getSimpleName());
if (clazz.isAnnotationPresent(TService.class)) {
TService service = clazz.getAnnotation(TService.class);
if (!"".equals(service.value())) {
beanName = service.value();
}
}
beanDefinitions.add(new TBeanDefinition(beanName, clazz.getName()));
//如果有实现接口的 需要加载接口 接口全名当key 同时存入实现类beanName 注入需要用到
Class<?>[] interfaces = clazz.getInterfaces();
for (Class<?> i : interfaces) {
beanDefinitions.add(new TBeanDefinition(i.getName(), clazz.getName(), beanName));
}
} catch (Exception e) {
e.printStackTrace();
}
}
return beanDefinitions;
}
//排除类名小写开头
private String toFristLowerCase(String simpleName) {
char[] chars = simpleName.toCharArray();
chars[0] += 32;
return new String(chars);
}
public Properties getConfig() {
return contextConfig;
}
3.注册信息的封装 TBeanDefinition
String factoryBeanName; //beanName
String beanClassName; //类名全路径
String interfaceImplBeanName; //如果是接口 对应实现类的beanNma
public TBeanDefinition(String beanName, String beanClassName) {
this.factoryBeanName = beanName;
this.beanClassName = beanClassName;
}
public TBeanDefinition(String factoryBeanName, String beanClassName, String interfaceImplBeanName) {
this.factoryBeanName = factoryBeanName;
this.beanClassName = beanClassName;
this.interfaceImplBeanName = interfaceImplBeanName;
}
4.AOP切面,JDK代理类
- 只贴了部分,修改了切面的参数传递。让日志打印更加的还原
/**
* 代理会走到这个方法
* 1.获取aspect AopConfig配置,切面信息和原始实例类
* 2.保存method -> list<TAopConfig>配置 到map 可能多个切面
* 3.依次调用 invoke
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Exception {
Map<String, List<TAdvice>> advicesMap = new HashMap<String, List<TAdvice>>();
Object result = null;
JoinPoint joinPoint = new JoinPoint();
joinPoint.setArgs(args);
joinPoint.setTarget(config.getTarget());
joinPoint.setMethod(method);
try {
advicesMap = config.getMethodAdvices(method);
invokeAspect(advicesMap.get(config.getAopConfig().getAspectBefore()), joinPoint);
result = method.invoke(config.getTarget(), args);
joinPoint.setResult(result);
invokeAspect(advicesMap.get(config.getAopConfig().getAspectAfter()), joinPoint);
} catch (Exception e) {
joinPoint.setThrowName(e.getCause().getMessage());
invokeAspect(advicesMap.get(config.getAopConfig().getAspectAfterThrowing()), joinPoint);
throw e;
}
return result;
}
private void invokeAspect(List<TAdvice> advices, JoinPoint joinPoint) {
try {
if (advices == null || advices.size() == 0) return;
for (TAdvice advice : advices) {
joinPoint.setChainTarget(advice.getAspectClass());
joinPoint.setChainMethod(advice.getAspectMethod());
joinPoint.proceed();
}
} catch (Exception e) {
e.printStackTrace();
}
}
5.MVC初始类TDispatcherServlet
- 各司其职,保证单一原则。
- 参数处理统一交给THandlerAdapter、页面渲染交给TView
public class TDispatcherServlet extends HttpServlet {
private TestApplicationContext context;
//spring 源码复制出来的 为什么不用map 可能不止用url请求 源码用的是遍历
//通过urlPath 拿到 THandlerMapping
private List<THandlerMapping> handlerMappings = new ArrayList<THandlerMapping>();
// Map<String,THandlerMapping> hadlerMappingMap = new HashMap<String, THandlerMapping>();
private List<THandlerAdapter> handlerAdapters = new ArrayList<THandlerAdapter>();
//spring采用的是list 这里简化吧
private TViewResolver viewResolver;
// private List<TViewResolver> viewResolvers = new ArrayList<TViewResolver>();
@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 {
//调用
doDispatcher(req, resp);
} catch (Exception e) {
//404的 直接返回了 这里的异常全是服务器500异常
Map<String, Object> modelMap = new HashMap<String, Object>();
modelMap.put("msg", e.getMessage());
modelMap.put("error", Arrays.toString(e.getStackTrace()));
try {
processDispatchResult(req, resp, new TModelAndView("500", modelMap));
} catch (Exception e1) {
e1.printStackTrace();
resp.getWriter().write("500 Exception,Detail : " + Arrays.toString(e.getStackTrace()));
}
}
}
private void doDispatcher(HttpServletRequest req, HttpServletResponse resp) throws Exception {
//先匹配url拿到mapping
THandlerMapping handler = getHandler(req);
if (handler == null) {
processDispatchResult(req, resp, new TModelAndView("404"));
return;
}
//从这里开始 异常都是 服务器异常了
//根据mapping到HandlerAdapter 取回参数处理器
THandlerAdapter ha = getHandlerAdapter(handler);
//处理参数 反射调用
TModelAndView mv = ha.handle(req, resp, handler);
//返回值处理 是否返回页面
processDispatchResult(req, resp, mv);
}
private void processDispatchResult(HttpServletRequest req, HttpServletResponse resp, TModelAndView mv) throws Exception {
if(mv != null){
TView view = viewResolver.resolveViewName(mv.getHtmlName());
//渲染
view.render(req,resp,mv.getModel());
}
}
private THandlerAdapter getHandlerAdapter(THandlerMapping handler) {
for (THandlerAdapter adapter : this.handlerAdapters) {
if (adapter.handlerMapping.equals(handler)) {
return adapter;
}
}
return null;
}
private THandlerMapping getHandler(HttpServletRequest req) {
String reqUrl = req.getRequestURI().replaceAll(req.getContextPath(), "");
for (THandlerMapping handlerMapping : this.handlerMappings) {
if (handlerMapping.getUrl().equals(reqUrl)) { //如果是正则 这里直接正则匹配
return handlerMapping;
}
}
return null;
}
@Override
public void init(ServletConfig config) throws ServletException {
//IOC DI阶段 直接调用上下文
this.context = new TestApplicationContext(
config.getInitParameter("contextConfigLocation"));
//2.参考springMvc DispatcherServlet 初始化9大组件
initStrategies(context);
System.out.println("spring mvc is init");
}
//源码里复制
private void initStrategies(TestApplicationContext context) {
// //多文件上传的组件
// this.initMultipartResolver(context);
// //初始化本地语言环境
// this.initLocaleResolver(context);
// //初始化模板处理器
// this.initThemeResolver(context);
//url请求
this.initHandlerMappings(context);
//初始化参数适配器
this.initHandlerAdapters(context);
// //初始化异常拦截器
// this.initHandlerExceptionResolvers(context);
// //初始化视图预处理器
// this.initRequestToViewNameTranslator(context);
//初始化视图转换器
this.initViewResolvers(context);
// //FlashMap管理器
// this.initFlashMapManager(context);
}
private void initViewResolvers(TestApplicationContext context) {
//初始化模板的位置
URL url = this.getClass().getClassLoader().getResource(context.getConfig().getProperty("htmlRootTemplate"));
this.viewResolver = new TViewResolver(url.getPath());
}
private void initHandlerAdapters(TestApplicationContext context) {
//初始化参数的位置
for (THandlerMapping handlerMapping : this.handlerMappings) {
//通过构造方法初始化 初始化时处理好参数
this.handlerAdapters.add(new THandlerAdapter(handlerMapping));
}
}
private void initHandlerMappings(TestApplicationContext context) {
//从IOC容器中拿到实例化对象 遍历方法找到TRequestMapping 注解 取到URL
Map<String, TBeanDefinition> beanDefinitionMap = context.getBeanDefinitionMap();
for (String beanName : beanDefinitionMap.keySet()) {
Object instance = context.getBean(beanName);
Class<?> clazz = instance.getClass();
//只有加了TController注解才扫描
if (!clazz.isAnnotationPresent(TController.class)) continue;
//类路径
String baseUrl = "";
if (clazz.isAnnotationPresent(TRequestMapping.class)) {
TRequestMapping baseMapping = clazz.getAnnotation(TRequestMapping.class);
if (!"".equals(baseMapping.value())) {
baseUrl = baseMapping.value();
}
//获取所有的方法 扫描注解
for (Method method : clazz.getMethods()) {
if (!method.isAnnotationPresent(TRequestMapping.class)) continue;
TRequestMapping mapping = method.getAnnotation(TRequestMapping.class);
if ("".equals(mapping)) {
throw new RuntimeException("the method " + method.toString() + " TRequestMapping.value is null!");
}
//如果请求路径想要用到正则 将requrl转成 正则匹配即可
String reqUrl = (baseUrl + "/" + mapping.value()).replaceAll("/+", "/");
//保存对应关系 原始类对象 方法对象 reqUrl 后面invoke调用
handlerMappings.add(new THandlerMapping(instance, method, reqUrl));
System.out.println("添加访问路径:" + reqUrl);
}
}
}
}
}
6.Spring测试
- 运行里面的测试类
public static void main(String[] args) {
TestApplicationContext context = new TestApplicationContext("classPath:application.properties");
HelloAction action = (HelloAction) context.getBean(HelloAction.class);
String hello = action.hello("张三");
System.out.println(hello);
}
- 输出结果
方法之前调用:JoinPoint(target=com.xxx.demo.service.impl.HelloServiceImpl@5f150435, method=public abstract java.lang.String com.xxx.demo.service.HelloService.sayHello(java.lang.String), args=[张三], result=null, throwName=null, chainTarget=com.xxx.demo.aspect.LogAspect@50cbc42f, chainMethod=public void com.xxx.demo.aspect.LogAspect.before(com.xxx.framework.Model.JoinPoint))
方法之后调用:JoinPoint(target=com.xxx.demo.service.impl.HelloServiceImpl@5f150435, method=public abstract java.lang.String com.xxx.demo.service.HelloService.sayHello(java.lang.String), args=[张三], result=my name is 张三, throwName=null, chainTarget=com.xxx.demo.aspect.LogAspect@50cbc42f, chainMethod=public void com.xxx.demo.aspect.LogAspect.after(com.xxx.framework.Model.JoinPoint))
my name is 张三
7.MVC返回页面测试
- 打包,配置tomcat。网页调用
http://localhost:8080/mvc/index.html?name=helloWorld
-
Title welcome to helloWorld
当前时间: 2022-01-11 16:45:36
token: bfd333b4-295e-412c-ab38-d8379bfb66d8
8.MVC返回字符串测试
- 网页调用
http://localhost:8080/mvc/hello1?name=helloWorld
- 日志输出
-
方法之前调用:JoinPoint(target=com.xxx.demo.service.impl.HelloServiceImpl@2907b742, method=public abstract java.lang.String com.xxx.demo.service.HelloService.sayHello(java.lang.String), args=[helloWorld], result=null, throwName=null, chainTarget=com.xxx.demo.aspect.LogAspect@51454af0, chainMethod=public void com.xxx.demo.aspect.LogAspect.before(com.xxx.framework.Model.JoinPoint))
方法之后调用:JoinPoint(target=com.xxx.demo.service.impl.HelloServiceImpl@2907b742, method=public abstract java.lang.String com.xxx.demo.service.HelloService.sayHello(java.lang.String), args=[helloWorld], result=my name is helloWorld, throwName=null, chainTarget=com.xxx.demo.aspect.LogAspect@51454af0, chainMethod=public void com.xxx.demo.aspect.LogAspect.after(com.xxx.framework.Model.JoinPoint)) - 结果输出
-
my name is helloWorld
本文仅供参考,不支持转载。源码地址点这里获取
以上就是本章的全部内容了。
上一篇:Spring源码分析第五弹 - 神级的spring还有其他什么功效?
下一篇:Springboot源码分析第一弹 - 自动装配实现
问渠那得清如许,为有源头活水来