注解容器配置
在之前的学习中,我们的Bean需要在ApplicationCentext.xml
文件中配置,如果Bean很多,就需要写大量的xml文件,既费力又难于维护。利用注解开发就不需要在xml文件中配置Bean了,Spring会自动扫描和装配这些Bean,所以接下来要学习的就是如何利用注解提高开发效率。
装配类的注解
注解的介绍
在Spring中,作用域类的注解是@Component
,该注解用于将一个普通类标识为Spring Bean类,它还有三个衍生的注解,目的适用于区分各层级。当使用了注解,就相当于将这个类交由Spring管理l。
衍生注解:
- 数据访问层(DAO):
@Respository
,标识一个持久化层组件。 - 业务逻辑层(Service):
@Service
,标识一个业务逻辑层组件。 - 控制层(MVC):
@Controller
,标识一个表述层控制器组件。
这三个衍生的注解功能都与
@Component
,目的只是为了表示不同层级,所以都用一个也没有关系。
注解的使用
配置注解可以方便我们梳理逻辑,并且提高效率,使用注解非常简单:
- 在实体类上通过注解进行标识;
- 将实体类所在的包配置扫描;
- 测试。
所以我们需要三个文件:
- 实体类:
User
- xml配置文件:
ApplicationContext.xml
- 测试类:
MyTest
// 等价于<bean id="user" class="POJO.User"/>
@Component
public class User {
//@Value:属性注入
@Value("张三")
public String name;
}
<!--将User所属的包配置扫描-->
<context:component-scan base-package="POJO"/>
//测试
@org.junit.Test
public void TestUser(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("ApplicationContext.xml");
User user = applicationContext.getBean("user", User.class);
System.out.println(user.name);
}
/**
* 输出:
*张三
*/
装配Bean的注解
注解的介绍
@Autowired
:根据ByType
自动装配,添加@Qualifier
则可以再根据ByName
自动装配;可用于为类的属性、构造方法、方法进行注值。
@Qualifier
:只能搭配@Autowired
使用,不能单独使用。
@Resource
:默认通过ByName
的方法实现装配,如果找不到名字,再通过ByType
实现装配(常用)。
@Resource
的作用相当于@Autowired
,均可标注在字段或属性的setter方法上。
其他注解
@Scope注解用于指定作用域:
- singleton(单例模式):默认的,Spring会采用单例模式创建这个对象。关闭工厂 ,所有的对象都会销毁。
- prototype(多例模式):关闭工厂 ,所有的对象不会销毁。内部的垃圾回收机制会回收。
@Controller("user")
@Scope("prototype")
public class User {
@Value("张三")
public String name;
}
@value:利用该注解注入属性,也可以使用在set方法上。
// 等价于<bean id="user" class="POJO.User"/>
@Component
public class User {
// 等价于配置文件中 <property name="name" value="张三"/>
@Value("张三")
public String name;
}
注解容器配置小结
通过配置注解再在xml文件中配置扫描,就不需要像之前在xml中配置Bean了。
如果配置了注解,不配置扫描等于无效注解。
Java容器配置
Java 配置支持的核心就是@Configuration 类、@Bean方法(方法级别的注释)。
@Configuration是类级别的注解,表明一对象是bean定义的来源。@Configuration类通过@Bean方法声明 bean 。@Bean对@Configuration类方法的调用也可用于定义 bean 间的依赖关系。
配置类主要使用 @Bean 注解的方法来为 Spring 的 IoC 容器管理的对象定义实例、配置和初始化逻辑。
@Bean
注解扮演着与<bean/>
元素相同的角色。
一个简单@Configuration 类如下:
// 这代表一个配置类,相当于之前的ApplicationContext.xml
@Configuration
public class AppConfig {
// 这里相当于注册一个bean,等同于之前写的一个bean标签
// transferService等同于id属性
// 返回值相当于bean标签的class
@Bean
public MyService myService() {
return new MyServiceImpl();
}
}
相当于:
<beans>
<bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>
实例化Spring容器
我们知道,当时我们写ApplicationContext.xml时,是这样实例化的
new ClassPathXmlApplicationContext("ApplicationContext.xml");
而现在我们不再那么实例化,而是:
new AnnotationConfigApplicationContext(xxx.class);
现在取得是class文件(是不是类似MyBatis的用法),而并非之前的xml文件。现在这种通用ApplicationContext
实现不仅能够接受@Configuration
类作为输入,还能够接受普通@Component
类和用 JSR-330 元数据注释的类。
当
@Configuration
类作为输入提供时,@Configuration
类本身被注册为 bean 定义,并且@Bean
类中所有声明的方法也被注册为 bean 定义。当
@Component
和 JSR-330 类被提供时,它们被注册为 bean 定义,并且假定 DI 元数据如@Autowired
或@Inject
在必要时在这些类中使用。
启用组件扫描
在利用注解配置容器时需要配置扫描,用到的是:
<beans>
<context:component-scan base-package="com.acme"/>
</beans>
这样当扫描com.acme
包时,就能查找带@Component注解的类,并将这些类注册为容器内的Spring bean定义。
AnnotationConfigApplicationContext
允许通过scan(String)
实现相同的组件扫描功能。
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new
AnnotationConfigApplicationContext();
ctx.scan("com.acme");
ctx.refresh();
MyService myService = ctx.getBean(MyService.class);
}
⚠️注意:也可以将@ComponentScan("acme")
,使用在acme
类上(效果一样)。
使用@Bean注解
@Bean的方法级别的注解,是XML的直接模拟。@Bean可以使用在@Configuration或@Component类中。
声明一个Bean
通过@Bean注解可以声明一个bean。可以使用此方法在ApplicationContext指定方法返回值的类型类注册bean的定义(返回值为bean)。
默认情况下,bean名称于方法名相同!
@Configuration
public class AppConfig {
@Bean
public TransferServiceImpl transferService() {
return new TransferServiceImpl();
}
}
相当于:
<beans>
<bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>
上面代码实现的是:
//bean命名为transferService,绑定到类型的对象实例TransferServiceImpl
transferService -> com.acme.TransferServiceImpl
指定Bean的范围
通过@Scope注解能够指定bean的范围。默认为单例模式(singleton)。
与实体类定义范围类似。
@Configuration
public class MyConfiguration {
@Bean
@Scope("prototype")
public Encryptor encryptor() {
// ...
}
}
Bean自定义命名
默认情况下,@Bean使用方法名作为bean的名称,但是也可以通过定义其他名称来覆盖默认值。
@Configuration
public class AppConfig {
@Bean("myThing")
public Thing thing() {
return new Thing();
}
}
同样,使用此方法可以定义多个别名:
@Configuration
public class AppConfig {
@Bean({"dataSource", "subsystemA-dataSource", "subsystemB-dataSource"})
public DataSource dataSource() {
// instantiate, configure and return DataSource bean...
}
}
Bean描述
Spring可以给bean提供了@Description注解,我们可以定义详细的文本描述:
@Configuration
public class AppConfig {
@Bean
@Description("Provides a basic example of a bean")
public Thing thing() {
return new Thing();
}
}
Bean的依赖
使用@Bean方法可以具有描述构建bean所需要的任意数量的依赖关系,当@Bean依赖对方时,表达依赖的方式也很简单,只需要有一个bean方法调用另一个:
@Configuration
public class AppConfig {
@Bean
public Foo foo() {
return new Foo(bar());
}
@Bean
public Bar bar() {
return new Bar();
}
}
@Import注解
当我们定义了多个配置类时,由于实例化上下文时需要指定到对应的配置类,这就加大了代码量。
通过@Import注解可以将多个配置类合并为一个。
@Configuration
public class ConfigA {
@Bean
public A a() {
return new A();
}
}
@Configuration
@Import(ConfigA.class) // 导入上面定义好的配置类ConfigA
public class ConfigB {
@Bean
public B b() {
return new B();
}
}
public static void main(String[] args) {
ApplicationContext ctx =
new AnnotationConfigApplicationContext(ConfigB.class);
// 现在,两个bean A和B都将可用……
A a = ctx.getBean(A.class);
B b = ctx.getBean(B.class);
}
生命周期回调
@Bean 注解支持指定任意的初始化和销毁的回调方法。如果 bean 实现InitializingBean
、DisposableBean
或Lifecycle
,则容器会调用它们各自的方法。
public class BeanOne {
public void init() {
// initialization logic
}
}
public class BeanTwo {
public void cleanup() {
// destruction logic
}
}
@Configuration
public class AppConfig {
@Bean(initMethod = "init")
public BeanOne beanOne() {
return new BeanOne();
}
@Bean(destroyMethod = "cleanup")
public BeanTwo beanTwo() {
return new BeanTwo();
}
}
Java容器配置小结
@Configuration:这代表一个配置类,相当于之前的ApplicationContext.xml
@Bean:这里相当于注册一个bean,等同于之前写的一个bean标签
参考:
Core Technologies
Spring 基于 Java 的配置