springmvc运行机制

     介绍Springmvc的文章很多,这里不对其具体功能模块进行介绍,而是由宏观理解上介绍其如何运行,包括配置文件如何加载,请求如何被处理等方面。

    这里先简要介绍下面两个概念,了解了这些概念,有助于理解Springmvc的运行机制。

1、Java注解

    首先我们需要了解,为什么会有注解,它主要有以下几种作用:

  • 生成文档
  • 跟踪代码依赖性,实现替代配置文件功能
  • 在编译时进行格式检查

    这里主要介绍第二种作用,spring2.5以后就采用了基于注解的配置,这有利于减少配置文件的数量。下面是一个简单的自定义的注解类。

package AnnotationTest;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Documented
@Target(value={ElementType.METHOD,ElementType.TYPE})
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface TestA{
   String author() default "Pankaj";
   String date();
   int revision() default 1;
   String comments();
}

    其中@Target说明了Annotation所修饰的对象范围,METHOD表示该注解可用于方法,TYPE表示可用于类。@Retention定义了该Annotation被保留的时间长短,RUNTIME表示该注解只能在运行时起作用。@Documented用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。

    对该注释使用如下:

package AnnotationTest;
import java.lang.reflect.ParameterizedType;
 
@TestA(author = "class", comments = "class", date = "2017-9-12", revision = 1)
public class useTestA {
 
    private Class entity;
    public useTestA() {
        test();
    } 
    @SuppressWarnings("unchecked")
    public void test(){
        entity = (Class)((ParameterizedType)this.getClass().getGenericSuperclass())  
                .getActualTypeArguments()[0];  
        System.out.println(entity);
    }
    @Override
    @TestA(author = "Pankaj", comments = "Main method", date = "Nov 17 2012", revision = 1)
    public String toString() {
        return "Overriden toString method";
    }
 
}

2、Java反射

    首先提一下Java文件的运行过程:.java -> 编译 ->.class -> JVM解释 -> 计算机执行。

    然后再说说为什么需要Java反射。

    比如我们的Java文件只有一行代码:new MyTest();运行程序后,JVM启动并对该文件进行解释,new完对象后,程序结束,JVM停止运行。在整个过程中,只new了一个对象,因此在执行过程中,只能而且必须new一个对象。但是,在如下场景中:一个运行中的服务需要根据需求,动态判断需要mysql数据库还是需要oracle数据库。这样,如果我们我们new了两个对象,那么肯定有一个对象资源浪费掉,因此我们需要根据实时需求,动态的判断需要哪个对象。反射可以在程序运行过程中,动态地加载类,类似以上这种在程序运行前不确定需要加载哪些类的程序,可以通过反射在程序运行中加载。

    可以通过反射获取类的具体信息,如包含的方法,对象等

package AnnotationTest;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

public class parsing {

	  public static void main(String[] args) {
                try {
               // Method[] mm = Class.forName("AnnotationTest.useTestA").getMethods();
                    for (Method method : parsing.class
	                    .getClassLoader()
	                    .loadClass(("AnnotationTest.useTestA"))
	                    .getMethods()) {
	                // checks if MethodInfo annotation is present for the method
	                if (method.isAnnotationPresent(AnnotationTest.TestA.class)) {
	                    try {
	                        // iterates all the annotations available in the method
	                        for (Annotation anno : method.getDeclaredAnnotations()) {
	                            System.out.println("Annotation in Method '"
	                                    + method + "' : " + anno);
	                        }
	                        TestA methodAnno = method.getAnnotation(TestA.class);
	                        if (methodAnno.revision() == 1) {
	                            System.out.println("Method with revision no 1 = "
	                                    + method);
	                        }
	 
	                    } catch (Throwable ex) {
	                        ex.printStackTrace();
	                    }
	                }
	            }
	        } catch (SecurityException | ClassNotFoundException e) {
	            e.printStackTrace();
	        }
	    }
}

    这样,通过反射,我们就可以获取到注解所标识的类或者方法。

   

    了解了上述概念之后,接下来,我们开始说说Springmvc是怎么运行的。

    java web服务通过servlet处理来自http的请求,因此启动一个web服务,需要启动servlet进程。web服务的web.xml配置文件保证了servlet可以被启动并处理请求。Springmvc中的DispatcherServlet类继承了FrameworkServlet,因此,当使用了Springmvc的web服务被启动后,DispatcherServlet类也就被加载了。DispatcherServlet类起到动态代理的作用。

     我们知道,在Springmvc中一般是通过在变量前加@Resource之类的注解来定义一个资源变量,这样做是为了在程序运行时注入对象,优点是使用这个资源变量时可以不通过定义而直接使用(因为在这之前就已经加载了这个类,下文解释),节省了对象创建销毁的开销,而且这样拿到的资源变量是单例模式的。单利模式因为节省了实例的创建和销毁,有利于提高性能,而ThreadLocal可以在单例模式下处理并发请求,保证了线程的安全性。

    上面主要介绍了web程序的启动以及资源变量的定义,接下来说说Springmvc是如何初始化web服务并注入对象的。

    首先,Springmvc会通过spring提供的接口加载xml配置文件。spring读取xml文件的方式有3中,下面我们介绍其中的一种:ClassPathXmlApplicationContext。

    ClassPathXmlApplicationContext的入参是xml文件名,通过这个接口,可以找到xml文件中的所有bean,并实例化,之后可以通过遍历工程类中的注解来反射得到需要依赖注入的对象。如用@Autowired修改的变量,spring会将加载到的bean类赋值给它。这样,在web服务启动过程中,就完成了对所有资源变量的赋值。

    然后是web服务controller的初始化。在DispatcherServlet加载的过程中,会出现很多使用map来实现的handler,这些handler用于对请求进行处理并返回结果。其中有两个最主要的handler:mappingHandler和adapterHandler。前者用于判断接收到的请求应该被哪个controller处理,后者用于判断请求应该以什么样的方式被渲染然后返回。handler的工作原理也是注解,如有@RequestMapping注解的方法会被放在mappingHandler中,当请求路径与注解入参相匹配时,就会执行相应的方法。

    对上述过程总结如下:DispatcherServlet在被加载过程中,遍历所有被注解标注的变量,为其赋值。然后通过查找被标注的controller方法,将其放到mappinghandler中,保存了访问路径与controller方法的对应关系。当有http请求时,tomcat服务器找到对应的servlet服务,这里就是DispatcherServlet,然后DispatcherServlet通过请求路径查找mappinghandler,得到可以处理请求的controller方法。处理完请求后,根据注解查找adapterHandler得到对应的返回页面,完成一次http请求。

    



你可能感兴趣的:(springmvc运行机制)