文章目录
介绍
前面的文章介绍了如何使用注解来实现Bean的自动装配(依赖注入),但还需要XML文档并且使用<bean>
标记来告诉Spring IoC容器要生成哪些Bean。
本篇文章则继续介绍如何进一步简化XML文档,使用注解来生产Bean。
移除XML中的Bean定义并启用组件扫描:component-scan标记
spring-ioc-test.xml:
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="test" />
</beans>
可以看到,现在的XML文件中不仅没有了Bean的依赖注入,甚至连Bean的定义都没有了。
与上一篇文章唯一的不同在于把之前的启用自动装配的标记<annotation-config>
标记,替换成了启用自动扫描的标记<component-scan>
。
首先,<component-scan>
标记隐含地包含了<annotation-config>
标记的功能,也就是说启用了自动扫描,也就启用了自动装配。
其次,它有一个属性base-package
,用于设置需要自动扫描的包,本例中所有组件都在test
包及其子包下面。
Bean定义注解:@Service
@Service
注解的作用其实就相当于XML文档中<bean>
标记的作用,就是告诉Spring IoC容器这里有一个Bean定义,运行时要生成一个以此类为类型的Bean。
不过,@Service
这个注解可以从名字上看出该Bean的作用是一个服务层Bean,在语义上更清晰。
ServiceA.java
package test.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import test.repository.RepositoryA;
@Service
public class ServiceA {
private ServiceB serviceB;
private RepositoryA repositoryA;
@Autowired
public ServiceA (ServiceB serviceB, RepositoryA repositoryA) {
this.serviceB = serviceB;
this.repositoryA = repositoryA;
}
public void doWork() {
System.out.println("ServiceA doWork start");
serviceB.doWork();
repositoryA.save();
System.out.println("ServiceA doWork end");
}
}
与之前的ServiceA
唯一的不同,就是在类定义之前多了一个注解@Service
。
同理,在ServiceB
类也加上@Service
注解即可。
ServiceB.java
package test.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import test.repository.RepositoryB;
@Service
public class ServiceB {
@Autowired
private RepositoryB repositoyB;
public void doWork() {
System.out.println("ServiceB doWork start");
repositoyB.update();
System.out.println("ServiceB doWork end");
}
}
Bean定义注解:@Repository
@Repository
注解的作用类似于@Service
,也是相当于XML文档中<bean>
标记的作用,就是告诉Spring IoC容器这里有一个Bean定义,运行时要生成一个以此类为类型的Bean。
不过,@Repository
这个注解可以从名字上看出该Bean的作用是一个持久层(就是数据访问层)Bean,在语义上更清晰。
RepositoryA.java
package test.repository;
import org.springframework.stereotype.Repository;
@Repository
public class RepositoryA {
public void save() {
System.out.println("RepositoryA save ...");
}
}
与之前的RepositoryA
唯一的不同,就是在类定义之前多了一个注解@Repository
。
同理,在RepositoryB
类也加上@Repository
注解即可。
RepositoryB.java
package test.repository;
import org.springframework.stereotype.Repository;
@Repository
public class RepositoryB {
public void update() {
System.out.println("RepositoryB update ...");
}
}
验证
SpringMain.java:
package test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import test.service.ServiceA;
public class SpringMain {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-ioc-test.xml");
ServiceA svcA = context.getBean("svcA", ServiceA.class);
svcA.doWork();
}
}
保持不变,在Eclipse直接运行,得到结果:
很不幸,出现了异常。
仔细分析一下异常,提示说“No bean named ‘svcA’ available”,就是找不到id为svcA的Bean。有两种解决方案:
ServiceA
类的注解添加Bean的id:@Service("svcA")
getBean
方法使用默认的id:context.getBean("serviceA", ServiceA.class);
所以,自动扫描的注解也可以指定Bean的id,如果不指定,就使用Spring IoC的默认规则,即将类名的第一个字母变为小写(不过,似乎对于UUID这样的连续多个首字母大写的类名,会有一些问题,以后再讨论)。
大家可以尝试修改并运行一下。
其他注解
@Contorller
@Contorller
注解与@Service
和@Repository
注解类似,不过它的语义是控制层(也可以称为展示层)的Bean。
@Configuration
@Configuration
注解实际上是为Spring IoC容器提供配置元数据的第三种方式,即基于Java的方式。
Spring IoC容器也会为该注解所修饰的类生成Bean,显然它的语义是配置层的Bean。
@Component
@Component
实际上是@Service
、@Repository
、@Contorller
和@Configuration
这几个注解的元注解,关于元注解,以后再讨论。
就是说,@Service
等注解的定义需要由@Component
来修饰。
实际上,Spring IoC容器也会为该注解所修饰的类生成Bean,不过,它的语义没有那些注解那么明确,仅仅表明是一个组件Bean而已。
总结
所有这些标记和注解都还有很多属性可以配置,比如组件扫描的过滤器和命名生成器等,大部分情况下,使用Spring IoC容器的默认规则即可,这些内容以后再讨论。
最核心的内容就是:
<component-scan>
标记启用自动扫描和自动装配,实际上,有一个对应的注解@ComponentScan
也是相同的作用,只不过它用在基于Java的配置中,而不是在XML中。@Component
、@Service
、@Repository
、@Controller
、@Configuration
等注解告诉Spring IoC容器要为这些注解所修饰的类在运行时生成Bean。- 自动扫描生成Bean的id默认是将类名首字母变为小写转化而来。