SpringMVC的主要作用是:从http请求中得到一个url字符串和对应的请求参数,根据该字符串找到Controller中的一个方法,利用反射执行该方法,将结果返回给前端

1,初始化

  将url请求路径和controller中的方法一一对应

  将url请求路径和object一一对应, 反射调用方法时要传入object和方法入参

2,执行请求

  获取http请求路径和参数,找到对应的方法,执行该方法后返回给前端

================================================================================================================================

包扫描是怎么实现的

递归指定路径的目录(class文件在项目中也是放在某个目录当中的),找出所有的全局限定类名,利用反射生成该类的实例,判断该类是否有特定的注解修饰,比如说@MyController,有注解修饰的就将实例放入Map中

一,web.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
<servlet>
<servlet-name>MVC</servlet-name>
<servlet-class>com.irish.servlet.MyDispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>application.properties</param-value>
</init-param>
<load-on-startup></load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>MVC</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>

二,DispathcherServlet

public class MyDispatcherServlet extends HttpServlet{

    private static final long serialVersionUID = 1L;

    private Properties properties = new Properties();

    private List<String> classNames = new ArrayList<>();

    private Map<String, Object> ioc = new HashMap<>();

    private Map<String, Method> handlerMapping = new  HashMap<>();

    private Map<String, Object> controllerMap  =new HashMap<>();

    @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();
System.out.println("requestUri:"+url);
System.out.println("contextPath:"+contextPath);
url=url.replace(contextPath, "").replaceAll("/+", "/");
System.out.println("url:"+url);
if(!this.handlerMapping.containsKey(url)){
resp.getWriter().write("404 NOT FOUND!");
return;
} Method method =this.handlerMapping.get(url); //获取方法的参数列表
Class<?>[] parameterTypes = method.getParameterTypes();
//获取方法的参数列表的注解值,顺序和长度与parameterTypes相同,
String [] paramNames = getMethodParameterNamesByAnnotation(method); //获取请求的参数
Map<String, String[]> parameterMap = req.getParameterMap(); //保存参数值
Object [] paramValues= new Object[parameterTypes.length]; //方法的参数列表
for (int i = ; 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")){
String theParamName = paramNames[i];
for (Entry<String, String[]> param : parameterMap.entrySet()) {
if(param.getKey().equals(theParamName)) {
String value =Arrays.toString(param.getValue()).replaceAll("\\[|\\]", "").replaceAll(",\\s", ",");
//[abc, ed] 替换为 abc,ed
paramValues[i]=value;
System.out.println(theParamName + " : "+value);
}
}
}
}
//利用反射机制来调用
try {
method.invoke(this.controllerMap.get(url), paramValues);
} catch (Exception e) {
e.printStackTrace();
} } /**
*
* 按照方法参数的顺序获取注解的值,没有注解的参数对应的位置为空
*/
public static String[] getMethodParameterNamesByAnnotation(Method method) {
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
if (parameterAnnotations == null || parameterAnnotations.length == ) {
return null;
}
String[] parameterNames = new String[parameterAnnotations.length];
int i = ;
for (Annotation[] parameterAnnotation : parameterAnnotations) {
if(parameterAnnotation.length == ) {
parameterNames[i++] = null;
}
for (Annotation annotation : parameterAnnotation) {
if (annotation instanceof MyRequestParam) {
MyRequestParam param = (MyRequestParam) annotation;
parameterNames[i++] = param.value();
}
}
}
return parameterNames;
} @Override
public void init(ServletConfig config) throws ServletException { //1.加载配置文件,将classpath路径下的application.properties文件加载到内存
//config.getInitParameter("contextConfigLocation") 获取的是Servlet的配置参数
doLoadConfig(config.getInitParameter("contextConfigLocation")); //2.将指定路径下的全局限定类名添加到classNames集合当中
doScanner(properties.getProperty("scanPackage")); //3.通过反射实例化标注了MyController注解的类,并且放到ioc容器中
doInstance(); //4.初始化url和Method的对应关系,url和Object的对应关系
initHandlerMapping(); } 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);
}
}
} private void doInstance() {
if (classNames.isEmpty()) {
return;
}
for (String className : classNames) {
try {
//把类搞出来,反射来实例化(只有加@MyController需要实例化)
Class<?> clazz =Class.forName(className);
if(clazz.isAnnotationPresent(MyController.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(MyController.class)){
continue;
}
//拼url时,是controller头部的url拼上方法上的url
String baseUrl ="";
if(clazz.isAnnotationPresent(MyRequestMapping.class)){
MyRequestMapping annotation = clazz.getAnnotation(MyRequestMapping.class);
baseUrl=annotation.value();
}
Method[] methods = clazz.getMethods();
for (Method method : methods) {
if(!method.isAnnotationPresent(MyRequestMapping.class)){
continue;
}
MyRequestMapping annotation = method.getAnnotation(MyRequestMapping.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();
} } /**
* 字符串首字母小写
*/
private String toLowerFirstWord(String name){
char[] charArray = name.toCharArray();
charArray[] += ;
return String.valueOf(charArray);
} }

三,自定义注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyController {
/**
* 表示给controller注册别名
* @return
*/
String value() default ""; }
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyRequestMapping {
/**
* 表示访问该方法的url
* @return
*/
String value() default ""; }
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyRequestParam {
/**
* 表示参数的别名,必填
* @return
*/
String value(); }

四,controller

@MyController
@MyRequestMapping("/test")
public class TestController { @MyRequestMapping("/doTest")
public void test1(HttpServletRequest request, HttpServletResponse response,
@MyRequestParam("param1") String param1 , @MyRequestParam("param2") String param2 ){
System.out.println("param1 = "+ param1);
System.out.println("param2 = "+ param2);
try {
response.getWriter().write( "doTest method success! param1:"+param1 + " , param2:"+ param2);
} catch (IOException e) {
e.printStackTrace();
}
} @MyRequestMapping("/doTest2")
public void test2(HttpServletRequest request, HttpServletResponse response){
try {
response.getWriter().println("doTest2 method success!");
} catch (IOException e) {
e.printStackTrace();
}
}
}

五,application.properties

scanPackage=com.irish.controller

六,pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.</modelVersion>
<groupId>com.irish</groupId>
<artifactId>mySpringMVC</artifactId>
<version>0.0.-SNAPSHOT</version>
<packaging>war</packaging> <properties>
<project.build.sourceEncoding>UTF-</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<java.version>1.8</java.version>
</properties> <dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.</version>
<scope>provided</scope>
</dependency>
</dependencies> </project>

七,测试

项目结构

github地址    https://github.com/jake1263/MySpringMVC

最新文章

  1. ViewPager与Tab结合使用
  2. iOS开发之多线程技术——GCD篇
  3. Centos6.5 下安装PostgreSQL9.4数据库
  4. c#连接SFTP上传文件
  5. php 安装xdebug扩展
  6. POJ 1811 Prime Test (Pollard rho 大整数分解)
  7. 关于Tokenizer与TokenFilter的区别
  8. 教了几天C语言 C语言竞赛------家长们你们为什么这么急!!
  9. AngularJS ng-class用法
  10. 快排 quicksort 快速排序
  11. iOS面试题05-父子控制器、内存管理
  12. [CLR via C#]7. 常量和字段
  13. ASP.NET MVC:多语言的三种技术处理策略
  14. JSP的getRequestDispatcher()与sendRedirect()的区别
  15. 前端开发chrome console的使用 :评估表达式 – Break易站
  16. mybatis从mapper接口跳转到相应的xml文件的eclipse插件
  17. Unity应用架构设计(4)——设计可复用的SubView和SubViewModel(Part 2)
  18. IdentityServer4 中文文档 -6- (简介)示例服务器和测试
  19. mobx 入门
  20. 常见的机器学习&amp;数据挖掘知识点

热门文章

  1. 36氪首发 | 掘金RPA百亿新蓝海,弘玑Cyclone获DCM、源码千万美元A轮融资
  2. python zlib模块缺失报错:RuntimeError: Compression requires the (missing) zlib module
  3. 决策树——ID3
  4. (3) esp8266 官方库文件,没有求逆函数
  5. LeetCode 971. Flip Binary Tree To Match Preorder Traversal
  6. 【loj2341】【WC2018】即时战略
  7. string拼接时去掉最后一个逗号
  8. 处理 MySQL 因为 SLAVE 崩溃导致需要手动跳过 GTID 的问题 | 关于 GTID
  9. mysql 升序降序
  10. 「ZJOI2019」开关