Spring註解 總結

1:Spring的註解@Qualifier小結

  近期在捯飭spring的註解,現將遇到的問題記錄下來,以供遇到一樣問題的童鞋解決~html

  先說明下場景,代碼以下:java

有以下接口:web

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

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

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

調用代碼以下:spring

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

  在啓動tomcat時報以下錯誤:json

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不知道應該綁定哪一個實現類,因此拋出瞭如上錯誤。tomcat

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

@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) {
        #略
    }
}

 

2:springMVC註解@initbinder

 

在實際操做中常常會碰到表單中的日期 字符串和Javabean中的日期類型的屬性自動轉換, 而springMVC默認不支持這個格式的轉換,因此必需要手動配置, 自定義數據類型的綁定才能實現這個功能。app

比較簡單的能夠直接應用springMVC的註解@initbinder和spring自帶的WebDataBinder類和操做異步

  1. @InitBinder  
  2.     public void initBinder(WebDataBinder binder) {  
  3.         SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");  
  4.         dateFormat.setLenient(false);  
  5.         binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));  
  6.     }  

還要在springMVC配置文件中加上

  1. <!-- 解析器註冊 -->  
  2.     <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">  
  3.         <property name="messageConverters">  
  4.             <list>  
  5.                 <ref bean="stringHttpMessageConverter"/>  
  6.             </list>  
  7.         </property>  
  8.     </bean>  
  9.     <!-- String類型解析器,容許直接返回String類型的消息 -->  
  10.     <bean id="stringHttpMessageConverter" class="org.springframework.http.converter.StringHttpMessageConverter"/>  

這樣就能夠直接將上傳的日期時間字符串綁定爲日期類型的數據了

 

3:@Responsebody與@RequestBody

 

預備知識:@RequestMapping
RequestMapping是一個用來處理請求地址映射的註解,可用於類或方法上。用於類上,表示類中的全部響應請求的方法都是以該地址做爲父路徑。
@RequestMapping(value = "/aaa")//類級別,能夠沒有
public class myController {
    @RequestMapping(value = "/bbb")//方法級別,必須有
    public String getMyName() {
        return "myReturn";
    }
}
對應的action就是:<form action="aaa/bbb">
返回頁面就是myReturn.jsp

@Responsebody與@RequestBody
@Responsebody表示該方法的返回結果直接寫入HTTP response body中
通常在異步獲取數據時使用,在使用@RequestMapping後,返回值一般解析爲跳轉路徑,
加上@Responsebody後返回結果不會被解析爲跳轉路徑,而是直接寫入HTTP response body中。
好比異步獲取json數據,加上@Responsebody後,會直接返回json數據。
@RequestBody將HTTP請求正文插入方法中,使用適合的HttpMessageConverter將請求體寫入某個對象。
function login() {//頁面異步請求
    var mydata = '{"name":"' + $('#name').val() + '","id":"'
            + $('#id').val() + '","status":"' + $('#status').val() + '"}';
    $.ajax({
        type : 'POST',
        contentType : 'application/json',
        url : "${pageContext.request.contextPath}/person/login",
        processData : false,
        dataType : 'json',
        data : mydata,
        success : function(data) {
            alert("id: " + data.id + "\nname: " + data.name + "\nstatus: "
                    + data.status);
        },
        error : function() {
            alert('出錯了!');
        }
    });
};
    @RequestMapping(value = "person/login")
    @ResponseBody
    public Person login(@RequestBody Person person) {//將請求中的mydata寫入Person對象中
        return person;//不會被解析爲跳轉路徑,而是直接寫入HTTP response body中
    }

擴展:@PathVariable獲取請求路徑變量
function profile() {
    var url = "${pageContext.request.contextPath}/person/profile/";
    var query = $('#id').val() + '/' + $('#name').val() + '/'
            + $('#status').val();
    url += query;
    $.get(url, function(data) {
        alert("id: " + data.id + "\nname: " + data.name + "\nstatus: "
                + data.status);
    });
}
    @RequestMapping(value = "person/profile/{id}/{name}/{status}")
    @ResponseBody
    public Person porfile(@PathVariable int id,@PathVariable String name,@PathVariable boolean status) {
        return new Person(id, name, status);
    }
  //@RequestMapping(value = "/person/profile/{id}/{name}/{status}")中的{id}/{name}/{status}與@PathVariabl

e int id, @PathVariable String name,@PathVariable boolean status一一對應,按名匹配。

 

@Controller和@RestController的區別?

官方文檔:
@RestController is a stereotype annotation that combines @ResponseBody and @Controller.
意思是:
@RestController註解至關於@ResponseBody + @Controller合在一塊兒的做用。
 

1)若是隻是使用@RestController註解Controller,則Controller中的方法沒法返回jsp頁面,配置的視圖解析器InternalResourceViewResolver不起做用,返回的內容就是Return 裏的內容。

例如:原本應該到success.jsp頁面的,則其顯示success.

2)若是須要返回到指定頁面,則須要用 @Controller配合視圖解析器InternalResourceViewResolver才行。
3)若是須要返回JSON,XML或自定義mediaType內容到頁面,則須要在對應的方法上加上@ResponseBody註解。

 

@RequestParam  和@PathVariable  區別

 

spring MVC中,二者的做用都是將request裏的參數的值綁定到contorl裏的方法參數裏的,區別在於,URL寫法不一樣。

使用@RequestParam時,URL是這樣的:http://host:port/path?參數名=參數值

使用@PathVariable時,URL是這樣的:http://host:port/path/參數值

例如:

  1. @RequestMapping(value="/user",method = RequestMethod.GET)  
  2.    public @ResponseBody  
  3.    User printUser(@RequestParam(value = "id", required = false, defaultValue = "0")  
  4.    int id) {  
  5.     User user = new User();  
  6.        user = userService.getUserById(id);  
  7.        return user;  
  8.    }  
  9.      
  10.    @RequestMapping(value="/user/{id:\\d+}",method = RequestMethod.GET)  
  11.    public @ResponseBody  
  12.    User printUser2(@PathVariable int id) {  
  13.        User user = new User();  
  14.        user = userService.getUserById(id);  
  15.        return user;  
  16.    }  

上面兩個方法,訪問路徑分別以下:

 

 

 

5:Spring - lookup-method方式實現依賴注入

假設一個單例模式的bean A須要引用另一個非單例模式的bean B,爲了在咱們每次引用的時候都能拿到最新的bean B,咱們可讓bean A經過實現ApplicationContextWare來感知applicationContext(便可以得到容器上下文),從而能在運行時經過ApplicationContext.getBean(String beanName)的方法來獲取最新的bean B。可是若是用ApplicationContextAware接口,就讓咱們與Spring代碼耦合了,違背了反轉控制原則(IoC,即bean徹底由Spring容器管理,咱們本身的代碼只須要用bean就能夠了)。

因此Spring爲咱們提供了方法注入的方式來實現以上的場景。方法注入方式主要是經過<lookup-method/>標籤。

實例

下面咱們用一個例子來講明lookup-method的用法。

假設有一個果盤,果盤裏放了一些水果,好比蘋果,香蕉等,咱們但願咱們每次在果盤裏拿到的都是最新鮮的水果。

java代碼:

// 定義一個水果類
public class Fruit {
    public Fruit() {
        System.out.println("I got Fruit");
    }
}

// 蘋果
public class Apple extends Fruit {
    public Apple() {
        System.out.println("I got a fresh apple");
    }
}

// 香蕉
public class Bananer extends Fruit {
    public Bananer () {
        System.out.println("I got a  fresh bananer");
    }
}

// 水果盤,能夠拿到水果
public abstract class FruitPlate{
    // 抽象方法獲取新鮮水果
    protected abstract Fruit getFruit();
}

spring配置:

<!-- 這是2個非單例模式的bean -->
<bean id="apple" class="cn.com.willchen.test.di.Apple" scope="prototype"/>
<bean id="bananer" class="cn.com.willchen.test.di.Bananer " scope="prototype"/>
 
<bean id="fruitPlate1" class="cn.com.willchen.test.di.FruitPlate">
    <lookup-method name="getFruit" bean="apple"/>
</bean>
<bean id="fruitPlate2" class="cn.com.willchen.test.di.FruitPlate">
    <lookup-method name="getFruit" bean="bananer"/>
</bean>

測試代碼:

public static void main(String[] args) {
    ApplicationContext app = new ClassPathXmlApplicationContext("classpath:resource/applicationContext.xml");

    FruitPlate fp1= (FruitPlate)app.getBean("fruitPlate1");
    FruitPlate fp2 = (FruitPlate)app.getBean("fruitPlate2");

    fp1.getFruit();
    fp2.getFruit();
}

測試結果:

I got Fruit

I got a fresh apple

I got Fruit

I got a fresh bananer

 

示例說明:

從上面例子咱們能夠看到,在代碼中,咱們沒有用到Spring的任何類和接口,實現了與Spring代碼的解耦合。

其中,最爲核心的部分就是lookup-method的配置和FruitPlate.getFruit()方法。上面代碼中,咱們能夠看到getFruit()方法是個抽象方法,咱們並無實現它啊,那它是怎麼拿到水果的呢。這裏的奧妙就是Srping應用了CGLIB(動態代理)類庫。Spring在初始化容器的時候對配置<lookup-method/>的bean作了特殊處理,Spring會對bean指定的class作動態代理,代理<lookup-method/>標籤中name屬性所指定的方法,返回bean屬性指定的bean實例對象。每次咱們調用fruitPlate1或者fruitPlate2這2個bean的getFruit()方法時,實際上是調用了CGLIB生成的動態代理類的方法。關於CGLIB你們可自行在網上查閱。

lookup-method實現方式說明:

<bean class="beanClass">
    <lookup-method name="method" bean="non-singleton-bean"/>
</bean>

method是beanClass中的一個方法,beanClass和method是否是抽象都無所謂,不會影響CGLIB的動態代理,根據項目實際需求去定義。non-singleton-bean指的是lookup-method中bean屬性指向的必須是一個非單例模式的bean,固然若是不是也不會報錯,只是每次獲得的都是相同引用的bean(同一個實例),這樣用lookup-method就沒有意義了。

 

另外對於method在代碼中的簽名有下面的標準:

<public|protected> [abstract] <return-type> theMethodName(no-arguments);

public|protected要求方法必須是能夠被子類重寫和調用的;

abstract可選,若是是抽象方法,CGLIB的動態代理類就會實現這個方法,若是不是抽象方法,就會覆蓋這個方法,因此沒什麼影響;

return-type就是non-singleton-bean的類型咯,固然能夠是它的父類或者接口。

no-arguments不容許有參數。

相關文章
相關標籤/搜索