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不容许有参数。

相关文章
相关标签/搜索