首页 > 编程语言 > 详细

如何自己构建一个类似springmvc的框架(springmvc原理理解)

时间:2021-03-15 22:56:14      阅读:28      评论:0      收藏:0      [点我收藏+]

构建一个类似springmvc的框架

大致结构:

  • DispatcherServlet,接收所有请求,然后进行分发
  • 自定义Controller注解,RequestMapping注解
  • 设置ViewResolver 视图解析器

技术分享图片

大致步骤:

  1. 把controller和注解进行关联,可以使用url找到对应的方法
  2. 解析springmvc.xml进行扫包
  3. 把类和url关联到一起

 

1.自定义controller注解类

/*
自定义的controller注解类
 */
//target的作用是声明该注解可以标记类还是方法还是方法还是属性的,Type代表该注解作用在类上面
//Retention标记该注解什么时候生效,runtime代表运行时生效
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface HainaController {
    //这其实相当于是一个value属性,用来给controller类设置url
    String value() default "";
}

可以通过自己设定的注解,来设置controller类,实现在项目中通过url能够访问到这个类

2.自定义requestmapping注解类

//可以用在类上,也可以用在方法上
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface HainaRequestMapping {
    String value() default "";
}

通过自己设定的注解,这个注解注释的方法,能够在项目中通过url直接调用该方法

 

3.设置视图解析类

/*
 *视图解析器
 */
public class HainaViewResolver {
</span><span style="color: #0000ff;">private</span><span style="color: #000000;"> String prefix;
</span><span style="color: #0000ff;">private</span><span style="color: #000000;"> String suffix;

</span><span style="color: #0000ff;">public</span><span style="color: #000000;"> String jspMapping (String value){
    </span><span style="color: #008000;">//</span><span style="color: #008000;">给视图拼接前缀和后缀</span>
    <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">this</span>.prefix + value + <span style="color: #0000ff;">this</span><span style="color: #000000;">.suffix;
}

</span><span style="color: #0000ff;">public</span><span style="color: #000000;"> String getPrefix() {
    </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> prefix;
}

</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> setPrefix(String prefix) {
    </span><span style="color: #0000ff;">this</span>.prefix =<span style="color: #000000;"> prefix;
}

</span><span style="color: #0000ff;">public</span><span style="color: #000000;"> String getSuffix() {
    </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> suffix;
}

</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> setSuffix(String suffix) {
    </span><span style="color: #0000ff;">this</span>.suffix =<span style="color: #000000;"> suffix;
}

}

将url中的方法字段传入视图解析器,就可以调用相应的方法

 

4.自定义分发器DispatcherServlet

/**
 * 自定义的分发器
 */
public class HainaDispatcherServlet extends HttpServlet {
    //用来保存url和controller对象的集合
    private Map<String, Object> controllerMap = new HashMap<>();
    //用来保存url和方法之间的映射
    private Map<String, Method> requestMappingMap = new HashMap<>();
</span><span style="color: #0000ff;">private</span> HainaViewResolver resolver = <span style="color: #0000ff;">new</span><span style="color: #000000;"> HainaViewResolver();

@Override
</span><span style="color: #0000ff;">protected</span> <span style="color: #0000ff;">void</span> doGet(HttpServletRequest req, HttpServletResponse resp) <span style="color: #0000ff;">throws</span><span style="color: #000000;"> ServletException, IOException {
    doPost(req, resp);
}

@Override
</span><span style="color: #0000ff;">protected</span> <span style="color: #0000ff;">void</span> doPost(HttpServletRequest req, HttpServletResponse resp) <span style="color: #0000ff;">throws</span><span style="color: #000000;"> ServletException, IOException {
    String uri </span>= req.getRequestURI();<span style="color: #008000;">//</span><span style="color: #008000;">获取uri</span><span style="color: #000000;">
    String controllerUri </span>= uri.split("/")[2];<span style="color: #008000;">//</span><span style="color: #008000;">获取controller</span><span style="color: #000000;">
    String methodUri </span>= uri.split("/")[3];<span style="color: #008000;">//</span><span style="color: #008000;">获取方法名</span><span style="color: #008000;">//</span><span style="color: #008000;">通过路径找到对应的controller对象和方法对象</span>
    Object obj =<span style="color: #000000;"> controllerMap.get(controllerUri);
    Method method </span>=<span style="color: #000000;"> requestMappingMap.get(methodUri);

    </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
        </span><span style="color: #008000;">//</span><span style="color: #008000;">使用反射,调用对应的方法,返回值实际就是页面的名字</span>
        String result =<span style="color: #000000;"> (String) method.invoke(obj);
        </span><span style="color: #008000;">//</span><span style="color: #008000;">把视图名拼接完整</span>
        String url =<span style="color: #000000;"> resolver.jspMapping(result);
        </span><span style="color: #008000;">//</span><span style="color: #008000;">使用请求转发的形式跳转到对应的视图上</span>

req.getRequestDispatcher(url).forward(req, resp);
}
catch (Exception e) {
e.printStackTrace();
}
}

</span><span style="color: #008000;">//</span><span style="color: #008000;">初始化</span>

@Override
public void init(ServletConfig config) throws ServletException {
//1.扫描controller,创建对应的实例对象,存入自定义容器中
scanController(config);
//2.初始化url和方法映射的容器
initRequestMapping();

    resolver.setPrefix(</span>"/"<span style="color: #000000;">);<span style="color: #008000;">//设置前缀</span>
    resolver.setSuffix(</span>".jsp"<span style="color: #000000;">);<span style="color: #008000;">//设置后缀</span>
}

</span><span style="color: #008000;">//</span><span style="color: #008000;">解析springmvc.xml文件,扫描文件中配置的包名中的使用了HainaController注解标注的类</span>
<span style="color: #0000ff;">private</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> scanController(ServletConfig config) {
    SAXReader reader </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> SAXReader();
    </span><span style="color: #008000;">//</span><span style="color: #008000;">这里其实是在读取web.xml文件的配置,通过web.xml文件,读取到对应springmvc
    </span><span style="color: #008000;">//</span><span style="color: #008000;">文件的名称,然后拼接好完整路径</span>
    String path = config.getServletContext().getRealPath(""<span style="color: #000000;">)
            </span>+ "\\WEB-INF\\classes\\"
            + config.getInitParameter("contextConfigLocation"<span style="color: #000000;">);
    System.out.println(</span>"path:" +<span style="color: #000000;"> path);
    </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
        Document root </span>=<span style="color: #000000;"> reader.read(path);
        Element rootElement </span>=<span style="color: #000000;"> root.getRootElement();

        Iterator it </span>=<span style="color: #000000;"> rootElement.elementIterator();
        </span><span style="color: #0000ff;">while</span><span style="color: #000000;"> (it.hasNext()) {
            Element next </span>=<span style="color: #000000;"> (Element) it.next();
            </span><span style="color: #0000ff;">if</span> ("component-scan"<span style="color: #000000;">.equals(next.getName())) {
                </span><span style="color: #008000;">//</span><span style="color: #008000;">获取要扫描的包名</span>
                String packageName = next.attributeValue("base-package"<span style="color: #000000;">);

                </span><span style="color: #008000;">//</span><span style="color: #008000;">有了包名之后,我们就需要根据包名,找到该包下的所有类名,然后根据类名
                </span><span style="color: #008000;">//</span><span style="color: #008000;">创建出对应的class对象</span>
                List&lt;String&gt; list =<span style="color: #000000;"> getClassNameList(packageName);
                </span><span style="color: #0000ff;">for</span><span style="color: #000000;"> (String str : list) {
                    </span><span style="color: #008000;">//</span><span style="color: #008000;">使用类反射,将类名加载进内存,转换成class类型对象</span>
                    Class clazz =<span style="color: #000000;"> Class.forName(str);
                    </span><span style="color: #008000;">//</span><span style="color: #008000;">判断是否使用了HainaController注解标注</span>
                    <span style="color: #0000ff;">if</span> (clazz.isAnnotationPresent(HainaController.<span style="color: #0000ff;">class</span><span style="color: #000000;">)) {
                        </span><span style="color: #008000;">//</span><span style="color: #008000;">获取当前类上标注的注解,获取到之后,我们可以获取该注解中配置的路径</span>
                        HainaController annotation = (HainaController) clazz.getAnnotation(HainaController.<span style="color: #0000ff;">class</span><span style="color: #000000;">);
                        </span><span style="color: #008000;">//</span><span style="color: #008000;">路径都用/开头,所以在获取的时候,我们把斜杠过滤掉</span>
                        String value = annotation.value().substring(1<span style="color: #000000;">);
                        </span><span style="color: #008000;">//</span><span style="color: #008000;">这样,路径以及它对应的类就都有了,我们可以把路径作为key,类的对象作为value,存入map中
                        </span><span style="color: #008000;">//</span><span style="color: #008000;">方便之后我们使用url来找对应的类和方法</span>

controllerMap.put(value, clazz.newInstance());
}
}
}
}
}
catch (Exception e) {
e.printStackTrace();
}

}

</span><span style="color: #008000;">//</span><span style="color: #008000;">获取所有类名</span>
<span style="color: #0000ff;">public</span> List&lt;String&gt;<span style="color: #000000;"> getClassNameList(String packageName) {
    String packagePath </span>= packageName.replace(".", "/"<span style="color: #000000;">);
    </span><span style="color: #008000;">//</span><span style="color: #008000;">获取类加载器,通过类加载器去找文件路径</span>
    ClassLoader loader =<span style="color: #000000;"> Thread.currentThread().getContextClassLoader();
    URL url </span>= loader.getResource(packagePath);<span style="color: #008000;">//</span><span style="color: #008000;">获取文件的路径</span>
    List&lt;String&gt; result = <span style="color: #0000ff;">new</span> ArrayList&lt;&gt;<span style="color: #000000;">();
    </span><span style="color: #0000ff;">if</span> (url != <span style="color: #0000ff;">null</span><span style="color: #000000;">) {
        File file </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> File(url.getPath());
        </span><span style="color: #008000;">//</span><span style="color: #008000;">获取当前包下的所有文件</span>
        File[] files =<span style="color: #000000;"> file.listFiles();
        </span><span style="color: #0000ff;">for</span><span style="color: #000000;"> (File child : files) {
            </span><span style="color: #008000;">//</span><span style="color: #008000;">拼接完整的包名加类名</span>
            String className = packageName + "." +<span style="color: #000000;"> child.getName()
                    .replace(</span>".class", ""<span style="color: #000000;">);
            result.add(className);
        }
    }
    </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> result;
}

</span><span style="color: #008000;">//</span><span style="color: #008000;">初始化url和方法的映射关系</span>
<span style="color: #0000ff;">private</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> initRequestMapping() {
    </span><span style="color: #008000;">//</span><span style="color: #008000;">遍历controllerMap,获取里面的所有key</span>
    <span style="color: #0000ff;">for</span><span style="color: #000000;"> (String str : controllerMap.keySet()) {
        </span><span style="color: #008000;">//</span><span style="color: #008000;">通过key,取出对应的值,然后调用getclass方法,把对象转换成class类型
        </span><span style="color: #008000;">//</span><span style="color: #008000;">因为只有class类型的对象,才可以获取该类里的所有方法</span>
        Class&lt;?&gt; clazz =<span style="color: #000000;"> controllerMap.get(str).getClass();

        Method[] methods </span>=<span style="color: #000000;"> clazz.getMethods();
        </span><span style="color: #008000;">//</span><span style="color: #008000;">遍历每个方法,看看方法有没有被requestmapping注解标注</span>
        <span style="color: #0000ff;">for</span><span style="color: #000000;"> (Method method : methods) {
            </span><span style="color: #0000ff;">if</span> (method.isAnnotationPresent(HainaRequestMapping.<span style="color: #0000ff;">class</span><span style="color: #000000;">)) {
                </span><span style="color: #008000;">//</span><span style="color: #008000;">获取当前方法的requestMapping注解对象</span>
                HainaRequestMapping annotation = method.getAnnotation(HainaRequestMapping.<span style="color: #0000ff;">class</span><span style="color: #000000;">);</span><span style="color: #000000;">
                String value </span>= annotation.value().substring(1<span style="color: #000000;">);
                </span><span style="color: #008000;">//</span><span style="color: #008000;">把url和方法对象,添加到map中</span>

requestMappingMap.put(value, method);
}
}
}

}
}

 springmvc本质上就是通过一个servlet类来接收所有的请求,并且将这些请求分发到指定注解的controller类中,再调用controller类中指定注解的方法。

 

5.springmvc.xml文件的配置

<?xml version="1.0" encoding="UTF-8" ?>
<beans>
    <component-scan base-package="com.haina.springmvc"></component-scan>
&lt;bean <span style="color: #0000ff;">class</span>="com.haina.springmvc.HainaViewResolver"&gt;
    &lt;property name="prefix" value="/"&gt;&lt;/property&gt;
    &lt;property name="suffix" value=".jsp"&gt;&lt;/property&gt;
&lt;/bean&gt;

</beans>

 

6.web.xml文件的配置

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
&lt;servlet&gt;
    &lt;servlet-name&gt;myDispatcher&lt;/servlet-name&gt;
    &lt;servlet-<span style="color: #0000ff;">class</span>&gt;com.haina.springmvc.HainaDispatcherServlet&lt;/servlet-<span style="color: #0000ff;">class</span>&gt;
    &lt;init-param&gt;
        &lt;param-name&gt;contextConfigLocation&lt;/param-name&gt;
        &lt;param-value&gt;springmvc.xml&lt;/param-value&gt;
    &lt;/init-param&gt;
&lt;/servlet&gt;
&lt;servlet-mapping&gt;
    &lt;servlet-name&gt;myDispatcher&lt;/servlet-name&gt;
    &lt;url-pattern&gt;/&lt;/url-pattern&gt;
&lt;/servlet-mapping&gt;

</web-app>

 

7.测试,创建testcontroller类进行测试

@HainaController("/haina")
public class TestController {
@HainaRequestMapping(</span>"/test"<span style="color: #000000;">)
</span><span style="color: #0000ff;">public</span><span style="color: #000000;"> String abc(){
    System.out.println(</span>"come in controller"<span style="color: #000000;">);
    </span><span style="color: #0000ff;">return</span> "test"<span style="color: #000000;">;
}

}

测试结果:

技术分享图片

 成功访问controller类,并且调用了abc方法

如何自己构建一个类似springmvc的框架(springmvc原理理解)

原文:https://www.cnblogs.com/TidalCoast1034/p/14539767.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!