自动扫描自定义的Bean
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包含了context:annotation-config
base-package属性定义了spring自动扫描的类的路径,此路径下所有被相关注解的类都将被扫描
-->
<context:component-scan base-package="com.yyoo"/>
</beans>
本文将使用此方式来讲解
<context:annotation-config>配置隐式注册了如下处理器:
ConfigurationClassPostProcessor
AutowiredAnnotationBeanPostProcessor
CommonAnnotationBeanPostProcessor
PersistenceAnnotationBeanPostProcessor
EventListenerMethodProcessor
Java配置的方式
@Configuration
@ComponentScan(basePackages = "com.yyoo")
public class AppConfig {
// ...
}
此方式我们将在后续讲解
@Component及其引伸的注解
- @Component 所有的bean我们都可以使用该注解,只是为了后续区分新增了下面的注解
- @Controller MVC中控制层的注解,也就是我们的Controller用该注解
- @Service 服务层的注解,我们所有的Service使用该注解
- @Repository 持久层的注解
- @Configuration Java编程配置使用的注解
- @RestController 由@Controller和Spring MVC中的@ResponseBody组合起来的注解
- @Scope 定义bean的作用范围
在我们定义的bean有明确的定义时我们就用对应的注解,如果我们的Controller,就用@Controller注解,当我们的Bean没有明确的定义时,我们就是要@Component注解进行配置即可。
@Component及其相关注解的命名规则
默认情况下,如果@Component注解没有定义value属性,那么默认使用对应类的类名首字母小写为bean的名称。
BeanNameGenerator接口
我们可以通过实现BeanNameGenerator接口来自定义bean的名称规则。
特殊情况,如在不同的包下有相同类名的类,那么默认的规则下,会出现相同名称的bean,这个时候我们可以使用FullyQualifiedAnnotationBeanNameGenerator在排除这种情况。
虽然我们自定义命名规则可以达到效果,但实际工作中我们还是建议在出现冲突的类的@Component及其相关注解上使用value属性定义名称。
依赖注入注解
@Autowired
该注解可以作用在字段上、方法上、构造方法上、方法参数上
- 作用在字段上时,该字段可以没有setter方法
- 作用在构造方法上时,如果同时作用在多个构造方法,那么所有的@Autowired的required属性都必须为false。
- required属性表示依赖的类是否必须能查找到。默认为true,如果没有查找到对应依赖将会报错。如果为false,没有查找到对应依赖将不会报错,对应依赖项为空。
@Autowired注入过程
- 首先按照byType方式查找对应的Bean,如果对应的结果唯一,则直接装配。如果不唯一,进行第二步。
2.按照 byName方式筛选第一步的结果,如果结果唯一,则装配。如果不唯一且required属性为true,则抛出异常,如果required属性为false,则不注入,也不拋异常。
@Autowired自动注入ApplicationContext
在容器Bean中加入如下代码即可直接使用ApplicationContext实例
@Autowired
private ApplicationContext applicationContext;
无需再实现对应的Aware接口。相应的我们也可以通过同样的方式得到如下实例
- BeanFactory
- ApplicationContext
- Environment
- ResourceLoader
- ApplicationEventPublisher
- MessageSource
@Primary
用于控制byType装配时有多个匹配的结果,如果结果中某一个bean使用了@Primary注解,那么这个bean就会被自动装配,而不会抛出异常。
@Primary有如下两个地方可以使用
java编程试配置
@Configuration
public class MovieConfiguration {
@Bean
@Primary
public MovieCatalog firstMovieCatalog() { ... }
@Bean
public MovieCatalog secondMovieCatalog() { ... }
// ...
}
直接在对应bean上注解
@Primary
@Component
public class MovieCatalog{
// ...
}
@Qualifier
用于提供更细范围的Bean装配控制,@Qualifier对应bean的id或名称,可以限定为某一个特定的bean作为装配结果。
与@Autowired结合使用
public class MovieRecommender {
@Autowired
@Qualifier("main")
private MovieCatalog movieCatalog;
// ...
}
会自动装配类型为MovieCatalog,且id或名称是main的bean。
@Qualifier还可以在构造函数或方法上使用
public class MovieRecommender {
private MovieCatalog movieCatalog;
private CustomerPreferenceDao customerPreferenceDao;
@Autowired
public void prepare(@Qualifier("main") MovieCatalog movieCatalog,
CustomerPreferenceDao customerPreferenceDao) {
this.movieCatalog = movieCatalog;
this.customerPreferenceDao = customerPreferenceDao;
}
// ...
}
@Resource
javax.annotation.Resource注解,只能作用于字段和普通方法上,不能作用于构造函数。
@Resource有两个重要的属性,name和type,默认情况下@Resource使用byName进行装配。如果同时指定name和type属性,则按照name和type共同匹配的规则进行装配(也就是name和type都必须匹配)。
示例
MyService接口
package com.yyoo.boot.annotation;
public interface MyService {
void print();
}
对应的两个实现类
package com.yyoo.boot.annotation;
import org.springframework.stereotype.Service;
@Service
public class MyServiceImplOne implements MyService{
@Override
public void print() {
System.out.println("MyServiceImplOne print");
}
}
package com.yyoo.boot.annotation;
import org.springframework.stereotype.Service;
@Service
public class MyServiceImplTwo implements MyService{
@Override
public void print() {
System.out.println("MyServiceImplTwo print");
}
}
TestController
package com.yyoo.boot.annotation;
import org.springframework.stereotype.Controller;
import javax.annotation.Resource;
@Controller
public class TestController {
@Resource(name = "myServiceImplOne")
private MyService service;
public void print(){
service.print();
}
}
我们的@Service注解没有定义bean的名字,其默认使用类名首字母小写为名称,所以这里我们的@Resource的name属性对应写为类名首字母小写的名称。
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包含了context:annotation-config
base-package属性定义了spring自动扫描的类的路径,此路径下所有被相关注解的类都将被扫描
-->
<context:component-scan base-package="com.yyoo"/>
</beans>
Demo主程序
package com.yyoo.boot.annotation;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Demo8 {
@Test
public void test(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-annotation.xml");
// 添加一个关闭的钩子(hook)
context.registerShutdownHook();
TestController controller = context.getBean(TestController.class);
controller.print();
}
}
打印结果
MyServiceImplOne print
如果我们在示例的@Resource注解中添加type属性,那么如果我们定义的type不是MyService接口类型,则代码报错。因为没有名称为myServiceOne且类型不是MyService接口类型的bean。
我们对比@Autowired,其实@Resource实现了@Autowired和@Primary、@Qualifier组合的功能,而且在底层实现上也会更简单明了,所以官方推荐使用@Resource代替@Autowired。
@Autowired可以作用在字段上、构造函数上、方法上、方法参数上
@Resource不能作用在构造函数上,也不能作用在方法参数上
JSR 330的标准注解
@Inject和@Named组合
import javax.inject.Inject;
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Inject
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
public void listMovies() {
this.movieFinder.findMovies(...);
// ...
}
}
与 一样@Autowired,您可以@Inject在字段级别、方法级别和构造函数参数级别使用。
@Named用于限定名称
import javax.inject.Inject;
import javax.inject.Named;
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Inject
public void setMovieFinder(@Named("main") MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
@Inject没有required属性,我们可以使用@Nullable在达到同样的效果
public class SimpleMovieLister {
@Inject
public void setMovieFinder(@Nullable MovieFinder movieFinder) {
// ...
}
}
@Named和@ManagedBean与@Component等效
import javax.inject.Inject;
import javax.inject.Named;
@Named("movieListener") // @ManagedBean("movieListener") could be used as well
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Inject
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
不直接命名的方式也一样
import javax.inject.Inject;
import javax.inject.Named;
@Named
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Inject
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
JSR 330注解这里介绍得很粗略,有些可能还没见过,但基本的意思都表达了,示例也是官网示例。