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

相关推荐
小梁不秃捏2 小时前
深入浅出Java虚拟机(JVM)核心原理
java·开发语言·jvm
yngsqq5 小时前
c# —— StringBuilder 类
java·开发语言
Asthenia04126 小时前
浏览器缓存机制深度解析:电商场景下的性能优化实践
后端
星星点点洲6 小时前
【操作幂等和数据一致性】保障业务在MySQL和COS对象存储的一致
java·mysql
xiaolingting6 小时前
JVM层面的JAVA类和实例(Klass-OOP)
java·jvm·oop·klass·instanceklass·class对象
风口上的猪20157 小时前
thingboard告警信息格式美化
java·服务器·前端
databook7 小时前
『Python底层原理』--Python对象系统探秘
后端·python
追光少年33227 小时前
迭代器模式
java·迭代器模式
超爱吃士力架8 小时前
MySQL 中的回表是什么?
java·后端·面试
扣丁梦想家8 小时前
设计模式教程:装饰器模式(Decorator Pattern)
java·前端·装饰器模式