欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

SSM框架流程及原理分析

程序员文章站 2022-03-18 17:36:50
前言:学ssm框架已经有很长时间,今天来复习一下ssm图示流程:spring核心:java反射mybatis:动态代理,而动态代理又是基于反射的,所以,ssm框架核心原理在反射。(1)spring(对...

前言:学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框架原理的资料请关注其它相关文章!

相关标签: SSM 框架 原理