spring理解與springMVC的使用
本文主要內容:
一:spring的配置準備和注入使用
二:spring的IOC/DI常用注入
三:spring中的AOP
四:springMVC的基本配置與使用
五:springMVC的檢視定位、使用註解、客戶端跳轉、接收表單資料、以及session的獲取。
六:springMVC的攔截器的使用、攔截器與控制器的比較
七:springMVC的一些配置問題深入解析
spring框架是一個很NB的框架,它的設計是基於IOC和AOP結構的J2EE系統框架,所謂IOC/DI就是也就是反轉控制,什麼意思呢,從功能上理解,原本在程式中需要某個類中的一些屬性和方法時,我們會去通過呼叫構造器new一個新物件出來,然後進行使用。現在這件事情交給spring去做了,我們通過spring對某個bean引入類或者對該類中的某些屬性注入值的方式,將該類物件new出來,在後臺程式中直接呼叫該bean即可直接使用已經注入值的物件。
那麼會有人有疑問,原本一個物件我在後臺直接new一下,就完事了,何必這麼麻煩spring來做呢?我原本也有類似的疑問,但是當專案的類和介面和實現類足夠多時,就會發現spring注入的方便和好處了,我們可以通過對action類中需要用的介面層層對它注入物件,在action中直接使用的方式,省去了很多繁瑣的步驟,而且對於自己的專案使用了哪些介面哪些實現類一目瞭然,很方便追蹤和管理。
一:spring的配置準備和注入使用
1. 要使用spring,則必然需要匯入該框架所需的jar包使用相關類,相關jar可以在官網下載獲取,將jar包匯入專案中
src下新建 spring的核心控制配置檔案:applicationContext.xml檔案,接下來我們通過spring給實體類屬性注入值,然後在action中直接獲取該值。
2. 在pojo包下新建category實體類,並設定name和id屬性,設定get/set方法。
3. applicationContext.xml檔案給name注入值type1
<?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:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <bean name="c" class="pojo.Category"> <property name="name" value="type1" /> </bean> </beans>
4. 準備test類,獲取該bean並輸入name值:
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(
new String[] { "applicationContext.xml" });
Category c = (Category) context.getBean("c");
System.out.println(c.getName());
}
}
此時輸出的值為type1。到這裡,獲取就已經大概明白了spring是用來做什麼的了,但是spring能做的還遠遠不止這些。
二:spring的常用注入
設想一個場景,我們在action中需要呼叫一些操作資料庫的方法,根據設計結構,我們分為了service層、service實現層和dao層、dao實現層。現在通過spring注入,在action中直接呼叫service介面,訪問dao的實現方法。
配置檔案層層注入:
<bean id="testIn" class="action.TestIn">
<property name="productService" ref="productService" />
</bean>
<bean id="productService" class="serviceImpl.ProductService">
<property name="productDao" ref="productDao" />
</bean>
<bean id="productDao" class="DaoImpl.ProductDao">
</bean>
action中:可直接呼叫dao實現層的add方法。
public class TestIn {
private ProductService productService;
public static void show() {
ApplicationContext context = new ClassPathXmlApplicationContext(
new String[] {"applicationContext.xml"});
TestIn ti = (TestIn) context.getBean("testIn");
ti.getProductService().show();
}
public static void main(String[] args) {
show();
}
public ProductService getProductService() {
return productService;
}
public void setProductService(ProductService productService) {
this.productService= productService;
}
}
列印輸出了ProductDaoImpl最終的show方法。
spring的IOC反轉控制(也叫DI)注入時,ref可以引用其他xml檔案中的bean,但是最好做一下區分,例如,如果引入本xml中的bean,則可以<ref local="">
如果引用其他xml中的bean,可以:<ref bean="">當然,直接<ref="">也是可以的,但是為了專案的可讀性,萬一存在相同名字的bean,豈不是悲劇了。
三:spring中的AOP
AOP即為Aspect Oriented Program意思為面向切面程式設計。何為面向切面程式設計,假如我們把處理主業務邏輯的後臺類作為一箇中心,那AOP就是針對這個中心作為切面來搞一些事情。也就是說針對主線任務,做一些副本任務和輔助業務。可以在action執行前,執行後做一些處理,可以是列印日誌、效能統計、異常收集等操作。把主線任務和副本任務分開單獨開發,最後通過spring組織在一起。這就是AOP。
1. 編寫主線任務:main方法中執行s的方法。
public class TestSpring {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(new String[] { "applicationContext.xml" });
ProductService s = (ProductService) context.getBean("s");
s.doSomeService();
}
}
public class ProductService {
public void doSomeService(){
System.out.println("main");
}
}
<bean name="s" class="com.how2java.service.ProductService">
</bean>
2. 現在編寫切面類副本任務,設定在該方法執行前和執行後分別做一些其他操作。
import org.aspectj.lang.ProceedingJoinPoint;
public class LoggerAspect {
public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("執行前 log:" + joinPoint.getSignature().getName());
Object object = joinPoint.proceed(); //執行主線任務
System.out.println("執行後 log:" + joinPoint.getSignature().getName());
return object;
}
}
3. 配置檔案宣告切面,將切面和主線纏綿在一起
<bean name="s" class="com.how2java.service.ProductService">
</bean>
<bean id="loggerAspect" class="com.how2java.aspect.LoggerAspect"/>
<aop:config>
<aop:pointcut id="loggerCutpoint"
expression=
"execution(* com.how2java.service.ProductService.*(..)) "/>
<aop:aspect id="logAspect" ref="loggerAspect">
<aop:around pointcut-ref="loggerCutpoint" method="log"/>
</aop:aspect>
</aop:config>
宣告主線任務bean:s,宣告副本任務bean:loggerAspect,通過aop:config標籤把他們編制在一起。
<aop:pointcut id="loggerCutpoint"
expression=
"execution(* com.how2java.service.ProductService.*(..)) "/>
此處聲明瞭在aop中執行那個核心業務,即執行哪個是主線任務,這裡的意思有三個任意:第一個*指任意返回型別,第二個*指任意方法,(..)指任意型別的引數。滿足該類下這些條件都將是主線任務。都會被執行切面操作。
<aop:aspect id="logAspect" ref="loggerAspect">
<aop:around pointcut-ref="loggerCutpoint" method="log"/>
</aop:aspect>
指定執行什麼切面,這裡指向了bean:loggerAspect。則將會把這個作為副本任務。
然後通過aop:config包起來。配置完成。
spring可以通過一些註解完成aop的配置和spring注入的配置
springMVC
springMVC簡單來講就是前臺頁面發起請求,交由springMVC攔截請求跳轉到指定的控制器中,在控制器中處理完業務邏輯之後,返回指定的頁面。
四:springMVC的基本配置與使用
1. 在web專案中匯入所需要的jar包,然後在WEB-INF下的web.xml檔案中配置一個dispatcherServlet類的servlet,並給它命名,匯入的該類是springMVC所用的核心類,是一個前置控制器,他負責攔截交給springMVC處理的所有請求。交給對應的配置檔案的bean處理。配置檔案如下:
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
設定servlet-name,該請求走到這一步時,伺服器會自動尋找WEB-INF下的springmvc-servlet.xml也就是{servlet-name}-servlet.xml檔案。所以此時就需要建立springMVC的配置檔案了。
注:該配置檔案可以系統自動去找,也可以自行配置,其實該檔案的尋找是根據DispatcherServlet這個servlet的初始化方法來決定的。預設會去WEBI-INF下找servlet-name}-servlet.xml檔案。如果需要自行設定,則只需將該servlet設定為預設自啟動(隨著伺服器的啟動而啟動),並設定初始化方法中的引數,告訴伺服器去哪裡尋找這個配置檔案。如下:
<servlet>
<servlet-name>springmcv</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:/springmvc-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
2. springMVC的配置檔案:WEB-INF下建立springmvc-servlet.xml
該檔案主要就是配置springMVC的bean,通過註冊獲取這些bean來完成對專案的控制,首先建立一個simpleUrlHandlerMapping的bean,用它來完成攔截特定的請求,並將該請求交給那個控制器來完成後臺處理。如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="simpleUrlHandlerMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/addProduct">proController</prop>
</props>
</property>
</bean>
<bean id="proController" class="controller.ProController"/>
</beans>
該配置檔案的意思為:請求客戶端發起,dispatcherServlet攔截該請求,交給springmvc-servlet.xml,該配置檔案註冊了該bean,該bean中配置了將會攔截/addProduct這個請求字尾,並對應了proController這個bean,
註冊了一個proController的bean,指向我們的控制器類中。則該請求就會跳轉至該控制器類中去了。
3. 控制器類:
springMVC能作為控制器的類的方式有兩種,一種是實現controller介面,一種是通過註解@controller的方式。
先介紹通過實現介面的方式
public class IndexController implements Controller {
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView mav = new ModelAndView("index.jsp");//跳轉至index.jsp
mav.addObject("message", "Hello Spring MVC");//放入message引數
return mav;返回該物件
}
}
該請求會訪問handleRequest這個方法,在該方法中,有一個modelAndView類,該類是springMVC中比較重要的類,(具體怎麼重要需要看框架的原始碼,我還沒看完-.-),通過該類完成跳轉,也可以在該類中新增類似map一樣的key,value物件。在前臺頁面可以直接EL表示式獲取並顯示。然後返回該物件。此時頁面會跳轉至index.jsp(專案的根目錄)中。在index.jsp中通過${message}可以顯示該引數中的值。至此,完成了springMVC跳轉並傳值的過程。
五:springMVC的檢視定位、使用註解、客戶端跳轉、接收表單資料、以及session的獲取。
1. springMVC的試圖定位
所謂檢視定位,也就是給前臺頁面制定跳轉路徑,上面的例子中,ModelAndView指定跳轉了預設根目錄下的index.jsp,那如果我們需要跳轉至WEB-INF/page/index.jsp怎麼設定呢?此時需要我們在springMVC的配置檔案中宣告跳轉時定位在哪個路徑下,則跳轉時控制器會自動加上該路徑,完成跳轉。配置檔案如下:此時就會把ModelAndView中設定的跳轉路徑加上該字首和字尾名。
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/page/"/>
<property name="suffix" value=".jsp"/>
</bean>
控制器中:修改為:ModelAndView mv = new ModelAndView("index");即可完成跳轉。此例知識介紹瞭如何將進行檢視定位,具體的專案使用中,當然不會如此單調的進行配置。
2. 使用註解
springMVC提供了強大的註解功能,也正是因為這些註解使用很方便和簡介,使springMVC很受歡迎,使用註解之後,我們就不需要每攔截一個請求都去設定一個bean了,直接在配置檔案中宣告,讓springMVC去掃描制定路徑下的包,掃描那些類被@controller註解過則被認為這是一個控制器,在指定方法前加@requestMapping("/index")註解,則index請求會執行該方法。配置檔案如下:
<!-- 告訴springMVC,掃描註解,掃描包controller下的所有註解,去完成請求。 -->
<context:component-scan base-package="controller" />
類檔案:指定的方法名和引數不是固定的,根據需求來確定引數,方法名任意。
@Controller
public class IndexController1 {
@RequestMapping("/index")
public ModelAndView handleRequest(HttpServletRequest req, HttpServletResponse res)
throws Exception {
ModelAndView mv = new ModelAndView("index");
mv.addObject("msg","Hello Spring MVC");
return mv;
}
}
3. 客戶端跳轉以及接收表單資料
springMVC的ModelAndView類跳轉是進行的伺服器端跳轉,那如何進行客戶端跳轉呢?很簡單,只需要在該類的構造器中宣告new ModelAndView("redirect:/index");即可。如下:
@RequestMapping("/jump")
public ModelAndView jump() {
ModelAndView mv = new ModelAndView("redirect:/index");
mv.addObject("msgg","hello jump");
return mv;
}
接收表單資料:jsp頁面中提交表單請求,在控制器中是如何接收資料的呢?建立add.jsp頁面,提交form表單:
<body>
<form action="addProduct" method="post">
商品名稱:<input name="name"/><br>
商品價格:<input name="price"/><br>
<input type="submit" value="提交">
</form>
</body>
在控制器中:
@Controller
public class ProductController {
private ModelAndView mv;
@RequestMapping("/addProduct")
public ModelAndView add(Product product) {
mv = new ModelAndView("showPro");
return mv;
}
}
注:jsp中提交資料時,name屬性中不需要寫product.name,直接提交name屬性名(需要和product類中的屬性名一模一樣),在進入到控制器時,springMVC會自動根據引數的型別,將提交的name和price屬性注入到給物件中,此時該方法中的product形參物件已經有了值,並且在該方法中,ModelAndView會自動新增product這個形參到該物件中,即自動完成了:mv.addObject("product",product);然後在showPro.jsp中可以使用${product.name}直接顯示該值了。
拓展:springMVC是如何進行引數繫結的呢?首先form表單提交時,在該方法中的形參,springMVC會呼叫框架提供的轉換器(converter)來將頁面中的引數繫結到方法的形參中,springMVC提供了很多轉換器,一般都能夠自動轉換完成,特殊的情況下需要自定義轉換器滿足業務需求。轉換器的引數繫結大致可分為:普通型別轉換(java Bean)、簡單型別轉換(前臺在引數中傳入id,方法中直接定義integer id形參的方式)、預設支援的型別(request、session等)、集合型別的繫結。具體繫結以及如何使用,還有如何自動以轉換器。請大家參考這位博主的部落格。講的很詳細:https://blog.csdn.net/qq_33530388/article/details/72784199
或者參考這位博主的文章,也很簡潔的介紹了引數繫結的使用:http://www.cnblogs.com/HD/p/4107674.html
4. session的獲取
對於session的作用,可以說它很強大,我們在專案開發的過程中,遇見難以解決的問題一般都會想到session。那在springMVC中如何獲取session呢?這裡設定一個例子,訪問一個地址,每訪問一次,就在該方法的session中+1,然後在jsp頁面中顯示session的值,
方法如下:訪問check,返回check.jsp,在jsp中使用${count}顯示session中的值。
@RequestMapping("/check")
public ModelAndView check(HttpSession session) {
Integer i = (Integer) session.getAttribute("count");
if (i == null)
i = 0;
i++;
session.setAttribute("count", i);
ModelAndView mav = new ModelAndView("check");
return mav;
}
上面介紹controller類中方法的引數繫結時,講到過預設支援的型別轉換,其中就包括了HttpSession型別,HttpSession物件在web程式中是一直存在的,springMVC不過是將該型別獲取,通過形參的方式給出到方法中進行使用。
六:springMVC的攔截器的使用、攔截器和控制器的比較
springMVC提供了非常強大的攔截器,基於AOP切面程式設計的設計思想,攔截器可以滲入到執行控制器的方法執行前後。協助專案完成一些輔助操作。springMVC的攔截器如何使用呢?它主要通過繼承handlerInterceptorAdaper父類完成攔截器的建立。
1. 新建一個攔截器類,攔截某個請求方法,並在該方法執行前、執行後返回view前、view後列印輸出一些記錄。註釋中詳細介紹了三個方法的執行功能和順序。仔細查閱。
public class IndexInterceptor extends HandlerInterceptorAdapter {
/**
* 可以進行安全設定,許可權設定,編碼設定等
*
*
* 此方法是在controller被訪問之前呼叫,web.xml將所有的請求交給springmvc-servlet.xml進行處理,該檔案會去尋找url訪問的請求所匹配的業務控制器
* 則此方法就是在控制器被訪問之前進行的攔截操作,例如可以用作驗證使用者是否登入操作。
* 如果返回true,則會按照順序執行下面的攔截器的preHandle方法,攔截器執行完之後,訪問controller業務控制器,然後再跳轉到檢視層也就是前臺頁面之前
* 逆序訪問攔截器的postHandle方法,然後再進入到檢視層,
* 在訪問完檢視層之後,最後逆序執行攔截器的afterCompletion方法。
* 結束該流程。
*
* 如果返回false,則代表次請求攔截器沒有通過,相當於過濾器的不放行,則不會訪問controller層,
* 則此時攔截器會直接逆序呼叫當前攔截器之前的所有攔截器的afterCompletion方法,攔截器相當於假裝已經請求響應完成了,工作完成了,執行afterCompletion完事之後回家吃飯了。
*/
public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler)
throws Exception{
System.out.println("preHandle()方法被執行,在訪問controller之前被呼叫。");
return true;
}
/**
* 此方法在controller之後,jsp之前,可以在modelAndView中add引數,帶到JSP中進行使用或者驗證。
* 可以有機會修改modelAndView物件。
*/
public void postHandle(HttpServletRequest req, HttpServletResponse res, Object handler, ModelAndView mv)
throws Exception{
System.out.println("執行了postHandle(),此時位置是:訪問完成了controller,在跳轉到jsp之前執行的方法.");
mv.addObject("nowDate",new Date());
}
/**
* 最後執行,可用於資源的釋放、日誌監控等功能
*/
public void afterCompletion(HttpServletRequest req, HttpServletResponse res, Object handler, Exception ex)
throws Exception{
System.out.println("執行了afterCompletion(),在檢視層jsp之後呼叫。");
}
}
2. 建立好了攔截器,需要告訴springMVC攔截哪個請求,以及該請求呼叫哪個攔截器,則需要在springmvc-servlet.xml配置檔案中:
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/index"/>
<!-- 定義在mvc:interceptor下面的表示是對特定的請求才進行攔截的 -->
<bean class="interceptor.IndexInterceptor"/> <!--攔截器類的路徑--!>
</mvc:interceptor>
</mvc:interceptors>
則當我們訪問/index時,攔截器是什麼時候執行的呢?此時這個請求經過控制器介面卡,交給handleMapping,handleMapping去尋找有沒有執行該請求的控制器,如果有則會執行配置好的攔截器進行攔截,進行攔截器的工作。如果沒有找到對應的控制器,則攔截器不會執行。
springMVC並沒有一個總的攔截器,不可以對所有請求進行攔截,它是屬於handleMapping級別的,可以設定多個intercept分別對不同的handleMapping進行攔截。可以通過實現handleInterceptor介面或者繼承handleInterceptorAdapter類來完成攔截器的建立。
合理合適的使用攔截器,能夠讓專案更好管理更加完善。
3. 攔截器、過濾器的比較:
首先,過濾器是什麼:過濾器本質上講他是基於java的反射機制。執行在servlet容器中的,只被初始化一次,只能用在servlet前後也就是隻能用在請求前後。
攔截器是什麼:攔截器是一種AOP的執行,執行在外掛上的,比如hibernate、struts、spring等,本質上講它是基於方法的回撥函式完成工作的,它不僅能攔截web的請求,servlet的請求,還可以攔截action請求,可以在某個方法執行前後進行攔截,使用更靈活。
兩者均可以對請求進行攔截,只不過他們的作用不同,攔截器更細化,過濾器更專注於統一原則。
七:springMVC的一些配置問題
1. 中文配置問題:
在springMVC架構下,經常會出現表單提交導致中文亂碼問題,解決這個問題很簡單,有一個一勞永逸的方法,就是在web.xml中配置一個過濾器,將所有的請求統一使用中文編碼即可:配置如下:
<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>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
2. 對於web.xml中dispatcherServlet攔截哪些請求的問題,
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
此時是將所有的不帶字尾名的請求進行過濾,提交給springMVC處理,
<url-pattern>*.do</url-pattern>這種方式是過濾所有以.do為字尾的請求。
如果直接設定為/的話,此時在專案中如果需要訪問.js/.css/.jpg等靜態資源時也會被過濾,交給springMVC處理該請求,可是springMVC中如果沒有訪問該靜態資源的設定,就會導致404,找不到資源。如果解決這種問題呢?
①將過濾請求修改為.do之類的具體字尾名,此時.js/.css/.jpg等請求不符合以.do結尾,dispatcherServlet不會進行過濾,則可以正常訪問靜態資源
②如果非要用/的方式,可以啟用servlet的預設servlet:defaultServlet,並設定在dispatcherServlet這個servlet之前,也就是說在.js/.css/.jpg這些請求進入到web.xml時,會先被預設servlet攔截,然後成功去訪問靜態資源,就不回進入到springMVC的工作中去了。
配置如下:需要訪問什麼靜態資源就設定什麼,本例需要訪問.jpg/.js這些字尾。
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.jpg</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.js</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
③如果以上方法都不想用,還有一種最繁瑣的方法,就是允許.jpg這些請求被dispatcherServlet攔截,進入到springMVC的工作中,我們在springMVC的配置中宣告對這些訪問的設定。也可以訪問到靜態資源。在springmvc-servlet.xml配置檔案中如下:
<mvc:resources mapping="/images/**" location="/images/" />
使用該配置需要宣告<mvc:annotation-driven />自動註冊兩個bean
指定靜態資源的位置,則請求會自動去尋找,具體使用方法不是很熟悉,這裡只介紹有這樣的方法行得通。