Spring IoC

介绍

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 名称默认为 方法名。

相关推荐
小蒜学长1 小时前
springboot多功能智能手机阅读APP设计与实现(代码+数据库+LW)
java·spring boot·后端·智能手机
追逐时光者2 小时前
精选 4 款开源免费、美观实用的 MAUI UI 组件库,助力轻松构建美观且功能丰富的应用程序!
后端·.net
你的人类朋友3 小时前
【Docker】说说卷挂载与绑定挂载
后端·docker·容器
间彧3 小时前
在高并发场景下,如何平衡QPS和TPS的监控资源消耗?
后端
间彧3 小时前
QPS和TPS的区别,在实际项目中,如何准确测量和监控QPS和TPS?
后端
zizisuo3 小时前
解决在使用Lombok时maven install 找不到符号的问题
java·数据库·maven
间彧4 小时前
消息队列(RocketMQ、RabbitMQ、Kafka、ActiveMQ)对比与选型指南
后端·消息队列
笨蛋少年派4 小时前
JAVA基础语法
java·开发语言
渡我白衣4 小时前
深入剖析:boost::intrusive_ptr 与 std::shared_ptr 的性能边界和实现哲学
开发语言·c++·spring
Haooog4 小时前
654.最大二叉树(二叉树算法)
java·数据结构·算法·leetcode·二叉树