近期在捯飭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) { #略 } }
在實際操做中常常會碰到表單中的日期 字符串和Javabean中的日期類型的屬性自動轉換, 而springMVC默認不支持這個格式的轉換,因此必需要手動配置, 自定義數據類型的綁定才能實現這個功能。app
比較簡單的能夠直接應用springMVC的註解@initbinder和spring自帶的WebDataBinder類和操做異步
還要在springMVC配置文件中加上
這樣就能夠直接將上傳的日期時間字符串綁定爲日期類型的數據了
預備知識:@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註解。
在spring MVC中,二者的做用都是將request裏的參數的值綁定到contorl裏的方法參數裏的,區別在於,URL寫法不一樣。
使用@RequestParam時,URL是這樣的:http://host:port/path?參數名=參數值
使用@PathVariable時,URL是這樣的:http://host:port/path/參數值
例如:
上面兩個方法,訪問路徑分別以下:
假設一個單例模式的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不容許有參數。