先用後學:搭建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,結構如下:
src/main/java是放源代碼的源檔案夾,src/main/resources是放資源的源檔案夾,也可以改為src/java和src/resources。
這個結構可以去屬性頁面修改,把項目的編碼統一為UTF-8,jdk為1.x,對應的module為2.5或者3.0或者3.1:
(區分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,一旦釋出到伺服器,其目錄結構變為以下所示,這就是伺服器上的目錄:
因為我們指定了源檔案夾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包(純淨簡潔版):
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、
使得請求能夠通路app根目錄下除了WEB-INF的檔案内容。如果通路路徑在WEB-INF中,使用<mvc:default-servlet-handler />
4、在控制器中傳回的是相對路徑,視圖解析器就是為這個相對路徑加上字首和字尾。<mvc:resources location="src路徑" mapping="url"/>
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、配置檔案中的所有配置在容器中都是有根據的,也是有規律的,可以從原理和源碼來分析。