純Java啓動Web(無配置web.xml)

純Java啓動Web(無配置web.xml)

前言

  突然奇想不用SpringBoot,並且不配置xml文件來啓動Web應用程序!

正文

  先貼出程序所需的Java代碼以及Pom,如下:

Pom.xml

<?xml version="1.0" encoding="UTF-8"?>

<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.0</modelVersion>
    <groupId>vip.wulang</groupId>
    <artifactId>web-test</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>

    <properties>
        <mysql.version>5.1.34</mysql.version>
        <spring.version>4.3.0.RELEASE</spring.version>
        <hibernate.version>5.1.0.Final</hibernate.version>
        <druid.version>1.1.10</druid.version>
        <jpa.version>1.10.4.RELEASE</jpa.version>
        <test.version>4.12</test.version>
        <common.version>3.3.2</common.version>
        <servlet.version>3.1.0</servlet.version>
        <fastjson.version>1.2.47</fastjson.version>
        <slf4j.version>1.7.25</slf4j.version>
        <aspectj.version>1.8.10</aspectj.version>
        <validation.version>1.1.0.Final</validation.version>
    </properties>

    <dependencies>
        <!-- Spring Framework start -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <!-- Spring Framework end -->

        <!-- Druid start -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>${druid.version}</version>
        </dependency>
        <!-- Druid end -->

        <!-- JPA start -->
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-jpa</artifactId>
            <version>${jpa.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-core</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-aop</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-beans</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-tx</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-orm</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-context</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- JPA end -->

        <!-- Hibernate start -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>${hibernate.version}</version>
        </dependency>

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>${hibernate.version}</version>
        </dependency>
        <!-- Hibernate end -->

        <!-- Mysql start -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
        </dependency>
        <!-- Mysql end -->

        <!-- Test start -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${test.version}</version>
            <scope>test</scope>
        </dependency>
        <!-- Test end -->

        <!-- Common start -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>${common.version}</version>
        </dependency>
        <!-- Common end -->

        <!-- Servlet start -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>${servlet.version}</version>
            <scope>provided</scope>
        </dependency>
        <!-- Servlet end -->

        <!-- FastJson start -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>${fastjson.version}</version>
        </dependency>
        <!-- FastJson end -->

        <!-- Slf4j start -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <!-- Slf4j end -->

        <!-- Aspectj start -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>${aspectj.version}</version>
        </dependency>
        <!-- Aspectj end -->

        <!-- Validation start -->
        <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
            <version>${validation.version}</version>
        </dependency>
        <!-- Validation end -->

    </dependencies>
</project>

WebAppInitializer.java

package vip.wulang.start;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import vip.wulang.config.RootConfig;
import vip.wulang.config.WebMVCConfig;

/**
 * @author CoolerWu on 2018/11/12.
 * @version 1.0
 */
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[]{RootConfig.class};
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[]{WebMVCConfig.class};
    }

    @Override
    protected String[] getServletMappings() {
        return new String [] {"/"};
    }

}

WebMVCConfig.java

package vip.wulang.config;

import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

import java.util.ArrayList;
import java.util.List;

/**
 * @author CoolerWu on 2018/11/12.
 * @version 1.0
 */
@Configuration
@EnableWebMvc // 啓動 Spring MVC
@ComponentScan("vip.wulang.controller") // 啓用組件掃描
public class WebMVCConfig extends WebMvcConfigurerAdapter {

    // 配置 JSP 視圖解析器
    @Bean
    public ViewResolver viewResolver(){
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/view/");
        resolver.setSuffix(".jsp");
        resolver.setExposeContextBeansAsAttributes(true);
        return resolver;
    }

    // 配置靜態資源的處理
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer){
        configurer.enable();
    }

    // 使用 alibaba 的 fastjson 來解析 Http 消息轉換
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();

        FastJsonConfig fastJsonConfig = new FastJsonConfig();
        fastJsonConfig.setSerializerFeatures(SerializerFeature.WriteMapNullValue);

        List<MediaType> fastMediaTypes = new ArrayList<>();
        fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);

        fastJsonHttpMessageConverter.setSupportedMediaTypes(fastMediaTypes);
        fastJsonHttpMessageConverter.setFastJsonConfig(fastJsonConfig);

        converters.add(fastJsonHttpMessageConverter);
    }
}

RootConfig.java

package vip.wulang.config;

import org.springframework.context.annotation.*;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

/**
 * @author CoolerWu on 2018/11/14.
 * @version 1.0
 */
@Configuration
@ComponentScan(
        basePackages = {"vip.wulang"},
        excludeFilters = {
            @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {EnableWebMvc.class})
        }
)
public class RootConfig {
}

  把項目配置到 Tomcat 服務器中,就能夠啓動了。就這麼幾行就可以啓動了呢。爲什麼?在Servlet 3.0環境中,容器會在類路徑中查找實現 javax.servlet.ServletContainerInitializer 接口的類,如果發現的話,就會用它來配置Servlet容器。ServletContainerInitializer.java Spring源碼如下:

public interface ServletContainerInitializer {
    public void onStartup(Set<Class<?>> c, ServletContext ctx)
        throws ServletException; 
}

  Spring提供了這個接口的實現,名爲 org.springframework.web.SpringServletContainerInitializer ,這個類反過來又會查找實現 org.springframework.web.WebApplicationInitializer 的類並將配置的任務交給它們來完成。SpringServletContainerInitializer.java Spring源碼如下:

@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
	@Override
	public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
			throws ServletException {

		List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>();

		if (webAppInitializerClasses != null) {
			for (Class<?> waiClass : webAppInitializerClasses) {
				// Be defensive: Some servlet containers provide us with invalid classes,
				// no matter what @HandlesTypes says...
				if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
						WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
					try {
						initializers.add((WebApplicationInitializer) waiClass.newInstance());
					}
					catch (Throwable ex) {
						throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
					}
				}
			}
		}

		if (initializers.isEmpty()) {
			servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
			return;
		}

		servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
		AnnotationAwareOrderComparator.sort(initializers);
		for (WebApplicationInitializer initializer : initializers) {
			initializer.onStartup(servletContext);
		}
	}

}

  其中@HandlesTypes註解表示 SpringServletContainerInitializer.java 可以處理的類,本文中就是 WebApplicationInitializer.java,在 onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) 方法中,可以通過參數 webAppInitializerClasses 獲取得到。

  Spring 3.2 引入了一個便利的 WebApplicationInitializer 基礎實現,也就是 org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer 。因爲我們的 WebAppInitializer 擴展了 AbstractAnnotationConfigDispatcherServletInitializer (同時也就實現了 WebApplicationInitializer ),因此當部署到 Servlet 3.0 容器中的時候,容器會自動發現它,並用它來配置Servlet上下文。

  在我們的實現類 WebAppInitializer 中,getServletMappings() ,它會將一個或者多個路徑映射到 org.springframework.web.servlet.DispatcherServlet 上。即在本文中,它映射的是"/",這表示它會是應用的默認Servlet。它會處理進入應用的所有請求。getServletConfigClasses(),將會用來配置 DispatcherServlet 來加載 SpringMVC 子容器,即在本文中,將會加載 WebMVCConfig.class 。一般在web.xml中,我們還會在配置一個 org.springframework.web.context.ContextLoaderListener 監聽器,用來啓動 Spring 父容器。實際上,org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer 會同時創建 DispatcherServlet 和 ContextLoaderListener。所以 getRootConfigClasses() 將會用來配置 ContextLoaderListener 通過來創建應用上下文,即在本文中,將會加載 RootConfig.class 。

  需要注意的是,如果按照這種方式配置,而不使用web.xml的話,那唯一問題在於它只能部署到支持 Servlet 3.0 的服務器中才能正常工作,如 Tomcat 7 或 更高版本。現在我們來看看 WebMVCConfig.java 和 RootConfig.java。

結束

  這樣就知道了爲什麼這樣可以啓動該項目了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章