本章中的大多数例子都使用【XML来指定配置元数据】,这些元数据在Spring容器启动时被扫描,每一个bean的元数据对应生成一个“BeanDefinition”。
本节我们可以通过【扫描类路径】隐式检测候选组件。 【候选组件】指的是通过扫描筛选并在容器中注册了相应beanDifination的类。 这样就不需要使用XML来执行bean注册。 相反,您可以使用注解(例如,【@Component 】)。
更多操作从Spring 3.0开始,Spring JavaConfig项目提供的许多特性都是核心Spring框架的一部分。 这允许您使用Java而不是使用传统的XML文件来定义bean。
1️⃣@Compo
nent
和及其派生出的其他注解
@Component 是任何spring管理组件的通用注解。
@Repository、@Service和@Controller是【@Component】用于更具体用例的注解(分别在持久性、服务和表示层中)。这些注解对于我们后期对特定bean进行批量处理时是有帮助的。
2️⃣自动检测类和注册beanDifination
Spring可以自动检测类的信息,并将相应的【BeanDefinition】实例注册到【ApplicationContext】中。 例如,以下两个类适合这样的自动检测:
@Service public class SimpleMovieLister { private MovieFinder movieFinder; public SimpleMovieLister(MovieFinder movieFinder) { this.movieFinder = movieFinder; } }
@Service public class SimpleMovieLister { private MovieFinder movieFinder; public SimpleMovieLister(MovieFinder movieFinder) { this.movieFinder = movieFinder; } }
要自动检测这些类并注册相应的bean,您需要将【@ComponentScan】添加到您的【 @Configuration】类中,其中【basePackages】属性是这两个类的公共父包。说人话就是:指定一个包名,自动扫描会检测这个包及其子包下的所有类信息。
@Configuration @ComponentScan(basePackages = "org.example") public class AppConfig { // ... }
为简单起见,前面的示例可能使用了注解的value属性 (即@ComponentScan ("org.example"))。
当然我们可以使用以下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="org.example"/> </beans>
注意: <context:component-scan> 的使用会隐式启用 <context:annotation-config>,当使用 <context:component-scan>时,通常不需要包含<context:annotation-config>元素。
3️⃣组件命名
当组件作为扫描过程的一部分被自动检测时,它的bean名是由该扫描器所知道的“BeanNameGenerator”策略生成的。
默认情况下,会使用【@Component】, 【@Repository】,【@Service】和【@Controller】注解的value值,因此将该名称会提供给相应的beanDefination。 如果你的注解不包含任何名称属性,会有默认bean名称生成器将返回【非首字母大写的非全限定类名】。 例如,如果检测到以下组件类,则名称为【myMovieLister】和【movieFinderImp】,这个和xml自动生成的标识符名称不同:
@Service("myMovieLister") public class SimpleMovieLister { // ... }
@Repository public class MovieFinderImpl implements MovieFinder { // ... }
4️⃣为自动检测组件提供scope
与spring管理的组件一样,自动检测组件的默认和最常见的作用域是“单例”。 然而,有时您需要一个不同的范围,可以由' @Scope '注解指定。 您可以在注解中提供作用域的名称,如下面的示例所示:
@Scope("prototype") @Repository public class MovieFinderImpl implements MovieFinder { // ... }
5️⃣使用过滤器自定义扫描
默认情况下,带有【@Component】、【@Repository】、【@Service】、【@Controller】、【@Configuration】注解的类是一定能被筛选器选中并进行注册的候选组件。 但是,您可以通过应用自定义过滤器来修改和扩展此行为,自由定制筛选哪些或不包含那些组件。 将它们作为@ComponentScan注解的includeFilters 或 excludeFilters 属性添加(或者作为XML配置中’ <context:include-filter /> ‘或’ <context:exclude-filter /> ‘元素的子元素)。 每个筛选器元素都需要’ type ‘和’ expression '属性。 下表描述了过滤选项:
过滤方式 | 示例表达式 | 描述 |
annotation (默认) | org.example.SomeAnnotation | 要在目标组件的类型级别上“存在”或“元注解存在”的注解。 |
assignable | org.example.SomeClass | 指定要排除的bean的类 |
aspectj | org.example…*Service+ | 要被目标组件匹配的AspectJ类型表达式,后边会学习 |
regex | org.example.Default.* | 由目标组件的类名匹配的正则表达式 |
custom | org.example.MyTypeFilter | ’ org.springframework.core.type的自定义实现,TypeFilter”接口。 |
下面的示例显示了忽略所有【@Repository】注解,而使用【stub】包下的类进行替换:
@Configuration @ComponentScan(basePackages = "org.example", includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"), excludeFilters = @Filter(Repository.class)) public class AppConfig { // ... }
下面的例子显示了等效的XML:
<beans> <context:component-scan base-package="org.example"> <context:include-filter type="regex" expression=".*Stub.*Repository"/> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/> </context:component-scan> </beans>
【小知识】: 您还可以通过在注解上设置useDefaultFilters=false 或通过提供use-default-filters="false" 作为<component-scan/> 元素的属性来禁用默认过滤器。 这将有效地禁用使用【@Component】、【@Repository 】、【@Service】、【 @Controller】、【@Configuration】注解或元注解的类的自动检测。
6️⃣在组件中定义Bean元数据
Spring组件还可以向容器提供beanDifination元数据。 可以使用 @Bean 注解来实现这一点。
@Component public class FactoryMethodComponent { @Bean @Qualifier("public") public TestBean publicInstance() { return new TestBean("publicInstance"); } public void doWork() { // Component method implementation omitted } }
前面的类是一个Spring组件,它的【doWork()】方法中包含特定于应用程序的代码。 然而,它还提供了一个beanDifination,该beanDifination有一个引用方法【public Instance()】的工厂方法。 【@Bean注解】标识工厂方法,通过【@Qualifier】注解标识一个限定符值。 其他可以指定的方法级注解有【@Scope 】, 【@Lazy 】等。
下面的例子展示了如何做到这一点:
@Component public class FactoryMethodComponent { private static int i; @Bean @Qualifier("public") public TestBean publicInstance() { return new TestBean("publicInstance"); } // use of a custom qualifier and autowiring of method parameters @Bean protected TestBean protectedInstance( @Qualifier("public") TestBean spouse, @Value("#{privateInstance.age}") String country) { TestBean tb = new TestBean("protectedInstance", 1); tb.setSpouse(spouse); tb.setCountry(country); return tb; } @Bean private TestBean privateInstance() { return new TestBean("privateInstance", i++); } }
7️⃣基于Java的容器配置
🍀(1)@Bean和@Configuration
Spring新的java配置支持的中心组件是带注解的【@Configuration】类和带注解的【@Bean】方法。
@Bean注解用于指示一个方法,该方法负责【实例化、配置和初始化】一个由Spring IoC容器管理的新对象。 对于那些熟悉Spring <beans/>XML配置的人来说,@Bean注解扮演着与<bean/> 元素相同的角色。 你可以在任何Spring @Component中使用@Bean注解方法。 但是,它们最常与@Configuration一起使用。
用@Configuration注解的一个类表明它的主要目的是作为beanDifination的源,我们通常称之为【配置类】。 此外,【@Configuration】类允许通过调用同一类中的其他【@Bean 】方法来【定义bean间的依赖关系】。 最简单的【@Configuration】类如下所示:
@Configuration public class AppConfig { @Bean public MyService myService() { return new MyServiceImpl(); } }
前面的’ AppConfig '类等价于下面的Spring <beans/>
XML:
<beans> <bean id="myService" class="com.acme.services.MyServiceImpl"/> </beans>
🍀(2)使用 AnnotationConfigApplicationContext实例化Spring容器
下面的章节记录了Spring 3.0中引入的【AnnotationConfigApplicationContext】。 这个通用的【ApplicationContext】实现不仅能够接受【@Configuration】类作为输入,还能够接受普通的【@Component】类和用JSR-330元数据注解的类。
当提供【@Configuration】类作为输入时,【@Configuration】类本身被注册为一个beanDifination,并且类中所有声明的【@Bean】方法也被注册为beanDifination。
当提供【@Component 】和JSR-330相关的注解类时,它们被注册为beanDifination。
a、结构简洁
就像Spring XML文件在实例化【ClassPathXmlApplicationContext】时被用作输入一样,当实例化【AnnotationConfigApplicationContext】时,你可以使用【@Configuration】类作为输入。 这允许Spring容器完全不使用xml,如下例所示:
public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class); MyService myService = ctx.getBean(MyService.class); myService.doStuff(); }
正如前面提到的,【AnnotationConfigApplicationContext】并不局限于只与【@Configuration】类一起工作。 任何【@Component】或JSR-330注解类都可以作为输入提供给构造函数,如下面的例子所示:
public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(MyServiceImpl.class, Dependency1.class, Dependency2.class); MyService myService = ctx.getBean(MyService.class); myService.doStuff(); }
前面的例子假设【MyServiceImpl】、【Dependency1】和【Dependency2】使用Spring依赖注入注解,比如【@Autowired】。
b、通过使用’ register(Class<?>…)'以编程方式构建容器
你可以使用一个【没有参数的构造函数】来实例化一个【AnnotationConfigApplicationContext】,然后使用【register()】方法来配置它。 当以编程方式构建一个“AnnotationConfigApplicationContext”时,这种方法特别有用。 下面的例子展示了如何做到这一点:
public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.register(AppConfig.class, OtherConfig.class); ctx.register(AdditionalConfig.class); ctx.refresh(); MyService myService = ctx.getBean(MyService.class); myService.doStuff(); }
c、使用 scan(String…)启用组件扫描
要启用组件扫描,你可以像下面这样注解你的 @Configuration 类:
@Configuration @ComponentScan(basePackages = "com.acme") public class AppConfig { // ... }
<beans> <context:component-scan base-package="com.ydlclass" / > </beans>
同时,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); }
请记住,【@Configuration】类是带有【@Component】元注解的一个注解,因此它们是组件扫描的候选对象。 在前面的例子中,假设【AppConfig】在"com.acme"中声明。 在’ refresh() ‘之后,它的所有’ @Bean '方法都被处理并注册为容器中的beanDifination。
🍀(3) @Bean注解
【@Bean】是一个方法级注解,与XML<bean/> 元素具有相同的能力。 注解支持<bean/>提供的一些属性,例如:
init-method
destroy-method
autowiring
name
你可以在带有【@Configuration】注解的类或带有【@Component】注解的类中使用【@Bean】注解。
a、声明一个 Bean
使用【@Bean】对方法进行注解可以帮助我们申明一个bean。 您可以使用此方法在【ApplicationContext】中注册一个beanDifination,该bean的类型会被指定为【方法的返回值类型】,而具体的返回值则是交由spring管理的bean实例。 默认情况下,bean名与方法名相同。 下面的例子显示了一个【 @Bean 】方法声明:
@Configuration public class AppConfig { @Bean public TransferServiceImpl transferService() { return new TransferServiceImpl(); } }
上面的配置与下面的Spring XML完全相同:
<beans> <bean id="transferService" class="com.acme.TransferServiceImpl"/> </beans>
注意: 你也可以使用接口(或基类)作为返回类型来声明你的@Bean方法,如下面的例子所示:
@Configuration public class AppConfig { @Bean public TransferService transferService() { return new TransferServiceImpl(); } }
b、Bean的依赖关系
带注解的【@Bean】方法可以有任意数量的参数,这些参数描述构建该bean所需的依赖关系。 例如,如果我们的【TransferService】需要一个【AccountRepository】,我们可以用一个方法参数来实现这个依赖,如下例所示:
@Configuration public class AppConfig { @Bean public TransferService transferService(AccountRepository accountRepository) { return new TransferServiceImpl(accountRepository); } }
c、接受生命周期回调
任何用【@Bean】注解定义的类都支持常规的生命周期回调,并且可以使用JSR-250的’ @PostConstruct ‘和’ @PreDestroy '注解。
也完全支持常规的Spring lifecycle回调。 如果一个bean实现了’ InitializingBean ‘、’ DisposableBean ‘或’ Lifecycle ',则容器会调用它们各自的方法。
标准的【Aware 】接口也完全支持。
【@Bean注解】支持指定任意的初始化和销毁回调方法,就像Spring XML在’ bean ‘元素上的’ init-method ‘和’ destroy-method '属性一样,如下面的示例所示:
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(); } }
小知识: 对于上面例子中的’ BeanOne ‘,在构造过程中直接调用’ init() '方法同样有效,如下例所示:
@Configuration public class AppConfig { @Bean public BeanOne beanOne() { BeanOne beanOne = new BeanOne(); beanOne.init(); return beanOne; } // ... }
当您直接在代码中进行配置时,您可以对您的对象做任何您想做的事情,而不总是需要依赖于容器生命周期。