Spring實戰第七章

一、SpringMVC配置代替方案

1自定DispatcherServlet

按照AbstractAnnotationConfigDispatcherServletInitializer的定義,它會創建DispatcherServlet和ContextLoaderListener。

AbstractAnnotationConfigDispatcherServletInitializer有三個方法是必須要重載的abstract方法。但是實際上還有更多的方法可以進行重載,從而實現額外的配置。

此類的方法之一就是customizeRegistration()。在AbstractAnnotationConfigDispatcherServletInitializer將DispatcherServlet注冊到Servlet容器中之后,就會調用customizeRegistration(),并將Servlet注冊后得到的Registration.Dynamic傳遞進來。通過重載customizeRegistration()方法,我們可以對DispatcherServlet進行額外的配置。

2、添加其他的Servlet和Filter

如果你想注冊其他的Servlet、Filter或Listener的話,基于Java的初始化器(initializer)的一個好處就在于我們可以定義任意數量的初始化器類。因此,如果我們想往Web容器中注冊其他組件的話,只需創建一個新的初始化器就可以了。最簡單的方式就是實現Spring的WebApplicationInitializer接口

?

如何創建WebApplicationInitializer實現并注冊一個Servlet。

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration.Dynamic;import org.springframework.web.WebApplicationInitializer;import spittr.servlet.MyServlet;public class MySevletInitializer implements WebApplicationInitializer {@Overridepublic void onStartup(ServletContext servletContext) throws ServletException {Dynamic myServlet = servletContext.addServlet("myServlet", MyServlet.class);//注冊servletmyServlet.addMapping("/custom/**");  //添加映射Servlet
    }
}

?它注冊了一個Servlet并將其映射到一個路徑上。

?還可以創建新的WebApplicationInitializer實現來注冊Listener和Filter。

import javax.servlet.FilterRegistration.Dynamic;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;import org.springframework.web.WebApplicationInitializer;import spittr.servlet.MyFilter;public class MyFilterInitializer implements WebApplicationInitializer {@Overridepublic void onStartup(ServletContext servletContext) throws ServletException {Dynamic filter = servletContext.addFilter("MyFilter", MyFilter.class); 注冊filter.addMappingForUrlPatterns(null, false, "/custom/*");   添加Filter的映射路徑}}

?

?如果你只是注冊Filter,并且該Filter只會映射到DispatcherServlet上的話,那么在AbstractAnnotationConfigDispatcherServletInitializer中還有一種快捷方式。

為了注冊Filter并將其映射到DispatcherServlet,所需要做的僅僅是重載AbstractAnnotationConfigDispatcherServletInitializer的getServletFilters()方法。

    @Overrideprotected Filter[] getServletFilters() {
        return new Filter[]{new MyFilter()};}

?

?這個方法返回的是一個javax.servlet.Filter的數組。在這里它只返回了一個Filter,但它實際上可以返回任意數量的Filter。在這里沒有必要聲明它的映射路徑,getServletFilters()方法返回的所有Filter都會映射到DispatcherServlet上

3、在web.xml中聲明DispatcherServlet

在典型的Spring MVC應用中,我們會需要DispatcherServlet和ContextLoaderListener。AbstractAnnotationConfigDispatcherServletInitializer會自動注冊它們,但是如果需要在web.xml中注冊的話,那就需要我們自己來完成這項任務了

3.1讓DispatcherServlet和ContextLoaderListener從XML中加載各自的應用上下文。

搭建DispatcherServlet和ContextLoaderListener。

?

<?xml version="1.0" encoding="UTF-8"?>
<web-app 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns
="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation
="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
id
="WebApp_ID" version="3.1"><!-- 設置root上下文配置文件位置 --> <context-param><param-name>contextConfigLocation</param-name><param-value>/WEB-INF/spring/root-context.xml</param-value> </context-param><!-- 注冊ContextLoaderListener --> <listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener><!-- 注冊DispatcherServlet --> <servlet><servlet-name>appServlet</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name> <!-- 指定的路徑上加載應用上下文--><param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value></init-param><load-on-startup>1</load-on-startup> </servlet><servlet-mapping><servlet-name>appServlet</servlet-name><url-pattern>/</url-pattern> </servlet-mapping> </web-app>

?

?其中對于ContextLoaderListener,上下文參數contextConfigLocation指定了一個XML文件的地址,這個文件定義了根應用上下文,它會被ContextLoaderListener加載。

DispatcherServlet會根據Servlet的名字找到一個文件,并基于該文件加載應用上下文。Servlet的名字是appServlet,因此DispatcherServlet會從“/WEB-INF/appServlet-context.xml”文件中加載其應用上下文。但是上面改變了加載的配置文件的路勁(在Servlet上指定一個contextConfigLocation初始化參數。)。

?3.2在java配置類中加載

要在Spring MVC中使用基于Java的配置,需要告訴DispatcherServlet和ContextLoaderListener使用AnnotationConfigWebApplicationContext,這是一個WebApplicationContext的實現類,它會加載Java配置類,而不是使用XML。要實現這種配置,可以設置contextClass上下文參數以及DispatcherServlet的初始化參數。在下面文件中,它所搭建的Spring MVC使用基于Java的Spring配置:

<?xml version="1.0" encoding="UTF-8"?>
<web-app 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns
="http://xmlns.jcp.org/xml/ns/javaee"
  xsi:schemaLocation
="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
  id
="WebApp_ID" version="3.1"><context-param><param-name>contextClass</param-name> <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value> </context-param><!-- 設置root上下文配置文件位置 --> <context-param> <param-name>contextConfigLocation</param-name><param-value>spittr.config.RootConfig</param-value> </context-param><!-- 注冊ContextLoaderListener --> <listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener><!-- 注冊DispatcherServlet --> <servlet><servlet-name>appServlet</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param><param-name>contextClass</param-name><param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value></init-param> <init-param><param-name>contextConfigLocation</param-name> <!-- 指定應用上下文的加載路徑 --><param-value>spittr.config.WebConfig</param-value></init-param><load-on-startup>1</load-on-startup> </servlet><servlet-mapping><servlet-name>appServlet</servlet-name><url-pattern>/</url-pattern> </servlet-mapping> </web-app>

?

?二、處理multipart形式數據

?multipart對于處理文件的上傳有很好的處理。

multipart格式的數據會將一個表單拆分為多個部分(part),每個部分對應一個輸入域。在一般的表單輸入域中,它所對應的部分中會放置文本型數據,但是如果上傳文件的話,它所對應的部分可以是二進制,下面展現了multipart的請求體:

在這個multipart的請求中,我們可以看到profilePicture部分與其他部分明顯不同。除了其他內容以外,它還有自己的Content-Type頭,表明它是一個JPEG圖片。profilePicture部分的請求體是二進制數據,而不是簡單的文本。

在編寫控制器方法處理文件上傳之前,我們必須要配置一個multipart解析器,通過它來告訴DispatcherServlet該如何讀取multipart請求。

DispatcherServlet沒有實現任何解析multipart請求數據的功能。它將該任務委托給了Spring中MultipartResolver策略接口的實現,通過這個實現類來解析multipart請求中的內容。從Spring 3.1開始,Spring內置了兩個MultipartResolver的實現供我們選擇:

    CommonsMultipartResolver:使用Jakarta Commons?FileUpload解析multipart請求;

    StandardServletMultipartResolver:依賴于Servlet 3.0對multipart請求的支持(始于Spring 3.1)。

2.1使用StandardServletMultipartResolver

其沒有構造器參數,也沒有要設置的屬性。

  在Spring應用上下文中配置:

 

public class WebConfig extends WebMvcConfigurerAdapter{

      @Bean
      public MultipartResolver multipartResolver()throws IOException{
        return new StandardServletMultipartResolver();
       }

}    

?

雖然沒辦法直接通過StandardServletMultipartResolver配置限制條件的。但在Servlet中能指定multipart的配置。具體來講,我們必須要在web.xml或Servlet初始化類中,將multipart的具體細節作為DispatcherServlet配置的一部分。

方法一:(沒用過)

如果采用Servlet初始化類的方式來配置DispatcherServlet的話,這個初始化類應該已經實現了WebApplicationInitializer,那我們可以在Servlet registration上調用setMultipartConfig()方法,傳入一個MultipartConfig-Element實例。如下是最基本的DispatcherServlet multipart配置,它將臨時路徑設置為“/tmp/spittr/uploads”:

方法二:

如果配置DispatcherServlet的Servlet初始化類繼承了Abstract AnnotationConfigDispatcherServletInitializer或AbstractDispatcherServletInitializer的話,那么我們不會直接創建DispatcherServlet實例并將其注冊到Servlet上下文中。這樣的話,將不會有對Dynamic Servlet registration的引用供我們使用了。但是,我們可以通過重載customizeRegistration()方法(它會得到一個Dynamic作為參數)來配置multipart的具體細節:

 

public class SpittrWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer{

  @Overrideprotected void customizeRegistration(Dynamic registration) {registration.setMultipartConfig(new MultipartConfigElement("/tmp/spittr/uploads"));}
}

?

MultipartConfigElement構造器,這個參數指定的是文件系統中的一個絕對目錄,上傳文件將會臨時寫入該目錄中。還可以通過其他的構造器來限制上傳文件的大小。除了臨時路徑的位置,其他的構造器所能接受的參數如下:

  上傳文件的最大容量(以字節為單位)。默認是沒有限制的。

  整個multipart請求的最大容量(以字節為單位),不會關心有多少個part以及每個part的大小。默認是沒有限制的。

  在上傳的過程中,如果文件大小達到了一個指定最大容量(以字節為單位),將會寫入到臨時文件路徑中。默認值為0,也就是所有上傳的文件都會寫入到磁盤上。

假設我們想限制文件的大小不超過2MB,整個請求不超過4MB,而且所有的文件都要寫到磁盤中。

    @Overrideprotected void customizeRegistration(Dynamic registration) {registration.setMultipartConfig(new MultipartConfigElement("/tmp/spittr/uploads",2097152,4194304,0));}

?

或者在xml中配置:

使用<servlet>中的<multipart-config>元素

2.2處理multipart請求

例如傳一張圖片:

  <body><div id="content"><h1>Register</h1><form method="POST"  enctype="multipart/form-data" action="upload/up"><label>Profile Picture</label>:<input type="file"name="profilePicture"accept="image/jpeg,image/png,image/gif" /><br/><input type="submit" value="Register" /></form></div></body>

?

注意:<form>標簽現在將enctype屬性設置為multipart/form-data,這會告訴瀏覽器以multipart數據的形式提交表單,而不是以表單數據的形式進行提交。在multipart中,每個輸入域都會對應一個part。

添加了一個新的<input>域,其type為file。這能夠讓用戶選擇要上傳的圖片文件。accept屬性用來將文件類型限制為JPEG、PNG以及GIF圖片。根據其name屬性,圖片數據將會發送到multipart請求中的profilePicture part之中

2.2.1MultipartFile接收

Spring還提供了MultipartFile接口,它為處理multipart數據提供了內容更為豐富的對象。

Spring所提供的MultipartFile接口,用來處理上傳的文件

MultipartFile提供了獲取上傳文件byte的方式,還能獲得原始的文件名、大小以及內容類型。它還提供了一個InputStream,用來將文件數據以流的方式進行讀取。

MultipartFile還提供了一個便利的transferTo()方法,它能夠幫助我們將上傳的文件寫入到文件系統中。

?在processRegistration()方法中添加如下的幾行代碼,從而將上傳的圖片文件寫入到文件系統中:

      @RequestMapping(value="/up", method=RequestMethod.POST)public String processRegistration(@RequestPart("profilePicture")MultipartFile profilePicture,Model model) throws Exception{  // 文件保存路徑  String filePath = "E:/spittr/image/";System.out.println(filePath+profilePicture.getSize()+".jpg");profilePicture.transferTo(new File(filePath+profilePicture.getSize()+".jpg"));model.addAttribute("imgSrc","/spittr/image/"+profilePicture.getSize()+".jpg");return "displayImg";}

?讀取存儲的內容:

  <body><img  src="${imgSrc}"></body>

?2.3以part形式處理

?將應用部署到Servlet 3.0的容器中,那么會有MultipartFile的一個替代方案。Spring MVC也能接受javax.servlet.http.Part作為控制器方法的參數。

Part接口與MultipartFile并沒有太大的差別。

Part方法的名稱與MultipartFile方法的名稱是完全相同的。有一些比較類似,但是稍有差異,比如getSubmittedFileName()對應于getOriginalFilename()。類似地,write()對應于transferTo(),借助該方法我們能夠將上傳的文件寫入文件系統中.

如果在編寫控制器方法的時候,通過Part參數的形式接受文件上傳,那么就沒有必要配置MultipartResolver了。只有使用MultipartFile的時候,我們才需要MultipartResolver。

?

      @RequestMapping(value="/up", method=RequestMethod.POST)public String processRegistration(@RequestPart("profilePicture") Part profilePicture,Model model) throws Exception{  // 文件保存路徑  String filePath = "E:/spittr/image/";System.out.println(filePath+profilePicture.getSize()+".jpg"); profilePicture.write(filePath+profilePicture.getSize()+".jpg");model.addAttribute("imgSrc","/spittr/image/"+profilePicture.getSize()+".jpg");return "displayImg";}

?三、處理異常

不管發生什么事情,不管是好的還是壞的,Servlet請求的輸出都是一個Servlet響應。如果在請求處理的時候,出現了異常,那它的輸出依然會是Servlet響應。異常必須要以某種方式轉換為響應。

Spring提供了多種方式將異常轉換為響應:

  特定的Spring異常將會自動映射為指定的HTTP狀態碼;
  異常上可以添加@ResponseStatus注解,從而將其映射為某一個HTTP狀態碼;

  在方法上可以添加@ExceptionHandler注解,使其用來處理異常。

3.1將異常映射為HTTP狀態碼

在默認情況下,Spring會將自身的一些異常自動轉換為合適的狀態碼。

  Spring的一些異常會默認映射為HTTP狀態碼

Spring異常HTTP狀態碼
BindException400 - Bad Request
ConversionNotSupportedException500 - Internal Server Error
HttpMediaTypeNotAcceptableException406 - Not Acceptable
HttpMediaTypeNotSupportedException415 - Unsupported Media Type
HttpMessageNotReadableException400 - Bad Request
MissingServletRequestParameterException400 - Bad Request
MissingServletRequestPartException400 - Bad Request
NoSuchRequestHandlingMethodException404 - Not Found
TypeMismatchException400 - Bad Request
HttpMessageNotWritableException500 - Internal Server Error
HttpRequestMethodNotSupportedException405 - Method Not Allowed

異常一般會由Spring自身拋出,作為DispatcherServlet處理過程中或執行校驗時出現問題的結果。

如果DispatcherServlet無法找到適合處理請求的控制器方法,那么將會拋出NoSuchRequestHandlingMethodException異常,最終的結果就是產生404狀態碼的響應(Not Found)。

3.2@ResponseStatus注解

Spring提供了一種機制,能夠通過@ResponseStatus注解將異常映射為HTTP狀態碼。

     public String spittle(@PathVariable("spittleId") long spittledId ,Model  model){Spittle spittle = sipttleRepository.findOne(spittleId);if(spittle == null){throw new SpittleNotFoundException();}model.addAttribute(spittle);return "spittle";}

?通過ID檢索Spittle對象。如果findOne()方法能夠返回Spittle對象的話,那么會將Spittle放到模型中,然后名為spittle的視圖會負責將其渲染到響應之中。但是如果findOne()方法返回null的話,那么將會拋出SpittleNotFoundException異常。

public class SpittleNotFoundException extends RuntimeException {}

如果調用spittle()方法來處理請求,并且給定ID獲取到的結果為空,那么SpittleNotFoundException(默認)將會產生500狀態碼(Internal Server Error)的響應。實際上,如果出現任何沒有映射的異常,響應都會帶有500狀態碼,故返回的不精確,可以修改。

使用@ResponseStatus注解將SpittleNotFoundException映射為HTTP狀態碼404。

@ResponseStatus(value=HttpStatus.NOT_FOUND,reason="Spittle Not Found")
public class SpittleNotFoundException extends RuntimeException {}

在引入@ResponseStatus注解之后,如果控制器方法拋出SpittleNotFoundException異常的話,響應將會具有404狀態碼,這是因為Spittle Not Found。

3.3異常處理的方法

若在響應中不僅要包括狀態碼,還要包含所產生的錯誤,此時的話,就不能將異常視為HTTP錯誤了,而是要按照處理請求的方式來處理異常了。

假設用戶試圖創建的Spittle與已創建的Spittle文本完全相同,那么SpittleRepository的save()方法將會拋出DuplicateSpittle Exception異常。這意味著SpittleController的saveSpittle()方法可能需要處理這個異常。

  程序的處理:

     @RequestMapping(method=RequestMethod.POST)public String saveSpittle(SpittleForm form,Model model){try{spittleRepository.save(new Spittle(null,form,getMessage(),new Date()));return "redirect:/spittles";}catch(DuplicateSpittleException e){return "err/duplicate";}}

?

如果能讓saveSpittle()方法只關注正確的路徑,而讓其他方法處理異常的話,那么它就能簡單一些。

修改:

     @RequestMapping(method=RequestMethod.POST)public String saveSpittle(SpittleForm form,Model model){spittleRepository.save(new Spittle(null,form,getMessage(),new Date()));return "redirect:/spittles";}@ExceptionHandler(DuplicateSpittleException.class)public String handlerDuplicateSpittle(){return "err/duplicate";}

handleDuplicateSpittle()方法上添加了@ExceptionHandler注解,當拋出DuplicateSpittleException異常的時候,將會委托該方法來處理。它返回的是一個String,這與處理請求的方法是一致的,指定了要渲染的邏輯視圖名,它能夠告訴用戶他們正在試圖創建一條重復的條目。

對于@ExceptionHandler注解標注的方法來說,比較有意思的一點在于它能處理同一個控制器中所有處理器方法所拋出的異常。所以,盡管我們從saveSpittle()中抽取代碼創建了handleDuplicateSpittle()方法,但是它能夠處理SpittleController中所有方法所拋出的DuplicateSpittleException異常。我們不用在每一個可能拋出DuplicateSpittleException的方法中添加異常處理代碼,這一個方法就涵蓋了所有的功能。

3.4為控制器添加通知

如果多個控制器類中都會拋出某個特定的異常,那么你可能會發現要在所有的控制器方法中重復相同的@ExceptionHandler方法。或者,為了避免重復,我們會創建一個基礎的控制器類,所有控制器類要擴展這個類,從而繼承通用的@ExceptionHandler方法。

但是:Spring 3.2為這類問題引入了一個新的解決方案:控制器通知。控制器通知(controller advice)是任意帶有@ControllerAdvice注解的類,這個類會包含一個或多個如下類型的方法:

    @ExceptionHandler注解標注的方法;
    @InitBinder注解標注的方法;
    @ModelAttribute注解標注的方法。

在帶有@ControllerAdvice注解的類中,以上所述的這些方法會運用到整個應用程序所有控制器中帶有@RequestMapping注解的方法上。

@ControllerAdvice注解本身已經使用了@Component,因此@ControllerAdvice注解所標注的類將會自動被組件掃描獲取到,就像帶有@Component注解的類一樣。

@ControllerAdvice最為實用的一個場景就是將所有的@ExceptionHandler方法收集到一個類中,這樣所有控制器的異常就能在一個地方進行一致的處理。

如果任意的控制器方法拋出了DuplicateSpittleException,不管這個方法位于哪個控制器中,都會調用這個duplicateSpittleHandler()方法來處理異常。

四:跨重定向請求傳遞數據

當控制器方法返回的String值以“redirect:”開頭的話,那么這個String不是用來查找視圖的,而是用來指導瀏覽器進行重定向的路徑。

具體來講,正在發起重定向功能的方法該如何發送數據給重定向的目標方法呢?一般來講,當一個處理器方法完成之后,該方法所指定的模型數據將會復制到請求中,并作為請求中的屬性,請求會轉發(forward)到視圖上進行渲染。同一個請求,所以在轉發的過程中,請求屬性能夠得以保存。

當控制器的結果是重定向的話,原始的請求就結束了,并且會發起一個新的GET請求。原始請求中所帶有的模型數據也就隨著請求一起消亡了。在新的請求屬性中,沒有任何的模型數據,這個請求必須要自己計算數據。

有一些其他方案,能夠從發起重定向的方法傳遞數據給處理重定向方法中:

    使用URL模板以路徑變量和/或查詢參數的形式傳遞數據;
    通過flash屬性發送數據。

4.1通過URL模板進行重定向

通過路徑變量和查詢參數傳遞數據看起來非常簡單。以路徑變量的形式傳遞了新創建Spitter的username。但是按照現在的寫法,username的值是直接連接到重定向String上的。這能夠正常運行,但是還遠遠不能說沒有問題。當構建URL或SQL查詢語句的時候,使用String連接是很危險的

Spring還提供了使用模板的方式來定義重定向URL。

username作為占位符填充到了URL模板中,而不是直接連接到重定向String中,所以username中所有的不安全字符都會進行轉義。這樣會更加安全,這里允許用戶輸入任何想要的內容作為username,并會將其附加到路徑上。

模型中所有其他的原始類型值都可以添加到URL中作為查詢參數。作為樣例,假設除了username以外,模型中還要包含新創建Spitter對象的id屬性,那processRegistration()方法可以改寫為如下的形式:

所返回的重定向String并沒有太大的變化。但是,因為模型中的spitterId屬性沒有匹配重定向URL中的任何占位符,所以它會自動以查詢參數的形式附加到重定向URL上

如果username屬性的值是habuma并且spitterId屬性的值是42,那么結果得到的重定向URL路徑將會是“/spitter/habuma?spitterId=42”。

通過路徑變量和查詢參數的形式跨重定向傳遞數據是很簡單直接的方式,但它也有一定的限制。它只能用來發送簡單的值,如String和數字的值

?4.2使用flash屬性

Spitter對象要比String和int更為復雜。因此,我們不能像路徑變量或查詢參數那么容易地發送Spitter對象。它只能設置為模型中的屬性。

模型數據最終是以請求參數的形式復制到請求中的,當重定向發生的時候,這些數據就會丟失。因此,我們需要將Spitter對象放到一個位置,使其能夠在重定向的過程中存活下來。有個方案是將Spitter放到會話中。會話能夠長期存在,并且能夠跨多個請求。所以我們可以在重定向發生之前將Spitter放到會話中,并在重定向后,從會話中將其取出。當然,我們還要負責在重定向后在會話中將其清理掉

Spring認為我們并不需要管理這些數據,相反,Spring提供了將數據發送為flash屬性(flash attribute)的功能。按照定義,flash屬性會一直攜帶這些數據直到下一次請求,然后才會消失

Spring提供了通過RedirectAttributes設置flash屬性的方法,這是Spring 3.1引入的Model的一個子接口。RedirectAttributes提供了Model的所有功能。

具體來講,RedirectAttributes提供了一組addFlashAttribute()方法來添加flash屬性。重新看一下processRegistration()方法

調用了addFlashAttribute()方法,并將spitter作為key,Spitter對象作為值。另外,我們還可以不設置key參數,讓key根據值的類型自行推斷得出:因為我們傳遞了一個Spitter對象給addFlashAttribute()方法,所以推斷得到的key將會是spitter

在重定向執行之前,所有的flash屬性都會復制到會話中。在重定向后,存在會話中的flash屬性會被取出,并從會話轉移到模型之中。

?

?showSpitterProfile()方法所做的第一件事就是檢查是否存有key為spitter的model屬性。如果模型中包含spitter屬性,那就什么都不用做了。這里面包含的Spitter對象將會傳遞到視圖中進行渲染。但是如果模型中不包含spitter屬性的話,那么showSpitterProfile()將會從Repository中查找Spitter,并將其存放到模型中

?

?

轉載于:https://www.cnblogs.com/mswangblog/p/6558550.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/255102.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/255102.shtml
英文地址,請注明出處:http://en.pswp.cn/news/255102.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

EPSON TCP/IP 通信

EPSON SCARA機器人TCP/IP 通信時&#xff0c;涉及到的相關指令說明。 14.3 TCP/IP命令。 OpenNet //打開TCP/IP端口。 ChkNet //返回端口狀態&#xff1a;等待讀取的字節數或錯誤條件。 CloseNet //關閉TCP/IP端口。 SetNet //運行時或從命令窗口中設置通信端…

JDBC(九)DatabaseMetaData 數據庫元數據

通過java.sql.DatabaseMetaData 接口&#xff0c;我們能獲取到數據庫的列表、列等信息。 DatabaseMetaData 接口包含了許多方法&#xff0c;這里值介紹常用的。 ###獲取 DatabaseMetaData 實例對象 DatabaseMetaData databaseMetaData connection.getMetaData(); 復制代碼###獲…

C++多線程(一)

C多線程&#xff08;一&#xff09; WIN 多線程API一 簡單實例比較簡單的代碼&#xff0c;創建10個線程&#xff0c;其中使第4個線程在一創建就掛起&#xff0c;等到其他的線程執行的差不多的時候再使第4個線程恢復執行。#include <stdio.h>#include <stdlib.h>#i…

HALCON示例程序measure_ball_bond.hdev電路板焊點位置測量

HALCON示例程序measure_ball_bond.hdev電路板焊點位置測量 示例程序源碼&#xff08;加注釋&#xff09; 關于顯示類函數解釋 dev_update_off () dev_close_window () FileName : ‘bonds/ball_bond_ccd_’ read_image (Image, FileName 1$‘02’) dev_open_window_fit_imag…

rank()over 函數的使用

1. over()是分析函數&#xff0c;可以和rank()函數配合使用&#xff0c;也可以和其他函數配合使用。取每個學科排名前三的分數&#xff0c;sql語句如下&#xff1a; select * from (select rank() over(partition by subject order by mark desc) rk,S.* from S) T where T.rk&…

天梯賽2016-L2

L2-001. 緊急救援 作為一個城市的應急救援隊伍的負責人&#xff0c;你有一張特殊的全國地圖。在地圖上顯示有多個分散的城市和一些連接城市的快速道路。每個城市的救援隊數量和每一條連接兩個城市的快速道路長度都標在地圖上。當其他城市有緊急求助電話給你的時候&#xff0c;你…

伺服系統控制網絡的重要性! 現場總線的重要性! SSCNET運動控制系統與發展趨勢

引言&#xff1a;在2010年的時候&#xff0c;在北京的一個數控公司工作。產品采用的是通過運動控制卡發脈沖的方式&#xff0c;控制機床的X、Y、Z軸進行加工。 機床在加工產品的時候&#xff0c;一直存在著精度的問題&#xff0c;例如DMG的機床可以達到0.01的加工精度&#x…

apache配置

wamp環境安裝需要VC運行庫的支持 apache如果需要外網訪問&#xff0c;在其配置文件中尋找127.0.0.1然后替換為all 外網訪問需要關閉防火墻&#xff1f;轉載于:https://www.cnblogs.com/gremlin/p/5581486.html

TCP/IP 通信示例

TCP/IP 通信示例 Global String ReadData_P_All$, ReadData_P$(10), data$ Global Preserve Double x Global Integer foundnumber_PFunction TCPServerSetNet #201, "192.168.0.1", 4000, CRLF, NONE, 0OpenNet #201 As ClientWaitNet #201Print "TCP Connect…

MySQL分庫分表總結參考

單庫單表 單庫單表是最常見的數據庫設計&#xff0c;例如&#xff0c;有一張用戶(user)表放在數據庫db中&#xff0c;所有的用戶都可以在db庫中的user表中查到。 單庫多表 隨著用戶數量的增加&#xff0c;user表的數據量會越來越大&#xff0c;當數據量達到一定程度的時候對u…

小兔伴伴家庭動物園AR智能早教產品上市

2016年6月&#xff0c;經過樂卓大家庭所有人的共同努力&#xff0c;公司旗下首款新品——小兔伴伴之《家庭動物園》3D智能學習卡正式面世。 每個孩子都應該在合適的時間去體驗豐富的聲音、色彩和動作&#xff0c;《家庭動物園》&#xff0c;專為2-6歲兒童貼心設計&#xff0c;是…

EPSON 自帶CCD圖像處理包使用舉例

EPSON 機器人可以購買CCD圖像處理包選項&#xff0c;CCD圖像處理包與SPEL語言高度結合&#xff0c;可以非常快的將項目投入應用&#xff0c;舉例說明CCD圖像處理包與SPEL的聯合使用。 EPSON 以視覺序列定義圖像處理的一個項目&#xff0c;視覺序列是一組按照特定順序排列的視覺…

索引使用原則

前兩篇文章我總結了一些SQL數據庫索引的問題&#xff0c;這篇主要來分析下索引的優缼點&#xff0c;以及如何正確使用索引。 索引的優點&#xff1a;這個顯而易見&#xff0c;正確的索引會大大提高數據查詢&#xff0c;對結果進行排序、分組的操作效率。 索引的缺點…

根據時間變換頁面背景

1.概述 有些時侯為了豐富頁面的顯示效果&#xff0c;將頁面制作成根據時間變換頁面背景的樣式&#xff0c;這樣會使瀏覽者對此網站不會感覺厭倦&#xff0c;同時也會覺得網站制作的非常新穎。本實例通過Date對象的getHours()方法獲得當前系統時間的小時&#xff0c;然后根據不同…

EPSON 自帶CCD圖像處理包的典型應用框架

EPSON 自帶CCD圖像處理包的典型應用框架 Function main ******************************************** Very important statement below: Use the * //非常重要的是在實際運行時&#xff0c;用合適的Z數值代替預定義的 ZHeight 。Z height which you wrote down earlier in *…

EPSON 利用CCD圖像處理包標定工具坐標系

EPSON 利用CCD圖像處理包標定工具坐標系 仰視式安裝的相機可以用來計算工具偏移&#xff0c;下例使用仰視式相機來計算工具偏移。該功能首先運行一個序列來定位工具的尖端。然后計算出工具偏移&#xff08;前提是CCD已標定&#xff09;。 Function CalcTool Boolean foundReal…

掛馬方式研究、掛馬檢測技術研究

1. 掛馬定義 所謂的掛馬&#xff0c;就是黑客通過各種手段&#xff0c;包括SQL注入&#xff0c;網站敏感文件掃描&#xff0c;服務器漏洞&#xff0c;網站程序0day, 等各種方法獲得網站管理員賬號&#xff0c;然后登陸網站后臺&#xff0c;通過數據庫"備份/恢復"或者…

大幅面多相機高精度定位及測量解決方案

隨著機器視覺應用的日益廣泛&#xff0c;大幅面多相機視覺系統的需求越來越多&#xff0c;主要應用方向為大幅面高精度的定位與測量和場景拼接等。多相機視覺系統的難點在于多相機坐標系的統一&#xff0c;可以分為兩類&#xff0c;一是相機視野間無重疊部分&#xff0c;二是相…

Hadoop 使用FileSystem API 讀取數據

代碼&#xff1a; package com.hadoop;import java.io.IOException; import java.io.InputStream; import java.net.URI;import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; import org.apache.hadoop.…