介绍
IoC (Inversion of Control)控制反转。
在传统的开发模式中,对象的创建的需要自己 new 的,但是 Spring IoC 就不需要我们自己来创建对象,而是把创建对象的任务交给 IoC 容器。
ApplicationContext (Spring 上下文)
IoC 容器帮我们创建好对象了,那如果我们要获取对象的话,就要从 Sping 中获取,那么就需要得到 Spring 的上下文。
ApplicationContext 就是 Spring 上下文
在启动类的main 方法里就可以获取到:
java
@SpringBootApplication
public class SpringIocDemoApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
}
}
获取 Bean 对象的方式
在上面我们获取到的Spring 上下文之后,就可以通过 getBean 方法来获取 Bean 对象了。
getBean 传参如下:
其中第 1,2,4 是我们常用的。
Object getBean(String name) 是通过 Bean 对象的名称来获取的
Bean 对象的名称默认是小驼峰,例如类名为 TestController,那么它的 Bean 名称就是 testController
如果开头大于等于两个连续的字符的名称均为大写的话,那么 Bean 就是类名,例如类名为 TEstController,那么 Bean 名称就是 TEstController,如果类名为 TESTCONtroller,那么Bean 名称就是 TESTCONtroller
< T> T getBean(Class< T> requiredType) 通过 Bean 的类型来获取,即通过 Bean 对象的类名来获取。
< T> T getBean(String name, Class< T> requiredType) 通过 Bean 的名称和类型来获取。
Application VS BeanFactory
Spring 容器有两个顶级的接口,分别是 ApplicationContext 和 BeanFactory
其中 BeanFactory 提供了基础的访问容器的能力,而 ApplicationContext 是 BeanFactory 的子类,除了继承 BeanFactory 的所有功能之外,它还拥有独特的特性,还添加了国际化支持,资源访问支持,以及事务传播等方面的支持。
从性能方面来说,ApplicationContext 是一次性加载并初始化所有 Bean 对象 (类比饿汉模式),而 BeanFactory 是需要哪个采取加载哪个(类比懒汉模式),因此 BeanFactory 更加轻量
类注解
五大类注解的使用方法是一样的。
@Controller
java
@Controller
public class TestController {
public void print() {
System.out.println("testController");
}
}
java
TestController bean = context.getBean(TestController.class);
bean.print();
TestController bean1 = (TestController) context.getBean("testController");
bean1.print();
TestController bean2 = context.getBean("testController", TestController.class);
bean2.print();

我们可以通过注解指定名称:
java
@Controller("t")
public class TestController {
public void print() {
System.out.println("testController");
}
}
加入我们获取 Bean 对象的代码没有改的话,就会发生下面的报错:

没有对应的 Bean 定义
我们应该把需要传入Bean 名称的代码修改过来:
java
TestController bean = context.getBean(TestController.class);
bean.print();
TestController bean1 = (TestController) context.getBean("t");
bean1.print();
TestController bean2 = context.getBean("t", TestController.class);
bean2.print();
我们通过打印来观察 Bean 对象:
java
System.out.println(bean);
System.out.println(bean1);
System.out.println(bean2);

会发现这些 Bean 对象是相同的,且唯一的。
也就说五大类注解只能创建唯一的 Bean 对象,无法创建多个
如果把 类注解给去掉的话,IoC容器是不会帮我们创建对象的:
当使用类注解指定名称的时候,要注意只能使用唯一的名称,不能出现不同的对象却有着相同的名称!!!
例如上面两个不同的 Bean 对象却使用相同的名称 t
这时候就会报冲突的Bean 定义

@Service
和 Controller 用法一样,既可以不指定名称,也可以指定名称
java
@Service
public class HelloService {
public void print() {
System.out.println("HelloService 启动");
}
}
@Service("t1")
public class TestService {
public void print() {
System.out.println("TestService 启动");
}
}
java
HelloService bean = context.getBean(HelloService.class);
bean.print();
HelloService bean1 = (HelloService) context.getBean("helloService");
bean1.print();
HelloService bean2 = context.getBean("helloService", HelloService.class);
bean2.print();
TestService bean3 = context.getBean(TestService.class);
bean3.print();
TestService bean4 = (TestService) context.getBean("t1");
bean4.print();
TestService bean5 = context.getBean("t1", TestService.class);
bean5.print();

@Repository
java
@Repository
public class HelloRepository {
public void print() {
System.out.println("HelloRepository 启动");
}
}
@Repository("t2")
public class TestRepository {
public void print() {
System.out.println("TestRepository 启动");
}
}
java
HelloRepository bean = context.getBean(HelloRepository.class);
bean.print();
HelloRepository bean1 = (HelloRepository) context.getBean("helloRepository");
bean1.print();
HelloRepository bean2 = (HelloRepository) context.getBean("helloRepository", HelloRepository.class);
bean2.print();
TestRepository bean0 = (TestRepository) context.getBean(TestRepository.class);
bean0.print();
TestRepository bean3 = (TestRepository) context.getBean("t2");
bean3.print();
TestRepository bean4 = context.getBean("t2", TestRepository.class);
bean4.print();
@Component
java
@Component("h")
public class HelloComponent {
public void print() {
System.out.println("HelloComponent 启动");
}
}
@Component
public class TestComponent {
public void print() {
System.out.println("Test Component 启动");
}
}
java
HelloComponent bean = context.getBean(HelloComponent.class);
bean.print();
HelloComponent bean1 = (HelloComponent) context.getBean("h");
bean1.print();
HelloComponent bean2 = context.getBean("h", HelloComponent.class);
bean2.print();
TestComponent bean3 = context.getBean(TestComponent.class);
bean3.print();
TestComponent bean4 = (TestComponent) context.getBean("testComponent");
bean4.print();
TestComponent bean5 = context.getBean("testComponent", TestComponent.class);
bean5.print();
@Configuration
java
@Configuration("h1")
public class HelloConfiguration {
public void print() {
System.out.println("HelloConfiguration 启动");
}
}
@Configuration
public class TestConfiguration {
public void print() {
System.out.println("TestConfiguration 启动");
}
}
java
HelloConfiguration bean = context.getBean(HelloConfiguration.class);
bean.print();
HelloConfiguration bean1 = (HelloConfiguration) context.getBean("h1");
bean1.print();
HelloConfiguration bean2 = context.getBean("h1", HelloConfiguration.class);
bean2.print();
TestConfiguration bean3 = context.getBean(TestConfiguration.class);
bean3.print();
TestConfiguration bean4 = (TestConfiguration) context.getBean("testConfiguration");
bean4.print();
TestConfiguration bean5 = context.getBean("testConfiguration", TestConfiguration.class);
bean5.print();
联系与区别
通过查看源码得知:@Controller、@Service、@Repository、@Configuration 是 @Component 的衍生注解

@Controller :控制层 【控制层必须使用这个注解,不可以被其他注解替代】
@Service :业务逻辑层
@Repository : 数据层
@Configuration : 配置层
@Component : 组件层
使用类注解创建的 Bean 对象只有一个
方法注解 @Bean
@Bean 要搭配类注解一起使用,如果没有搭配类注解 Spring 是不会进行扫描的,并且会抛出没有 Bean 定义的异常

java
@Controller
public class TestBean {
@Bean
public Student s1() {
return new Student("zhangsan", 18);
}
@Bean
public Student s2() {
return new Student("isi", 20);
}
}
java
@AllArgsConstructor
@Data
public class Student {
private String name;
private Integer age;
}
java
Student bean = (Student)context.getBean("s1");
System.out.println(bean);
Student bean1 = (Student) context.getBean("s2");
System.out.println(bean1);

@Bean 默认的Bean 名称就是方法的名称
当然你也可以自定义 Bean 名称,Bean 名称可以是多个【即以数组的形式定义】
java
@Bean("s3")
//@Bean(name = "s3")
//@Bean(value = "s3")
public Student s1() {
return new Student("zhangsan", 18);
}
@Bean({"s4","s5"})
/*@Bean(name = {"s4","s5"})
@Bean(value = {"s4","s5"})*/
public Student s2() {
return new Student("isi", 20);
}

如果被 @Bean 修饰的方法需要传参的话,这些参数是从 Spring 容器中获取的,所以这些参数要事先存储好
如果没有在 Spring 容器中找到 name 的话,就会导致 Spring 项目启动失败
解决办法:事先将 name 交给 Spring 管理
java
@Bean
public String name() {
return "hello";
}

@Bean 定义的名称不能重复
java
@Bean("s2")
public Student s1() {
return new Student("zhangsan", 18);
}
@Bean
public Student s2() {
return new Student("isi", 20);
}

这里提示 s2 已经被注册过了,不能再进行注册了
扫描路径
Spring 扫描的路径默认为 启动类所在的目录,包括其子孙目录
例如下面的启动在 springiocdemo 这个包下,就会扫描这个包包含的所有路径
我们可以使用 @ComponentScan() 来自定义扫描路径
java
@ComponentScan("org.example.springiocdemo")
一般我们将启动类放到对应的位置即可,不需要使用 @ComponentScan 注解
总结
类注解:Bean 名称一般为对应的类名的小驼峰形式,如果类名开头大于等于两个字符都为大写,则 Bean 名称为类名本身,但是类注解只能创建唯一的 Bean 对象
@Controller 必须在 控制层使用,@Controller、@Service、@Repository、@Configuration 是 @Component 的衍生注解,每个类注解都有自己的使用场景。
@Bean : 方法注解,要搭配类注解一起使用,通过 @Bean 可以对一个类创建多个对象,Bean 名称默认为 方法名。