前言:学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语句。
动态代理: