天天看點

從javaweb入門到spring+mvc架構(二)——環境配置先用後學:搭建spring+mvc環境及配置解析

先用後學:搭建spring+mvc環境及配置解析

javaweb跟架構的不同

通常學習java的路線是,先學基礎的類型、集合架構、泛型、異常、io流、多線程等,然後是javaee部分的servlet、jdbc、xml、filter和listener等。

在javaweb架構中,伺服器接收到http請求參數後封裝為到request中,然後從伺服器經過過濾器一直傳遞進來到servlet中,我的每個請求都對應一個servlet,通過重寫doGet或者doPost方法處理,同時每個servlet都需要在web.xml中配置servlet或者使用@WebServlet注解。

其實一個servlet類就能處理全部内容,比如:

@WebServlet({ "/login" })
public class LoginServlet extends HttpServlet {
    //--1--
    public void doGet(HttpServletRequest request, HttpServletResponse response) {
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        //--2--
        if("".equals(username) || "".equals(password)){
            return;
        }
        //--3--
        try {
            Class.forName("com.mysql.jdbc.Driver");
            Connection connection = DriverManager.getConnection("url", "root", "******");

            String psql = "select username,password,age from user where username='" + username + "'";
            PreparedStatement prepareStatement = connection
                    .prepareStatement("INSERT into user(username,PASSWORD) VALUES('你好','aaaa')");
            prepareStatement.executeUpdate();

            if (connection != null)
                connection.close();
        } catch (SQLException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
           

為了排除代碼的臃腫,後來使用三層架構,把servlet業務按類型分開,像下面這樣:

//web層
@WebServlet({ "/login" })
public class LoginServlet extends HttpServlet {

    LoginService loginService = new LoginService();

    public void doGet(HttpServletRequest request, HttpServletResponse response) {
        String username = request.getParameter("username");

        String password = request.getParameter("password");

        loginService.login(username, password);
    }
}
//service層
public class LoginService {

    LoginDao loginDao = new LoginDao();

    public void login(String username, String password) {
        if("".equals(username) || "".equals(password)){
            return;
        } else {
            loginDao.findUser(username, password);
        }
    }
}
//dao層
public class LoginDao {

    public void findUser(String username, String password) {
        try {
            Class.forName("com.mysql.jdbc.Driver");
            Connection connection = DriverManager.getConnection(
                    "url", "root", "******");

            String psql = "select username,password,age from user where username='" + username + "'";
            PreparedStatement prepareStatement = connection.prepareStatement("INSERT into user(username,PASSWORD) VALUES('你好','aaaa')");
            prepareStatement.executeUpdate();

            if (connection != null)
                connection.close();

        } catch (SQLException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
           

所謂的三層架構(web、service、dao)其實并沒有真正解耦合,因為他們之間隻是通過分為三種class,對應分别處理自己的特定内容,web層處理請求轉發,service層業務邏輯都放這,dao層資料庫邏輯都放這。但是這個三層架構是非常好的思想,跟架構的思想雖然不一樣但是目标是一緻的。

架構,在web基礎上封裝和內建,通過實作底層接口繼承底層基礎類,最後形成自己的一套api架構。

從javaweb到架構,其實是對原本底層基礎的代碼做了更多的封裝了,好比之前是面向具體的類和方法來程式設計,現在就等于面對架構的元件來開發了,很多代碼已經被寫好,使用也有針對特定功能。

這裡也沒有提及架構原理相關知識。

建立springmvc+spring+自帶jdbc,完成相關配置

建立web project,結構如下:

從javaweb入門到spring+mvc架構(二)——環境配置先用後學:搭建spring+mvc環境及配置解析

src/main/java是放源代碼的源檔案夾,src/main/resources是放資源的源檔案夾,也可以改為src/java和src/resources。

這個結構可以去屬性頁面修改,把項目的編碼統一為UTF-8,jdk為1.x,對應的module為2.5或者3.0或者3.1:

從javaweb入門到spring+mvc架構(二)——環境配置先用後學:搭建spring+mvc環境及配置解析

(區分source folder、package、folder的差別:source folder隻會在eclipse中顯示目錄結構,比如src/main/java/com/lwr/shop釋出之後就隻有com/lwr/shop目錄結構,作為source folder的src/main/java是不帶釋出的,同時com/lwr/shop目錄結構所釋出到的位置也是上面那樣指定的,一般是釋出到classes目錄下,變成classes/com/lwr/shop,是以最後變成了“根目錄/WEB-INF/classes/com/lwr/shop“,是以所有放在source folder下的内容都會按其目錄結構釋出到classes,在配置檔案路徑時候特别注意要按照釋出之後的檔案路徑來配置,”“表示從根目錄/開始,”classpath:”表示從classes開始)

上面結構的project,一旦釋出到伺服器,其目錄結構變為以下所示,這就是伺服器上的目錄:

從javaweb入門到spring+mvc架構(二)——環境配置先用後學:搭建spring+mvc環境及配置解析

因為我們指定了源檔案夾src/main/java和src/main/resources的輸出目錄都是WEB-INF/classes,同時WebRoot下的檔案預設輸出到app釋出目錄的根目錄下。是以,寫在WEB-INF下的web.xml會釋出到WEB-INF下,而寫在src/main/java和src/main/resources目錄下的所有檔案原樣釋出到classes下。

然後添加jar包(純淨簡潔版):

從javaweb入門到spring+mvc架構(二)——環境配置先用後學:搭建spring+mvc環境及配置解析

spring核心包:

core包(依賴common-logging)

beans包(依賴core包)

expression包(依賴core包)

aop包(依賴beans,core包)

context包(依賴core,beans,expression,aop包)

web包(依賴core,beans,context,aop包)

springmvc核心包:

webmvc包:(依賴core,beans,expression,aop,context,web包)

事務管理核心包:

tx包

資料庫核心包:

jdbc包(依賴tx包)

web.xml配置

web.xml内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring/applicationContext.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring/springmvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <filter>
        <filter-name>characterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>characterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
        <welcome-file>index.htm</welcome-file>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>

</web-app>
           

web.xml配置解析:

1、注意

<web-app >

中屬性值的配置,複制即可,除了版本号其他一樣的。

2、上面所配置的監聽器ContextLoaderListener是用來引導spring容器初始化工作的,它是個實作類,與spring架構中的很多類有着錯綜複雜的關系,applicationContext.xml檔案的加載和applicationContext.xml中的具體配置就是在這種複雜的關系調用中被初始化的。

3、在xml檔案下直接寫的

<context-param></context-param>

一般用來配置applicationContext.xml路徑,ContextLoaderListener起作用之後的一系列動作,就包括從web.xml中擷取到這個applicationContext.xml路徑,上面路徑

classpath:spring/applicationContext.xml

表示applicationContext.xml在伺服器的classes/spring/下,對應到web project的相應位置。當然路徑可以改。如果不寫

<context-param></context-param>

配置,則預設在web.xml同目錄下尋找這個applicationContext.xml(預設名稱)。

4、配置org.springframework.web.servlet.DispatcherServlet,servlet-name自定義,

<load-on-startup>1</load-on-startup>

表示spring容器啟動後首先加載(按正數從小到大順序加載)。注意到這裡的配置參數

<init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring/springmvc.xml</param-value>
        </init-param>
           

也有contextConfigLocation這個,它是配置DispatcherServlet初始化的配置檔案位置contextConfigLocation的,預設值的路徑是跟web.xml同目錄,配置檔案名是

<servlet-name>-servlet.xml

,比如這裡的預設檔案名是dispatcherServlet-servlet.xml。如果寫了,就按照自定義的路徑查找springmvc配置檔案。

5、org.springframework.web.filter.CharacterEncodingFilter顧名思義就是字元編碼過濾,原理請搜尋網絡。

6、web.xml配置完後,架構就會加載其中的applicationContext.xml和dispatcherServlet-servlet.xml,找到對應路徑,建立xml檔案,接下來是這兩個配置檔案。

dispatcherServlet-servlet.xmll配置

dispatcherServlet-servlet.xml附帶注釋部分如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd   
       http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd  
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">

    <!--檢測注解 -->
    <!-- springmvc-servlet配置需要排除Service注解,因為springmvc的注解執行個體會先于spring的注解執行個體被spring容器使用,但是springmvc的執行個體無法用于事務,一旦spring使用這裡的注解執行個體,會導緻事務管理失效 -->
    <context:component-scan base-package="cn.lwr.shop"
        use-default-filters="false">
        <context:include-filter type="annotation"
            expression="org.springframework.stereotype.Controller" />
    </context:component-scan>

    <!-- 啟動注解驅動的Spring MVC功能 -->
    <mvc:annotation-driven>
        <!-- 這裡配置需要的編碼轉換器Converter -->
        <mvc:message-converters>
            <!-- 配置上Jackjson的依賴jar,使用MappingJacksonHttpMessageConverter -->
            <bean
                class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" />
            <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                <constructor-arg value="UTF-8" index="0" />
                <property name="supportedMediaTypes">
                    <list>
                        <value>text/plain;charset=utf-8</value>
                        <value>text/html;charset=UTF-8</value>
                        <value>application/json;charset=UTF-8</value>
                    </list>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

    <!-- 對根目錄下靜态資源檔案的通路,如果是WEB-INF下資源,需要用resource -->
    <mvc:default-servlet-handler />

    <!-- 配置視圖解析器 如何把handler 方法傳回值解析為實際的實體視圖 -->
    <bean
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>
</beans>
           

dispatcherServlet-servlet.xml配置解析和注意地方:

這部分主要配置1、注解的package掃描,2、注解驅動啟動,3、對靜态資源的通路,4、視圖解析器

開頭導入xsd檔案是對xml标簽格式的定義檔案,需要用到哪些包下的類就導入哪些xsd。

1、在springmvc中掃package,如果@Controller注解都是放在同一個包中,可以直接掃描這個包。或者像上面那樣掃描全部,然後用

context:include-filter

過濾@Controller注解。springmvc架構的注解注入隻需要Controller控制器即可,其他的注入留給spring來做,請看上面注釋。

2、注解驅動啟動直接用

<mvc:annotation-driven />

,如果像上面帶有參數就按開閉标簽寫,上面參數是配置編碼類型轉換器。引用網絡上的内容,其實

<mvc:annotation-driven />

幫我們做了這些事情:注冊以下HandlerMapping(處理器映射器),HandlerAdapter(處理器擴充卡),Resolver。這樣就不用手動配置控制器了。

RequestMappingHandlerMapping 

BeanNameUrlHandlerMapping

RequestMappingHandlerAdapter

HttpRequestHandlerAdapter

SimpleControllerHandlerAdapter

ExceptionHandlerExceptionResolver 

ResponseStatusExceptionResolver 

DefaultHandlerExceptionResolver 
           

大概原理是這樣:一開始是通過配置好那個通路路徑對應哪個控制器的,後來改為配置注解類,通過注解來配置路徑。最後用一個mvc注解驅動注冊所有注解類。

  • 在xml中配置路徑,springmvc容器會通過讀取配置檔案,mapping路徑和類及方法。
  • 在xml中配置注解,springmvc容器通過反射調用

    getAnnocation()

    等方法擷取到類和方法上的注解,然後執行該類型注解對應的内容,最後把類、方法mapping路徑。

    3、

    <mvc:default-servlet-handler />

    使得請求能夠通路app根目錄下除了WEB-INF的檔案内容。如果通路路徑在WEB-INF中,使用

    <mvc:resources location="src路徑" mapping="url"/>

    4、在控制器中傳回的是相對路徑,視圖解析器就是為這個相對路徑加上字首和字尾。

applicationContext.xml配置

内容如下:

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
                        http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
                        http://www.springframework.org/schema/context 
                        http://www.springframework.org/schema/context/spring-context-4.3.xsd 
                        http://www.springframework.org/schema/aop 
                        http://www.springframework.org/schema/aop/spring-aop-4.3.xsd   
                        http://www.springframework.org/schema/tx 
                        http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">

    <!-- @Required, @Autowired, @PostConstruct, @PreDestroy, @Resource, @PersistenceContext 
        and @PersistenceUnit 等注解需要配置相應的bean才可以使用,但是context:annotation-config就能自動注冊它們 -->
    <context:annotation-config />

    <!-- applicationContext是spring的配置檔案,裡面配置的注解對springmvc可見,可以被其使用 -->
    <!-- 作為父容器的spring容器 不需要controller注解,這個注解的執行個體應該由springmvc提供并使用 -->
    <!-- context:component-scan配置能夠使@Component, @Repository, @Service, @Controller, 
        @RestController, @ControllerAdvice, and @Configuration 等原型注入spring bean。 
        同時context:annotation-config配置的功能也被啟用,無需再配置。 -->
    <context:component-scan base-package="cn.lwr.shop">
        <context:exclude-filter type="annotation"
            expression="org.springframework.stereotype.Controller" />
    </context:component-scan>

    <!-- 配置讀取外部配置檔案 -->
    <context:property-placeholder location="classpath:spring/jdbc.properties" />

    <!-- spring自帶資料源配置 destroy-method="close" -->
    <!-- 第一種配置寫法 -->
    <bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource"
        p:driverClassName="${jdbc.driverClassName}" p:url="${jdbc.url}"
        p:username="${jdbc.username}" p:password="${jdbc.password}" />
    <!-- 第二種寫法 -->
    <bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName">
            <value>${jdbc.driverClassName}</value>
        </property>
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>

    <!-- 此行語句使得resource autowired 等四個注解可以使用 執行個體化jdbcTemplate,同時注入資料源 -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"
        p:dataSource-ref="dataSource" />

    <!-- 配置事務管理器 -->
    <bean id="txManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <!-- 事務掃描開始(開啟@Tranctional) -->
    <tx:annotation-driven transaction-manager="txManager" />

</beans>
           

applicationContext.xml配置解析:

關于spring容器的配置都是在這個xml檔案中。

注意的點:

1、

<context:component-scan

掃描包要把controller注解排除。

2、用了

<context:component-scan

,就已經包括

<context:annotation-config />

的所有功能,注解能用了。

3、配置properties檔案,擷取${…}的值,使用

<context:property-placeholder

來引入properties檔案

4、${…}好像是EL語言,spring-expression.jar就是使用這個語言的基礎。

5、這裡配置的bean都會被spring容器生成執行個體,而bean的屬性ref=”bean_id”表示在在這個執行個體裡對于id為bean_id的bean可以直接引用,容器來注入執行個體,等同注解@autowried。

6、配置檔案中的所有配置在容器中都是有根據的,也是有規律的,可以從原理和源碼來分析。