Spring的注解
Spring常见的注解:@Component、@Repository、@Service、@Controller @Resource、@Autowired、@Qualifier
这几个注解是基于类的,当你需要定义某个类为一个bean时,则在这个类的类名前一行使用类似@Service("XXX")的注解,就相当于把这个类定义为一个bean,bean名称为XXX。当然我们可以定义名称,也可以不定义,不定义名称的话,会默认以类名为bean的名称(类首字母小写)。
首先,先看一下这几个注解:@Component、@Repository、@Service、@Controller
通过看一些项目代码,我发现@Repository、@Service、@Controller 这几个注解好像是一个类型,其实@Component 跟他们也是一个类型的。
Spring 2.5 中除了提供 @Component 注释外,还定义了几个拥有特殊语义的注释,它们分别是:@Repository、@Service和 @Controller ,其实在目前的 Spring 版本中,这三个注解跟@Component 功能是等效的。但是从注释类的命名上,很容易看出这 3 个注释分别和持久层、业务层和控制层(Web 层)相对应。
虽然目前这3 个注释和 @Component 相比没有什么新意,但 Spring 将在以后的版本中为它们添加特殊的功能。所以,如果 Web 应用程序采用了经典的三层分层结构的话,最好在持久层、业务层和控制层分别采用上述注解对分层中的类进行注释。
@Service用于标注业务层组件(通常在service层就用这个,一般是service接口的实现类)
@Controller用于标注控制层组件(如struts中的action)
@Repository用于标注数据访问组件(如DAO组件)
@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
例如:
@Service
public class UserServiceImpl implements UserService {
}
@Repository
public class UserDaoImpl implements IUserDao {
}
因为在一个稍大的项目中,往往会涉及到很多组件,如果组件采用xml的bean定义来配置,显然会增加配置文件的体积,查找以及维护起来也不太方便。Spring2.5以后的版本为我们引入了组件自动扫描机制,它会在配置好的类路径下寻找标注了上述注解的类,并把这些类纳入到spring容器中管理。对组件使用注解的作用和在xml文件中使用bean节点配置组件时是一样的。要使用自动扫描机制,我们需要打开以下配置信息:
<?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-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<context:component-scan base-package=”com.foo.spring”/>
</beans>
component-scan标签默认情况下自动扫描指定路径下的包(含所有子包),将带有@Component、@Repository、@Service、@Controller标签的类自动注册到spring容器。对标记了@Required、@Autowired(spring);@PostConstruct、@PreDestroy、@Resource(JSR250);@WebServiceRef(JAX-WS);@EJB(EJB3); @PersistenceContext、@PersistenceUnit(JPA)等注解的类进行对应的操作使注解生效(这其实就包含了annotation-config标签的作用,所以配置了<context:component-scan base-package=""/>之后,便无需再配置<context:annotation-config/>)。
注意:
在使用注解的情况下,getBean的默认名称是类名(头字母小写),如果想自定义,可以用类似@Service(“aaaaa”)这样的方式来指定。这种bean默认是“singleton”的,如果想改变,可以使用@Scope(“prototype”)的方式来改变bean的作用域。
可以使用以下方式指定初始化方法和销毁方法:
//若想让<bean>实例化之后去执行初始化方法,可以使用@PostConstruct标注在该方法上
@PostConstruct
public void init() {
}
//同样@PreDestroy注解标注在方法上,可以用来指定<bean>销毁时执行的方法
@PreDestroy
public void destory() {
}
然后再看后面的几个注解:@Resource、@Autowired、@Qualifier
当需要在某个类中定义一个属性,并且该属性是一个已存在的bean,要为该属性赋值我们就用着三个。我们通过看代码可以看到这三个都是定义在一个属性上的,比如:
@Resource
private IIocDao iocDao;
@Autowired
private IocService service;
那这几个到底有什么区别呢?
我们先看@Resource,它是javax.annotation.Resource; 这个包中,也就是说是javaEE中的,并不是spring包中的。而且@Resource("xxx") 是可以定义bean名称的,就是说我这个属性要用那个bean来赋值。
@Autowired,它是org.springframework.beans.factory.annotation.Autowired 是这个包中,它是spring的包。而且它没有@Autowired("xxx"),那我要为这个bean定义名称怎么办?这个时候可以用@Qualifier("xxx")定义名称,这个注解也是spring包中的。这个xxx定义bean名称有什么用呢?
如果对于某一个接口,我们定义的该接口的实现类不止有一个,而是两个以上。比如IIocDao这个接口有两个实现类IocDao、IocDao2:
package com.ulewo.ioc;
import org.springframework.stereotype.Repository;
@Repository
public class IocDao implements IIocDao{
public void add(){
System.out.println("调用了dao");
}
}
package com.ulewo.ioc;
import org.springframework.stereotype.Repository;
@Repository
public class IocDao2 implements IIocDao{
public void add(){
System.out.println("调用了dao2");
}
}
其他不变,我们再运行:testIoc(),控制台打印出 调用了dao,所以在service层中
@Resource
private IIocDao iocDao;
这个iocDao 注入的是IocDao 这个实现类。奇怪了,它怎么知道我要调用哪个实现呢?
好,我们修改一下,把 service层中的private IIocDao iocDao;改一下,改成 private IIocDao iocDaox 把属性名改一下,再运行,会报错:
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.ulewo.ioc.IIocDao] is defined: expected single matching bean but found 2: iocDao,iocDao2
错误很明显啊,虽然容器中有两个bean iocDao和iocDao2,但是我们定义的属性名的是iocDaox,所以找不到了。所以可以看出来在用 @Repository注解来生成bean的时候,如果没有定义名称那么就会根据类名来生成。所以我们要调用第二个实现的时候可以定义为private IIocDao iocDao2 。我们再运行打印出:调用了dao2,所以可以根据属性名来区分,到底注入那个bean。但是有的人说,我不想定义bean名称跟类实现一样,我要定义其他的名称,那怎么玩呢,方法有2种:
第一种:我们在生成bean的时候就给bean定义个名称
@Repository("myIocDao")
public class IocDao implements IIocDao{
public void add(){
System.out.println("调用了dao");
}
}
这样就把这个实现类的名称定义为myIocDao了,而不是默认的类名 iocDao。
那么我们在使用这个bean的时候就要这么定义了:
@Resource
private IIocDao myIocDao;
运行,输出了:调用了dao
如果你这里不是用的 myIocDao,你又多加了一个x,成了myIocDaox,你运行会是这样的:
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.ulewo.ioc.IIocDao] is defined: expected single matching bean but found 2: myIocDao,iocDao2
所以,想要自定义bean的名称,可以在类注解的时候就指定。
第二种:在注入bean的时候指定名称:
①先看@Resource
我们在service层先这么定义下:
@Resource(name="iocDao")
private IIocDao xxx;
注意:
@Repository
public class IocDao implements IIocDao{
public void add(){
System.out.println("调用了dao");
}
}
这里还是用会默认的,也就是这个实现对应的是 iocDao这bean。如果你要为这个类指定别名bean,@Repository("myIocDao"),那@Resource(name="myIocDao") 就要这么写了。就是这里的name要跟实现类对应的bean名称保持一致。private IIocDao xxx; 这个属性名就随便写了。
运行:调用了dao
②如果用@Autowired,
那么在service层就要这么定义了
@Autowired
@Qualifier("iocDao")
private IIocDao xxx;
因为Autowired 不能像Resource 那样带个参数指定一个name,就要用Qualifier来指定了。
@Repository
public class IocDao implements IIocDao{
public void add(){
System.out.println("调用了dao");
}
}
这里也还是用会默认的,也就是这个实现对应的是 iocDao这bean。如果你要为这个类指定别名bean,@Repository("myIocDao"),那么@Qualifier ("myIocDao") 就要这么写了。就是这里的name要跟实现类对应的bean名称保持一致。private IIocDao xxx; 这个属性名就随便写了。
运行:调用了dao
而且还可以这么用
@Resource
@Qualifier("iocDao")
private IIocDao xxx;
等同于
@Resource(name="iocDao")
private IIocDao xx;
记住一点:@Resource的作用相当于@Autowired,只不过@Autowired按byType自动注入,如果发现找到多个bean,则,又按照byName方式比对,如果还有多个,则报出异常。而@Resource默认按 byName自动注入罢了。其实spring注解,最常用的还是根据名称,根据类型和构造方法,用的非常少。所以在多个实现的时候我们定义好bean的名称就行,就不会错乱。所以比较推荐使用@Resource!
总结:其实上面主要说了Spring的主要常见的几个注解的使用场景以及使用方法。通过以上的阐述,基本可以得出一个比较保险的套路:@Repository、@Service、@Controller @Resource这四大注解基本就可以满足Spring 的注解使用范围了。
补充说明:关于@Autowired的注入方式:
把DAO实现类注入到action的service接口(注意不要是service的实现类)中,注入时不要new 这个注入的类,因为Spring会自动注入,如果手动再new的话会出现错误,然后属性加上@Autowired后不需要getter()和setter()方法,Spring也会自动注入。
在接口前面标上@Autowired注释使得接口可以被容器注入,如:
@Autowired
@Qualifier("chinese")
private Man man;
当接口存在两个实现类的时候必须使用@Qualifier指定注入哪个实现类,否则可以省略,只写@Autowired。