栏目分类:
子分类:
返回
终身学习网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
终身学习网 > IT > 软件开发 > 后端开发 > Java

《看透SpringMVC源代码分析与实践》网站基础知识、俯视SpringMVC、SpringMVC组件分析

Java 更新时间:发布时间: 百科书网 趣学号
目录
  • Java中Socket的用法
  • 详解Servlet
  • Tomcat的分析
  • 俯视SpringMVC

一、Java中Socket的用法 1、普通Socket的用法

Java中的网络通信是由Socket实现的,Socket分为

  • ServerSocket:服务于服务端,可以通过accept方法监听请求,监听到请求后返回Socket
  • Socket:完成数据传输,客户端直接使用Socket发起请求并传输数据

ServerSocket的使用分为三步

  • 创建ServerSocket,含有绑定端口的构造参数
  • acept监听:阻塞监听,收到请求返回Socket
  • 客户端接收到Socker进行通信

举栗

2、NioSocket的用法

从JDK1.4开始,Java新增的新的io模式,nio(new IO)

  • 底层采用的新的处理方式,提高了IO的效率
  • 提供的ServerSicketChannel和SocketChannel对应传统的IO,支持非阻塞,ServerSicketChannel通过configBlocking方法设置是否采用阻塞模式,非阻塞模式才能使用Selector
  • 涉及三个概念:Buffer缓冲、Channel通道、Selector分发,工作流程就是先建立通道,然后注册分发,接下来就可以使用分发处理请求了

服务端流程

Buffer类

  • java.nio包的一个类专门用于存储数据,有4个属性非常重要

HTTP协议是在应用层解析内容的,只需要按照它的报文的格式封装和解析数据就可以了,具体传输还是使用Socket

二、 详解Servlet

Servlet 是 Server + Applet的缩写,表示一个服务器应用,简单理解Servlet就是一套规范,我们按照鞋套规范写代码就可以直接在Java服务器上面运行

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package javax.servlet;

import java.io.IOException;

public interface Servlet {

	// 方法在容器启动时被容器调用,只会调用一次,也可以使用web.xml文件中的配置init的时机
	// 被调用时接受 ServletConfig  参数,是容器传进去的(Tomcat)
	// 在web.xml配置的信息,如SpringMVC的contextConfigLocation信息
    void init(ServletConfig var1) throws ServletException;

	// 用于获取ServletConfig
    ServletConfig getServletConfig();
	// 具体处理一个请求
    void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
	// 获取Servlet相关的一些信息,版权、作者等,这个方法需要自己实现,默认返回空字符串
    String getServletInfo();

	// 服务器关闭,Servlet销毁时释放一些资源,也只会调用一次
    void destroy();
}

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package javax.servlet;

import java.util.Enumeration;

public interface ServletConfig {
    String getServletName();

    ServletContext getServletContext();

    String getInitParameter(String var1);

    Enumeration getInitParameterNames();
}


Tomcat和Servlet的关系

Tomcat的org.apache.catalina.core#StandardWrapper类初调用Servlet的init方法

 private synchronized void initServlet(Servlet servlet) throws ServletException {
        if (!this.instanceInitialized || this.singleThreadModel) {
            try {
                if (Globals.IS_SECURITY_ENABLED) {
                    boolean success = false;

                    try {
                        Object[] args = new Object[]{this.facade};
                        SecurityUtil.doAsPrivilege("init", servlet, classType, args);
                        success = true;
                    } finally {
                        if (!success) {
                            SecurityUtil.remove(servlet);
                        }

                    }
                } else {
                // facade 就是 StandardWrapperFacade 
                // StandardWrapperFacade implements ServletConfig
                    servlet.init(this.facade);
                }

                this.instanceInitialized = true;
            } catch (UnavailableException var10) {
                this.unavailable(var10);
                throw var10;
            } catch (ServletException var11) {
                throw var11;
            } catch (Throwable var12) {
                ExceptionUtils.handleThrowable(var12);
                this.getServletContext().log(sm.getString("standardWrapper.initException", new Object[]{this.getName()}), var12);
                throw new ServletException(sm.getString("standardWrapper.initException", new Object[]{this.getName()}), var12);
            }
        }
    }

需要将web.xml的配置文件在调用init方法的时候,封装成 StandardWrapperFacade 传过去

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.apache.catalina.core;

import java.util.Enumeration;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;

public final class StandardWrapperFacade implements ServletConfig {
    private final ServletConfig config;
    private ServletContext context = null;

    public StandardWrapperFacade(StandardWrapper config) {
        this.config = config;
    }

    public String getServletName() {
        return this.config.getServletName();
    }

    public ServletContext getServletContext() {
        if (this.context == null) {
            this.context = this.config.getServletContext();
            if (this.context instanceof ApplicationContext) {
                this.context = ((ApplicationContext)this.context).getFacade();
            }
        }

        return this.context;
    }

    public String getInitParameter(String name) {
        return this.config.getInitParameter(name);
    }

    public Enumeration getInitParameterNames() {
        return this.config.getInitParameterNames();
    }
}

1、GenericServlet和HttpServlet

GenericServlet是Servlet的默认实现,主要做了三件事

  • 实现了ServletConfig接口,我们可以直接调用ServletConfig里面的方法,变为自己的成员变量config了,因此无需getServletconfig().getServletContext(),直接getServletContext
  • 提供了无参的init方法,还是ServletConfig变为自己的成员变量,调用自己的无参init方法,方便之后子类实现的时候,重写init之后,不用关心ServletConfig
  • 提供了log方法

HttpServlet是用HTTP协议实现的Servlet的基类,写Servlet直接继承它就可以了,不需要从头实现Servlet接口

  • SpringMVC的DispatchServlet就是继承了HttpServlet
  • HttpServlet主要重写了setvice方法处理请求,在service方法首先将ServletRequest、ServletResponse转为HttpServletRequest、HttpServletResponse,然后根据请求方式的不通,路由到不同的处理方法
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        HttpServletRequest request;
        HttpServletResponse response;
        try {
            request = (HttpServletRequest)req;
            response = (HttpServletResponse)res;
        } catch (ClassCastException var6) {
            throw new ServletException(lStrings.getString("http.non_http"));
        }
		// 重载方法
        this.service(request, response);
    }
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String method = req.getMethod();
        long lastModified;
        if (method.equals("GET")) {
            lastModified = this.getLastModified(req);
            if (lastModified == -1L) {
                this.doGet(req, resp);
            } else {
                long ifModifiedSince;
                try {
                    ifModifiedSince = req.getDateHeader("If-Modified-Since");
                } catch (IllegalArgumentException var9) {
                    ifModifiedSince = -1L;
                }

                if (ifModifiedSince < lastModified / 1000L * 1000L) {
                    this.maybeSetLastModified(resp, lastModified);
                    this.doGet(req, resp);
                } else {
                    resp.setStatus(304);
                }
            }
        } else if (method.equals("HEAD")) {
            lastModified = this.getLastModified(req);
            this.maybeSetLastModified(resp, lastModified);
            this.doHead(req, resp);
        } else if (method.equals("POST")) {
            this.doPost(req, resp);
        } else if (method.equals("PUT")) {
            this.doPut(req, resp);
        } else if (method.equals("DELETe")) {
            this.doDelete(req, resp);
        } else if (method.equals("OPTIONS")) {
            this.doOptions(req, resp);
        } else if (method.equals("TRACE")) {
            this.doTrace(req, resp);
        } else {
            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[]{method};
            errMsg = MessageFormat.format(errMsg, errArgs);
            resp.sendError(501, errMsg);
        }

    }

需要注意的是SpringMVC的处理思路并不是这样的,又将所有的请求合并到一个tonguide方法进行处理,后续详细介绍

三、 Tomcat的分析 1、Tomcat的顶层结构

org.apache.catalina.startup#Catalina类主要进行的是对Server 进行生命周期处理,通过反射调用server对应的方法,自己的方法

  • start()
  • load():加载配置文件,创建初始化Server,加载Tomcat的conf/server.xml文件,调用通过反射调用server的init()方法,进而调用Servlet接口实现类HttpServletBean的init()方法
  • stop()
  • await()
 public void load() {
        if (!this.loaded) {
            this.loaded = true;
            long t1 = System.nanoTime();
            this.initDirs();
            this.initNaming();
            ConfigFileLoader.setSource(new CatalinabaseConfigurationSource(Bootstrap.getCatalinabaseFile(), this.getConfigFile()));
            File file = this.configFile();
            Digester digester = this.createStartDigester();

            try {
                Resource resource = ConfigFileLoader.getSource().getServerXml();
                Throwable var6 = null;

                try {
                    InputStream inputStream = resource.getInputStream();
                    InputSource inputSource = new InputSource(resource.getURI().toURL().toString());
                    inputSource.setByteStream(inputStream);
                    digester.push(this);
                    digester.parse(inputSource);
                } catch (Throwable var18) {
                    var6 = var18;
                    throw var18;
                } finally {
                    if (resource != null) {
                        if (var6 != null) {
                            try {
                                resource.close();
                            } catch (Throwable var17) {
                                var6.addSuppressed(var17);
                            }
                        } else {
                            resource.close();
                        }
                    }

                }
            } catch (Exception var20) {
                log.warn(sm.getString("catalina.configFail", new Object[]{file.getAbsolutePath()}), var20);
                if (file.exists() && !file.canRead()) {
                    log.warn(sm.getString("catalina.incorrectPermissions"));
                }

                return;
            }

            this.getServer().setCatalina(this);
            this.getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
            this.getServer().setCatalinabase(Bootstrap.getCatalinabaseFile());
            this.initStreams();

            try {
                this.getServer().init();
            } catch (LifecycleException var21) {
                if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
                    throw new Error(var21);
                }

                log.error(sm.getString("catalina.initError"), var21);
            }

            long t2 = System.nanoTime();
            if (log.isInfoEnabled()) {
                log.info(sm.getString("catalina.init", new Object[]{(t2 - t1) / 1000000L}));
            }

        }
    }

    public void load(String[] args) {
        try {
            if (this.arguments(args)) {
                this.load();
            }
        } catch (Exception var3) {
            var3.printStackTrace(System.out);
        }

    }

    public void start() {
        if (this.getServer() == null) {
            this.load();
        }

        if (this.getServer() == null) {
            log.fatal(sm.getString("catalina.noServer"));
        } else {
            long t1 = System.nanoTime();

            try {
                this.getServer().start();
            } catch (LifecycleException var7) {
                log.fatal(sm.getString("catalina.serverStartFail"), var7);

                try {
                    this.getServer().destroy();
                } catch (LifecycleException var6) {
                    log.debug("destroy() failed for failed Server ", var6);
                }

                return;
            }

            long t2 = System.nanoTime();
            if (log.isInfoEnabled()) {
                log.info(sm.getString("catalina.startup", new Object[]{(t2 - t1) / 1000000L}));
            }

            if (this.useShutdownHook) {
                if (this.shutdownHook == null) {
                    this.shutdownHook = new Catalina.CatalinaShutdownHook();
                }

                Runtime.getRuntime().addShutdownHook(this.shutdownHook);
                LogManager logManager = LogManager.getLogManager();
                if (logManager instanceof ClassLoaderLogManager) {
                    ((ClassLoaderLogManager)logManager).setUseShutdownHook(false);
                }
            }

            if (this.await) {
                this.await();
                this.stop();
            }

        }
    }

    public void stop() {
        try {
            if (this.useShutdownHook) {
                Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
                LogManager logManager = LogManager.getLogManager();
                if (logManager instanceof ClassLoaderLogManager) {
                    ((ClassLoaderLogManager)logManager).setUseShutdownHook(true);
                }
            }
        } catch (Throwable var3) {
            ExceptionUtils.handleThrowable(var3);
        }

        try {
            Server s = this.getServer();
            LifecycleState state = s.getState();
            if (LifecycleState.STOPPING_PREP.compareTo(state) > 0 || LifecycleState.DESTROYED.compareTo(state) < 0) {
                s.stop();
                s.destroy();
            }
        } catch (LifecycleException var4) {
            log.error(sm.getString("catalina.stopError"), var4);
        }

    }

需要注意的是org.apache.catalina.startup#Bootstrap类才是Tomcat的入口,也就是main方法所在,类似CatalinaAdapter,这样做的好处可以把启动入口和具体的管理类分开,从而可以很方便的创建出多种启动方式,每种启动方式只需写一个对应的CatalinaAdapter即可

public static void main(String[] args) {
        synchronized(daemonLock) {
            if (daemon == null) {
            	// 新建一个BootStrap
                Bootstrap bootstrap = new Bootstrap();

                try {
                	// 初始化了ClassLoader,并用ClassLoder创建了Catalina实例,赋值给catalianDaemon变量
                    bootstrap.init();
                } catch (Throwable var5) {
                    handleThrowable(var5);
                    var5.printStackTrace();
                    return;
                }

                daemon = bootstrap;
            } else {
                Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
            }
        }

        try {
        	// 处理main方法传入的参数,为空执行satrt
            String command = "start";
            if (args.length > 0) {
                command = args[args.length - 1];
            }

            if (command.equals("startd")) {
                args[args.length - 1] = "start";
                daemon.load(args);
                daemon.start();
            } else if (command.equals("stopd")) {
                args[args.length - 1] = "stop";
                daemon.stop();
            } else if (command.equals("start")) {
                daemon.setAwait(true);
                // 这里调用Servlet的init()方法
                daemon.load(args);
                daemon.start();
                if (null == daemon.getServer()) {
                    System.exit(1);
                }
            } else if (command.equals("stop")) {
                daemon.stopServer(args);
            } else if (command.equals("configtest")) {
                daemon.load(args);
                if (null == daemon.getServer()) {
                    System.exit(1);
                }

                System.exit(0);
            } else {
                log.warn("Bootstrap: command "" + command + "" does not exist.");
            }
        } catch (Throwable var7) {
            Throwable t = var7;
            if (var7 instanceof InvocationTargetException && var7.getCause() != null) {
                t = var7.getCause();
            }

            handleThrowable(t);
            t.printStackTrace();
            System.exit(1);
        }

    }



通过启动类加载器,通过反射创建 catalinaDaemon 实例,之后daemon.load(args);daemon.start();

// BootStrap Tomcat入口类的init()方法,主要是反射创建 catalinaDaemon  实例,调用load(),start()
public void init() throws Exception {
        this.initClassLoaders();
        Thread.currentThread().setContextClassLoader(this.catalinaLoader);
        SecurityClassLoad.securityClassLoad(this.catalinaLoader);
        if (log.isDebugEnabled()) {
            log.debug("Loading startup class");
        }

        Class startupClass = this.catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
        Object startupInstance = startupClass.getConstructor().newInstance();
        if (log.isDebugEnabled()) {
            log.debug("Setting startup class properties");
        }

        String methodName = "setParentClassLoader";
        Class[] paramTypes = new Class[]{Class.forName("java.lang.ClassLoader")};
        Object[] paramValues = new Object[]{this.sharedLoader};
        Method method = startupInstance.getClass().getMethod(methodName, paramTypes);
        method.invoke(startupInstance, paramValues);
        this.catalinaDaemon = startupInstance;
    }
public void start() throws Exception {
		// 无实例 先init()
        if (this.catalinaDaemon == null) {
            this.init();
        }

        Method method = this.catalinaDaemon.getClass().getMethod("start", (Class[])null);
        method.invoke(this.catalinaDaemon, (Object[])null);
    }
2、Server启动过程

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.apache.catalina;

import java.io.File;
import java.util.concurrent.ScheduledExecutorService;
import javax.naming.Context;
import org.apache.catalina.deploy.NamingResourcesImpl;
import org.apache.catalina.startup.Catalina;

public interface Server extends Lifecycle {
    NamingResourcesImpl getGlobalNamingResources();

    void setGlobalNamingResources(NamingResourcesImpl var1);

    Context getGlobalNamingContext();

    int getPort();

    void setPort(int var1);

    int getPortOffset();

    void setPortOffset(int var1);

    int getPortWithOffset();

    String getAddress();

    void setAddress(String var1);

    String getShutdown();

    void setShutdown(String var1);

    ClassLoader getParentClassLoader();

    void setParentClassLoader(ClassLoader var1);

    Catalina getCatalina();

    void setCatalina(Catalina var1);

    File getCatalinabase();

    void setCatalinabase(File var1);

    File getCatalinaHome();

    void setCatalinaHome(File var1);

    int getUtilityThreads();

    void setUtilityThreads(int var1);

    void addService(Service var1);

    void await();

    Service findService(String var1);

    Service[] findServices();

    void removeService(Service var1);

    Object getNamingToken();

    ScheduledExecutorService getUtilityExecutor();
}


protected void initInternal() throws LifecycleException {
        super.initInternal();
        this.reconfigureUtilityExecutor(getUtilityThreadsInternal(this.utilityThreads));
        this.register(this.utilityExecutor, "type=UtilityExecutor");
        this.onameStringCache = this.register(new StringCache(), "type=StringCache");
        MBeanFactory factory = new MBeanFactory();
        factory.setContainer(this);
        this.onameMBeanFactory = this.register(factory, "type=MBeanFactory");
        this.globalNamingResources.init();
        if (this.getCatalina() != null) {
            for(ClassLoader cl = this.getCatalina().getParentClassLoader(); cl != null && cl != ClassLoader.getSystemClassLoader(); cl = cl.getParent()) {
                if (cl instanceof URLClassLoader) {
                    URL[] urls = ((URLClassLoader)cl).getURLs();
                    URL[] var4 = urls;
                    int var5 = urls.length;

                    for(int var6 = 0; var6 < var5; ++var6) {
                        URL url = var4[var6];
                        if (url.getProtocol().equals("file")) {
                            try {
                                File f = new File(url.toURI());
                                if (f.isFile() && f.getName().endsWith(".jar")) {
                                    ExtensionValidator.addSystemResource(f);
                                }
                            } catch (URISyntaxException var9) {
                            } catch (IOException var10) {
                            }
                        }
                    }
                }
            }
        }

        for(int i = 0; i < this.services.length; ++i) {
            this.services[i].init();
        }

    }
 protected void startInternal() throws LifecycleException {
        this.fireLifecycleEvent("configure_start", (Object)null);
        this.setState(LifecycleState.STARTING);
        this.globalNamingResources.start();
        synchronized(this.servicesLock) {
            int i = 0;

            while(true) {
                if (i >= this.services.length) {
                    break;
                }

                this.services[i].start();
                ++i;
            }
        }

        if (this.periodicEventDelay > 0) {
            this.monitorFuture = this.getUtilityExecutor().scheduleWithFixedDelay(new Runnable() {
                public void run() {
                    StandardServer.this.startPeriodicLifecycleEvent();
                }
            }, 0L, 60L, TimeUnit.SECONDS);
        }

    }

更多Tomcat知识此处省略…

四、俯视SpringMVC 1、使用

使用步骤

  • 导依赖在web.xml中配置Servlet
  • 创建.xml配置文件
  • 创建controller和view

2、整体结构

  • XxxAware类:简单理解用处就是某个类想要使用spring的一些东西,需要实现XxxAware告诉spring,spring看到之后就会传送过来,接受唯一方式是重写XxxAware接口的ser-Xxx方法

    public interface ApplicationContextAware extends Aware {
        void setApplicationContext(ApplicationContext var1) throws BeansException;
    }
    public interface EnvironmentAware extends Aware {
        void setEnvironment(Environment var1);
    }
    // 可以自己写一个Cobtroller然后实现 EnvironmentAware 重写setEnvironment方法,直接向spring通过方法参数的方式要数据
    
    

  • ApplicationContext:类似前面的ServletContext

  • Environment:环境,实际在HttpServletBean的Environment使用的是Standard-Servlet-Environment,封装了ServletContext、ServletConfig、JndiProperty、系统环境变量和系统属性,这些封装到了HttpServletBean的propertySources属性下

1、Envirment

org.springframework.web.context.support#StandardServletEnvironment 类

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.web.context.support;


public class StandardServletEnvironment extends StandardEnvironment implements ConfigurableWebEnvironment {
	// web.xml中的servletContext相关key
    public static final String SERVLET_CONTEXT_PROPERTY_SOURCE_NAME = "servletContextInitParams";
    // web.xml中的servletConfig相关key
    public static final String SERVLET_CONFIG_PROPERTY_SOURCE_NAME = "servletConfigInitParams";
    public static final String JNDI_PROPERTY_SOURCE_NAME = "jndiProperties";

    public StandardServletEnvironment() {
    }

    protected void customizePropertySources(MutablePropertySources propertySources) {
        propertySources.addLast(new StubPropertySource("servletConfigInitParams"));
        propertySources.addLast(new StubPropertySource("servletContextInitParams"));
        if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
            propertySources.addLast(new JndiPropertySource("jndiProperties"));
        }
		// 调用父类StandardEnvironment的方法,将配置传给父类
        super.customizePropertySources(propertySources);
    }

    public void initPropertySources(@Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {
        WebApplicationContextUtils.initServletPropertySources(this.getPropertySources(), servletContext, servletConfig);
    }
}

2、HttpServletBean

org.springframework.web.servlet#HttpServletBean类

Servlet的创建过程

  • 服务器(Tomcat)启动start()之后,Servlet创建时调用子类HttpServletBean实现的init()方法
  • 调用 createEnvironment() 获取Environment,init()内部需要获取配置环境配置,通过实现Environmetaware接口,获得容器中的的servlet配置信息Environment,转为ConfigurableEnvironment类型保存到environment 成员变量
  • 接着调用init()方法
  • init()内部需要获取上下文配置:通过调用ServletContext的getServletContext方法
  • init()内部需要获取环境配置:直接拿到成员变量environment
  • init()内部需要初始化DispatchServlet:调用模板方法initbaseWrapper(BeanWrapper),具体实现在子类,BeanWrapper就是DispatchServlet,然后将 environment 设置到DispatchServlet
  • 最后 调用模板方法this.initServletBean();即子类实现的方法
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.web.servlet;



public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware {
    protected final Log logger = LogFactory.getLog(this.getClass());
    @Nullable
    private ConfigurableEnvironment environment;
    private final Set requiredProperties = new HashSet(4);

    public HttpServletBean() {
    }

    protected final void addRequiredProperty(String property) {
        this.requiredProperties.add(property);
    }

	// 直接从sring容器活的ServletConfig
    public void setEnvironment(Environment environment) {
        Assert.isInstanceOf(ConfigurableEnvironment.class, environment, "ConfigurableEnvironment required");
        // 转为ConfigurableEnvironment类型
        this.environment = (ConfigurableEnvironment)environment;
    }

    public ConfigurableEnvironment getEnvironment() {
        if (this.environment == null) {
            this.environment = this.createEnvironment();
        }

        return this.environment;
    }

    protected ConfigurableEnvironment createEnvironment() {
    	// 使用 StandardServletEnvironment 作为配置文件的载体
        return new StandardServletEnvironment();
    }
	
	// 初始化方法,
    public final void init() throws ServletException {
    	// 封装ServletConfig
        PropertyValues pvs = new HttpServletBean.ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties);
        if (!pvs.isEmpty()) {
            try {
                BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
                ResourceLoader resourceLoader = new ServletContextResourceLoader(this.getServletContext());
                bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.getEnvironment()));
                // 初始化DispatchServlet
                this.initBeanWrapper(bw);
                // 设置配置信息,其实就是environment
                bw.setPropertyValues(pvs, true);
            } catch (BeansException var4) {
                if (this.logger.isErrorEnabled()) {
                    this.logger.error("Failed to set bean properties on servlet '" + this.getServletName() + "'", var4);
                }

                throw var4;
            }
        }

		// 模板方法,调用子类方法
        this.initServletBean();
    }

 
 	// 子类模板,也就是调用子类
    protected void initServletBean() throws ServletException {
    }

    @Nullable
    public String getServletName() {
        return this.getServletConfig() != null ? this.getServletConfig().getServletName() : null;
    }


	// 静态内部类,为了解析ServletConfig的配置信息
    private static class ServletConfigPropertyValues extends MutablePropertyValues {
        public ServletConfigPropertyValues(ServletConfig config, Set requiredProperties) throws ServletException {
			// 是否含必要的配置,不含为null,下面抛异常
            Set missingProps = !CollectionUtils.isEmpty(requiredProperties) ? new HashSet(requiredProperties) : null;
			// 配置项key
            Enumeration paramNames = config.getInitParameterNames();
			// 获取配置项value,调用addPropertyValue(),实际执行this.requiredProperties.add(property);
            while(paramNames.hasMoreElements()) {
                String property = (String)paramNames.nextElement();
                Object value = config.getInitParameter(property);
                this.addPropertyValue(new PropertyValue(property, value));
                // 检查全部必须项
                if (missingProps != null) {
                    missingProps.remove(property);
                }
            }

            if (!CollectionUtils.isEmpty(missingProps)) {
                throw new ServletException("Initialization from ServletConfig for servlet '" + config.getServletName() + "' failed; the following required properties were missing: " + StringUtils.collectionToDelimitedString(missingProps, ", "));
            }
        }
    }
}

// HttpServletBean需要 Envirment类转为ConfigurableEnvironment
public interface ConfigurableEnvironment extends Environment, ConfigurablePropertyResolver {
    void setActiveProfiles(String... var1);

    void addActiveProfile(String var1);

    void setDefaultProfiles(String... var1);

    MutablePropertySources getPropertySources();

    Map getSystemProperties();

    Map getSystemEnvironment();

    void merge(ConfigurableEnvironment var1);
}

可以看到在HttpServletBean的init中,首先将Servlet的配置参数使用BeanWrapper设置到DispatchServlet的相关属性,然后调用模板方法initServletNean子类就是通过这个方法初始化

3、frameworkServlet

由HttpServletBean得知,frameworkServlet的初始化入口方法应该是initServletBean,由父类HttpServletBean调用,主要作用是初始化 WebApplicationContext

 protected final void initServletBean() throws ServletException {
        this.getServletContext().log("Initializing Spring " + this.getClass().getSimpleName() + " '" + this.getServletName() + "'");
        if (this.logger.isInfoEnabled()) {
            this.logger.info("Initializing Servlet '" + this.getServletName() + "'");
        }

        long startTime = System.currentTimeMillis();

        try {
			// 1. 初始化 initWebApplicationContext
            this.webApplicationContext = this.initWebApplicationContext();
			// 初始化 initframeworkServlet,模板方法,可由子类重写
            this.initframeworkServlet();
        } catch (RuntimeException | ServletException var4) {
            this.logger.error("Context initialization failed", var4);
            throw var4;
        }

        if (this.logger.isDebugEnabled()) {
            String value = this.enableLoggingRequestDetails ? "shown which may lead to unsafe logging of potentially sensitive data" : "masked to prevent unsafe logging of potentially sensitive data";
            this.logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails + "': request parameters and headers will be " + value);
        }

        if (this.logger.isInfoEnabled()) {
            this.logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
        }

    }

initWebApplicationContext()方法,做了三件事

  • 获取spring的根容器rootContext:默认情况下spring会将自己的容器设置成ServletContext的属性,默认根容器的key是 String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
  • 设置webApplicationContext并根据情况调用onRefresh方法:
  • 将webApplicationContext设置到ServletContext中
protected WebApplicationContext initWebApplicationContext() {
        WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
        WebApplicationContext wac = null;
        // 如果已经通过构造方法设置 webApplicationContext 
        if (this.webApplicationContext != null) {
            wac = this.webApplicationContext;
            if (wac instanceof ConfigurableWebApplicationContext) {
                ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)wac;
                if (!cwac.isActive()) {
                    if (cwac.getParent() == null) {
                        cwac.setParent(rootContext);
                    }

                    this.configureAndRefreshWebApplicationContext(cwac);
                }
            }
        }

        if (wac == null) {
        	 // 当webApplicationContext 已经存在ServletContext之后,通过Servlet的contextAttribute参数获取
            wac = this.findWebApplicationContext();
        }

        if (wac == null) {
        	// 上一步没获取到,也就是还没创建,此处创建
            wac = this.createWebApplicationContext(rootContext);
        }

        if (!this.refreshEventReceived) {
        // 当COntextRefreshEvent事件没有被触发时调用,模板方法,子类可重写
            synchronized(this.onRefreshMonitor) {
                this.onRefresh(wac);
            }
        }

        if (this.publishContext) {
        // WebApplicationContext设置到ServletContext
            String attrName = this.getServletContextAttributeName();
            this.getServletContext().setAttribute(attrName, wac);
        }

        return wac;
    }
4、设置WebApplicationContext的方法

第一种

  • 在构造方法中已经传递webApplicationContext参数,这时候只要进行一些设置即可
  • 这种方法主要用在Servlet3.0之后再程序中使用Servlet.addServlet方式注册Servlet
  • 这个时候就可以在新建frameworkServlet和其子类的时候通过构造方法传递已经创建好的webApplicationContext

第二种

  • webApplicationContext已经存在ServletContext
  • 这时候只需配置Servlet时候将ServletContext的webApplicationContext的name配置到contextAttribute属性就可以了

第三种

  • 前两种方式都无效的情况下自己创建一个。正常情况就是使用这种方式createWebApplicationContext
  • 然后configureAndRefreshWebApplicationContext添加监听器,SourceFilteringListener此监听器可以指定参数,实际监听的是ContextRefreshListener,而他又是frameworkServlet的内部类,监听ContextRefreshedEvent 事件
  • 当接到ContextRefreshedEvent 事件调用frameworkServlet的onApplicationEvent方法,在onApplicationEvent会在调用一次onRefresh()方法,并将refreshEventReceived标志设置为true,对应着initWebApplicationContext方法的if判断
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {

		// 获取创建类型
        Class contextClass = this.getContextClass();
        // 检查创建类型
        if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
            throw new ApplicationContextException("Fatal initialization error in servlet with name '" + this.getServletName() + "': custom WebApplicationContext class [" + contextClass.getName() + "] is not of type ConfigurableWebApplicationContext");
        } else {
			// 具体创建
            ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);
            wac.setEnvironment(this.getEnvironment());
            wac.setParent(parent);
            // 将设置的contextConfigLocation参数传给wac,默认WEB-INFO/[ServletName]-Servlet.xml
            String configLocation = this.getContextConfigLocation();
            if (configLocation != null) {
                wac.setConfigLocation(configLocation);
            }
			
			// 给wac添加监听器
            this.configureAndRefreshWebApplicationContext(wac);
            return wac;
        }
    }

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
        if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
            if (this.contextId != null) {
                wac.setId(this.contextId);
            } else {
                wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(this.getServletContext().getContextPath()) + '/' + this.getServletName());
            }
        }

        wac.setServletContext(this.getServletContext());
        wac.setServletConfig(this.getServletConfig());
        wac.setNamespace(this.getNamespace());
        // 添加监听器
        // 实际监听的是 ContextRefreshListener 所监听的事件
        wac.addApplicationListener(new SourceFilteringListener(wac, new frameworkServlet.ContextRefreshListener()));
        ConfigurableEnvironment env = wac.getEnvironment();
        if (env instanceof ConfigurableWebEnvironment) {
            ((ConfigurableWebEnvironment)env).initPropertySources(this.getServletContext(), this.getServletConfig());
        }

        this.postProcessWebApplicationContext(wac);
        this.applyInitializers(wac);
        wac.refresh();
    }

frameworkServlet的内部类,监听ContextRefreshedEvent 事件

 private class ContextRefreshListener implements ApplicationListener {
        private ContextRefreshListener() {
        }

        public void onApplicationEvent(ContextRefreshedEvent event) {
            frameworkServlet.this.onApplicationEvent(event);
        }
    }
5、将WebApplicationContext设置到ServletContext中
frameworkServlet的initWebApplicationContext方法
 if (this.publishContext) {
            String attrName = this.getServletContextAttributeName();
            this.getServletContext().setAttribute(attrName, wac);
        }

最后会根据 publishContext 标志判断是都将创建出来的webApplicationContext设置到ServletContext中

  • publishContext可以在配置Servlet的web.xml文件中通过init-param参数进行设置
  • HttpServletBean初始化时将会设置到publishContext参数

6、DispatchServlet

onRefresh方法是DispatcherServlet的入口方法,调用了initStrategies,其中有9个初始化方法

	@Override
	protected void onRefresh(ApplicationContext context) {
		initStrategies(context);
	}

	
	 // 9 个组件初始化
	protected void initStrategies(ApplicationContext context) {
		initMultipartResolver(context);
		initLocaleResolver(context);
		initThemeResolver(context);
		initHandlerMappings(context);
		initHandlerAdapters(context);
		initHandlerExceptionResolvers(context);
		initRequestToViewNameTranslator(context);
		initViewResolvers(context);
		initFlashMapManager(context);
	}
具体的初始化过程

以LocalResolver为例,组件初始化分两步

  • 首先通过context.getBean在容器获取(根据注册名称或者类型,所以在SpringMVC的配置文件中只需要配置想应组件,容器就能找到,找不到就是用默认的getDefaultStrategy)。需要注意的是这里的context是frameworkContext创建的WebApplicationContext,而不是ServletContext
	private void initLocaleResolver(ApplicationContext context) {
		try {
			// 在context中获取
			this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);
			if (logger.isTraceEnabled()) {
				logger.trace("Detected " + this.localeResolver);
			}
			else if (logger.isDebugEnabled()) {
				logger.debug("Detected " + this.localeResolver.getClass().getSimpleName());
			}
		}
		catch (NoSuchBeanDefinitionException ex) {
			// We need to use the default.
			// 使用默认策略
			this.localeResolver = getDefaultStrategy(context, LocaleResolver.class);
			if (logger.isTraceEnabled()) {
				logger.trace("No LocaleResolver '" + LOCALE_RESOLVER_BEAN_NAME +
						"': using default [" + this.localeResolver.getClass().getSimpleName() + "]");
			}
		}
	}

getDefaultStrategy获取默认组件策略,因为HandleMapping的组件可能有多个,需要返回List,对于其他的去list.get(0)即可

**
	 * Return the default strategy object for the given strategy interface.
	 * 

The default implementation delegates to {@link #getDefaultStrategies}, * expecting a single object in the list. * @param context the current WebApplicationContext * @param strategyInterface the strategy interface * @return the corresponding strategy object * @see #getDefaultStrategies */ protected T getDefaultStrategy(ApplicationContext context, Class strategyInterface) { // 调用 getDefaultStrategies 获取所有默认策略 List strategies = getDefaultStrategies(context, strategyInterface); if (strategies.size() != 1) { throw new BeanInitializationException( "DispatcherServlet needs exactly 1 strategy for interface [" + strategyInterface.getName() + "]"); } // 因为HandleMapping的组件可能有多个,需要返回List,对于其他的去list.get(0)即可 return strategies.get(0); } @SuppressWarnings("unchecked") protected List getDefaultStrategies(ApplicationContext context, Class strategyInterface) { String key = strategyInterface.getName(); // 从 defaultStrategies 获取所需策略的类型 String value = defaultStrategies.getProperty(key); if (value != null) { // 策略有多个,以逗号分割数组 String[] classNames = StringUtils.commaDelimitedListToStringArray(value); List strategies = new ArrayList<>(classNames.length); for (String className : classNames) { try { // 按获取到的类型初始化策略 Class clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader()); Object strategy = createDefaultStrategy(context, clazz); strategies.add((T) strategy); } catch (ClassNotFoundException ex) { throw new BeanInitializationException( "Could not find DispatcherServlet's default strategy class [" + className + "] for interface [" + key + "]", ex); } catch (linkageError err) { throw new BeanInitializationException( "Unresolvable class definition for DispatcherServlet's default strategy class [" + className + "] for interface [" + key + "]", err); } } return strategies; } else { return new linkedList<>(); } }

核心代码Class clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());

  • 参数className来自classNames
  • classNames来自value
  • value来自defaultStrategies.getPrpperty(key)
  • private static final Properties defaultStrategies; 是静态成员变量,通过静态代码快复制
static {
		// Load default strategy implementations from properties file.
		// This is currently strictly internal and not meant to be customized
		// by application developers.
		try {
			// 也就是DispatcherServlet.properties特定键值对
			ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
			defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
		}
		catch (IOException ex) {
			throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
		}
	}

可以看到一共8各组件,对于处理上传组件MultipartResolver无默认配置

  • 默认配置并不是最优配置,有的已经被弃用
  • 使用之后,并不会全部使用默认配置,因为它配置;额HandlerMapping、HandlerAdapter、Handler-ExceptionResolver并且还做了很多别的工作

小结

转载请注明:文章转载自 www.051e.com
本文地址:http://www.051e.com/it/282032.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 ©2023-2025 051e.com

ICP备案号:京ICP备12030808号