【JavaEE】SpringIoC&DI

SpringIoC&DI

文章目录

什么是spring

Spring是⼀个开源框架, 他让我们的开发更加简单. 他⽀持⼴泛的应⽤场

景, 有着活跃⽽庞⼤的社区, 这也是Spring能够⻓久不衰的原因.

但是这个概念相对来说, 还是⽐较抽象.

我们⽤⼀句更具体的话来概括Spring, 那就是: Spring 是包含了众多⼯具⽅法的 IoC 容器

那问题来了,什么是容器?什么是 IoC 容器?接下来我们⼀起来看

Spring的两大核心思想:Ioc AOP

什么是容器

容器是⽤来容纳某种物品的(基本)装置。⸺来⾃:百度百科

⽣活中的⽔杯, 垃圾桶, 冰箱等等这些都是容器.

我们想想,之前课程我们接触的容器有哪些?

  • List/Map -> 数据存储容器

  • Tomcat -> Web 容器

  • 学校 -> 学生容器

什么是IoC

IoC:控制反转

也就是说 Spring 是⼀个"控制反转"的容器

什么是控制反转呢? 也就是控制权反转. 什么的控制权发⽣了反转? 获得依赖对象的过程被反转了

也就是说, 当需要某个对象时, 传统开发模式中需要⾃⼰通过 new 创建对象, 现在不需要再进⾏创

建, 把创建对象的任务交给容器, 程序中只需要依赖注⼊ (Dependency Injection,DI)就可以了.

这个容器称为:IoC容器. Spring是⼀个IoC容器, 所以有时Spring 也称为Spring 容器

IoC思想

举一个方便理解的例子:造车

传统方式

先设计轮子tire,设计底盘bottom,设计车身framework,设计汽车car

java 复制代码
public class Main {
    public static void main(String[] args) {
        Car car = new Car();
        car.run();
    }
}
java 复制代码
public class Car {
    private Framework framework;
    public Car(){
        framework = new Framework();
        System.out.println("cat init........");
    }
    public void run() {
        System.out.println("car run.........");
    }

}
java 复制代码
public class Framework {
    private Bottom bottom;

    public Framework() {
        bottom = new Bottom();
        System.out.println("framework init.....");
    }
}
java 复制代码
public class Bottom {
    private Tire tire;

    public Bottom() {
        tire = new Tire();
        System.out.println("bottom init.....");
    }
}
java 复制代码
public class Tire {
    private int size = 17;

    public Tire() {
        System.out.println("tire size = 17");
    }
}

运行:

java 复制代码
tire size = 17
bottom init.....
framework init.....
cat init........
car run.........

但是这样写耦合太高。

一处改 处处改

IoC写法

java 复制代码
spring的工作
public class Main {
    public static void main(String[] args) {
        Tire tire = new Tire(17);
        Bottom bottom = new Bottom(tire);
        Framework framework = new Framework(bottom);
        Car car = new Car(framework);
        car.run();
    }
}
java 复制代码
public class Car {
    private Framework framework;

    public Car(Framework framework) {
        this.framework = framework;
        System.out.println("car init.......");
    }

    public void run() {
        System.out.println("car run........");
    }
}
java 复制代码
public class Framework {
    private Bottom bottom;

    public Framework(Bottom bottom) {
        this.bottom = bottom;
        System.out.println("framework init...");
    }
}
java 复制代码
public class Bottom {
    private Tire tire;

    public Bottom(Tire tire) {
        this.tire = tire;
        System.out.println("bottom init...");
    }
}
java 复制代码
public class Tire {
    private int size;

    public Tire(int size) {
        System.out.println("tire size = " + size);
    }
}

在传统的代码中对象创建顺序是:Car -> Framework -> Bottom -> Tire

改进之后解耦的代码的对象创建顺序是:Tire -> Bottom -> Framework -> Car

我们发现了⼀个规律,通⽤程序的实现代码,类的创建顺序是反的,传统代码是 Car 控制并创建了

FrameworkFramework 创建并创建了 Bottom,依次往下,⽽改进之后的控制权发⽣的反转,不再

是使⽤⽅对象创建并控制依赖对象了,⽽是把依赖对象注⼊将当前对象中,依赖对象的控制权不再由

当前类控制了.

这样的话, 即使依赖类发⽣任何改变,当前类都是不受影响的,这就是典型的控制反转,也就是 IoC

实现思想。

这部分代码, 就是IoC容器做的⼯作.

从上⾯也可以看出来, IoC容器具备以下优点:

资源不由使⽤资源的双⽅管理,⽽由不使⽤资源的第三⽅管理,这可以带来很多好处。第⼀,资源集中管理,实现资源的可配置和易管理。第⼆,降低了使⽤资源双⽅的依赖程度,也就是我们说的耦合度。

  1. 资源集中管理: IoC容器会帮我们管理⼀些资源(对象等), 我们需要使⽤时, 只需要从IoC容器中去取就可以了

  2. 我们在创建实例的时候不需要了解其中的细节, 降低了使⽤资源双⽅的依赖程度, 也就是耦合度.

Spring 就是⼀种IoC容器, 帮助我们来做了这些资源管理


DI

DI: Dependency Injection(依赖注⼊)

容器在运⾏期间, 动态的为应⽤程序提供运⾏时所依赖的资源,称之为依赖注⼊。

程序运⾏时需要某个资源,此时容器就为其提供这个资源.

从这点来看, 依赖注⼊(DI)和控制反转(IoC)是从不同的⻆度的描述的同⼀件事情,就是指通过引⼊ IoC 容器,利⽤依赖关系注⼊的⽅式,实现对象之间的解耦。

上述代码中, 是通过构造函数的⽅式, 把依赖对象注⼊到需要使⽤的对象中的

IoC 是⼀种思想,也是"⽬标", ⽽思想只是⼀种指导原则,最终还是要有可⾏的落地⽅案,⽽ DI 就属于

具体的实现。所以也可以说, DIIoC的⼀种实现

⽐如说我今天⼼情⽐较好,吃⼀顿好的犒劳犒劳⾃⼰,那么"吃⼀顿好的"是思想和⽬标(是IoC),但最后我是吃海底捞还是杨国福?这就是具体的实现,就是 DI

Spring IoC/DI

对象的管理:

  1. 存对象 @Component
  2. 取对象@AutoWired

IoC详细用法

五大注解:

  • @Controller
  • @Service
  • @Component
  • @Repository
  • Configuration

方法注解:

  • @Bean

获取Bean的方式

  1. 通过类型获取 Bean:

    使用 ApplicationContextgetBean() 方法根据类型获取 Bean。

    java 复制代码
    // 假设有一个 UserService 接口和其实现类 UserServiceImpl
    UserService userService = applicationContext.getBean(UserService.class);
  2. 通过名称获取 Bean:

    使用 ApplicationContextgetBean() 方法根据 Bean 的名称获取。

    java 复制代码
    // 假设有一个名为 "userService" 的 Bean
    UserService userService = (UserService) applicationContext.getBean("userService");
  3. 通过类型和名称获取 Bean:

    使用 ApplicationContextgetBean() 方法根据类型和名称获取 Bean。

    java 复制代码
    // 假设有一个名为 "userService" 的 Bean
    UserService userService = applicationContext.getBean("userService", UserService.class);
  4. 通过 @Autowired 注解自动注入:

    在需要使用的地方使用 @Autowired 注解进行自动注入。

    java 复制代码
    @Autowired
    private UserService userService;
  5. 通过构造函数参数注入:

    在构造函数中接收 Bean 对象作为参数。

    java 复制代码
    public class UserController {
        private final UserService userService;
        
        public UserController(UserService userService) {
            this.userService = userService;
        }
    }
  6. 通过@Resource 注解注入:

    使用 @Resource 注解进行注入,可以指定 Bean 的名称。

    java 复制代码
    @Resource(name = "userService")
    private UserService userService;

@Controller(控制器存储)

java 复制代码
@Controller
public class UserController {
    public void sayHi(){
        System.out.println("hello, UserController...");
    }
}
java 复制代码
// 根据类型去拿
@SpringBootApplication
public class IocApplication {
    public static void main(String[] args) {
        // ApplicationContext : Spring 上下文、运行环境
        ApplicationContext context = SpringApplication.run(IocApplication.class, args);
        UserController bean = context.getBean(UserController.class);
        bean.sayHi();
    }
}

ApplicationContext 翻译过来就是: Spring 上下⽂

因为对象都交给 Spring 管理了,所以获取对象要从 Spring 中获取,那么就得先得到 Spring 的上下⽂

关于上下⽂的概念

上学时, 阅读理解经常会这样问: 根据上下⽂, 说⼀下你对XX的理解

在计算机领域, 上下⽂这个概念, 咱们最早是在学习线程时了解到过, ⽐如我们应⽤进⾏线程切换的时

候,切换前都会把线程的状态信息暂时储存起来,这⾥的上下⽂就包括了当前线程的信息,等下次该

线程⼜得到CPU时间的时候, 从上下⽂中拿到线程上次运⾏的信息

这个上下⽂, 就是指当前的运⾏环境, 也可以看作是⼀个容器, 容器⾥存了很多内容, 这些内容是当前

运⾏的环境

@Service(服务存储)

java 复制代码
@Service
public class UserService {
    public void syaHi(){
        System.out.println("hello UserService...");
    }
}
java 复制代码
// 根据名称去拿
@SpringBootApplication
public class IocApplication {
    public static void main(String[] args) {
        UserService userService = (UserService) context.getBean("userService");
        userService.syaHi();
    }
}

@Component(组件存储)

java 复制代码
// 根据名称("userComponent")和类型(UserComponent.class)来获取的
@SpringBootApplication
public class IocApplication {
    public static void main(String[] args) {
        UserComponent userComponent = context.getBean("userComponent", UserComponent.class);
        userComponent.sayHi();
    }
}
java 复制代码
@Component
public class UserComponent {
    public void sayHi(){
        System.out.println("hello,UserComponent...");
    }
}

@Repository(仓库存储)

java 复制代码
@Repository
public class UserRepository {
    public void sayHi(){
        System.out.println("hello, userRepository");
    }
}
java 复制代码
@SpringBootApplication
public class IocApplication {
    public static void main(String[] args) {
        UserRepository userRepository = (UserRepository) context.getBean("userRepository");
		userRepository.sayHi();
    }
}

@Configuration(配置存储)

java 复制代码
@Configuration
public class UserConfig {
    public void sayHi(){
        System.out.println("hello,userConfig");
    }
}
java 复制代码
@SpringBootApplication
public class IocApplication {
    public static void main(String[] args) {
        UserConfig userConfig = context.getBean(UserConfig.class);
		userConfig.sayHi();

    }
}

为什么要有这么多注解?

这和每个省/市都有⾃⼰的⻋牌号是⼀样的.

⻋牌号都是唯⼀的, 标识⼀个⻋辆的. 但是为什么还需要设置不同的⻋牌开头呢.

⽐如陕西的⻋牌号就是:陕X:XXXXXX,北京的⻋牌号:京X:XXXXXX,甚⾄⼀个省不同的县区也

是不同的,⽐如西安就是,陕A:XXXXX,咸阳:陕B:XXXXXX,宝鸡,陕C:XXXXXX,⼀样.

这样做的好处除了可以节约号码之外,更重要的作⽤是可以直观的标识⼀辆⻋的归属地.

  • @Controller:控制层, 接收请求, 对请求进⾏处理, 并进⾏响应.

  • @Servie:业务逻辑层, 处理具体的业务逻辑.

  • @Repository:数据访问层,也称为持久层. 负责数据访问操作

  • @Configuration:配置层. 处理项⽬中的⼀些配置信息

五大注解,从概念上还被赋予了别的含义

  1. @Component:用于将一个类标识为组件,并将其纳入 Spring 的管理中。它是通用的注解,可以用于任何层次的组件,如控制器、服务、存储库等。
  2. @Repository:用于标识一个数据访问层(DAO)的类。它表示一个持久化操作相关的类,负责与数据库或其他数据存储进行交互。
  3. @Service:用于标识一个服务层(Service)的类。它表示一个业务逻辑相关的类,通常用于封装和处理复杂的业务逻辑。
  4. @Controller:用于标识一个控制器层(Controller)的类。它表示一个处理 HTTP 请求和响应的类,通常用于处理用户的请求并返回相应的视图或数据。除了具备让Spring管理的功能之外,接口的入口,必须为@Controller
  5. @Configuration:用于标识一个配置类,它是 Spring 中定义 bean 的一种方式。通过在类上添加 @Configuration 注解,可以将该类声明为一个配置类,其中可以定义 bean 的创建和配置。

这五个注解之间有一定的关系和应用场景:

  1. @Component 是一个通用的注解,用于将一个类标识为组件。它可以用于任何层次的组件,包括控制器、服务、存储库等。其他四个注解 @Repository@Service@Controller 都是特定类型的组件,它们实际上是通过 @Component 注解派生而来的,具有更具体的语义。
  2. @Repository 注解通常用于标识数据访问层(DAO)的类,表示它是与数据库或其他数据存储进行交互的组件。@Repository 注解还具有将数据库相关的异常转换为 Spring 的数据访问异常的功能。
  3. @Service 注解通常用于标识服务层(Service)的类,表示它是应用程序中的一个服务组件。服务层通常封装和处理复杂的业务逻辑,并与其他组件进行交互。
  4. @Controller 注解通常用于标识控制器层(Controller)的类,表示它是处理 HTTP 请求和响应的组件。控制器层接收用户的请求,调用适当的服务层组件进行处理,并返回相应的视图或数据。
  5. @Configuration 注解用于标识配置类,其中定义了创建和配置 bean 的方法。配置类通常包含 @Bean 注解,用于声明和定义 Spring bean。其他组件类可以通过 @Autowired 注解注入这些配置类中定义的 bean。

这些注解之间的关系是:@Repository@Service@Controller 注解都是通过 @Component 注解派生而来的,它们具有更具体的语义,用于在组件层次上提供更明确的分类和区分。而 @Configuration 注解用于标识配置类,提供了声明和定义 bean 的方式。通过使用这些注解,我们可以更好地组织和管理应用程序中的组件,并实现依赖注入和其他特定的功能。

其他四个注解是@Component的衍生类

程序的应用分层,调用流程如下:

方法注解@Bean

类注解是添加到某个类上的, 但是存在两个问题:

  1. 使⽤外部包⾥的类, 没办法添加类注解

  2. ⼀个类, 需要多个对象, ⽐如多个数据源

这种场景, 我们就需要使⽤⽅法注解 @Bean

举个例子:

单对象
java 复制代码
public class BeanConfig {

    @Bean
    public UserInfo userInfo(){
        UserInfo userInfo = new UserInfo();
        userInfo.setId(5);
        userInfo.setName("zhangsan");
        userInfo.setAge(18);
        return userInfo;
    }
}
java 复制代码
@SpringBootApplication
public class IocApplication {
    public static void main(String[] args) {
        UserInfo userInfo = context.getBean(UserInfo.class);
        System.out.println(userInfo);
    }
}

报错。

原因就是在 Spring 框架的设计中,⽅法注解 @Bean 要配合类注解才能将对象正常的存储到 Spring 容器中

java 复制代码
@Configuration
public class BeanConfig {

    @Bean
    public UserInfo userInfo(){
        UserInfo userInfo = new UserInfo();
        userInfo.setId(5);
        userInfo.setName("zhangsan");
        userInfo.setAge(18);
        return userInfo;
    }
}

这样既可。

多对象

@Bean注解定义的对象,默认名称为方法名

java 复制代码
@Configuration
public class BeanConfig {

    @Bean
    public UserInfo userInfo(){
        UserInfo userInfo = new UserInfo();
        userInfo.setId(5);
        userInfo.setName("zhangsan");
        userInfo.setAge(18);
        return userInfo;
    }

    @Bean
    public UserInfo userInfo1(){
        UserInfo userInfo1 = new UserInfo();
        userInfo1.setId(6);
        userInfo1.setName("6666");
        userInfo1.setAge(66);
        return userInfo1;
    }
}
java 复制代码
@SpringBootApplication
public class IocApplication {
    public static void main(String[] args) {
        UserInfo userInfo = context.getBean("userInfo",UserInfo.class);
        System.out.println(userInfo);

        UserInfo userInfo1 = context.getBean("userInfo1",UserInfo.class);
        System.out.println(userInfo1);
    }
}
重命名
java 复制代码
@Configuration
public class BeanConfig {

    @Bean("u1")
    public UserInfo userInfo(){
        UserInfo userInfo = new UserInfo();
        userInfo.setId(5);
        userInfo.setName("zhangsan");
        userInfo.setAge(18);
        return userInfo;
    }
}
java 复制代码
@SpringBootApplication
public class IocApplication {
    public static void main(String[] args) {
        UserInfo userInfo = context.getBean("u1",UserInfo.class);
        System.out.println(userInfo);
    }
}

扫描路径

Q: 使⽤前⾯学习的四个注解声明的bean,⼀定会⽣效吗?

A: 不⼀定(原因:bean想要⽣效,还需要被Spring扫描)

DI详解

依赖注入有三种方式:

  1. 属性注入
  2. 构造方法注入
  3. Setter注入

属性注入

@Autowired

java 复制代码
@Controller
public class UserController {
    // 把 UserService 注入进来
    @Autowired
    private UserService userService;
    public void sayHi(){
        System.out.println("hello, UserController...");
        userService.syaHi();
    }
}

构造方法注入

java 复制代码
@Controller
public class UserController {
    private UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }
    public void sayHi(){
        System.out.println("hello, UserController...");
        userService.syaHi();
    }
}
java 复制代码
@Controller
public class UserController {
    private UserService userService;
    private UserConfig userConfig;

    public UserController(UserService userService, UserConfig userConfig) {
        this.userService = userService;
        this.userConfig = userConfig;
    }

    public void sayHi(){
        System.out.println("hello, UserController...");
        userService.syaHi();
        userConfig.sayHi();
    }
}

当只有一个构造函数的时候,Spring 会知道使用哪个

当有多个构造函数的时候,Spring 会使用默认无参的构造函数,如果没有这个函数,Spring 会报错

java 复制代码
Caused by: java.lang.NoSuchMethodException: org.haobin.ioc.demo.Controller.UserController.<init>()
	at java.lang.Class.getConstructor0(Class.java:3082) ~[na:1.8.0_361]
	at java.lang.Class.getDeclaredConstructor(Class.java:2178) ~[na:1.8.0_361]
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:78) ~[spring-beans-5.3.23.jar:5.3.23]
	... 18 common frames omitted

同样的 我们可以使用@Autowired指定构造函数

java 复制代码
@Controller
public class UserController {
    private UserService userService;
    private UserConfig userConfig;

    public UserController(){

    }

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @Autowired
    public UserController(UserService userService, UserConfig userConfig) {
        this.userService = userService;
        this.userConfig = userConfig;
    }

    public void sayHi(){
        System.out.println("hello, UserController...");
        userService.syaHi();
        userConfig.sayHi();
    }
}

Setter注入

java 复制代码
@Controller
public class UserController {
    private UserService userService;

    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    public void sayHi(){
        System.out.println("hello, UserController...");
        userService.syaHi();
    }
}

如果需要注入多个属性,就需要注入多个@Autowired

java 复制代码
@Controller
public class UserController {
	@Autowired
    private UserService userService;
    @Autowired
    private UserComponent userConponent'
    public void sayHi(){
        System.out.println("hello, UserController...");
        userService.syaHi();
        userComponent.sayHi();
    }
}

三种注入优缺点

  • 属性注⼊

    • 优点: 简洁,使⽤⽅便;
    • 缺点:
      • 只能⽤于 IoC 容器,如果是⾮ IoC 容器不可⽤,并且只有在使⽤的时候才会出现 NPE(空指针异常)
      • 不能注⼊⼀个Final修饰的属性
  • 构造函数注⼊(Spring 4.X推荐)

    • 优点:

      • 可以注⼊final修饰的属性

        • final修饰的属性有一个要求,需要满足一下条件

          1. 声明的时候需要完成初始化
          2. 在构造函数中进行赋值
      • 注⼊的对象不会被修改

      • 依赖对象在使⽤前⼀定会被完全初始化,因为依赖是在类的构造⽅法中执⾏的,⽽构造⽅法是在类加载阶段就会执⾏的⽅法.

      • 通⽤性好, 构造⽅法是JDK⽀持的, 所以更换任何框架,他都是适⽤的

    • 缺点:

      • 注⼊多个对象时, 代码会⽐较繁琐
  • Setter注⼊(Spring 3.X推荐)

    • 优点: ⽅便在类实例之后, 重新对该对象进⾏配置或者注⼊
    • 缺点:
      • 不能注⼊⼀个Final修饰的属性
      • 注⼊对象可能会被改变, 因为setter⽅法可能会被多次调⽤, 就有被修改的⻛险.

@Autowired存在的问题

java 复制代码
@Controller
public class UserController2 {
    @Autowired
    private UserInfo userInfo;
    public void sayHi(){
        System.out.println("hello, UserController...");
        System.out.println(userInfo);
    }
}
java 复制代码
Description:

Field userInfo in org.xxx.ioc.demo.Controller.UserController2 required a single bean, but 2 were found:
	- u1: defined by method 'userInfo' in class path resource [org/haobin/ioc/demo/config/BeanConfig.class]
	- userInfo1: defined by method 'userInfo1' in class path resource [org/haobin/ioc/demo/config/BeanConfig.class]

当同样的类型存在多个对象的时候,可能会报错

java 复制代码
@Autowired
    private UserInfo userInfo;

先根据名称来获取,如果获取到了,正确响应...

如果没有获取到,就根据类型匹配,此时,如果匹配到多个,则报错。

通常做法:

不使用变量名称来指定获取某个bean,而是通过其他手段来指定bean的名称。

通常我们会认为变量名的修改不影响我们的业务逻辑处理


如何解决上述问题呢?Spring提供了以下⼏种解决⽅案:

  • @Primary
  • @Qualifier
  • @Resource
1. @Primary

@Primary 注解用于标识一个 Bean 为首选项,当存在多个候选 Bean 时,被标记为 @Primary 的 Bean 会被优先选择进行注入。

示例:

java 复制代码
@Component
@Primary
public class PrimaryDataSource implements DataSource {
    // 实现 DataSource 接口的相关逻辑
}
java 复制代码
@Component
public class SecondaryDataSource implements DataSource {
    // 实现 DataSource 接口的相关逻辑
}

在这个示例中,当需要注入 DataSource 类型的 Bean 时,如果没有明确指定使用哪个 Bean,Spring 将会选择使用 PrimaryDataSource

2. @Qualifier

@Qualifier 注解用于指定注入的 Bean 名称或者 Bean 的限定符(Qualifier),用于消除歧义,明确指定要注入的是哪个 Bean。

示例:

java 复制代码
@Component
@Qualifier("primary")
public class PrimaryDataSource implements DataSource {
    // 实现 DataSource 接口的相关逻辑
}
java 复制代码
@Component
@Qualifier("secondary")
public class SecondaryDataSource implements DataSource {
    // 实现 DataSource 接口的相关逻辑
}
java 复制代码
@Component
public class DataSourceService {
    @Autowired
    @Qualifier("primary") // 指定注入 primary Bean
    private DataSource dataSource;
}

在这个示例中,DataSourceService 类中的 dataSource 字段会被注入 PrimaryDataSource Bean。

3. @Resource

@Resource 注解是 JavaEE 提供的一种依赖注入方式,Spring 也支持该注解。它默认按照名称(即 Bean 的名称)进行自动装配,当无法按照名称匹配时,再按照类型匹配。

示例:

java 复制代码
@Component
public class PrimaryDataSource implements DataSource {
    // 实现 DataSource 接口的相关逻辑
}
java 复制代码
@Component
public class SecondaryDataSource implements DataSource {
    // 实现 DataSource 接口的相关逻辑
}
java 复制代码
@Component
public class DataSourceService {
    @Resource // 默认按照名称匹配,会注入 PrimaryDataSource Bean
    private DataSource dataSource;
}

在这个示例中,DataSourceService 类中的 dataSource 字段会被注入 PrimaryDataSource Bean,因为 PrimaryDataSource 是唯一匹配的 Bean。

总结

告诉Spring管理bean、bean的存储

  1. 类注解:五大注解

    • @Controller
    • @Service
    • @Repository
    • @Configuration
    • @Component
  2. 方法注解:@Bean

    @Bean必须和方法注解一起使用

Bean的名称

使用场景:

  1. 五大注解:自己开发的程序
  2. @Bean
    1. 存储第三方的对象(代码不在自己的项目中)
    2. 一个类型需要创建多个对象的时候
  1. 五大注解

    类名首字母小写,如果前两位字母均为大写,则为原类名

    也可以指定Bean的名称 指定方法:@Controller("beanName")

  2. @Bean

    默认名称:方法名

    也可以指定名称@Bean("beanName")

获取Bean的方式

获取bean的功能是BeanFeactory提供的

BeanFeactory和ApplicationContext区别

"BeanFactory和ApplicationContext是Spring框架中两个重要的接口,它们在管理和访问bean对象方面有一些区别。

首先,BeanFactory是Spring框架的基础接口,提供了最简单的bean容器功能。它采用延迟加载策略,也就是说,只有在第一次访问bean时才会实例化对象。这样可以节省资源,但也可能导致在第一次访问时稍微延迟。

与之相比,ApplicationContext是BeanFactory的子接口,提供了更多的功能和特性。它在容器启动时会预加载和实例化所有的单例bean,这意味着它能够更早地发现配置错误,并且在启动时进行了校验和初始化。ApplicationContext还提供了对AOP的支持以及国际化资源处理的功能。

另外,BeanFactory对配置文件的处理是按需加载的,只有在需要的时候才会读取和解析配置文件,而ApplicationContext在容器启动时就会读取和解析配置文件。这也是为什么ApplicationContext能够提供更早的错误检测和更快的启动时间的原因之一。

所以,总的来说,BeanFactory适用于资源有限或对延迟加载有特定需求的情况,而ApplicationContext则提供了更多的特性和功能,适用于需要更高级功能的场景。在实际开发中,我们通常会使用ApplicationContext来获得更强大的容器管理和功能支持。"

八股文回答
  • 继承关系和功能⽅⾯来说:Spring 容器有两个顶级的接⼝:BeanFactory 和ApplicationContext。其中 BeanFactory 提供了基础的访问容器的能⼒,⽽ApplicationContext 属于 BeanFactory 的⼦类,它除了继承了 BeanFactory 的所有功能之外,它还拥有独特的特性,还添加了对国际化⽀持、资源访问⽀持、以及事件传播等⽅⾯的⽀持.
  • 从性能⽅⾯来说:ApplicationContext 是⼀次性加载并初始化所有的 Bean 对象,⽽BeanFactory 是需要那个才去加载那个,因此更加轻量. (空间换时间)
java 复制代码
		ApplicationContext context = SpringApplication.run(IocApplication.class, args);
        UserController bean = context.getBean(UserController.class);
        bean.sayHi();

        UserService userService = (UserService) context.getBean("userService");
        userService.syaHi();

        UserComponent userComponent = context.getBean("userComponent", UserComponent.class);
        userComponent.sayHi();

扫描路径

Spring 默认扫描路径是 启动类所在的路径。

@ComponentScan可以指定扫描路径

相关推荐
浮游本尊1 小时前
Java学习第22天 - 云原生与容器化
java
渣哥3 小时前
原来 Java 里线程安全集合有这么多种
java
间彧3 小时前
Spring Boot集成Spring Security完整指南
java
间彧3 小时前
Spring Secutiy基本原理及工作流程
java
Java水解4 小时前
JAVA经典面试题附答案(持续更新版)
java·后端·面试
洛小豆6 小时前
在Java中,Integer.parseInt和Integer.valueOf有什么区别
java·后端·面试
前端小张同学7 小时前
服务器上如何搭建jenkins 服务CI/CD😎😎
java·后端
ytadpole7 小时前
Spring Cloud Gateway:一次不规范 URL 引发的路由转发404问题排查
java·后端
华仔啊7 小时前
基于 RuoYi-Vue 轻松实现单用户登录功能,亲测有效
java·vue.js·后端