1.@Component修饰类,可对修饰的类在项目启动时注入ioc容器之中
(1)对于类中只有含参构造器:
在将该类注入到 Spring IoC 容器之前,Spring 会调用这个含参构造器。这是因为 Spring 需要通过含参构造器来完成依赖注入,创建类的实例。
(2)对于类中只有无参构造器:这种又会根据依赖的注入方式分为两种
1)使用setter方法注入,Spring会先调用无参构造器创建实例,接着使用setter方法进行注入。
代码示例:
import org.springframework.stereotype.Component;
@Component
class Dependency {
public void doWork() {
System.out.println("Dependency is working.");
}
}
@Component
public class MyComponent {
private Dependency dependency;
public MyComponent() {
System.out.println("MyComponent's no-arg constructor is called.");
}
public void setDependency(Dependency dependency) {
System.out.println("MyComponent's setDependency method is called.");
this.dependency = dependency;
}
public void useDependency() {
dependency.doWork();
}
}
在上述代码中,Spring 会先调用 MyComponent 的无参构造器创建实例,接着调用 setDependency 方法将 Dependency 类型的 Bean 注入到 MyComponent 中。
2)使用基于字段的自动装配,Spring 会先调用无参构造器创建类的实例,然后通过反射机制直接给字段赋值,而不会调用 setter 方法。
代码示例:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
class Dependency {
public void doWork() {
System.out.println("Dependency is working.");
}
}
@Component
public class MyComponent {
@Autowired
private Dependency dependency;
public MyComponent() {
System.out.println("MyComponent's no-arg constructor is called.");
}
public void useDependency() {
dependency.doWork();
}
}
Spring 先调用 MyComponent 的无参构造器创建实例,之后通过反射将 Dependency 实例赋值给 dependency 字段,不会调用 setter 方法。
(3)当既有含参构造器,又有无参构造器时,Spring的版本不同,处理方式也就不同
Spring 4.3 及以后版本:
从 Spring 4.3 版本开始,如果一个类存在多个构造器,且没有使用 @Autowired 注解显式指定使用哪个构造器,Spring 会优先选择带有参数且参数都能在 Spring 容器中找到对应 Bean 的构造器;若不存在这样的构造器,则会选择无参构造器。
Spring 4.3 以前版本:
在 Spring 4.3 之前的版本,如果一个类存在多个构造器,且没有使用 @Autowired 注解显式指定使用哪个构造器,Spring 会选择无参构造器进行实例化。若要使用含参构造器,必须使用 @Autowired 注解来明确指定。
(4)被@Component修饰的类中有静态方法和静态代码块
静态代码块会在类加载时(注入ioc之前)自动执行,并且只执行一次;静态方法则需要被显式调用才会执行。
2.@Bean修饰方法,可对修饰的方法的返回值在项目启动时注入ioc容器之中
(1)在注入时如果相同类的对象只有一个被注入到容器,那么获取时可通过类获取该对象
代码示例:
@Configuration
public class AppConfig {
@Bean
public MyService myService1() {
return new MyServiceImpl1();
}
}
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
MyService myService = context.getBean(MyService.class);
myService.doSomething();
context.close();
}
}
由于容器里只有一个 MyService 类型的 Bean,所以能顺利获取该 Bean 实例。
(2)在注入时如果相同类的对象有多个被注入到容器,那么获取时必须通过指定类名称获取该对象
代码示例:
@Configuration
public class AppConfig {
@Bean
public MyService myService1() {
return new MyServiceImpl1();
}
@Bean
public MyService myService2() {
return new MyServiceImpl2();
}
}
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
MyService myService = context.getBean(MyService.class);
myService.doSomething();
context.close();
}
}
当调用 context.getBean(MyService.class) 时,由于有两个 MyService 类型的 Bean(myService1 和 myService2),就会抛出异常。
(3)也可以使用@Primary注解来进行类的优先选择,这样即使多个相同类的对象在取出时也不会报错
代码示例:
@Configuration
public class AppConfig {
@Bean
public MyService myService1() {
return new MyServiceImpl1();
}
@Bean
public MyService myService2() {
return new MyServiceImpl2();
}
}
@Service
@Primary
public class MyServiceImpl1 implements MyService {
@Override
public void doSomething() {
System.out.println("MyServiceImpl1 is doing something.");
}
}
@Service
public class MyServiceImpl2 implements MyService {
@Override
public void doSomething() {
System.out.println("MyServiceImpl2 is doing something.");
}
}
在这个例子中,MyServiceImpl1 被 @Primary 注解标注,当调用 context.getBean(MyService.class) 时,会返回 MyServiceImpl1 的实例。