在Java的Spring開發中經常使用一些注解,例如 @XXX 等等,在網上看到收集整理碎片知識,便于懶人計劃^=^...
過去,Spring使用的Java Bean對象必須在配置文件[一般為application.xml] 中進行配置,然后才能使用,但Spring2.5版之后,引入了配置注解功能,操作更簡單,但是不了解的就抽象了,所以有必要了解一下一些注解的知識;
一,首選注意,注解,注入需要的JAR包,即用common-annotations.jar 包的支持;
二,要使用注解,注入功能需在Spring配置文件[一般為application.xml]進行必要的配置才能使用注解,注入功能,例如下面;
參見 http://gtgt1988.iteye.com/blog/1670030
[html] view plaincopyprint?
<beans?xmlns="...">??????????????<context:annotation-config/>?????????????????<context:component-scan?base-package="cn.org.xxx"?/>????????????????<!--??????<context:component-scan?base-package="xx.xxx.yyy"/>??????<context:exclude-filter?type="annotation"?expression="xx.xxx.yyy.Service"/>????????</context:component-scan>??????-->??????????????<mvc:annotation-driven?/>????????????????<mvc:resources?location="/resources/"?mapping="/resources/**"?/>???????????????<mvc:view-controller?path="/"?view-name="forward:/logon"/>???????<mvc:view-controller?path="/permission/login"?view-name="permission/login"/>??????<mvc:view-controller?path="/permission/logout"?view-name="permission/login"/>??????????????</beans>??
<beans xmlns="..."><!-- 添加注解驅動 --> <context:annotation-config/> <!-- 默認掃描的包路徑 --> <context:component-scan base-package="cn.org.xxx" /> <!--指定默認掃描的包路徑,同時指定不掃描的包,如默認掃描包下的Service不掃描--><!--<context:component-scan base-package="xx.xxx.yyy"/><context:exclude-filter type="annotation" expression="xx.xxx.yyy.Service"/> </context:component-scan>--><!-- Spring MVC 必須的配置 --><mvc:annotation-driven /> <!-- 配置js,css等靜態文件直接映射到對應的文件夾,不被DispatcherServlet處理 --><mvc:resources location="/resources/" mapping="/resources/**" /><!-- 定義一些視圖控制器,完成訪問路徑到返回視圖的映射關系 --><mvc:view-controller path="/" view-name="forward:/logon"/> <mvc:view-controller path="/permission/login" view-name="permission/login"/><mvc:view-controller path="/permission/logout" view-name="permission/login"/><!-- ...其他Bean的配置... -->
</beans>其中
<context:annotation-config/> 的作用是隱式地向 Spring 容器注冊如下四個Bean,這是注解,注入功能的驅動:
AutowiredAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor,
PersistenceAnnotationBeanPostProcessor,RequiredAnnotationBeanPostProcessor?
具體解釋例如:
.如果想使用@Resource 、@PostConstruct、@PreDestroy等注解就必須聲明CommonAnnotationBeanPostProcessor。 ?
.如果想使用@PersistenceContext注解,就必須聲明PersistenceAnnotationBeanPostProcessor的Bean。 ?
.如果你想使用@Autowired注解,那么就必須事先在 Spring 容器中聲明 AutowiredAnnotationBeanPostProcessor Bean。
傳統聲明方式如下: ?
< bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor "/> ??
.如果想使用 @Required的注解,就必須聲明RequiredAnnotationBeanPostProcessor的Bean。同樣,傳統的聲明方式如下: ?
< bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor"/>?
其中<context:component-scan base-package="xx.xxx.xxxx" /> 的作用是掃描指定的包,即尋找指定包內的類class文件,
類似于Spring配置文件中Bean的定義,如:<bean id="..." class="..."> ,
也可在該元素其中增加<context:exclude-filter type="annotation" expression="xx.yy"/>指定不掃描的包;
其中<mvc:annotation-driven /> 的作用是自動注冊DefaultAnnotationHandlerMapping與AnnotationMethodHandlerAdapter 這兩個bean,?是spring MVC為@Controllers分發請求所必須的。是一種簡寫形式,完全可以手動配置替代這種簡寫形式,簡寫形式可以讓初學快速應用默認配置方案,并提供了數據綁定支持,@NumberFormatannotation支持,@DateTimeFormat支持,@Valid支持,讀寫XML的支持(JAXB),讀寫JSON的支持(Jackson)。
三,在annotaion配置注解中用@Component來表示一個通用注釋,用于說明一個類是一個spring容器管理的類,也即就是該類已經被拉入到spring框架的管理中了。而@Controller, @Service, @Repository等等是@Component的細化,這三個注解比@Component帶有更多的語義,它們分別對應了控制層、服務層、持久層的類,下面逐步了解一下部分注解;
1,@Component
把普通pojo實例化到spring容器中,相當于配置文件中的<bean id="" class=""/>,即類的聲明;
@Component和<context:annotation-config/>和<context:component-scan base-package="com.xxx"/>
三者配合實現無XML配置,只通過注解配置即可將類放入Spring資源容器中。?
如果用注入方式的話就需要在Spring配置文件application.xml中引入component的掃描組件,?
< context:annotation-config/>和<context:component-scan base-package="com.xxx"> ??
其中base-package為需要掃描的包(含所有子包)
2,@Resource?
作用是在Spring容器里面找相應的資源,資源必須先通過Spring配置文件application.xml等方式預先加載到Spring框架容器中;
http://www.2cto.com/kf/201206/137806.html
可通過name屬性指定查找的資源名稱,有時name屬性可省,可注解到field或setter方法上面,例如:
[html] view plaincopyprint?
public?class?UserAction?{???????private?UserService?userService;??????????????@Resource(name="userService")???//或@Resource("userService")??????public?void?setUserService(UserService?userService){???????????this.userService?=?userService;???????}???????public?void?addUser(){???????????userService.HelloWorld();???????}???}???
public class UserAction { private UserService userService; @Resource(name="userService") //或@Resource("userService")public void setUserService(UserService userService){ this.userService = userService; } public void addUser(){ userService.HelloWorld(); }
}
3,@Autowired 和 @Resource
兩者都用于注入對象功能,
@Autowired 按 byType 自動注入,@Resource 的作用相當于 @Autowired,但@Resource 默認按 byName 自動注入罷了,
@Resource 有兩個屬性是比較重要的,分別是 name 和 type,Spring 將 @Resource 注釋的 name 屬性解析為 Bean 的名字,
而 type 屬性則解析為 Bean 的類型。所以如果使用 name 屬性,則使用 byName 的自動注入策略,
而使用 type 屬性時則使用 byType 自動注入策略。如果既不指定 name 也不指定 type 屬性,這時將通過反射機制使用 byName 自動注入策略。
Resource 注釋類位于 Spring 發布包的 lib/j2ee/common-annotations.jar 類包中;
4,@Repository
該注解是用來給持久層的類定義一個名字,讓Spring根據這個名字關聯到這個類。
例如:
[java] view plaincopyprint?
@Repository("userDao")???public?class?UserDaoImpl??implements?UserDao{????????}???
@Repository("userDao")
public class UserDaoImpl implements UserDao{ //...
} 聲明了UserDaoImpl ,它在Spring容器中叫userDao這個名字; 另外標簽:@Autowired 用來注入,例如:?
[java] view plaincopyprint?
@Autowired???private?UserDao?userDao;??
@Autowired
private UserDao userDao;這樣就注入到Spring容器中進去了,相當于我們new了這個實現類并且加入到Spring框架的容器中,我們就無需寫setter方法了。?
5,@Controller,?
使用 @Controller 注解定義一個 Controller 控制器。使用@Controller 標記的類就是一個SpringMVC Controller 對象;但還不能使用,需要把控制器類加入到Spring框架容器中才能使用;
[java] view plaincopyprint?
@Controller????public?class?MyController?{????????@RequestMapping?(?"/showView"?)????????public?ModelAndView?showView()?{???????????ModelAndView?modelAndView?=?new?ModelAndView();???????????modelAndView.setViewName(?"viewName"?);???????????modelAndView.addObject(?"需要放到model中的屬性名稱"?,?"對應的屬性值,它是一個對象");???????????return?modelAndView;????????}??????}???
@Controller
public class MyController { @RequestMapping ( "/showView" ) public ModelAndView showView() { ModelAndView modelAndView = new ModelAndView(); modelAndView.setViewName( "viewName" ); modelAndView.addObject( "需要放到model中的屬性名稱" , "對應的屬性值,它是一個對象"); return modelAndView; }
} 控制器類加入到Spring框架容器中有兩種方法:
(1)在SpringMVC 的配置文件[application.xml]中定義MyController的bean對象,如下:
< bean class="com.host.app.web.controller.MyController"/>
(2)在SpringMVC 的配置文件[application.xml]中告訴Spring 該到哪里去找標記為@Controller 的Controller控制器。
<context:component-scan base-package = "com.host.app.web.controller" > ?
? ?<context:exclude-filter type = "annotation" expression = "org.springframework.stereotype.Service" /> ?
< /context:component-scan > ??
注:上面 context:exclude-filter 標注的是不掃描 @Service 標注的類
6,@Service,?
@Service用于標注業務層組件,相當于定義一個bean然后添加到Spring容器中;如:@Service("名稱"),如果未指定名稱則自動根據Java Bean的類名生成一個首字母小寫跟bean類名稱同名的名稱,
[java] view plaincopyprint?
??@Service("myUserService")???public?class?UserServiceImpl?implements?userService?{??????????}??????@Service????public?class?LogServiceImpl?implements?LogService?{??????????}??
//(指定在Spring容器中的名稱),相當于在Spring容器中通過myUserService名稱即可找到UserServiceImpl類的實例
@Service("myUserService")
public class UserServiceImpl implements userService { //...code...
}//如果不指定名稱則生成一個跟類名相同,但首字母小寫的該類實例,并加到Spring容器中,即在Spring容器中通過logServiceImpl可找到該類實例
@Service
public class LogServiceImpl implements LogService { //...code...
}
7,@RequestMapping
使用 @RequestMapping 來完成 Request 請求到處理器或處理器方法的映射。
使用@RequestMapping 可把URL映射到控制器類,或者控制器類的某個處理方法上,當@RequestMapping 標記在Controller 類上時,
表明該控制器類所有方法處理的請求都基于前面的URL,控制器類里面的方法如果再使用@RequestMapping時,請求的路徑是相對于
類上面的請求路徑,也即基于前面的請求路徑;如果控制器類前沒有@RequestMapping 標記時,控制器類里面方法的@RequestMapping則
相對host根路徑,例如:
[java] view plaincopyprint?
@Controller????@RequestMapping?("/user")????public?class?MyController?{????????@RequestMapping?(?"/userInfo"?)???????public?ModelAndView?showView()?{???????????ModelAndView?modelAndView?=?new?ModelAndView();???????????modelAndView.setViewName(?"viewName"?);???????????modelAndView.addObject(?"需要放到model中的屬性名稱"?,?"對應的屬性值,它是一個對象");???????????return?modelAndView;????????}??????}???
@Controller
@RequestMapping ("/user") //markA
public class MyController { @RequestMapping ( "/userInfo" ) //markBpublic ModelAndView showView() { ModelAndView modelAndView = new ModelAndView(); modelAndView.setViewName( "viewName" ); modelAndView.addObject( "需要放到model中的屬性名稱" , "對應的屬性值,它是一個對象"); return modelAndView; }
} 上面的訪問路徑為http://host:port/user/userInfo.do
如果注釋掉標注markA行的代碼,訪問路徑為:http://host:port/userInfo.do
@RequestMapping 中還支持通配符“*”或“?”等,例如如果控制器類中markB行改成 @RequestMapping ( "/*Info" ),那訪問可以是
http://host:port/user/userInfo.do 或 http://host:port/user/getUserInfo.do
8,@PathVariable
URI 模板就是在URI 中給定一個變量,然后在映射的時候動態的給該變量賦值,即非常方便的實現URL的RestFul 風格,在SpringMVC 中,我們可以使用@PathVariable 來標記Controller里面的處理方法參數,表示該參數的值將使用 URI 模板中對應的變量的值來賦值。
[java] view plaincopyprint?
@Controller????@RequestMapping?(?"/test/{variable1}"?)????public?class?MyController?{??????????@RequestMapping?(?"/showView/{variable2}"?)????????public?ModelAndView?showView(?@PathVariable?String?variable1,?@PathVariable?("variable2")?int?variable2)?{???????????ModelAndView?modelAndView?=?new?ModelAndView();???????????modelAndView.setViewName(?"viewName"?);???????????modelAndView.addObject(?"需要放到model中的屬性名稱"?,?"對應的屬性值,它是一個對象");???????????return?modelAndView;????????}????}???
@Controller
@RequestMapping ( "/test/{variable1}" )
public class MyController { @RequestMapping ( "/showView/{variable2}" ) public ModelAndView showView( @PathVariable String variable1, @PathVariable ("variable2") int variable2) { ModelAndView modelAndView = new ModelAndView(); modelAndView.setViewName( "viewName" ); modelAndView.addObject( "需要放到model中的屬性名稱" , "對應的屬性值,它是一個對象"); return modelAndView; }
} 處理時,路徑中{variable1}的值替換showView方法參數variable1,路徑中{variable2}的值替換variable2,
如:有請求 /test/hello/showView/2.do 到控制器時,hello賦給showView方法的variable1,2賦給showView方法的variable2;
當你沒有明確指定從路徑中取哪個參數時,就默認去URI 模板中找跟參數名相同的變量,但是這種情況只有在使用debug 模式進行編譯的時候才可以,如果不是debug 編譯的就會報錯;當不是使用debug 模式進行編譯,或者是所需要使用的變量名跟參數名不相同的時候,就要明確指出使用的是URI 模板中的哪個變量
9,@RequestParam
使用 @RequestParam 可完成 HttpServletRequest 的請求參數到控制器方法參數的綁定,同時還可增加一些參數的選項。
[java] view plaincopyprint?
??@RequestMapping?(?"/test"?)????public?String?userInfo(?@RequestParam(value="name",required=true)?String?name,?@RequestParam?(value="age",required=false)?int?age)?{???????return?"requestParam"?;????}???
//例如:
@RequestMapping ( "/test" )
public String userInfo( @RequestParam(value="name",required=true) String name, @RequestParam (value="age",required=false) int age) { return "requestParam" ;
} 上面會把HttpServletRequest請求中的參數name綁定到控制器方法userInfo的參數name,同時指定name參數為必選參數,
把請求中的參數age(可選)綁定到控制器方法userInfo的參數age,即@RequestParam完成請求URL中的
[java] view plaincopyprint?
@RequestMapping?(value=?"testMethod",?method={RequestMethod.GET,?RequestMethod.DELETE?})????public?String?testMethod()?{???????System.?out?.println(?"test?Method..........."?);???????return?"method"?;????}????
@RequestMapping (value= "testMethod", method={RequestMethod.GET, RequestMethod.DELETE })
public String testMethod() { System. out .println( "test Method..........." ); return "method" ;
} 參數到控制器中方法的參數的綁定;
當有請求:/test.do?name=xiaoming&age=13 發送到控制的userInfo方法時,請求中的name參數值xiaoming賦給控制器的方法userInfo的name參數,
請求中的age參數值13賦給控制器的方法userInfo的age參數;
值得注意的是和@PathVariable 一樣,當你沒有明確指定從請求中取哪個參數時,Spring在代碼是debug編譯的情況下會默認取更方法參數同名的參數,如果不是debug 編譯的就會報錯。
@RequestMapping 的一些高級應用
在RequestMapping 中除了指定請求路徑value 屬性外,還有其他的屬性可以指定,如params 、method 和headers 。這樣屬性都可以用于縮小請求的映射范圍。
(1)params屬性
params 屬性用于指定請求參數的
[java] view plaincopyprint?
@RequestMapping?(value=?"testParams",?params={"param1=value1",?"param2",?"!param3"?})????public?String?testParams()?{???????System.?out?.println(?"test?Params..........."?);???????return?"testParams"?;????}????
@RequestMapping (value= "testParams", params={"param1=value1", "param2", "!param3" })
public String testParams() { System. out .println( "test Params..........." ); return "testParams" ;
} 用@RequestMapping 的params 屬性指定了三個參數,這些參數都是針對請求參數而言的,它們分別表示參數param1 的值必須等于value1 ,參數param2 必須存在,值無所謂,參數param3 必須不存在,只有當請求是/testParams.do并且滿足指定的三個參數條件的時候才能訪問到testParams方法;故請求/testParams.do?param1=value1¶m2=value2 能夠正確訪問到該testParams 方法;
請求/testParams.do?param1=value1¶m2=value2¶m3=value3 不能夠正常的訪問到該方法,因含param3參數,與規定不符合;
(2)method屬性
method 屬性主要是用于限制能夠訪問方法的請求類型。
[java] view plaincopyprint?
@RequestMapping?(value=?"testMethod",?method={RequestMethod.GET,?RequestMethod.DELETE?})????public?String?testMethod()?{???????System.?out?.println(?"test?Method..........."?);???????return?"method"?;????}????
@RequestMapping (value= "testMethod", method={RequestMethod.GET, RequestMethod.DELETE })
public String testMethod() { System. out .println( "test Method..........." ); return "method" ;
} 在上面的代碼中限制請求為/testMethod.do,并且使用GET或DELETE的請求方式,才能訪問到該控制器的testMethod方法。
(3)headers屬性
使用headers 屬性可以通過請求頭信息來縮小@RequestMapping 的映射范圍。
[java] view plaincopyprint?
@RequestMapping?(value=?"testHeaders"?,?headers={"host=localhost",?"Accept"})????public?String?testHeaders()?{???????System.out.println(?"test?Headers..........."?);???????return?"headers"?;????}??
@RequestMapping (value= "testHeaders" , headers={"host=localhost", "Accept"})
public String testHeaders() { System.out.println( "test Headers..........." ); return "headers" ;
}headers屬性的用法和功能與params屬性相似。上面代碼中當請求為/testHeaders.do并且只有當請求頭包含Accept信息,且請求的host為localhost時才能正確訪問到testHeaders方法。
10,@CookieValue
使用 @CookieValue 可將cookie的值綁定到Controller方法的參數上。
[java] view plaincopyprint?
@RequestMapping?(?"test"?)????public?String?getCookieValue(?@CookieValue?("hello")?String?cookieValue,?@CookieValue?String?hello)?{???????System.out.println(cookieValue?+?"-----------"?+?hello);???????return?"cookieValue"?;????}???
@RequestMapping ( "test" )
public String getCookieValue( @CookieValue ("hello") String cookieValue, @CookieValue String hello) { System.out.println(cookieValue + "-----------" + hello); return "cookieValue" ;
} 使用@CookieValue完成了把名為hello的cookie的值綁定控制器方法getCookieValue的參數cookieValue上;后一個沒有指定名稱時,默認自動查找同名的cookie的值綁定到getCookieValue的參數hello上;
注意,才沒有指定名稱時,在debug 編譯模式下將自動獲取跟方法參數名同名的cookie 值,非debug編譯環境出錯;
11,@RequestHeader
使用 @RequestHeader 注解可綁定 HttpServletRequest 請求的某個頭信息到 Controller 方法的參數;
[java] view plaincopyprint?
@RequestMapping?(?"/test"?)????public?String?getRequestHeader(?@RequestHeader("Host")?String?hostAddr,?@RequestHeader?String?Host,?@RequestHeader?String?host?)?{????????System.?out?.println(hostAddr?+?"-----"?+?Host?+?"-----"?+?host?);????????return?"requestHeader"?;????}???
@RequestMapping ( "/test" )
public String getRequestHeader( @RequestHeader("Host") String hostAddr, @RequestHeader String Host, @RequestHeader String host ) { System. out .println(hostAddr + "-----" + Host + "-----" + host ); return "requestHeader" ;
} 上面把請求的頭信息中,把頭信息Host的值綁定到控制器中的方法getRequestHeader的Host參數;@RequestHeader指定了頭信息名稱,則取指定名稱的
頭信息值[推薦];(在debug編譯模式下)如果沒有指定頭信息名稱則綁定請求頭信息中跟控制器參數同名的頭信息(非debug可能出錯);
注意:在使用 @RequestHeader 的時候是大小寫不敏感的;但在@PathVariable、@RequestParam和@CookieValue中都是大小寫敏感的。
12,@ModelAttribute?
SpringMVC 支持使用 @ModelAttribute 和 @SessionAttributes 在不同的模型和控制器之間共享數據。
首先看@ModelAttribute,主要有兩種使用方式,一種是標注在方法名稱上,一種是標注在 Controller 方法的參數上。
當@ModelAttribute標記在控制器的某個方法上時,則該方法將在控制器所有其它方法執行之前被執行,然后把返回的對象存放在模型屬性中;
屬性名稱可以使用@ModelAttribute("attributeName")在標記方法的時候指定,若未指定,則使用返回類型的類名稱(首字母小寫)作為屬性名稱。
[java] view plaincopyprint?
@Controller????@RequestMapping?("/myTest")????public?class?MyController?{????????????@ModelAttribute?("hello")????????public?String?getModel()?{???????????System.?out?.println(?"----------Hello---------"?);???????????return?"world"?;????????}????????????@ModelAttribute?("intValue")????????public?int?getInteger()?{???????????System.?out?.println(?"---------intValue---------"?);???????????return?10;????????}????????????@RequestMapping?("sayHello")????????public?void?sayHello(?@ModelAttribute?(?"hello"?)?String?hello,???????????????@ModelAttribute?(?"intValue"?)?int?num,?@ModelAttribute?(?"user2"?)?User?user,???????????Writer?writer,?HttpSession?session)?throws?IOException?{???????????writer.write(?"Hello?"?+?hello?+?"?,?Hello?"?+?user.getUsername()?+?num);???????????writer.write("\r");???????????Enumeration?enume?=?session.getAttributeNames();???????????while?(enume.hasMoreElements())???????????????writer.write(enume.nextElement()?+?"\r"?);????????}????????????@ModelAttribute?(?"user2"?)????????public?User?getUser()?{???????????System.?out?.println(?"---------getUser---------"?);???????????return?new?User(3,?"user2"?);????????}???}??
@Controller
@RequestMapping ("/myTest")
public class MyController { @ModelAttribute ("hello") public String getModel() { System. out .println( "----------Hello---------" ); return "world" ; } @ModelAttribute ("intValue") public int getInteger() { System. out .println( "---------intValue---------" ); return 10; } @RequestMapping ("sayHello") public void sayHello( @ModelAttribute ( "hello" ) String hello, @ModelAttribute ( "intValue" ) int num, @ModelAttribute ( "user2" ) User user, Writer writer, HttpSession session) throws IOException { writer.write( "Hello " + hello + " , Hello " + user.getUsername() + num); writer.write("\r"); Enumeration enume = session.getAttributeNames(); while (enume.hasMoreElements()) writer.write(enume.nextElement() + "\r" ); } @ModelAttribute ( "user2" ) public User getUser() { //User類需要另外定義System. out .println( "---------getUser---------" ); return new User(3, "user2" ); }
}當請求/myTest/sayHello.do時,使用@ModelAttribute標記的方法[getModel,getInteger,getUser]會先執行,然后把它們返回的對象存放到模型中,最終訪問到 sayHello 方法的時候,使用 @ModelAttribute 標記的方法參數都能被正確的注入值。
方法執行結果:Hello world,Hello user210
當 @ModelAttribute 標記在控制器的方法的參數上時,表示該參數的值將從模型或者Session中取對應名稱的屬性值,該名稱可以通過 @ModelAttribute("attributeName") 來指定,若未指定,則使用參數類型的類名稱(首字母小寫)作為屬性名稱。
13,@SessionAttributes
SpringMVC 支持使用@ModelAttribute和@SessionAttributes在不同的模型和控制器之間共享數據。
下面講用于標記需要在Session中使用到的數據,包括從Session 中取數據和存數據。
@SessionAttributes一般是標記在Controller類前面,可以通過指定名稱、類型或者名稱加類型的形式來指定哪些屬性是需要存放在session中。
名稱、類型分別對應@SessionAttributes注解的value和types屬性;
當使用名稱時放在大括號里多個屬性名稱間用逗號分隔,如:@SessionAttributes(value={"user1","blog1"}),當僅有一個名稱時可省value,簡寫為@SessionAttributes("user1"),
當使用的是types屬性時,那么使用的Session屬性名稱將會是對應類型的名稱(首字母小寫),如:@SessionAttributes(types={User.class, Blog.class}),那么
Session中使用的名稱是user和blog,也即Session.getAttribute("user"), Session.getAttribute("blog");
當同時使用名稱和類型時,如:@SessionAttributes(value={"user1", "blog1"}, types={User.class,Blog.class}) ,這時候取的是它們的并集,即Session中有
屬性名為user1,blog1,user,blog四個屬性對應的值,其中user,blog屬性對應的值分別為User.class,Blog.class類型對象;
[java] view plaincopyprint?
??@Controller????@RequestMapping?("/myTest")????@SessionAttributes(value={"user1",?"blog1"},?types={User.class,Blog.class})????public?class?MyController?{????????????@RequestMapping?("setSessionAttribute")????????public?void?setSessionAttribute(Map<String,?Object>?map,?Writer?writer)?throws?IOException?{???????????User?user?=?new?User(1,?"user"?);???????????User?user1?=?new?User(2,?"user1"?);???????????Blog?blog?=?new?Blog(1,?"blog"?);???????????Blog?blog1?=?new?Blog(2,?"blog1"?);???????????map.put("user"?,?user);???????????map.put("user1"?,?user1);???????????map.put("blog"?,?blog);???????????map.put("blog1"?,?blog1);???????????writer.write(?"---------set?value?over---------"?);????????}?????????????????@RequestMapping?("useSessionAttribute")????????public?void?useSessionAttribute(Writer?writer,?@ModelAttribute("user1")?User?user1,?@ModelAttribute("blog1")?Blog?blog1)???????throws?IOException?{???????????writer.write(user1.getId()?+?"--------"?+?user1.getUsername());???????????writer.write(?"\r"?);???????????writer.write(blog1.getId()?+?"--------"?+?blog1.getTitle());????????}????????????@RequestMapping?("useSessionAttribute2")????????public?void?useSessionAttribute(Writer?writer,?@ModelAttribute("user1")?User?user1,?@ModelAttribute("blog1")?Blog?blog1,???????@ModelAttribute?User?user,?HttpSession?session)?throws?IOException?{???????????writer.write(user1.getId()?+?"--------"?+?user1.getUsername());???????????writer.write(?"\r"?);???????????writer.write(blog1.getId()?+?"--------"?+?blog1.getTitle());???????????writer.write(?"\r"?);???????????writer.write(user.getId()?+?"---------"?+?user.getUsername());???????????writer.write(?"\r"?);???????????Enumeration?enume?=?session.getAttributeNames();???????????while?(enume.hasMoreElements())???????????????writer.write(enume.nextElement()?+?"?\r"?);????????}????????????@RequestMapping?("useSessionAttribute3")????????public?void?useSessionAttribute(@ModelAttribute("user2")?User?user){????????????}????}???
//例如
@Controller
@RequestMapping ("/myTest")
@SessionAttributes(value={"user1", "blog1"}, types={User.class,Blog.class})
public class MyController { @RequestMapping ("setSessionAttribute") public void setSessionAttribute(Map<String, Object> map, Writer writer) throws IOException { User user = new User(1, "user" ); //User類需事先定義好User user1 = new User(2, "user1" ); Blog blog = new Blog(1, "blog" ); Blog blog1 = new Blog(2, "blog1" ); map.put("user" , user); map.put("user1" , user1); map.put("blog" , blog); map.put("blog1" , blog1); writer.write( "---------set value over---------" ); } @RequestMapping ("useSessionAttribute") public void useSessionAttribute(Writer writer, @ModelAttribute("user1") User user1, @ModelAttribute("blog1") Blog blog1) throws IOException { writer.write(user1.getId() + "--------" + user1.getUsername()); writer.write( "\r" ); writer.write(blog1.getId() + "--------" + blog1.getTitle()); } @RequestMapping ("useSessionAttribute2") public void useSessionAttribute(Writer writer, @ModelAttribute("user1") User user1, @ModelAttribute("blog1") Blog blog1, @ModelAttribute User user, HttpSession session) throws IOException { writer.write(user1.getId() + "--------" + user1.getUsername()); writer.write( "\r" ); writer.write(blog1.getId() + "--------" + blog1.getTitle()); writer.write( "\r" ); writer.write(user.getId() + "---------" + user.getUsername()); writer.write( "\r" ); Enumeration enume = session.getAttributeNames(); while (enume.hasMoreElements()) writer.write(enume.nextElement() + " \r" ); } @RequestMapping ("useSessionAttribute3") public void useSessionAttribute(@ModelAttribute("user2") User user){ }
} 首先訪問/myTest/setSessionAttribute.do 調用MyController的setSessionAttribute 方法,完成往模型里面添加了user 、user1 、blog 和blog1 四個屬性,因控制器前面有@SessionAttributes定義的需要存到session中的屬性名稱相同或類型相同,所以這四個屬性同時被添加到Session中;
再訪問/myTest/useSessionAttribute.do時,方法參數中用@ModelAttribute指定了參數user1和參數blog1是綁定到session或模型中的同名屬性,
因前一步請求已經完成給session對應屬性名賦值(注意:如第一步訪問沒有,即沒有完成設置值,那后面的訪問將找不到值,但不報錯),故執行結果為:
2------user1
2------blog1
再訪問/myTest/useSessionAttribute2.do,方法參數user、user1和blog1用@ModelAttribute聲明了需要session或模型的屬性值注入,因前面請求已經完成給session和模型對應屬性名賦值,
故指定結果有值,如下:
2------user1
2------blog1
1------user
blog
user
user1
blog1
再訪問/myTest/useSessionAttribute3.do,因方法中user用@ModelAttribute("user2")進行標記,說明(綁定到)使用模型或session中屬性名為user2的屬性值,
因其之前都沒有定義,即不存在,故報錯;
14,@RequestBody?
參見http://snowolf.iteye.com/blog/1628861
將HTTP請求正文轉換為適合的HttpMessageConverter對象。
HttpMessageConverter接口,需要在Spring配置文件中開啟<mvc:annotation-driven />。
15,@ResponseBody
參見http://snowolf.iteye.com/blog/1628861
將內容或對象作為 HTTP 響應正文返回,并調用適合HttpMessageConverter的Adapter轉換對象,寫入輸出流。
HttpMessageConverter接口,需要在Spring配置文件中開啟<mvc:annotation-driven />。
16,@Valid
@Valid標注我們需要校驗的參數;
參見 http://haohaoxuexi.iteye.com/blog/1812584
那么當我們需要使用SpringMVC提供的Validator接口來對該實體類進行校驗的時候該如何做呢?這個時候我們應該提供一個Validator的實現類,并實現Validator接口的supports方法和validate方法。Supports方法用于判斷當前的Validator實現類是否支持校驗當前需要校驗的實體類,只有當supports方法的返回結果為true的時候,該Validator接口實現類的validate方法才會被調用來對當前需要校驗的實體類進行校驗。這里假設我們需要驗證User類的username和password都不能為空,先給出其代碼:
[java] view plaincopyprint?
??import?org.springframework.validation.Errors;????import?org.springframework.validation.ValidationUtils;????import?org.springframework.validation.Validator;?????????public?class?UserValidator?implements?Validator?{?????????????public?boolean?supports(Class<?>?clazz)?{????????????????????return?User.class.equals(clazz);????????}?????????????public?void?validate(Object?obj,?Errors?errors)?{????????????????????ValidationUtils.rejectIfEmpty(errors,?"username",?null,?"Username?is?empty.");???????????User?user?=?(User)?obj;???????????if?(null?==?user.getPassword()?||?"".equals(user.getPassword())){??????????????errors.rejectValue("password",?null,?"Password?is?empty.");???????}???????}???????}?????
//驗證類
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator; public class UserValidator implements Validator { public boolean supports(Class<?> clazz) { // TODO Auto-generated method stub return User.class.equals(clazz); } public void validate(Object obj, Errors errors) { // TODO Auto-generated method stub ValidationUtils.rejectIfEmpty(errors, "username", null, "Username is empty."); User user = (User) obj; if (null == user.getPassword() || "".equals(user.getPassword())){ errors.rejectValue("password", null, "Password is empty."); } }
}
//ValidationUtils類是Spring中提供的一個工具類。Errors就是Spring用來存放錯誤信息的對象。我們已經定義了一個對User類進行校驗的UserValidator了,但是這個時候UserValidator還不能對User對象進行校驗,因為我們還沒有告訴Spring應該使用UserValidator來校驗User對象。在SpringMVC中我們可以使用DataBinder來設定當前Controller需要使用的Validator。先來看下面一段代碼:
[java] view plaincopyprint?
import?javax.validation.Valid;????import?org.springframework.stereotype.Controller;????import?org.springframework.validation.BindingResult;????import?org.springframework.validation.DataBinder;????import?org.springframework.web.bind.annotation.InitBinder;????import?org.springframework.web.bind.annotation.RequestMapping;?????????@Controller????public?class?UserController?{???????????????@InitBinder????????public?void?initBinder(DataBinder?binder)?{???????????binder.setValidator(new?UserValidator());????????}?????????????@RequestMapping("login")????????public?String?login(@Valid?User?user,?BindingResult?result)?{???????????if?(result.hasErrors())?{?return?"redirect:user/login";?}?????????return?"redirect:/";????????}????}????
import javax.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.DataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RequestMapping; @Controller
public class UserController { @InitBinder public void initBinder(DataBinder binder) { binder.setValidator(new UserValidator()); } @RequestMapping("login") public String login(@Valid User user, BindingResult result) { if (result.hasErrors()) { return "redirect:user/login"; }return "redirect:/"; }
} @Valid標注我們需要校驗的參數,否則Spring不會對它進行校驗。另外我們的處理器方法必須給定包含Errors的參數,這可以是Errors本身,也可以是它的子類BindingResult,使用了Errors參數就是告訴Spring關于表單對象數據校驗的錯誤將由我們自己來處理,否則Spring會直接拋出異常,而且這個參數是必須緊挨著@Valid參數的,即必須緊挨著需要校驗的參數,這就意味著我們有多少個@Valid參數就需要有多少個對應的Errors參數,它們是一一對應的。
17,@Scope
@Scope 簡單點說就是用來指定bean的作用域(官方解釋:scope用來聲明IOC容器中的對象應該處的限定場景或者說該對象的存活空間,即在IOC容器在對象進入相應的scope之前,生成并裝配這些對象,在該對象不再處于這些scope的限定之后,容器通常會銷毀這些對象),其默認作用域是"singleton",如果要換成其他作用區域,直接后面添加類型即可,比如@Scope("prototype") ,注意spring2.0后 又增加了request ,session和global session 4個作用區域;
如果需要的bean實例是個單例,則定義@Scope("singleton"),如果是每次都new一個新的,則用@Scope("prototype");
四,以 @RequestMapping 標記的控制器(也稱:處理器)的方法支持的方法參數和返回類型
原文見: http://haohaoxuexi.iteye.com/blog/1753271
1,支持的方法參數類型
(1)HttpServlet 對象,主要包括HttpServletRequest 、HttpServletResponse 和HttpSession 對象。 這些參數Spring 在調用處理器方法的時候會自動給它們賦值,所以當在處理器方法中需要使用到這些對象的時候,可以直接在方法上給定一個方法參數的申明,然后在方法體里面直接用就可以了。但是有一點需要注意的是在使用HttpSession 對象的時候,如果此時HttpSession 對象還沒有建立起來的話就會有問題。
(2)Spring 自己的WebRequest 對象。 使用該對象可以訪問到存放在HttpServletRequest 和HttpSession 中的屬性值。
(3)InputStream 、OutputStream 、Reader 和Writer 。 InputStream 和Reader 是針對HttpServletRequest 而言的,可以從里面取數據;OutputStream 和Writer 是針對HttpServletResponse 而言的,可以往里面寫數據。
(4)使用@PathVariable 、@RequestParam 、@CookieValue 和@RequestHeader 標記的參數。
(5)使用@ModelAttribute 標記的參數。
(6)java.util.Map 、Spring 封裝的Model 和ModelMap 。 這些都可以用來封裝模型數據,用來給視圖做展示。
(7)實體類。 可以用來接收上傳的參數。
(8)Spring 封裝的MultipartFile 。 用來接收上傳文件的。
(9)Spring 封裝的Errors 和BindingResult 對象。 這兩個對象參數必須緊接在需要驗證的實體對象參數之后,它里面包含了實體對象的驗證結果。
2,支持的返回類型
(1)一個包含模型和視圖的ModelAndView 對象。
(2)一個模型對象,這主要包括Spring 封裝好的Model 和ModelMap ,以及java.util.Map ,當沒有視圖返回的時候視圖名稱將由RequestToViewNameTranslator 來決定。
(3)一個View 對象。這個時候如果在渲染視圖的過程中模型的話就可以給處理器方法定義一個模型參數,然后在方法體里面往模型中添加值。
(4)一個String 字符串。這往往代表的是一個視圖名稱。這個時候如果需要在渲染視圖的過程中需要模型的話就可以給處理器方法一個模型參數,然后在方法體里面往模型中添加值就可以了。
(5)返回值是void 。這種情況一般是我們直接把返回結果寫到HttpServletResponse 中了,如果沒有寫的話,那么Spring 將會利用RequestToViewNameTranslator 來返回一個對應的視圖名稱。如果視圖中需要模型的話,處理方法與返回字符串的情況相同。
(6)如果處理器方法被注解@ResponseBody 標記的話,那么處理器方法的任何返回類型都會通過HttpMessageConverters 轉換之后寫到HttpServletResponse?中,而不會像上面的那些情況一樣當做視圖或者模型來處理。
(7)除以上幾種情況之外的其他任何返回類型都會被當做模型中的一個屬性來處理,而返回的視圖還是由RequestToViewNameTranslator 來決定,添加到模型中的屬性名稱可以在該方法上用@ModelAttribute("attributeName") 來定義,否則將使用返回類型的類名稱的首字母小寫形式來表示。使用@ModelAttribute?標記的方法會在@RequestMapping 標記的方法執行之前執行。
五,定制自己的類型轉換器?
原文見:http://haohaoxuexi.iteye.com/blog/1753271
在通過處理器方法參數接收 request 請求參數綁定數據的時候,對于一些簡單的數據類型 Spring 會幫我們自動進行類型轉換,而對于一些復雜的類型由于?Spring 沒法識別,所以也就不能幫助我們進行自動轉換了,這個時候如果我們需要 Spring 來幫我們自動轉換的話就需要我們給 Spring 注冊一個對特定類型的識別轉換器。?
Spring 允許我們提供兩種類型的識別轉換器,一種是注冊在 Controller 中的,一種是注冊在 SpringMVC 的配置文件中。聰明的讀者看到這里應該可以想到它們的區別了,定義在 Controller 中的是局部的,只在當前 Controller 中有效,而放在 SpringMVC 配置文件中的是全局的,所有 Controller 都可以拿來使用。
1,在控制器類中定義局部的類型轉換器,并用 @InitBinder 標記"告知"當前控制器
我們可以使用 @InitBinder 注解標注在 Controller 方法上,然后在方法體里面注冊數據綁定的轉換器,這主要是通過 WebDataBinder 進行的。我們可以給需要注冊數據綁定的轉換器的方法一個 WebDataBinder 參數,然后給該方法加上 @InitBinder 注解,這樣當該 Controller 中在處理請求方法時如果發現有不能解析的對象的時候,就會看該類中是否有使用 @InitBinder 標記的方法,如果有就會執行該方法,然后看里面定義的類型轉換器是否與當前需要的類型匹配。
[java] view plaincopyprint?
@Controller????@RequestMapping?("/myTest")????public?class?MyController?{????????????@InitBinder????????public?void?dataBinder(WebDataBinder?binder)?{???????????DateFormat?dateFormat?=?new?SimpleDateFormat("yyyyMMdd");???????????PropertyEditor?propertyEditor?=?new?CustomDateEditor(dateFormat,?true?);????????????????binder.registerCustomEditor(Date.?class?,?propertyEditor);????????}????????????@RequestMapping?(?"dataBinder/{date}"?)????????public?void?testDate(?@PathVariable?Date?date,?Writer?writer)?throws?IOException?{???????????writer.write(String.valueOf?(date.getTime()));????????}??????}???
@Controller
@RequestMapping ("/myTest")
public class MyController { @InitBinder public void dataBinder(WebDataBinder binder) { DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd"); PropertyEditor propertyEditor = new CustomDateEditor(dateFormat, true ); // 第二個參數true,表示是否允許為空 binder.registerCustomEditor(Date. class , propertyEditor); } @RequestMapping ( "dataBinder/{date}" ) public void testDate( @PathVariable Date date, Writer writer) throws IOException { writer.write(String.valueOf (date.getTime())); }
} 在上面的代碼中當我們請求 /myTest/dataBinder/20121212.do 的時候,Spring 就會利用 @InitBinder 標記的方法里面定義的類型轉換器把字符串?20121212 轉換為一個 Date 對象。這樣定義的類型轉換器是局部的類型轉換器,一旦出了這個 Controller 就不會再起作用。類型轉換器是通過WebDataBinder 對象的 registerCustomEditor 方法來注冊的,要實現自己的類型轉換器就要實現自己的 PropertyEditor 對象。 Spring 已經給我們提供了一些常用的屬性編輯器,如 CustomDateEditor、 CustomBooleanEditor 等
PropertyEditor 是一個接口,要實現自己的 PropertyEditor 類我們可以實現這個接口,然后實現里面的方法。但是 PropertyEditor 里面定義的方法太多了,這樣做比較麻煩。在 java 中有一個封裝類是實現了 PropertyEditor 接口的,它是 PropertyEditorSupport 類。所以如果需要實現自己的PropertyEditor 的時候只需要繼承 PropertyEditorSupport 類,然后重寫其中的一些方法。一般就是重寫 setAsText 和 getAsText 方法就可以了,?setAsText 方法是用于把字符串類型的值轉換為對應的對象的,而 getAsText 方法是用于把對象當做字符串來返回的。在 setAsText 中我們一般先把字符串類型的對象轉為特定的對象,然后利用 PropertyEditor 的 setValue 方法設定轉換后的值。在 getAsText 方法中一般先使用 getValue 方法取代當前的對象,然后把它轉換為字符串后再返回給 getAsText 方法。下面是一個示例:
[java] view plaincopyprint?
@InitBinder????public?void?dataBinder(WebDataBinder?binder)?{????????????PropertyEditor?userEditor?=?new?PropertyEditorSupport()?{???????????????@Override???????????public?String?getAsText()?{??????????????????????????User?user?=?(User)?getValue();??????????????return?user.getUsername();???????????}???????????????@Override???????????public?void?setAsText(String?userStr)?throws?IllegalArgumentException?{??????????????????????????User?user?=?new?User(1,?userStr);??????????????setValue(user);???????????}???????};????????????binder.registerCustomEditor(User.?class?,?userEditor);????}??
@InitBinder
public void dataBinder(WebDataBinder binder) { // 定義一個 User 屬性編輯器 PropertyEditor userEditor = new PropertyEditorSupport() { @Override public String getAsText() { // TODO Auto-generated method stub User user = (User) getValue(); return user.getUsername(); } @Override public void setAsText(String userStr) throws IllegalArgumentException { // TODO Auto-generated method stub User user = new User(1, userStr); setValue(user); } }; // 使用 WebDataBinder 注冊 User 類型的屬性編輯器 binder.registerCustomEditor(User. class , userEditor);
}
2,實現 WebBindingInitializer 接口定義全局的類型轉換器
如果需要定義全局的類型轉換器就需要實現自己的 WebBindingInitializer 對象,然后把該對象注入到 AnnotationMethodHandlerAdapter 中,這樣 Spring在遇到自己不能解析的對象的時候就會到全局的 WebBindingInitializer 的 initBinder 方法中去找,每次遇到不認識的對象時, initBinder 方法都會被執行一遍。
[java] view plaincopyprint?
public?class?MyWebBindingInitializer?implements?WebBindingInitializer?{????????????@Override????????public?void?initBinder(WebDataBinder?binder,?WebRequest?request)?{????????????????????DateFormat?dateFormat?=?new?SimpleDateFormat("yyyyMMdd");???????????PropertyEditor?propertyEditor?=?new?CustomDateEditor(dateFormat,?true?);???????????binder.registerCustomEditor(Date.?class?,?propertyEditor);????????}??????}????
public class MyWebBindingInitializer implements WebBindingInitializer { @Override public void initBinder(WebDataBinder binder, WebRequest request) { // TODO Auto-generated method stub DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd"); PropertyEditor propertyEditor = new CustomDateEditor(dateFormat, true ); binder.registerCustomEditor(Date. class , propertyEditor); }
} 定義了這么一個 WebBindingInitializer 對象之后 Spring 還是不能識別其中指定的對象,這是因為我們只是定義了 WebBindingInitializer 對象,還沒有把它交給 Spring , Spring 不知道該去哪里找解析器。要讓 Spring 能夠識別還需要我們在 SpringMVC 的配置文件中定義一個AnnotationMethodHandlerAdapter 類型的 bean 對象,然后利用自己定義的 WebBindingInitializer 覆蓋它的默認屬性 webBindingInitializer 。
[java] view plaincopyprint?
<bean?class?=?"org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">???????<property?name?=?"webBindingInitializer">???????????<bean?class?=?"com.host.app.web.util.MyWebBindingInitializer"/>???????</property>????</bean>???
<bean class = "org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> <property name = "webBindingInitializer"> <bean class = "com.host.app.web.util.MyWebBindingInitializer"/> </property>
</bean>
3,觸發數據綁定方法的時間
當Controller處理器方法參數使用@RequestParam、@PathVariable、@RequestHeader、@CookieValue和@ModelAttribute標記的時候都會觸發initBinder方法的執行,這包括使用WebBindingInitializer定義的全局方法和在Controller中使用@InitBinder標記的局部方法。而且每個使用了這幾個注解標記的參數都會觸發一次initBinder方法的執行,這也意味著有幾個參數使用了上述注解就會觸發幾次initBinder方法的執行。
本文很多原文見如下博主鏈接,部分有修改或整理其它地方得到
http://haohaoxuexi.iteye.com/blog/1753271
更多見博客:
http://haohaoxuexi.iteye.com/blog/
總結
以上是生活随笔為你收集整理的有关Spring注解@xxx的零碎知识的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。