【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可以指定扫描路径

相关推荐
徐*红12 分钟前
java 线程池
java·开发语言
尚学教辅学习资料12 分钟前
基于SSM的养老院管理系统+LW示例参考
java·开发语言·java毕设·养老院
2401_8576363912 分钟前
计算机课程管理平台:Spring Boot与工程认证的结合
java·spring boot·后端
1 9 J14 分钟前
Java 上机实践4(类与对象)
java·开发语言·算法
Code apprenticeship15 分钟前
Java面试题(2)
java·开发语言
憨子周1 小时前
2M的带宽怎么怎么设置tcp滑动窗口以及连接池
java·网络·网络协议·tcp/ip
霖雨3 小时前
使用Visual Studio Code 快速新建Net项目
java·ide·windows·vscode·编辑器
SRY122404193 小时前
javaSE面试题
java·开发语言·面试
Fiercezm3 小时前
JUC学习
java
无尽的大道3 小时前
Java 泛型详解:参数化类型的强大之处
java·开发语言