《spring5核心原理》笔记
1.遍历实例化bean,找到带有@controller 标签的类,
2.解析该类,把@RequestMapping和方法,类名形参映射,封装到Mapping中。
3.接收请求,解析发送过来的request,拿到url,映射到具体的方法
4.拿到请求过来的参数,然后利用反射,调用method,执行获得结果
5.根据不同的返回结果进行返回,可能返回json,可能返回html。
6.增加一个静态页面渲染的功能。(未产生效果,正则表达式可能有问题)
1.XDispatcherServlet类
public class XDispatcherServlet extends HttpServlet {
private final String LOCATION="contextConfigLocation";
private List handlerMappings=new ArrayList<>(); //映射,封装
private Map handlerAdapters=new HashMap<>();
private List viewResolvers=new ArrayList<>();
private XApplicationContext context;
public void init(ServletConfig config) throws ServletException{
System.out.println("dispatcherServlet init().....");
System.out.println(config.getInitParameter(LOCATION)); //获取web.xml中配置的值!
context=new XApplicationContext(config.getInitParameter(LOCATION));
initStrategies(context);
}
protected void initStrategies(XApplicationContext context){
//有九种策略
//针对每个用户的请求,都会经过一些处理策略处理,最终才能有结果输出
//每种策略可以自定义干预,但是最终的结果都一致
//=====================传说中的九大组件==================
initMultipartResolver(context); //文件上传解析,如果请求类型是multipart,将通过该方法进行解析
initLocaleResolver(context); //本地化 解析
initThemeResolver(context); //主题 解析
initHandlerMappings(context); //将handlerMapping请求映射到 处理器 【实现】
initHandlerAdapters(context); //进行多类型的参数 动态匹配 【实现】
initHandlerExceptionResolvers(context); //如果执行过程中 遇到异常,将交给该方法
initRequestToViewNameTranslator(context); //直接将 请求解析到 视图名
initViewResolvers(context); //将逻辑视图解析到 具体视图实现 【实现】
initFlashMapManager(context); //flash映射管理器
}
private void initFlashMapManager(XApplicationContext context){}
private void initRequestToViewNameTranslator(XApplicationContext context){}
private void initHandlerExceptionResolvers(XApplicationContext context){}
private void initThemeResolver(XApplicationContext context){}
private void initLocaleResolver(XApplicationContext context){}
private void initMultipartResolver(XApplicationContext context){}
//---------------只 实现其中 三大组件!---------------------
//将controller 中配置的RequestMapping和Method进行一一对应
private void initHandlerMappings(XApplicationContext context){
//一一对应:Map map;( 注意requestMapping 分为两级!)
//首先从容器中 获取所有的实例
String []beanNames=context.getBeanDefinitionNames(); //所以返回的是所有bean的小名
try{
for(String beanName:beanNames){
//到了mvc层,对外提供的方法只有一个 getBean()方法
//返回的对象 不是BeanWrapper 怎么办?
Object controller=context.getBean(beanName); //直接获取
Class> clazz=controller.getClass();
if(!clazz.isAnnotationPresent(XController.class)) //这里读取的是标签。
continue;
//从bean中找到所有的controller。 把里面的requestMapping中的value解析出来
String baseUrl="";
if(clazz.isAnnotationPresent(XRequestMapping.class)){
XRequestMapping requestMapping=clazz.getAnnotation(XRequestMapping.class);
baseUrl=requestMapping.value();
}
//扫描所有的public 类型的方法
Method[] methods=clazz.getMethods();
for(Method method:methods){ //只处理requestMapping注解
if(!method.isAnnotationPresent(XRequestMapping.class))
continue;
XRequestMapping requestMapping=method.getAnnotation(XRequestMapping.class);
//这里的baseUrl中 应该有/了, // \* 切换为 * 但是这里一般没有使用\在匹配中。
String regex=("/"+baseUrl+requestMapping.value().replaceAll("\\*",".")).replaceAll("/+","/");
Pattern pattern=Pattern.compile(regex);
//url--controller----method
this.handlerMappings.add(new XHandlerMapping(pattern,controller,method));
System.out.println("Mapping: "+regex);
}
}
}catch (Exception e){
e.printStackTrace();
}
}
//给每一个mapping 配置一个 handler
private void initHandlerAdapters(XApplicationContext context){
for(XHandlerMapping handlerMapping:this.handlerMappings){
//每个方法 有一个参数列表,这里保存的是形参列表
this.handlerAdapters.put(handlerMapping,new XHandlerAdapter()) ;
}
}
private void initViewResolvers(XApplicationContext context){
//在页面中输入 http://localhost/first.html
//解决页面名字和 模板文件 关联的问题
String templateRoot =context.getConfig().getProperty("templateRoot"); //从application.properties中 读取 参数
String templateRootPath=this.getClass().getResource(templateRoot).getFile();
File templateRootDir=new File(templateRootPath);
//找到所有的 html模板
for(File template:templateRootDir.listFiles()){ //遍历
this.viewResolvers.add(new XViewResolver(templateRoot));
}
}
//===================================运行时调用====================
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try{
doDispatch(req,resp);
}catch (Exception e){
//这里可以返回一个 html页面
e.printStackTrace();
}
}
private void doDispatch(HttpServletRequest req,HttpServletResponse resp) throws Exception{
XHandlerMapping handler=getHandler(req); //根据请求的url 获得一个handler
if(handler==null){
processDispatchResult(req,resp,new XModelAndView("404"));
return;
}
//通过key 取value
XHandlerAdapter ha=getHandlerAdapter(handler);
//
XModelAndView mv=ha.handle(req,resp,handler);
processDispatchResult(req,resp,mv); //真的输出
}
private void processDispatchResult(HttpServletRequest request,HttpServletResponse response,XModelAndView mv)throws Exception{
if(null==mv)
return;
if(this.viewResolvers.isEmpty())
return;
if(this.viewResolvers!=null){
for(XViewResolver viewResolver:this.viewResolvers){
XView view=viewResolver.resolveViewName(mv.getViewName(),null);
if(view!=null){
view.render(mv.getModel(),request,response);
return;
}
}
}
}
private XHandlerAdapter getHandlerAdapter(XHandlerMapping handler){
if(this.handlerAdapters.isEmpty())
return null;
XHandlerAdapter ha=this.handlerAdapters.get(handler);
if(ha.supports(handler))
return ha;
return null;
}
private XHandlerMapping getHandler(HttpServletRequest req){
if(this.handlerMappings.isEmpty())
return null;
String url=req.getRequestURI();
String contextPath=req.getContextPath();
url=url.replace(contextPath,"").replaceAll("/+","/");
//通过url ,找到对应的handlerMapping,然后再去拿对应的map的value
for(XHandlerMapping handler:this.handlerMappings) {
Matcher matcher=handler.getPattern().matcher(url);
if (!matcher.matches())
continue;
return handler;
}
return null;
}
}
2.XHandlerMapping类
@Data
public class XHandlerMapping {
private Object controller;// 目标方法所在的controller对象
private Method method;// URL对应的目标方法
private Pattern pattern; //url的封装
public XHandlerMapping(Pattern pattern,Object controller,Method method){
this.controller=controller;
this.pattern=pattern;
this.method=method;
}
}
3.XHandlerAdapter
//映射 适配器
public class XHandlerAdapter {
//处理器 是否合法?
public boolean supports(Object handler){
return (handler instanceof XHandlerMapping);
}
public XModelAndView handle(HttpServletRequest request, HttpServletResponse response,Object handler)throws Exception{
XHandlerMapping handlerMapping=(XHandlerMapping) handler;
//每个方法有一个参数列表(形参),
Map paramMapping=new HashMap(); //形参名字+排第几个
// Annotation[] pa=handlerMapping.getMethod().getDeclaredAnnotations(); //这里抓的是注解 ----url映射 对应的method
Annotation[][] pa=handlerMapping.getMethod().getParameterAnnotations(); //上面抓是的声明注解,这里要抓参数注解
for(int i=0;i[] paramTypes=handlerMapping.getMethod().getParameterTypes(); //这里抓的是形参
for(int i=0;i type=paramTypes[i];
if(type==HttpServletRequest.class||type==HttpServletResponse.class){ //把两个特殊的 形参 也放入 形参标中。而且这个序号不会和上面的重合,因为用if 隔离了!
paramMapping.put(type.getName(),i);
}
}
//2.得到自定义命名参数所在的位置
//用户通过uri传来的参数列表
Map reqParameterMap=request.getParameterMap();//一步直接抓到 传入的参数
//3.构造实参列表
Object []paramValues=new Object[paramTypes.length]; //成立一个 参数组,与上面解析出来的参数 组长度一样!
//对传过来的参数 进行解析
for(Map.Entry param:reqParameterMap.entrySet()){
//为什么把string组,换成一个 value,然后进行匹配呢?
String value= Arrays.toString(param.getValue()).replaceAll("\\[|\\]","").replaceAll("\\s","");
if(!paramMapping.containsKey(param.getKey())) //如果形参列表中,没有这个参数名字,那么就【忽略】
continue;
int index=paramMapping.get(param.getKey()); //把值放到 参数组中
paramValues[index]=caseStringValue(value,paramTypes[index]);
}
//两个 参数特殊处理 直接注入的办法(从servlet中 拿到的两个对象,注入进去)
if(paramMapping.containsKey(HttpServletRequest.class.getName())){
int reqIndex=paramMapping.get(HttpServletRequest.class.getName());
paramValues[reqIndex]=request;
}
if(paramMapping.containsKey(HttpServletResponse.class.getName())){
int respIndex=paramMapping.get(HttpServletResponse.class.getName());
paramValues[respIndex]=response;
}
//4.从handler中取出 controller,method 然后利用反射进行调用 得到返回的结果
Object result=handlerMapping.getMethod().invoke(handlerMapping.getController(),paramValues); //【执行】
if(result ==null)
return null;
boolean isModelAndView =handlerMapping.getMethod().getReturnType()==XModelAndView.class;
if(isModelAndView){
return (XModelAndView)result;
}else{
return null;
}
}
//这个是int 的类型转换 绑定。
private Object caseStringValue(String value,Class> clazz){
if(clazz==String.class){
return value;
}else if(clazz==Integer.class){
return Integer.valueOf(value);
}else if(clazz==int.class){
return Integer.valueOf(value).intValue();
}else
return null;
}
}
辅助类
1.XModelAndView
@Data
public class XModelAndView {
private String viewName; //页面模板的名称
private Map model;//往页面传送的参数
public XModelAndView(String viewName,Map model){
this.viewName=viewName;
this.model=model;
}
public XModelAndView(String viewName){
this.viewName=viewName;
}
}
2.XView
public class XView {
public static final String DEFAULT_CONTENT_TYPE ="text/html;charset=utf-8";
private File viewFile;
public XView(File viewFile){
this.viewFile=viewFile;
}
public String getContentTYpe(){
return DEFAULT_CONTENT_TYPE;
}
public void render(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception{
StringBuffer sb=new StringBuffer();
RandomAccessFile ra=new RandomAccessFile(this.viewFile,"r");
System.out.println("render() html content: ");
try{
String line=null;
while(null!=(line=ra.readLine())){
line=new String(line.getBytes("ISO-8859-1"),"utf-8"); //解码(因为默认iso 使用的是 iso好像是这样
System.out.println(line);
Pattern pattern=Pattern.compile("$\\{[^\\}]+\\}",Pattern.CASE_INSENSITIVE);//这里是形成一个 匹配符号把
Matcher matcher=pattern.matcher(line); //这里使用匹配 进行对字符串 超过。。好像是对\进行转义标注。
while(matcher.find()){
String paramName=matcher.group();
paramName=paramName.replaceAll("$\\{|\\}",""); //将¥{} 内容取出来(自定义 动态html,渲染)
Object paramValue=model.get(paramName); //从ModelAndView中 获得数据值,进行渲染
if(null==paramValue)
continue;
line=matcher.replaceFirst(makeStringForRegExp(paramValue.toString()));
matcher=pattern.matcher(line);
}
sb.append(line);
}
}finally {
ra.close();
}
response.setCharacterEncoding("utf-8");
response.getWriter().write(sb.toString()); //输出到前台
}
public static String makeStringForRegExp(String str){
return str.replace("\\","\\\\").replace("*","\\*")
.replace("+","\\+").replace("|","\\|")
.replace("{","\\{").replace("}","\\}")
.replace("(","\\(").replace(")","\\)")
.replace("^","\\^").replace("$","\\$")
.replace("[","\\[").replace("]","\\]")
.replace("?","\\?").replace(",","\\,")
.replace(".","\\.").replace("&","\\&")
;
}
}
3.XViewResolver
//涉及这个类的主要目的:
//1. 将一个静态文件 变为一个 动态文件
//2. 根据用户传送不同的 参数,产生不同的结果。
//最终输出字符串,交给response输出。
//这个就是找到 .html页面了!,就是对文件流的处理了
public class XViewResolver {
private final String DEFAULT_TEMPLATE_SUFFIX=".html";
private File templateRootDir;
private String viewName;
//先把文件 的指针拿到
public XViewResolver(String templateRoot){
String templateRootPath=this.getClass().getResource(templateRoot).getFile(); //这个有深度!
this.templateRootDir=new File(templateRootPath);
}
public XView resolveViewName(String viewName, Locale locale) throws Exception{
this.viewName=viewName;
if(null==viewName||"".equals(viewName.trim()))
return null;
viewName=viewName.endsWith(DEFAULT_TEMPLATE_SUFFIX)?viewName:(viewName+DEFAULT_TEMPLATE_SUFFIX); //如果有了后缀,就不用再加了!容错性
File templateFile=new File((templateRootDir.getPath()+"/"+viewName).replaceAll("/+","/"));
return new XView(templateFile);
}
public String getViewName(){
return viewName;
}
}