spring 經常使用註解

一、model層

1.一、@Required

@Required註解檢查 但他只檢查屬性是否已經設置而不會測試屬性是否非空。@Required註解適用於bean屬性的setter方法而且它指示,受影響的bean屬性必須在配置時被填充在XML配置文件中,不然容器將拋出BeanInitializationException例外。java

public class SimpleMovieLister {
    private String movieFinder;
    @Required
    public void setMovieFinder(String movieFinder) {
        this.movieFinder = movieFinder;
    }
    // ...
}

必須在xml中進行配置web

<?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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.0.xsd">

   <context:annotation-config/>

   <bean id="SimpleMovieLister" class="com.yuexiu.SimpleMovieLister">
      <property name="movieFinder"  value="defualt String" />
   </bean>

</beans>

補充資料

http://www.yiibai.com/spring/spring_required_annotation.htmlspring

1.二、@Autowired

Spring 2.5 引入了 @Autowired 註釋,它能夠對類成員變量、方法及構造函數進行標註,完成自動裝配的工做。 經過 @Autowired的使用來消除 set ,get方法。數據庫

public class MovieRecommender {
    private final CustomerPreferenceDao customerPreferenceDao;
    @Autowired
    public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
        this.customerPreferenceDao = customerPreferenceDao;
    }
    // ...
}

在applicationContext.xml中加入:express

<!-- 該 BeanPostProcessor 將自動對標註 @Autowired 的 Bean 進行注入 -->   
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>

Spring 經過一個 BeanPostProcessor 對 @Autowired 進行解析,因此要讓 @Autowired 起做用必須事先在 Spring 容器中聲明 AutowiredAnnotationBeanPostProcessor Bean。json

補充資料

http://blog.csdn.net/heyutao007/article/details/5981555api

1.三、@primary

當有多個可注入對象而致使使用模糊的時候可使用@primary進行註解spring-mvc

@Component
public class MetalSinger implements Singer{

    @Override
    public String sing(String lyrics) {
        return "I am singing with DIO voice: "+lyrics;
    }
}
public class OperaSinger implements Singer {  
    @Override  
    public String sing(String lyrics) {  
        return "I am singing in Bocelli voice: "+lyrics;  
    }  
}

這兩個類都實現了singer接口tomcat

public interface Singer {  
    String sing(String lyrics);  
}

那麼來個注入的:

@Component  
public class SingerService {  
    private static final Logger logger = LoggerFactory.getLogger(SingerService.class);  
  
    @Autowired  
    private Singer singer;  
  
    public String sing(){  
        return singer.sing("song lyrics");  
    }  
}

那麼最後輸出應該是:I am singing with DIO voice: song lyrics.

緣由是隻有MetalSinger使用了註解@component,那麼autowird會只尋找這個標記的
進行注入:
若是也在OperaSinger 中使用了註解,則spring會報異常,不知道到底應該用
哪一個:

org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [main.service.Singer] is defined: expected single matching bean but found 2: metalSinger,operaSinger

而若是要讓spring知道必須注入的是OperaSinger ,則能夠用@primary註解告訴
spring:

@Primary  
@Component  
public class OperaSinger implements Singer{  
    @Override  
    public String sing(String lyrics) {  
        return "I am singing in Bocelli voice: "+lyrics;  
    }  
}

1.四、@Qualifier

有以下接口:

public interface EmployeeService {
    public EmployeeDto getEmployeeById(Long id);
}

同時有下述兩個實現類 EmployeeServiceImpl和EmployeeServiceImpl1:

@Service("service")
public class EmployeeServiceImpl implements EmployeeService {
    public EmployeeDto getEmployeeById(Long id) {
        return new EmployeeDto();
    }
}

@Service("service1")
public class EmployeeServiceImpl1 implements EmployeeService {
    public EmployeeDto getEmployeeById(Long id) {
        return new EmployeeDto();
    }
}

調用代碼以下:

@Controller
@RequestMapping("/emplayee.do")
public class EmployeeInfoControl {
    
    @Autowired
    EmployeeService employeeService;
     
    @RequestMapping(params = "method=showEmplayeeInfo")
    public void showEmplayeeInfo(HttpServletRequest request, HttpServletResponse response, EmployeeDto dto) {
        #略
    }
}

在啓動tomcat時報以下錯誤:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'employeeInfoControl': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: com.test.service.EmployeeService com.test.controller.EmployeeInfoControl.employeeService; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [com.test.service.EmployeeService] is defined: expected single matching bean but found 2: [service1, service2]

其實報錯信息已經說得很明確了,在autoware時,因爲有兩個類實現了EmployeeService接口,因此Spring不知道應該綁定哪一個實現類,因此拋出瞭如上錯誤。

這個時候就要用到@Qualifier註解了,qualifier的意思是合格者,經過這個標示,代表了哪一個實現類纔是咱們所須要的,咱們修改調用代碼,添加@Qualifier註解,須要注意的是@Qualifier的參數名稱必須爲咱們以前定義@Service註解的名稱之一!

@Controller
@RequestMapping("/emplayee.do")
public class EmployeeInfoControl {
    
    @Autowired
    @Qualifier("service")
    EmployeeService employeeService;
    
    @RequestMapping(params = "method=showEmplayeeInfo")
    public void showEmplayeeInfo(HttpServletRequest request, HttpServletResponse response, EmployeeDto dto) {
        #略
    }
}

1.五、@configuration和@Bean

@Configuration標註在類上,至關於把該類做爲spring的xml配置文件中的 ,做用爲:配置spring容器(應用上下文).提供這類註解,只是爲了在某些狀況下簡化XML的配置,並不是要取代XML配置方式。
@bean是註解在類上面的,聲明這是個bean,不用去xml配置什麼
@configuration也是註解在類上面的,聲明這是個配置做用的bean,替代xml配置

@configuration註解與XML配置方式結合使用

@Configuration
@ImportResource("classpath:applicationContext.xml")
public class WebConfig {
}

web.xml配置spring配置文件位置

<!-- 配置spring監聽器 -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<!-- 指定spring相關配置文件的位置 -->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>com.stepwell.configure.WebConfig</param-value>
</context-param>

註解配置引入其餘的註解配置

@Configuration
@Import(WebSocketConfig.class)
public class WebConfig {
    @Autowired
    WebSocketConfig webSocketConfig;

}

註解配置spring mvc

@Configuration
@EnableWebMvc
public class WebConfig {

}

註解配置Bean

@Configuration
public class AppConfig {

    @Bean
    public MyService myService() {
        return new MyServiceImpl();
    }

}

同配置:

<beans>
    <bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>

掃描範圍配置

@Configuration
@ComponentScan(basePackages = "com.acme")
public class AppConfig  {
    ...
}

同配置

<beans>
    <context:component-scan base-package="com.acme"/>
</beans>

@Resource

@Service
public class Zoo
{
    @Resource(name = "tiger")
    private Tiger tiger;
    
    @Resource(type = Monkey.class)
    private Monkey monkey;
    
    public String toString()
    {
        return tiger + "\n" + monkey;
    }
}

這是詳細一些的用法,說一下@Resource的裝配順序:

  • 一、@Resource後面沒有任何內容,默認經過name屬性去匹配bean,找不到再按type去匹配

  • 二、指定了name或者type則根據指定的類型去匹配bean

  • 三、指定了name和type則根據指定的name和type去匹配bean,任何一個不匹配都將報錯

而後,區分一下@Autowired和@Resource兩個註解的區別:

  • 一、@Autowired默認按照byType方式進行bean匹配,@Resource默認按照byName方式進行bean匹配

  • 二、@Autowired是Spring的註解,@Resource是J2EE的註解,這個看一下導入註解的時候這兩個註解的包名就一清二楚了

Spring屬於第三方的,J2EE是Java本身的東西,所以,建議使用@Resource註解,以減小代碼和Spring之間的耦合。

補充資料

http://www.cnblogs.com/szlbm/p/5512931.html

@PostConstruct and @PreDestroy

關於在spring 容器初始化 bean 和銷燬前所作的操做定義方式有三種:

  • 第一種:經過@PostConstruct 和 @PreDestroy 方法 實現初始化和銷燬bean以前進行的操做
  • 第二種是:經過 在xml中定義init-method 和 destory-method方法
  • 第三種是: 經過bean實現InitializingBean和 DisposableBean接口

下面演示經過 @PostConstruct 和 @PreDestory

1:定義相關的實現類:

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

public class PersonService {
  
    private String  message;

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
    
    @PostConstruct
    public void  init(){
        System.out.println("I'm  init  method  using  @PostConstrut...."+message);
    }
    
    @PreDestroy
    public void  dostory(){
        System.out.println("I'm  destory method  using  @PreDestroy....."+message);
    }
    
}

2:定義相關的配置文件:

<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd">

<!-- <context:component-scan  base-package="com.myapp.core.jsr330"/> -->

<context:annotation-config />

<bean id="personService" class="com.myapp.core.annotation.init.PersonService">
  <property name="message" value="123"></property>
</bean>

</beans>

其中 告訴spring 容器採用註解配置:掃描註解配置;

3:測試類:

package com.myapp.core.annotation.init;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainTest {
    
    public static void main(String[] args) {        
        ApplicationContext  context = new ClassPathXmlApplicationContext("resource/annotation.xml");    
        PersonService   personService  =  (PersonService)context.getBean("personService");
        personService.dostory();
    }

}

測試結果:
I'm init method using @PostConstrut....123
I'm destory method using @PreDestroy.....123

1.八、@Scope

默認是單例模式,即scope="singleton"。另外scope還有prototype、request、session、global session做用域。scope="prototype"多例

@scope默認是單例模式(singleton)

若是須要設置的話@scope("prototype")

1.singleton單例模式,

  全局有且僅有一個實例

2.prototype原型模式,

  每次獲取Bean的時候會有一個新的實例

3.request

  request表示該針對每一次HTTP請求都會產生一個新的bean,同時該bean僅在當前HTTP request內有效,配置實例:
request、session、global session使用的時候首先要在初始化web的web.xml中作以下配置:
若是你使用的是Servlet 2.4及以上的web容器,那麼你僅須要在web應用的XML聲明文件web.xml中增長下述ContextListener便可:

3
4
5
6
7
<web-app>
   ...
  <listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
  </listener>
   ...
</web-app>

4.session

  session做用域表示該針對每一次HTTP請求都會產生一個新的bean,同時該bean僅在當前HTTP session內有效

5.global session

global session做用域相似於標準的HTTP Session做用域,不過它僅僅在基於portlet的web應用中才有意義。Portlet規範定義了全局Session的概念,它被全部構成某個 portlet web應用的各類不一樣的portlet所共享。在global session做用域中定義的bean被限定於全局portlet Session的生命週期範圍內。若是你在web中使用global session做用域來標識bean,那麼web會自動當成session類型來使用。

Dao層

@Repository

spring 自 2.0 版本開始,陸續引入了一些註解用於簡化 Spring 的開發。@Repository註解便屬於最早引入的一批,它用於將數據訪問層 (DAO 層 ) 的類標識爲 Spring Bean。具體只需將該註解標註在 DAO類上便可。同時,爲了讓 Spring 可以掃描類路徑中的類並識別出 @Repository 註解,須要在 XML 配置文件中啓用Bean 的自動掃描功能,這能夠經過 實現。

首先使用 @Repository 將 DAO 類聲明爲 Bean

package bookstore.dao; 
 @Repository 
 public class UserDaoImpl implements UserDao{ …… }

其次,在 XML 配置文件中啓動 Spring 的自動掃描功能

<beans … > 
    ……
 <context:component-scan base-package=」bookstore.dao」 /> 
……
 </beans>

如此,咱們就再也不須要在 XML 中顯式使用 進行Bean 的配置。Spring 在容器初始化時將自動掃描 base-package 指定的包及其子包下的全部 class文件,全部標註了 @Repository 的類都將被註冊爲 Spring Bean。

爲何 @Repository 只能標註在 DAO 類上呢?這是由於該註解的做用不僅是將類識別爲Bean,同時它還能將所標註的類中拋出的數據訪問異常封裝爲 Spring 的數據訪問異常類型。 Spring自己提供了一個豐富的而且是與具體的數據訪問技術無關的數據訪問異常結構,用於封裝不一樣的持久層框架拋出的異常,使得異常獨立於底層的框架。

web層

一、@Controller

在SpringMVC 中,控制器Controller 負責處理由DispatcherServlet 分發的請求,它把用戶請求的數據通過業務處理層處理以後封裝成一個Model ,而後再把該Model 返回給對應的View 進行展現。在SpringMVC 中提供了一個很是簡便的定義Controller 的方法,你無需繼承特定的類或實現特定的接口,只需使用@Controller 標記一個類是Controller ,而後使用@RequestMapping 和@RequestParam 等一些註解用以定義URL 請求和Controller 方法之間的映射,這樣的Controller 就能被外界訪問到。此外Controller 不會直接依賴於HttpServletRequest 和HttpServletResponse 等HttpServlet 對象,它們能夠經過Controller 的方法參數靈活的獲取到。

@Controller    //控制器
@RequestMapping("/mall/cartitem/*")        //映射路徑
public class CartItemController {

    @Resource
//默認按bean的name進行查找,若是沒有找到會按type進行查找此時與@Autowired相似,在沒有爲 @Resource 註解
//其它狀況 :                                                             
//顯式指定 name 屬性的前提下,若是將其標註在 BeanFactory 類型、ApplicationContext 類型、ResourceLoader 類型、
//一、@Resource(name="dataSource")                 
//ApplicationEventPublisher 類型、MessageSource 類型上,那麼 Spring 會自動注入這些實現類的實例,不須要額外的
//二、@Resource(type=DataSource.class)              
//操做。此時 name 屬性不須要指定 ( 或者指定爲""),不然注入失敗;
    private IAdminLogService iadminlogservice;          
                                                                                          
}

@Controller用於標記在一個類上,使用它標記的類就是一個SpringMVC Controller 對象。分發處理器將會掃描使用了該註解的類的方法,並檢測該方法是否使用了@RequestMapping 註解。

@Controller 只是定義了一個控制器類,而使用@RequestMapping 註解的方法纔是真正處理請求的處理器。單單使用@Controller 標記在一個類上還不能真正意義上的說它就是SpringMVC 的一個控制器類,由於這個時候Spring 還不認識它。那麼要如何作Spring 才能認識它呢?這個時候就須要咱們把這個控制器類交給Spring 來管理。有兩種方式:

  • (1)在SpringMVC 的配置文件中定義MyController 的bean 對象。

  • (2)在SpringMVC 的配置文件中告訴Spring 該到哪裏去找標記爲@Controller 的Controller 控制器。

<!--方式一-->
<bean class="com.host.app.web.controller.MyController"/>
<!--方式二-->
<context:component-scan base-package = "com.host.app.web" />
//路徑寫到controller的上一層(掃描包詳解見下面淺析)

二、@RequestMapping

RequestMapping是一個用來處理請求地址映射的註解,可用於++類++或++方法++上。用於類上,表示類中的全部響應請求的方法都是以該地址做爲父路徑。

RequestMapping註解有六個屬性,下面咱們把她分紅三類進行說明(下面有相應示例)。

2.一、 value, method

  • value: 指定請求的實際地址,指定的地址能夠是URI Template 模式(後面將會說明);

  • method: 指定請求的method類型, GET、POST、PUT、DELETE等;

2.二、consumes,produces

  • consumes: 指定處理請求的提交內容類型(Content-Type),例如application/json, text/html;

  • produces: 指定返回的內容類型,僅當request請求頭中的(Accept)類型中包含該指定類型才返回;

2.三、params,headers

  • params: 指定request中必須包含某些參數值是,才讓該方法處理。

  • headers: 指定request中必須包含某些指定的header值,才能讓該方法處理請求。

三、@Resource和@Autowired

@Resource和@Autowired都是作bean的注入時使用,其實@Resource並非Spring的註解,它的包是javax.annotation.Resource,須要導入,可是Spring支持該註解的注入。

3.一、共同點

二者均可以寫在字段和setter方法上。二者若是都寫在字段上,那麼就不須要再寫setter方法。

3.二、不一樣點

(1)@Autowired

@Autowired爲Spring提供的註解,須要導入包org.springframework.beans.factory.annotation.Autowired;只按照byType注入。

public class TestServiceImpl {
    // 下面兩種@Autowired只要使用一種便可
    @Autowired
    private UserDao userDao; // 用於字段上
    
    @Autowired
    public void setUserDao(UserDao userDao) { // 用於屬性的方法上
        this.userDao = userDao;
    }
}

@Autowired註解是==按照類型(byType)裝配==依賴對象,默認狀況下它要求依賴對象必須存在,若是容許null值,能夠設置它的required屬性爲false。若是咱們想使用==按照名稱(byName)來裝配==,能夠結合@Qualifier註解一塊兒使用。以下:

public class TestServiceImpl {
    @Autowired
    @Qualifier("userDao")
    private UserDao userDao; 
}
(2)@Resource

@Resource默認按照ByName自動注入,由J2EE提供,須要導入包javax.annotation.Resource
@Resource有兩個重要的屬性:name和type,而Spring將@Resource註解的name屬性解析爲bean的名字,而type屬性則解析爲bean的類型。因此,若是使用name屬性,則使用byName的自動注入策略,而使用type屬性時則使用byType自動注入策略。++若是既不制定name也不制定type屬性,這時將經過反射機制使用byName自動注入策略。++

public class TestServiceImpl {
    // 下面兩種@Resource只要使用一種便可
    @Resource(name="userDao")
    private UserDao userDao; // 用於字段上
    
    @Resource(name="userDao")
    public void setUserDao(UserDao userDao) { // 用於屬性的setter方法上
        this.userDao = userDao;
    }
}

注:最好是將@Resource放在setter方法上,由於這樣更符合面向對象的思想,經過set、get去操做屬性,而不是直接去操做屬性。

@Resource裝配順序:

  • ①若是同時指定了name和type,則從Spring上下文中找到惟一匹配的bean進行裝配,找不到則拋出異常。

  • ②若是指定了name,則從上下文中查找名稱(id)匹配的bean進行裝配,找不到則拋出異常。

  • ③若是指定了type,則從上下文中找到相似匹配的惟一bean進行裝配,找不到或是找到多個,都會拋出異常。

  • ④若是既沒有指定name,又沒有指定type,則自動按照byName方式進行裝配;若是沒有匹配,則回退爲一個原始類型進行匹配,若是匹配則自動裝配。

==@Resource的做用至關於@Autowired,只不過@Autowired按照byType自動注入。==

四、@ModelAttribute和 @SessionAttributes

表明的是:該Controller的全部方法在調用前,先執行此@ModelAttribute方法,可用於註解和方法參數中,能夠把這個@ModelAttribute特性,應用在BaseController當中,全部的Controller繼承BaseController,便可實如今調用Controller時,先執行@ModelAttribute方法。

@SessionAttributes即將值放到session做用域中,寫在class上面。

具體示例參見下面:使用 @ModelAttribute 和 @SessionAttributes 傳遞和保存數據

五、@PathVariable

用於將請求URL中的模板變量映射到功能處理方法的參數上,即取出uri模板中的變量做爲參數。如:

@Controller  
public class TestController {  
     @RequestMapping(value="/user/{userId}/roles/{roleId}",method = RequestMethod.GET)  
     public String getLogin(@PathVariable("userId") String userId,  
         @PathVariable("roleId") String roleId){  
         System.out.println("User Id : " + userId);  
         System.out.println("Role Id : " + roleId);  
         return "hello";  
     }  
     @RequestMapping(value="/product/{productId}",method = RequestMethod.GET)  
     public String getProduct(@PathVariable("productId") String productId){  
           System.out.println("Product Id : " + productId);  
           return "hello";  
     }  
     @RequestMapping(value="/javabeat/{regexp1:[a-z-]+}",  
           method = RequestMethod.GET)  
     public String getRegExp(@PathVariable("regexp1") String regexp1){  
           System.out.println("URI Part 1 : " + regexp1);  
           return "hello";  
     }  
}

六、@requestParam

@requestParam主要用於在SpringMVC後臺控制層獲取參數,相似一種是request.getParameter("name"),它有三個經常使用參數:defaultValue = "0", required = false, value = "isApp";

  • defaultValue 表示設置默認值
  • required 銅過boolean設置是不是必需要傳入的參數
  • value 值表示接受的傳入的參數類型。

七、@ResponseBody

做用: 該註解用於將Controller的方法返回的對象,經過適當的HttpMessageConverter轉換爲指定格式後,寫入到Response對象的body數據區。

使用時機:返回的數據不是html標籤的頁面,而是其餘某種格式的數據時(如json、xml等)使用;

八、@Component

至關於通用的註解,當不知道一些類歸到哪一個層時使用,可是不建議。

九、@Repository

用於註解dao層,在daoImpl類上面註解。

十、@Transactional

spring 事務註解

默認遇到throw new RuntimeException("...");會回滾
須要捕獲的throw new Exception("...");不會回滾

// 指定回滾
@Transactional(rollbackFor=Exception.class) 
    public void methodName() {
       // 會回滾
       throw new Exception("...");
    } 

//指定不回滾
@Transactional(noRollbackFor=Exception.class)
    public ItimDaoImpl getItemDaoImpl() {
        //不會回滾
        throw new RuntimeException("註釋");
    } 

    // 若是有事務,那麼加入事務,沒有的話新建一個(不寫的狀況下)
    @Transactional(propagation=Propagation.REQUIRED) 
    // 容器不爲這個方法開啓事務
    @Transactional(propagation=Propagation.NOT_SUPPORTED)
    // 無論是否存在事務,都建立一個新的事務,原來的掛起,新的執行完畢,繼續執行老的事務
    @Transactional(propagation=Propagation.REQUIRES_NEW) 
    // 必須在一個已有的事務中執行,不然拋出異常
    @Transactional(propagation=Propagation.MANDATORY)
    // 必須在一個沒有的事務中執行,不然拋出異常(與Propagation.MANDATORY相反)
    @Transactional(propagation=Propagation.NEVER) 
    // 若是其餘bean調用這個方法,在其餘bean中聲明事務,那就用事務.若是其餘bean沒有聲明事務,那就不用事務.
    @Transactional(propagation=Propagation.SUPPORTS) 

    /*
    public void methodName(){
       // 本類的修改方法 1
       update();
       // 調用其餘類的修改方法
       otherBean.update();
       // 本類的修改方法 2
       update();
    }
    other失敗了不會影響 本類的修改提交成功
    本類update的失敗,other也失敗
    */

@Transactional(propagation=Propagation.NESTED) // readOnly=true只讀,不能更新,刪除 
@Transactional (propagation = Propagation.REQUIRED,readOnly=true) // 設置超時時間
@Transactional (propagation = Propagation.REQUIRED,timeout=30)// 設置數據庫隔離級別
@Transactional (propagation = Propagation.REQUIRED,isolation=Isolation.DEFAULT)

注:

一、使用 @RequestMapping 來映射 Request 請求與處理器

  • 方式1、經過常見的類路徑和方法路徑結合訪問controller方法

  • 方式2、使用uri模板

@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;
    }
}

URI 模板就是在URI 中給定一個變量,而後在映射的時候動態的給該變量賦值。如URI 模板http://localhost/app/{variable1}/index.html ,這個模板裏面包含一個變量variable1 ,那麼當咱們請求http://localhost/app/hello/index.html 的時候,該URL 就跟模板相匹配,只是把模板中的variable1 用hello 來取代。這個變量在SpringMVC 中是使用@PathVariable 來標記的。在SpringMVC 中,咱們可使用@PathVariable 來標記一個Controller 的處理方法參數,表示該參數的值將使用URI 模板中對應的變量的值來賦值。

代碼中咱們定義了兩個URI 變量,一個是控制器類上的variable1 ,一個是showView 方法上的variable2 ,而後在showView 方法的參數裏面使用@PathVariable 標記使用了這兩個變量。因此當咱們使用/test/hello/showView/2.do 來請求的時候就能夠訪問到MyController 的showView 方法,這個時候variable1 就被賦予值hello ,variable2 就被賦予值2 ,而後咱們在showView 方法參數裏面標註了參數variable1 和variable2 是來自訪問路徑的path 變量,這樣方法參數variable1 和variable2 就被分別賦予hello 和2 。方法參數variable1 是定義爲String 類型,variable2 是定義爲int 類型,像這種簡單類型在進行賦值的時候Spring 是會幫咱們自動轉換的。

在上面的代碼中咱們能夠看到在標記variable1 爲path 變量的時候咱們使用的是@PathVariable ,而在標記variable2 的時候使用的是@PathVariable(「variable2」)
這二者有什麼區別呢?

  • 第一種狀況就默認去URI 模板中找跟參數名相同的變量,可是這種狀況只有在使用debug 模式進行編譯的時候才能夠。
  • 第二種狀況是明確規定使用的就是URI 模板中的variable2 變量。當不是使用debug 模式進行編譯,或者是所須要使用的變量名跟參數名不相同的時候,就要使用第二種方式明確指出使用的是URI 模板中的哪一個變量。

除了在請求路徑中使用URI 模板,定義變量以外,@RequestMapping 中還支持通配符"*"。以下面的代碼我就可使用/myTest/whatever/wildcard.do 訪問到Controller 的testWildcard 方法。如:

@Controller
@RequestMapping ( "/myTest" )
public class MyController {
   @RequestMapping ( "*/wildcard" )
   public String testWildcard() {
      System. out .println( "wildcard------------" );
      return "wildcard" ;
   }  
}

當@RequestParam中沒有指定參數名稱時,Spring 在代碼是debug 編譯的狀況下會默認取更方法參數同名的參數,若是不是debug 編譯的就會報錯。

#### 二、使用 @RequestMapping 的一些高級用法

(1)params屬性
@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.do?param1=value1&param2=value2 的時候可以正確訪問到該testParams 方法,當請求/testParams.do?param1=value1&param2=value2&param3=value3 的時候就不可以正常的訪問到該方法,由於在@RequestMapping 的params 參數裏面指定了參數param3 是不能存在的。

(2)method屬性
@RequestMapping (value= "testMethod" , method={RequestMethod. GET , RequestMethod. DELETE })
    public String testMethod() {
       return "method" ;
    }

在上面的代碼中就使用method 參數限制了以GET 或DELETE 方法請求/testMethod 的時候才能訪問到該Controller 的testMethod 方法。

(3)headers屬性
@RequestMapping (value= "testHeaders" , headers={ "host=localhost" , "Accept" })
    public String testHeaders() {
       return "headers" ;
    }

headers 屬性的用法和功能與params 屬性類似。在上面的代碼中當請求/testHeaders.do 的時候只有當請求頭包含Accept 信息,且請求的host 爲localhost 的時候才能正確的訪問到testHeaders 方法。

三、 @RequestMapping 標記的處理器方法支持的方法參數和返回類型

3.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 對象。 這兩個對象參數必須緊接在須要驗證的實體對象參數以後,它裏面包含了實體對象的驗證結果。
3.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 標記的方法執行以前執行。

四、使用 @ModelAttribute 和 @SessionAttributes 傳遞和保存數據

SpringMVC 支持使用 @ModelAttribute 和 @SessionAttributes 在不一樣的模型(model)和控制器之間共享數據。 @ModelAttribute 主要有兩種使用方式,一種是標註在方法上,一種是標註在 Controller 方法參數上。

當 @ModelAttribute 標記在方法上的時候,該方法將在處理器方法執行以前執行,而後把返回的對象存放在 session 或模型屬性中,屬性名稱可使用 @ModelAttribute(「attributeName」) 在標記方法的時候指定,若未指定,則使用返回類型的類名稱(首字母小寫)做爲屬性名稱。關於 @ModelAttribute 標記在方法上時對應的屬性是存放在 session 中仍是存放在模型中,咱們來作一個實驗,看下面一段代碼。

@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" );
    }
}

當咱們請求 /myTest/sayHello.do 的時候使用 @ModelAttribute 標記的方法會先執行,而後把它們返回的對象存放到模型中。最終訪問到 sayHello 方法的時候,使用 @ModelAttribute 標記的方法參數都能被正確的注入值。執行結果以下所示:

Hello world,Hello user210

由執行結果咱們能夠看出來,此時 session 中沒有包含任何屬性,也就是說上面的那些對象都是存放在模型屬性中,而不是存放在 session 屬性中。那要如何才能存放在 session 屬性中呢?這個時候咱們先引入一個新的概念 @SessionAttributes ,它的用法會在講完 @ModelAttribute 以後介紹,這裏咱們就先拿來用一下。咱們在 MyController 類上加上 @SessionAttributes 屬性標記哪些是須要存放到 session 中的。看下面的代碼:

@Controller
@RequestMapping ( "/myTest" )
@SessionAttributes (value={ "intValue" , "stringValue" }, types={User. class })
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(Map<String, Object> map, @ModelAttribute ( "hello" ) String hello, @ModelAttribute ( "intValue" ) int num, @ModelAttribute ( "user2" ) User user, Writer writer, HttpServletRequest request) throws IOException {
       map.put( "stringValue" , "String" );
       writer.write( "Hello " + hello + " , Hello " + user.getUsername() + num);
       writer.write( "\r" );
       HttpSession session = request.getSession();
       Enumeration enume = session.getAttributeNames();
       while (enume.hasMoreElements())
           writer.write(enume.nextElement() + "\r" );
       System. out .println(session);
    }

    @ModelAttribute ( "user2" )
    public User getUser() {
       System. out .println( "---------getUser-------------" );
       return new User(3, "user2" );
    }
}

在上面代碼中咱們指定了屬性爲 intValue 或 stringValue 或者類型爲 User 的都會放到 Session中,利用上面的代碼當咱們訪問 /myTest/sayHello.do 的時候,結果以下:

Hello world,Hello user210

仍然沒有打印出任何 session 屬性,這是怎麼回事呢?怎麼定義了把模型中屬性名爲 intValue 的對象和類型爲 User 的對象存到 session 中,而實際上沒有加進去呢?難道咱們錯啦?咱們固然沒有錯,只是在第一次訪問 /myTest/sayHello.do 的時候 @SessionAttributes 定義了須要存放到 session 中的屬性,並且這個模型中也有對應的屬性,可是這個時候尚未加到 session 中,因此 session 中不會有任何屬性,等處理器方法執行完成後 Spring 纔會把模型中對應的屬性添加到 session 中。因此當請求第二次的時候就會出現以下結果:

Hello world,Hello user210
user2
intValue
stringValue

當 @ModelAttribute 標記在處理器方法參數上的時候,表示該參數的值將從模型或者 Session 中取對應名稱的屬性值,該名稱能夠經過 @ModelAttribute(「attributeName」) 來指定,若未指定,則使用參數類型的類名稱(首字母小寫)做爲屬性名稱。

五、@PathVariable和@RequestParam的區別

請求路徑上有個id的變量值,能夠經過@PathVariable來獲取 @RequestMapping(value = "/page/{id}", method = RequestMethod.GET) @RequestParam用來得到靜態的URL請求入參 spring註解時action裏用到。

5.1 簡介:

handler method 參數綁定經常使用的註解,咱們根據他們處理的Request的不一樣內容部分分爲四類:(主要講解經常使用類型)

  • A、處理requet uri 部分(這裏指uri template中variable,不含queryString部分)的註解: @PathVariable;

  • B、處理request header部分的註解: @RequestHeader, @CookieValue;

  • C、處理request body部的註解:@RequestParam, @RequestBody;

  • D、處理attribute類型是註解: @SessionAttributes, @ModelAttribute;

(1)、@PathVariable

當使用@RequestMapping URI template 樣式映射時, 即 someUrl/{paramId}, 這時的paramId可經過 @Pathvariable註解綁定它傳過來的值到方法的參數上。

示例代碼:

@Controller  
@RequestMapping("/owners/{ownerId}")  
public class RelativePathUriTemplateController {  
  
  @RequestMapping("/pets/{petId}")  
  public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) {      
    // implementation omitted   
  }  
}

上面代碼把URI template 中變量 ownerId的值和petId的值,綁定到方法的參數上。若方法參數名稱和須要綁定的uri template中變量名稱不一致,須要在@PathVariable("name")指定uri template中的名稱。

(2)、 @RequestHeader、@CookieValue

@RequestHeader 註解,能夠把Request請求header部分的值綁定到方法的參數上。

示例代碼:

這是一個Request 的header部分:

Host                    localhost:8080  
Accept                  text/html,application/xhtml+xml,application/xml;q=0.9  
Accept-Language         fr,en-gb;q=0.7,en;q=0.3  
Accept-Encoding         gzip,deflate  
Accept-Charset          ISO-8859-1,utf-8;q=0.7,*;q=0.7  
Keep-Alive              300  
@RequestMapping("/displayHeaderInfo.do")  
public void displayHeaderInfo(@RequestHeader("Accept-Encoding") String encoding,  
                              @RequestHeader("Keep-Alive") long keepAlive)  {  
}

上面的代碼,把request header部分的 Accept-Encoding的值,綁定到參數encoding上了, Keep-Alive header的值綁定到參數keepAlive上。

@CookieValue 能夠把Request header中關於cookie的值綁定到方法的參數上。

例若有以下Cookie值:

  JSESSIONID=415A4AC178C59DACE0B2C9CA727CDD84

@RequestMapping("/displayHeaderInfo.do")  
public void displayHeaderInfo(@CookieValue("JSESSIONID") String cookie)  {  
}

即把JSESSIONID的值綁定到參數cookie上。

(3)、@RequestParam, @RequestBody
@RequestParam

A) 經常使用來處理簡單類型的綁定,經過Request.getParameter() 獲取的String可直接轉換爲簡單類型的狀況( String--> 簡單類型的轉換操做由ConversionService配置的轉換器來完成);由於使用request.getParameter()方式獲取參數,因此能夠處理get 方式中queryString的值,也能夠處理post方式中 body data的值;

B)用來處理Content-Type: 爲 application/x-www-form-urlencoded編碼的內容,提交方式GET、POST;

C) 該註解有兩個屬性: value、required; value用來指定要傳入值的id名稱,required用來指示參數是否必須綁定;

示例代碼:

@Controller  
@RequestMapping("/pets")  
@SessionAttributes("pet")  
public class EditPetForm {  
    @RequestMapping(method = RequestMethod.GET)  
 public String setupForm(@RequestParam("petId") int petId, ModelMap model) {  
       Pet pet = this.clinic.loadPet(petId);  
   model.addAttribute("pet", pet);  
   return "petForm";  
   }
}
@RequestBody

該註解經常使用來處理Content-Type: 不是application/x-www-form-urlencoded編碼的內容,例如application/json, application/xml等;

它是經過使用HandlerAdapter 配置的HttpMessageConverters來解析post data body,而後綁定到相應的bean上的。

由於配置有FormHttpMessageConverter,因此也能夠用來處理 application/x-www-form-urlencoded的內容,處理完的結果放在一個MultiValueMap<String, String>裏,這種狀況在某些特殊需求下使用,詳情查看FormHttpMessageConverter api;

示例代碼:

@RequestMapping(value = "/something", method = RequestMethod.PUT)  
public void handle(@RequestBody String body, Writer writer) throws IOException {  
  writer.write(body);  
}
(4)、@SessionAttributes, @ModelAttribute
@SessionAttributes:

該註解用來綁定HttpSession中的attribute對象的值,便於在方法中的參數裏使用。

該註解有value、types兩個屬性,能夠經過名字和類型指定要使用的attribute 對象;

示例代碼:

@Controller  
@RequestMapping("/editPet.do")  
@SessionAttributes("pet")  
public class EditPetForm {  
    // ...   
}
@ModelAttribute

該註解有兩個用法,一個是用於方法上,一個是用於參數上;

  • 用於方法上時: 一般用來在處理@RequestMapping以前,爲請求綁定須要從後臺查詢的model;

  • 用於參數上時: 用來經過名稱對應,把相應名稱的值綁定到註解的參數bean上;要綁定的值來源於:

    • A) @SessionAttributes 啓用的attribute 對象上;

    • B) @ModelAttribute 用於方法上時指定的model對象;

    • C) 上述兩種狀況都沒有時,new一個須要綁定的bean對象,而後把request中按名稱對應的方式把值綁定到bean中。

用到方法上@ModelAttribute的示例代碼:

@ModelAttribute  
public Account addAccount(@RequestParam String number) {  
    return accountManager.findAccount(number);  
}

這種方式實際的效果就是在調用@RequestMapping的方法以前,爲request對象的model裏put(「account」, Account)。

用在參數上的@ModelAttribute示例代碼:

@RequestMapping(value="/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST)  
public String processSubmit(@ModelAttribute Pet pet) {  
     
}

首先查詢 @SessionAttributes有無綁定的Pet對象,若沒有則查詢@ModelAttribute方法層面上是否綁定了Pet對象,若沒有則將URI template中的值按對應的名稱綁定到Pet對象的各屬性上。

六、< context:component-scan base-package = "" />淺析

component-scan 默認掃描的註解類型是 @Component,不過,在 @Component 語義基礎上細化後的 @Repository, @Service 和 @Controller 也一樣能夠得到 component-scan 的青睞

有了 ,另外一個 標籤根本能夠移除掉,由於已經被包含進去了

另外 還提供了兩個子標籤

  • 1. //指定掃描的路徑

  • 2. //排除掃描的路徑

有一個use-default-filters屬性,屬性默認爲true,表示會掃描指定包下的所有的標有@Component的類,並註冊成bean.也就是@Component的子註解@Service,@Reposity等。

這種掃描的粒度有點太大,若是你只想掃描指定包下面的Controller或其餘內容則設置use-default-filters屬性爲false,表示再也不按照scan指定的包掃描,而是按照 指定的包掃描,示例:

<context:component-scan base-package="com.tan" use-default-filters="false">
        <context:include-filter type="regex" expression="com.tan.*"/>//注意後面要寫.*
</context:component-scan>

當沒有設置use-default-filters屬性或者屬性爲true時,表示基於base-packge包下指定掃描的具體路徑

<context:component-scan base-package="com.tan" >
        <context:include-filter type="regex" expression=".controller.*"/>
        <context:include-filter type="regex" expression=".service.*"/>
        <context:include-filter type="regex" expression=".dao.*"/>
</context:component-scan>

效果至關於:

<context:component-scan base-package="com.tan" >
        <context:exclude-filter type="regex" expression=".model.*"/>
</context:component-scan>

注意:本人嘗試時不管哪一種狀況 都不能同時存在

相關文章
相關標籤/搜索