Spring2.5之后,引入了大量的注解,现在到现在位置已经可以使用注解来完成大部分的XML配置功能。
因为注解和Java代码是在同一文件中的,但是XML配置采用的是独立的配置文件,如果使用XML配置文件,程序员在开发的时候,往往需要在代码和配置文件中不停的切换,这样会影响开发效率。相对来说使用注解的方式,更易于阅读,而不是全部配置文件堆成一大片。
但是XML也并不是完全没有优势,在完成整个项目之后,如果在其他项目中,只是依赖关系不同的话,只修改配置文件就应用于其他项目当中,而注解的方式则需要改写源代码才可以。
但毫无疑问的是,使用注解配置的方式越来越多人使用。
上面说了,注解和Java代码是位于同一文件中的,那么Spring如何知道将哪些类作为bean处理呢?
Spring将几个特殊的注解来标注bean。
1.@Component: 标注一个普通的spring Bean类。
2.@Controller: 标注一个控制器组件类。
3.@Service: 标注一个业务逻辑组件类。
4.@Repository: 标注一个DAO组件类。
一般来说,用这些注解来标注的类的bean的id是bean类名,将首字母小写。
除了这几种注解之外,Spring也会将这些注解的子注解标注的类当作bean,这涉及到自定义注解,将上面的注解作为元注解即可。
一般来说,我们都会尽量的细致的使用注解,如业务逻辑层使用@Service注解,持久层使用@Repository,使得阅读便于理解,并且在Spring更高的版本中,会存在更加深层的语义。
单单使用注解标注类是不够的,还需要在XML文件中告诉Spring,你需要将哪里的类当作bean,而不能让Spring扫描整个文件去寻找,以减小Spring的开销。
<context:component-scan base-package="..."/>
在xml配置文件中,增加这么一句,告诉Spring扫描这个包下的所有文件。
例子:
xml配置文件:
<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-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<context:component-scan base-package="Spring.AnnotationTest2" />
</beans>
这里需要增加扫描的包名,在schemaLocation中记得加入注解的命名空间。
public interface IDAO {
public void save(String data);
}
@Repository
public class DAOImpl implements IDAO{
public void save(String data) {
System.out.println("save to database: " + data);
}
}
测试类:
public class BeanTest {
private ClassPathXmlApplicationContext context;
@Test
public void Test1() {
try {
context = new ClassPathXmlApplicationContext("classpath:spring-annotation.xml");
context.start();
IDAO iDAO = (IDAO) context.getBean("DAOImpl");
iDAO.save("myData");
} catch (Exception e){
e.printStackTrace();
}
context.destroy();
}
}
这样,Spring就会为DAOImpl自动的创建一个bean对象,我们可以通过上下文获取。这里细心的同学会注意到,bean的id并不是我上面所说的首字母小写,而是原名,这里的主要因为是因为我的类名前两个字母都是大写的,这样的话就会是原名,而如果只有第一个字母大写,那么还是按照上面所说的。
其实我们可以自己自定义bean的id,只需要在@Repository后面增加("id名字")就可以自定义id。
如:
@Repository("iDAO")
public class DAOImpl implements IDAO{
public void save(String data) {
System.out.println("save to database: " + data);
}
}
除此之外,我们还可以通过实现BeanNameGeneration这个接口来重新规定bean的id的命名规则,默认的命名规则就是首字母小写。
在前面说过,我们可以通过在配置文件中增加扫描的配置告诉Spring我们需要扫描哪里的包。其实我们可以更加细致的告诉它。
我们可以通过为<context:component-scan>添加<include-filter...>或<exclude-filter...>两种过滤器,他们的作用是在扫描的时候进行判断,是否满足要求。
<include-filter...>: 满足该规则的java类会被当初bean类处理。
<exclude-filter...>: 指定满足该规则的java类不会被当初bean类处理。
如果是bean的依赖关系,那么用注解的方式如何实现呢?
这就需要用到@Autowired注解。
@Autowired 注解,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。
例如:
public interface IService {
public void save(String data);
}
@Service
public class ServiceImpl implements IService {
@Autowired
private IDAO iDAO;
public void save(String data) {
iDAO.save(data);
}
}
在ServiceImpl类中,有关于IDAO类的依赖,而我们仅仅需要使用@Autowired注解去标注一下,就可以自动的完成装配。是不是特别简单?
如果使用@Autowired标注成员变量,默认通过byType的类型进行装配。
也可以通过标注方法和构造器的方式来达到目的。
如果自动装配找不到对应的类型,默认会抛出异常,如果我们希望找不到的时候,值为null的话,就要设置required = false,就不会抛出异常了。
而对于一些众所周知的接口,例如BeanFactory,ApplicationContext,Environment,ResourceLoader,ApplicationEventPublisher,MessageSource等等,我们可以直接通过@Autowired对它们进行自动装配,就可以得到这些对象了,而不必像在XML篇里面实现Aware接口来获取对象。
数组使用@Autowired
对于数组,Set,Map使用@Autowired注解的话,得到的是对应的类型的所有的bean。
例如我们有接口IDAO,类DAOImpl1和DAOImpl2,如果我们使用@Autowired标注List<IDAO>的话,List中会存在两个bean,分别对应DAOImpl1和DAOImpl2。
对于数组,我们可以定义他们的顺序,使用@Order(value = "...")标注,按照value从小到大进行排序。
例如:使用@Order(value = 1)标注DAOImpl1,使用value = 2标注DAOImpl2,则遍历的时候,会先访问DAOImpl1。
除了上面的几种注解之外,还有@Scope,@Qualifier,@Resource,@Bean,@Configuration,@ImportResource,@Value等注解。
@Scope
用来指明bean的作用域,在XML篇说过,取值有5中,默认是单例singleton.
@Qualifier
@Qualifier注解可以缩小或指定自动装配的范围。如果在使用@Autowired自动装配的过程中,出现了多个同种类型的bean,那么Spring就不知道选哪个好了,就会抛出异常。这时可以使用@Qulifier注解指定装配的对象。可以用来标注类和方法的参数。
@Resource
@Resource和@Autowired注解十分类似,都是做bean的注入,区别是@Resource并不是Spring的注解,并且默认装配以byName的方式进行装配。
@Bean @Configuration
这两个注解通常是一起使用的。通过这两个注解,可以将XML配置的方式转换成通过Java的方式来配置,减小了xml的配置。
例子:
@Configuration
public class MyConfig {
@Bean
public IDAO getIDAO() {
return new DAOImpl();
}
}
<bean id = "getIDAO" class = "DAOImpl"></bean>
两种方式是等同的。可以将XML配置转移成Java代码的方式进行配置。默认按照方法名作为id,我们也可以自定义id,@Bean("...")来自定义。
@ImportResource @Value
这两个注解配合使用可以加载properties资源文件,并且读取其中的键值对,这样,到项目后期,我们就只需要修改键值对中的值。
由于@ImportResource只能读取xml文件,所以我们通过在xml文件中加载properties文件,类中加载xml文件的方法来读取properties中的键值对。
例如:
有properties文件:
jdbc.username="myName"
jdbc.password="myPassword"
在xml文件中加入如下语句:
<context:property-placeholder location = "classpath:jdbc.properties"/>
通过这个标签让Spring加载properties文件。
在Java配置文件中读取properties的键值对:
@Configuration
@ImportResource("classpath:spring-annotation.xml")
public class MyConfig {
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean
public IDAO getIDAO() {
System.out.println(username + " " + password);
return new DAOImpl();
}
}
如此一来,就可以在处理bean之前读取properties中的值。成功的加载了文件。