- @Autowired是什么?
依赖注入的地方,前提是被注入的对象和注入的对象都要在Spring中注册为bean(利用注解@Configuration)
public interface HelloWorldService {
void sayHi(String name);
}
public class HelloWorldServiceImpl implements HelloWorldService {
public void sayHi(String message) {
System.out.println(message);
}
}
public class HelloWorldServiceClient {
@Autowired
private HelloWorldService helloWorld;
public void showMessage() {
helloWorld.sayHi("Hello world!");
}
}
- 一个小例子
@Configuration使用Java类进行配置,该类还需要负责在main方法中开启Spring容器。
每个@Configuration注解的类都会在Spring容器中注册成一个Bean,并且Spring会使用CGLib对其进行动态代理,这样可以管理不同Bean不同的声明周期和Bean的代理对象;
Spring容器可以有多个@Configuration注解的类,这点从构造函数中就可以看出来,public AnnotationConfigApplicationContext(Class>... componentClasses);
可以输入多个类。
@Bean的方法创建并返回了对应的bean对象,只会调用一次并且缓存创建的对象
@Configuration
public class AppRunner {
@Bean
public HelloWorldService createHelloWorldService() {
}
@Bean
public HelloWorldServiceClient createClient() {
return new HelloWorldServiceClient();
}
public static void main(String... strings) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(AppRunner.class);
HelloWorldServiceClient bean = context.getBean(HelloWorldServiceClient.class);
bean.showMessage();
}
}
AnnotationConfigApplicationContext是啥玩意?
AnnotationConfigApplicationContext实现了ApplicationContext接口,代表Spring容器,实现了实例化、配置、组装等功能,输入为注解的类。输出
Hello world!@ComponentScan
之前我们自己在@Configuration注解的类中用@Bean注解要创建Bean的方法,而如果我们使用@ComponentScan和@Configuration,Spring就可以自动创建类。-
@Import
除了在AnnotationConfigApplicationContext的构造函数中输入多个@Configuration注解的类之外,还可以使用@Import在其他配置类中加入其他配置类,这种方法更加模块化,引用的方式呈现树状,而不是直接输入多个配置类的平面状。
可以将@Import注解跟Java中引包的import关键字对比下,一样的。
下图中AppConfig类用@Import引入了其他配置类DataSourceConfig,AnnotationConfigApplicationContext初始化的时候只需要引入AppConfig类即可。注意DataSourceConfig同样需要使用@Configuration注解。
@Bean
@Bean注解是一个方法注解,跟XML配置中的元素一样。
名字:实例有一个默认名字也就是方法的名字calculator,在多个实例的时候,可能可以用这个解决混淆(不知道注入哪个实例)的问题。也可以自行定义实例的名字@Bean(name = "aname")。同name,@Qualifier也可以定义“名字”。
要在注入点对上,需要将注入的属性名字定义为方法的名字,或者注入点、setter也加上@Qualifier。
@Bean
Calculator calculator() {
return new Calculator();
}
参数:
name:名字,默认是方法的名字。一般用在同一个类有多个实例的时候,需要为不同的实例命名,在注入点的地方需要加入@Qualifier注解选择对应的类。
autowire:注入类型
Autowire.NO:默认值,需要在注入点使用@Autowired。
Autowire.BY_TYPE:Spring会根据类型扫描bean的属性并用setter方法注入,所以属性不用使用@Autowired注解,非常简洁,但是也容易看不懂,因为没有明示注入点。
Autowire.BY_NAME:注入点需要同时使用@Qualifier和@Autowired两个注解。
initMethod/destroyMethod:
- @Autowired
@Autowired用来定义注入点,除了注解在字段上,还可以注解在方法上,之后Spring会运行这个方法,输入的参数是对应的Bean。
@Autowired有三种注入方法是,字段、setter、构造器等,从Spring4.3之后,构造器可以不用使用@Autowired,会隐式地注入。
@Autowired
public void configure(GreetingService greetingService, LocalDateTime appServiceTime) {
greetingFormat = String.format("%s. This app is running since: %s%n", greetingService.getGreeting(""),
appServiceTime.format(DateTimeFormatter.ofPattern("YYYY-MMM-d")));
}
@Inject
@Inject同@Autowired都是用来定义注入点,只不过前者是JSR 330's规范为依赖注入定义的注解,而后者是Spring自己的注解@Primary
可以解决ambiguity的问题,当有多个同类型的Bean的时候,会首先使用@Primary注解的Bean。ambiguity
1、注入的属性名字使用默认名字,也就是@Bean注解的方法的名字
Bean类
@Autowired
private ServiceBean serviceBean2;
Config类
@Bean
public ServiceBean serviceBean2 () {
return new ServiceBean("Service Bean 2");
}
2、@Bean类型注入(Autowire.BY_TYPE)时同一个类有两个实例
在setter参数中加上@Qualifier("serviceBean2") 注解,也可以在setter和bean两个地方都加上@Qualifier("serviceBean2") 注解
bean类
public void setServiceBean (@Qualifier("serviceBean2") ServiceBean serviceBean) {
this.serviceBean = serviceBean;
}
3、使用@Qualifier注解,可以只在注入的地方用,也可以在Bean定义和注入的地方用
Bean类
@Autowired
@Qualifier("serviceBean2")
private ServiceBean serviceBean;
4、使用@Resource注解
@Resource用在注入点,相当于@Qualifier和Autowired的组合
5、@Inject类似于@Autowired,因此也可以使用@Autowired加上@Qualifier
6、@Named类似于@Qualifier,因此也可以使用@Inject和@Named
7、@Primary可以指定在有多个bean的时候优先使用哪个
8、Spring 4.x具有泛型的注入能力,如果类型相同会根据具体的泛型进行注入,类型相同泛型不同不会引起
NoUniqueBeanDefinitionException
异常。
- @DependsOn
Spring除了注入对象先于被注入对象加载之外,并不能保证加载的顺序。@DependsOn可以使得别的对象先加载,再加载当前的类。
eventPublisherBean依赖eventListenerBean,因此eventListenerBean会先加载。
@Bean
@DependsOn("eventListenerBean")
public EventPublisher eventPublisherBean() {
return new EventPublisher();
}
@Bean
public EventListener eventListenerBean() {
return new EventListener();
}
懒加载
@Lazy with @Bean (or @Lazy with @Component):
应用启动的时候不加载,直到使用的时候
@Lazy with @Autowired:
要注入的类加载的时候不加载,直到使用的时候
原理:
在一开始的时候用的是Proxy,用到的时候才去用真正的类@Scope
和@Bean、@Conponent注解一起使用。singleton表示Spring容器中只有一个,用在一些无状态的场合,比如Service、DAO、Controller等,而prototype每次都返回一个新的,且容器不再记录这个Bean,因此PreDestroy对于这类bean是不会调用的,用在一些有状态的场合,比如购物车、request、session等。@ComponentScan
需要和@Configuration一起搭配使用,用来指定要扫描的包,然后使用@Configuration、@Component、@Controller、@Repository、@Service定义Bean,其中@Component最重要,其他都是@Component的增强版而已,它们可以更好地结合切面以及今后的功能。
这些注解都没有使用@Inherited,意味着这些注解不会作用到子类上。
@ComponentScan(basePackageClasses = {OnlineOrderClient.class, OnlineOrderService.class})
表示扫描OnlineOrderClient.class和OnlineOrderService.class这两个类所在的包。
过滤
@ComponentScan(includeFilters = @ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE, classes = {OrderService.class}))
-
@Component和@Configuration的不同
@Configuration会使用CGLib代理,调用anotherBean每次都返回同一个bean,而@Component每次调用anotherBean每次都返回新的bean。建议仅在@Configuration的类中使用@Bean
注入容器
可以使用, , and 等XML标签,再Java代码中可以直接定义
@Bean
public String[] fruits() {
return new String[]{"apple", "banana", "orange"};
}
private static class TestBean {
@Autowired
private String[] fruits;
@PostConstruct
public void postConstruct() {
System.out.println(Arrays.toString(fruits));
}
}
多个对象注入一个容器对象
@Component
class SavingAccount implements Account {
@Override
public String toString() {
return "SavingAccount";
}
}
@Component
class CheckingAccount implements Account {
@Override
public String toString() {
return "CheckInAccount";
}
}
@Component
class FixedDepositAccount implements Account {
@Override
public String toString() {
return "FixedDepositAccount";
}
}
private static class TestBean {
@Autowired
private List accounts;
@PostConstruct
public void init() {
System.out.println(accounts);
}
}
使用@Qualifier可以限定注入容器的元素,而不是全部
@Component
@Qualifier("basicAccount")
class SavingAccount implements Account {
@Override
public String toString() {
return "SavingAccount";
}
}
使用@Order可以按照顺序注入容器
循环依赖
循环依赖就是两个bean都想通过构造器注入彼此。
最简单可以通过setter注入解决,但这样就不能通过构造器注入了,构造器注入可以将字段定义为final。
@Lazy可以解决循环依赖,使用@Lazy的A会先用代理替代,这个代理初始化的时候不会要求实例化B,这样循环的链条就断了。依赖检查
检查是否有对象没有注入
一般在@PostConstruct的方法中检查对应的对象是否为null@Component和@Bean的区别
@Bean更加灵活,不用也不能注解在Bean的类上(也就是解耦了bean实例化和类定义),注解在实例化Bean的方法里面。