近期在捯饬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不容许有参数。