SSM框架流程及原理分析
前言:学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 */ public class mydispatcherservlet extends httpservlet { //加载属性文件 private properties properties = new properties(); //装载beanname private list<string> classnames = new arraylist<string>(); //ioc容器 private map<string, object> ioc = new hashmap<string, object>(); //类似于以前自定义的cache缓存容器,这里也是起到一个容器的作用 //用于加载各个mapping private map<string, method> handlermapping = new hashmap<string, method>(); //容器加载所有的controller private map<string, object> controllermap = new hashmap<string, object>(); @override public void init(servletconfig config) throws servletexception { // 1.加载配置文件 doloadconfig(config.getinitparameter("contextconfiglocation")); // 2.初始化所有相关联的类,扫描用户设定的包下面所有的类 doscanner(properties.getproperty("scanpackage")); // 3.拿到扫描到的类,通过反射机制,实例化,并且放到ioc容器中beanname默认是首字母小写 doinstance(); // 4.初始化handlermapping(将url和method对应上) inithandlermapping(); } @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) { resp.getwriter().write("500!! server exception"); } } private void dodispatch(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 = new object[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(); } } private void doloadconfig(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(); } } } } private void doscanner(string packagename) { // 把所有的.替换成/ url url = this.getclass().getclassloader().getresource("/" + packagename.replaceall("\\.", "/")); file dir = new file(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的反射机制 private void doinstance() { 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; } } } private void inithandlermapping() { if (ioc.isempty()) { return; } try { for (entry<string, object> entry : ioc.entryset()) { class<? extends object> 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 string tolowerfirstword(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 t newinstance(mapperproxy<t> mapperproxy) { return (t) proxy.newproxyinstance(mapperinterface.getclassloader(), new class[] { mapperinterface }, mapperproxy);
发人员只需要创建mapper接口,并使用mapper接口即可。mybatis会对mapper接口产生动态代理对象,这个动态代理对象实现了mapper接口,拥有mapper中定义的所有方法,并对这些方法进行了增强。增强的逻辑是获得sql语句和执行sql语句。
动态代理:
- 通过实现 invocationhandler 接口创建自己的调用处理器;
- 通过为 proxy 类指定 classloader 对象和一组 interface 来创建动态代理类;
- 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
- 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。
框架核心原理
- aop 面向切面编程(aop)提供另外一种角度来思考程序结构,通过这种方式弥补了面向对象编程(oop)的不足。除了类(classes)以外,aop提供了切面。切面对关注点进行模块化,例如横切多个类型和对象的事务管理。spring的一个关键的组件就是aop框架,可以*选择是否使用aop。提供声明式企业服务,特别是为了替代ejb声明式服务。最重要的服务是声明性事务管理,这个服务建立在spring的抽象事物管理之上。允许用户实现自定义切面,用aop来完善oop的使用可以把spring aop看作是对spring的一种增强。aop的实现乃至spring框架基本上核心代码都是基于java语言的反射机制(所谓的反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。)。
- aop主要作用就是不通过修改源代码的方式、将非核心功能代码织入来实现对方法的增强。那么spring aop的底层如何实现对方法的增强?实现的关键在于使用了代理模式。代理模式的作用就是为其它对象提供一种代理,以控制对这个对象的访问,用于解决在直接访问对象时带来的各种问题,比如要访问的对象在远程的机器上。在面向对象系统中,由于其他某些原因(对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问)等
- 代理主要分为两种方式:静态代理和动态代理
- ioc ioc不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。有了ioc容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以相较于传统的java servlet需要自己request.getparamiter等需要一系列取值,转换中文,转换值类型的繁琐,更重要的是使得程序的整个体系结构变得非常灵活。
- 自定义一个ioc容器的思路:
- map做一个容器,然后用解析xml文件的工具解析出需要扫描的包。利用java反射机制拿到锁具有的方法,属性,注入到map容器中
- di 依赖注入,是组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态地将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。
以上就是深入浅出ssm框架流程以及原理的详细内容,更多关于ssm框架原理的资料请关注其它相关文章!
上一篇: Nginx服务器的安装与一些基本配置总结
下一篇: nginx线程池源码分析