热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

深入浅出SSM框架流程以及原理

前言:学ssm框架已经有很长时间,今天来复习一下图示:(1)Spring(对象工厂):平时开发接触最多的估计就是这个IOC容器,它可以装载bean(也就是Java中的类,当然也包括

前言:学ssm框架已经有很长时间,今天来复习一下
SSM图示流程:
在这里插入图片描述
Spring核心:Java反射
Mybatis:动态代理,而动态代理又是基于反射的,所以,ssm框架核心原理在反射。
(1)Spring(对象工厂): 平时开发接触最多的估计就是这个IOC容器,它可以装载bean(也就是Java中的类,当然也包括service、dao里面的),有了这个机制,就不用在每次使用这个类的时候为它初始化,很少看到关键字new。
(2)SpringMVC(视图控制器): 核心为一个DispatcherServlet,控制所有请求
这里奉上手写SpringMVC的核心DispatcherServlet源码

package servlet;import java.io.File;import java.io.IOException;import java.io.InputStream;import java.lang.reflect.Method;import java.net.URL;import java.util.ArrayList;import java.util.Arrays;import java.util.HashMap;import java.util.List;import java.util.Map;import java.util.Map.Entry;import java.util.Properties;import javax.servlet.ServletConfig;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import annotation.HController;import annotation.HRequestMapping;/**
 * 手写SpringMVC
 * 
 * @author hxz
 * @description TODO
 * @data 2020年1月2日 上午9:39:57
 */publicclassMyDispatcherServletextendsHttpServlet{//加载属性文件private Properties properties=newProperties();//装载beannameprivate List<String> classNames=newArrayList<String>();//ioc容器private Map<String, Object> ioc=newHashMap<String, Object>();//类似于以前自定义的cache缓存容器,这里也是起到一个容器的作用//用于加载各个mappingprivate Map<String, Method> handlerMapping=newHashMap<String, Method>();//容器加载所有的controllerprivate Map<String, Object> controllerMap=newHashMap<String, Object>();@Overridepublicvoidinit(ServletConfig config)throws ServletException{// 1.加载配置文件doLoadConfig(config.getInitParameter("contextConfigLocation"));// 2.初始化所有相关联的类,扫描用户设定的包下面所有的类doScanner(properties.getProperty("scanPackage"));// 3.拿到扫描到的类,通过反射机制,实例化,并且放到ioc容器中beanName默认是首字母小写doInstance();// 4.初始化HandlerMapping(将url和method对应上)initHandlerMapping();}@OverrideprotectedvoiddoGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException{this.doPost(req, resp);}@OverrideprotectedvoiddoPost(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException{try{// 处理请求doDispatch(req, resp);}catch(Exception e){
			resp.getWriter().write("500!! Server Exception");}}privatevoiddoDispatch(HttpServletRequest req, HttpServletResponse resp)throws Exception{if(handlerMapping.isEmpty()){return;}

		String url= req.getRequestURI();
		String contextPath= req.getContextPath();

		url= url.replace(contextPath,"").replaceAll("/+","/");if(!this.handlerMapping.containsKey(url)){
			resp.getWriter().write("404 NOT FOUND!");return;}

		Method method=this.handlerMapping.get(url);// 获取方法的参数列表
		Class<?>[] parameterTypes= method.getParameterTypes();// 获取请求的参数
		Map<String, String[]> parameterMap= req.getParameterMap();// 保存参数值
		Object[] paramValues=newObject[parameterTypes.length];// 方法的参数列表for(int i=0; i< parameterTypes.length; i++){// 根据参数名称,做某些处理
			String requestParam= parameterTypes[i].getSimpleName();if(requestParam.equals("HttpServletRequest")){// 参数类型已明确,这边强转类型
				paramValues[i]= req;continue;}if(requestParam.equals("HttpServletResponse")){
				paramValues[i]= resp;continue;}if(requestParam.equals("String")){for(Entry<String, String[]> param: parameterMap.entrySet()){
					String value= Arrays.toString(param.getValue()).replaceAll("\\[|\\]","").replaceAll(",\\s",",");
					paramValues[i]= value;}}}// 利用反射机制来调用try{
			method.invoke(this.controllerMap.get(url), paramValues);// 第一个参数是method所对应的实例// 在ioc容器中}catch(Exception e){
			e.printStackTrace();}}privatevoiddoLoadConfig(String location){// 把web.xml中的contextConfigLocation对应value值的文件加载到流里面
		InputStream resourceAsStream=this.getClass().getClassLoader().getResourceAsStream(location);try{// 用Properties文件加载文件里的内容
			properties.load(resourceAsStream);}catch(IOException e){
			e.printStackTrace();}finally{// 关流if(null!= resourceAsStream){try{
					resourceAsStream.close();}catch(IOException e){
					e.printStackTrace();}}}}privatevoiddoScanner(String packageName){// 把所有的.替换成/
		URL url=this.getClass().getClassLoader().getResource("/"+ packageName.replaceAll("\\.","/"));
		File dir=newFile(url.getFile());for(File file: dir.listFiles()){if(file.isDirectory()){// 递归读取包doScanner(packageName+"."+ file.getName());}else{
				String className= packageName+"."+ file.getName().replace(".class","");
				classNames.add(className);}}}//利用java的反射机制privatevoiddoInstance(){if(classNames.isEmpty()){return;}for(String className: classNames){try{// 把类搞出来,反射来实例化
				Class<?> clazz= Class.forName(className);if(clazz.isAnnotationPresent(HController.class)){
					ioc.put(toLowerFirstWord(clazz.getSimpleName()), clazz.newInstance());}else{continue;}}catch(Exception e){
				e.printStackTrace();continue;}}}privatevoidinitHandlerMapping(){if(ioc.isEmpty()){return;}try{for(Entry<String, Object> entry: ioc.entrySet()){
				Class<?extendsObject> clazz= entry.getValue().getClass();if(!clazz.isAnnotationPresent(HController.class)){continue;}// 拼url时,是controller头的url拼上方法上的url
				String baseUrl="";if(clazz.isAnnotationPresent(HRequestMapping.class)){
					HRequestMapping annotation= clazz.getAnnotation(HRequestMapping.class);
					baseUrl= annotation.value();}
				Method[] methods= clazz.getMethods();for(Method method: methods){if(!method.isAnnotationPresent(HRequestMapping.class)){continue;}
					HRequestMapping annotation= method.getAnnotation(HRequestMapping.class);
					String url= annotation.value();

					url=(baseUrl+"/"+ url).replaceAll("/+","/");
					handlerMapping.put(url, method);
					controllerMap.put(url, clazz.newInstance());
					System.out.println(url+","+ method);}}}catch(Exception e){
			e.printStackTrace();}}/**
	 * 把字符串的首字母小写
	 * 
	 * @param name
	 * @return
	 */private StringtoLowerFirstWord(String name){char[] charArray= name.toCharArray();
		charArray[0]+=32;return String.valueOf(charArray);}}

从源码及可看出流程:
1)客户端发送请求到DispacherServlet(由web.xml拦截所有请求到改servlet);

2)由DispacherServlet(核心servlet)控制器查询HanderMapping,找到处理请求的Controller;(这里我用了一个map类似于缓存容器,装载所有的mapping即映射)

3)Controller层调用业务逻辑处理后,返回ModelAndView,即下一层(往往是service、serviceimpl,mapper层)返回的的数据;

4)DispacherSerclet查询视图解析器,找到ModelAndView指定的视图;

5)视图负责将结果显示到客户端。
(3)Mybatis(持久层框架):mybatis是对jdbc的封装,它让数据库底层操作变的透明。mybatis的操作都是围绕一个sqlSessionFactory实例展开的。mybatis通过配置文件关联到各实体类的Mapper文件,Mapper文件中配置了每个类对数据库所需进行的sql语句映射。在每次与数据库交互时,通过sqlSessionFactory拿到一个sqlSession,再通过得到Mapper文件sqlSession.getMapper()最后调用的是JDK的动态代理(exlipse一直Ctrl+鼠标左键点击到最后发现如此,源码附上)。

protected TnewInstance(MapperProxy<T> mapperProxy){return(T) Proxy.newProxyInstance(mapperInterface.getClassLoader(),newClass[]{ mapperInterface}, mapperProxy);

发人员只需要创建Mapper接口,并使用Mapper接口即可。Mybatis会对Mapper接口产生动态代理对象,这个动态代理对象实现了Mapper接口,拥有Mapper中定义的所有方法,并对这些方法进行了增强。增强的逻辑是获得sql语句和执行sql语句。
动态代理:

  1. 通过实现 InvocationHandler 接口创建自己的调用处理器;
  2. 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
  3. 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
  4. 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。

框架核心原理

  1. AOP 面向切面编程(AOP)提供另外一种角度来思考程序结构,通过这种方式弥补了面向对象编程(OOP)的不足。除了类(classes)以外,AOP提供了切面。切面对关注点进行模块化,例如横切多个类型和对象的事务管理。Spring的一个关键的组件就是AOP框架,可以自由选择是否使用AOP。提供声明式企业服务,特别是为了替代EJB声明式服务。最重要的服务是声明性事务管理,这个服务建立在Spring的抽象事物管理之上。允许用户实现自定义切面,用AOP来完善OOP的使用可以把Spring AOP看作是对Spring的一种增强。AOP的实现乃至spring框架基本上核心代码都是基于Java语言的反射机制(所谓的反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。)。
    AOP主要作用就是不通过修改源代码的方式、将非核心功能代码织入来实现对方法的增强。那么Spring AOP的底层如何实现对方法的增强?实现的关键在于使用了代理模式。代理模式的作用就是为其它对象提供一种代理,以控制对这个对象的访问,用于解决在直接访问对象时带来的各种问题,比如要访问的对象在远程的机器上。在面向对象系统中,由于其他某些原因(对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问)等
    代理主要分为两种方式:静态代理和动态代理
  2. IOC IoC不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以相较于传统的java servlet需要自己request.getParamiter等需要一系列取值,转换中文,转换值类型的繁琐,更重要的是使得程序的整个体系结构变得非常灵活。
    自定义一个IOC容器的思路:
    Map做一个容器,然后用解析xml文件的工具解析出需要扫描的包。利用Java反射机制拿到锁具有的方法,属性,注入到Map容器中
  3. DI 依赖注入,是组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态地将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。

推荐阅读
  • 基于Socket的多个客户端之间的聊天功能实现方法
    本文介绍了基于Socket的多个客户端之间实现聊天功能的方法,包括服务器端的实现和客户端的实现。服务器端通过每个用户的输出流向特定用户发送消息,而客户端通过输入流接收消息。同时,还介绍了相关的实体类和Socket的基本概念。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 本文介绍了Java工具类库Hutool,该工具包封装了对文件、流、加密解密、转码、正则、线程、XML等JDK方法的封装,并提供了各种Util工具类。同时,还介绍了Hutool的组件,包括动态代理、布隆过滤、缓存、定时任务等功能。该工具包可以简化Java代码,提高开发效率。 ... [详细]
  • Centos7.6安装Gitlab教程及注意事项
    本文介绍了在Centos7.6系统下安装Gitlab的详细教程,并提供了一些注意事项。教程包括查看系统版本、安装必要的软件包、配置防火墙等步骤。同时,还强调了使用阿里云服务器时的特殊配置需求,以及建议至少4GB的可用RAM来运行GitLab。 ... [详细]
  • 本文介绍了Perl的测试框架Test::Base,它是一个数据驱动的测试框架,可以自动进行单元测试,省去手工编写测试程序的麻烦。与Test::More完全兼容,使用方法简单。以plural函数为例,展示了Test::Base的使用方法。 ... [详细]
  • Imtryingtofigureoutawaytogeneratetorrentfilesfromabucket,usingtheAWSSDKforGo.我正 ... [详细]
  • Spring学习(4):Spring管理对象之间的关联关系
    本文是关于Spring学习的第四篇文章,讲述了Spring框架中管理对象之间的关联关系。文章介绍了MessageService类和MessagePrinter类的实现,并解释了它们之间的关联关系。通过学习本文,读者可以了解Spring框架中对象之间的关联关系的概念和实现方式。 ... [详细]
  • 本文介绍了Android中的assets目录和raw目录的共同点和区别,包括获取资源的方法、目录结构的限制以及列出资源的能力。同时,还解释了raw目录中资源文件生成的ID,并说明了这些目录的使用方法。 ... [详细]
  • 本文介绍了使用Spark实现低配版高斯朴素贝叶斯模型的原因和原理。随着数据量的增大,单机上运行高斯朴素贝叶斯模型会变得很慢,因此考虑使用Spark来加速运行。然而,Spark的MLlib并没有实现高斯朴素贝叶斯模型,因此需要自己动手实现。文章还介绍了朴素贝叶斯的原理和公式,并对具有多个特征和类别的模型进行了讨论。最后,作者总结了实现低配版高斯朴素贝叶斯模型的步骤。 ... [详细]
  • Hibernate延迟加载深入分析-集合属性的延迟加载策略
    本文深入分析了Hibernate延迟加载的机制,特别是集合属性的延迟加载策略。通过延迟加载,可以降低系统的内存开销,提高Hibernate的运行性能。对于集合属性,推荐使用延迟加载策略,即在系统需要使用集合属性时才从数据库装载关联的数据,避免一次加载所有集合属性导致性能下降。 ... [详细]
  • Android系统移植与调试之如何修改Android设备状态条上音量加减键在横竖屏切换的时候的显示于隐藏
    本文介绍了如何修改Android设备状态条上音量加减键在横竖屏切换时的显示与隐藏。通过修改系统文件system_bar.xml实现了该功能,并分享了解决思路和经验。 ... [详细]
  • Android工程师面试准备及设计模式使用场景
    本文介绍了Android工程师面试准备的经验,包括面试流程和重点准备内容。同时,还介绍了建造者模式的使用场景,以及在Android开发中的具体应用。 ... [详细]
  • 解决Sharepoint 2013运行状况分析出现的“一个或多个服务器未响应”问题的方法
    本文介绍了解决Sharepoint 2013运行状况分析中出现的“一个或多个服务器未响应”问题的方法。对于有高要求的客户来说,系统检测问题的存在是不可接受的。文章详细描述了解决该问题的步骤,包括删除服务器、处理分布式缓存留下的记录以及使用代码等方法。同时还提供了相关关键词和错误提示信息,以帮助读者更好地理解和解决该问题。 ... [详细]
author-avatar
hhqblog
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有