Spring IoC&DI

发布时间:2023年12月17日

一、Spring、Spring boot、Spring MVC之间的区别

1. Spring 是什么

Spring 是包含了众多工具方法的 IoC 容器

(1)容器

容器是用来容纳某种物品的基本装置,比如List/Map就是数据存储容器,Tomcat就是Web容器

(2)loC

1. 概述

loC:Inversion if Control(控制反转),也就是说Spring 是一个“控制反转”的容器,是 Spring 的核心思想

eg. 在类上添加@RestController 和 @Controller 注解,就是把这个对象交给 Spring管理,Spring 框架启动时就会加载该类,即实现了把对象交给Spring管理

在这里插入图片描述
这部分代码就是loC容器做的事

IoC容器的优点
资源不由使用资源的双方管理,而由不使用资源的第三方管理,这可以带来很多好处。第一,资源集中管理,实现资源的可配置和易管理。第二,降低了使用资源双方的依赖程度,也就是我们说的耦合度。

  1. 资源集中管理: IoC容器会帮我们管理一些资源(对象等),我们需要使用时, 只需要从IoC容器中去取就可以了
  2. 我们在创建实例的时候不需要了解其中的细节, 降低了使用资源双方的依赖程度, 也就是耦合度

代码方面理解:把材料外包出去,不自己制造

2. 使用

【1】Bean的存储

Spring 是一个loC容器,而作为容器,那【存】和【取】的功能就是最基本的。
Spring 容器 管理的主要是对象,这些对象, 我们称之为"Bean"。我们把这些对象交由Spring管理, 由Spring来负责对象的创建和销毁. 我们程序只需要告诉Spring,哪些需要存,以及如何从Spring中取出对象

对于 Spring 框架而言,共有两类注解可以将某个对象交给loC容器管理

  1. 类注解:@Controller、@Service、@Repository、@Component、@Configuration
  2. 方法注解:@Bean

Bean 的 名称:Spring bean 是Spring框架在运行时管理的对象,Spring 会给管理的对象起一个名字,而根据这个名称(BeanId)就可以获取到对应的对象

Bean的命名约定:程序开发人员不需要为bean指定名称(BeanId), 如果没有显式的提供名称(BeanId),Spring容器将为该bean生成唯?的名称

命名约定使用Java标准约定作为实例字段名. 也就是说,bean名称以小写字母开头,然后使用驼峰式大小写

  • 类名: UserController, Bean的名称为:userController
  • 类名: AccountManager, Bean的名称为:accountManager

也有?些特殊情况, 当有多个字符并且第?个和第?个字符都是大写时, 将保留原始的大小写

  • 类名: UController, Bean的名称为:UController
  • 类名: AManager, Bean的名称为:AManager

@Controller(控制器存储)

@Controller
public class UserController {
    public void sayHi(){
        System.out.println("这是UserController");
    }
}

@SpringBootApplication
public class demo {

    public static void main(String[] args) {
        //获取Spring上下文对象
        ApplicationContext context = SpringApplication.run(demo.class, args);
        //从上下文中获取对象,下面是三种不同的获取方式
        //根据类的类型获取
        UserController userController1 = context.getBean(UserController.class);
        //根据名称获取,需要强转
        UserController userController2 = (UserController) context.getBean("userController");
        //根据名称和类型获取,不需要强转
        UserController userController3 = context.getBean("userController", UserController.class);

        System.out.println(userController1);
        System.out.println(userController2);
        System.out.println(userController3);

    }

}

在这里插入图片描述

获取bean对象, 是父类BeanFactory提供的功能

  • ApplicationContext VS BeanFactory(常见面试题)
    • 继承关系和功能方面来说:Spring 容器有两个顶级的接口:BeanFactory 和 ApplicationContext。其中 BeanFactory 提供了基础的访问容器的能力,而ApplicationContext 属于 BeanFactory 的子类,它除了继承了 BeanFactory 的所有功能之外,它还拥有独特的特性,还添加了对国际化支持、资源访问支持、以及事件传播等方面的支持。
    • 从性能方面来说:ApplicationContext 是?次性加载并初始化所有的 Bean 对象,而
      BeanFactory 是需要那个才去加载那个,因此更加轻量。(空间换时间)

@Service(服务存储)

@Service
public class UserService {
    public void sayHi(){
        System.out.println("这是UserService");
    }
}


@SpringBootApplication
public class demo {

    public static void main(String[] args) {
        //获取Spring上下文对象
        ApplicationContext context = SpringApplication.run(demo.class, args);
        UserService service = context.getBean(UserService.class);
        service.sayHi();

    }

}

@Repository(仓库存储)、@Configuration(配置存储)、@Component(组件存储)的操作与上述都类似,只需要更改注解

@SpringBootApplication
public class SpringIocDemoApplication {
	public static void main(String[] args) {
		//获取Spring上下?对象
		ApplicationContext context = SpringApplication.run(SpringIocDemoApplicatio
		//从Spring上下?中获取对象
		UserConfiguration userConfiguration = context.getBean(UserConfiguration.cl
		//使?对象
		userConfiguration.sayHi();
	}
}

为什么需要这么多的注解???

让程序员看到类注解之后,就能直接了解当前类的用途

  • @Controller:控制层, 接收请求, 对请求进行处理, 并进行响应
  • @Servie:业务逻辑层, 处理具体的业务逻辑
  • @Repository:数据访问层,也称为持久层. 负责数据访问操作
  • @Configuration:配置层. 处理项目中的?些配置信息

应用分层的调用逻辑
在这里插入图片描述
方法注解 @Bean

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

  1. 使用外部包里的类, 没办法添加类注解
  2. ?个类, 需要多个对象,比如多个数据源
    这种场景, 我们就需要使用方法注解 @Bean,@Bean 注解的bean, bean的名称就是它的方法名

使用时的注意点

  • 在 Spring 框架的设计中,方法注解 @Bean 要配合类注解才能将对象正常的存储到 Spring 容器中
  • bean想要生效,还需要被Spring扫描
    • 通过 @ComponentScan 来配置扫描路径
    • 有时候 @ComponentScan 注解虽然没有显式配置,但是实际上已经包含在了启动类声明注解@SpringBootApplication 中了。默认扫描的范围是SpringBoot启动类所在包及其子包
@Configuration
public class BeanConfig {
    @Bean
    public UserInfo userInfo(){
        UserInfo user = new UserInfo();
        user.setName("zhangsan");
        user.setAge(18);
        return user;
    }
}

定义多个对象

@Configuration
public class BeanConfig {
    @Bean
    public UserInfo userInfo1(){
        UserInfo user = new UserInfo();
        user.setName("zhangsan");
        user.setAge(18);
        return user;
    }

    @Bean
    public UserInfo userInfo2(){
        UserInfo user = new UserInfo();
        user.setName("lisi");
        user.setAge(22);
        return user;
    }
}



@SpringBootApplication
public class St20231205Application {

    public static void main(String[] args) {
    	//根据名称获取Bean对象
        UserInfo userInfo1 = (UserInfo) context.getBean("userInfo1");
        UserInfo userInfo2 = (UserInfo) context.getBean("userInfo2");

        System.out.println(userInfo1);
        System.out.println(userInfo2);
    }

}

重命名Bean

@Bean(name = {"u1","user1"})
public User user1(){
    User user = new User();
    user.setName("zhangsan");
    user.setAge(18);
    return user;
}


@SpringBootApplication
public class SpringIocDemoApplication {
    public static void main(String[] args) {
        //获取Spring上下?对象
        ApplicationContext context = SpringApplication.run(SpringIocDemoApplicatio
                //从Spring上下?中获取对象
                User u1 = (User) context.getBean("u1");
        //使?对象
        System.out.println(u1);
    }
}

name={} 可以省略,如下代码所示,如:
@Bean({“u1”,“user1”})
public User user1(){
????????? User user = new User();
????????? user.setName(“zhangsan”);
????????? user.setAge(18);
????????? return user;
}

只有?个名称时, {}也可以省略, 如:
@Bean(“u1”)
public User user1(){
????????? User user = new User();
????????? user.setName(“zhangsan”);
????????? user.setAge(18);
????????? return user;
}

(3)DI

1. 概述

DI: Dependency Injection(依赖注入),即容器在运行期间, 动态的为应用程序提供运行时所依赖的资源,如果程序运行时需要某个资源(对象),此时容器就为其提供这个资源。

所以,DI 和 loC是从不同的角度去描述同一件事情,即都是通过引入loC容器,利用依赖关系注入的方式,实现对象之间的解耦。loC是一种思想,而DI是loC的一种具体实现

依赖注入是?个过程,是指IoC容器在创建Bean时, 去提供运行时所依赖的资源,而资源指的就是对象

2. 使用

关于依赖注入,Spring也给我们提供了三种方式:

  1. 属性注入(Field Injection)
  2. 构造方法注入(Constructor Injection)
  3. Setter 注入(Setter Injection)

(1)属性注入

属性注入是使用 @Autowired 实现的,将 Service 类注入到 Controller 类中

@Controller
public class UserController {
    @Autowired
    private UserService userService;

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

}

@Service
public class UserService {
    public void sayHi() {
        System.out.println("Hi,UserService");
    }
}


@SpringBootApplication
public class demo {

    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(demo.class, args);
        UserController userController = (UserController)context.getBean("userController");
        userController.sayHi();
    }

}

(2)构造方法注入

@Controller
public class UserController2 {
    private UserService userService;

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

    public void sayHi() {
        System.out.println("Hi,UserController2");
        userService.sayHi();
    }
}

(3)Setter注入

@Controller
public class UserController3 {
    //注??法3: Setter?法注?
    private UserService userService;
    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }
    public void sayHi(){
        System.out.println("hi,UserController3...");
        userService.sayHi();
    }
}

(4)三种注入优缺点分析

  • 属性注入
    • 优点: 简洁,使用方便
    • 缺点:
      • 只能用于 IoC 容器,如果是非 IoC 容器不可用,并且只有在使用的时候才会出现 NPE(空指针异常)
      • 不能注入?个Final修饰的属性
  • 构造函数注?(Spring 4.X推荐)
    • 优点:
      • 可以注入final修饰的属性
      • 注入的对象不会被修改
      • 依赖对象在使用前?定会被完全初始化,因为依赖是在类的构造方法中执行的,而构造方法是在类加载阶段就会执行的方法
      • 通用性好, 构造方法是JDK支持的, 所以更换任何框架,他都是适用的
    • 缺点:
      • 注入多个对象时, 代码会比较繁琐
  • Setter注入(Spring 3.X推荐)
    • 优点: 方便在类实例之后, 重新对该对象进行配置或者注入
    • 缺点:
      • 不能注入?个Final修饰的属性
      • 注入对象可能会被改变, 因为setter方法可能会被多次调用, 就有被修改的风险

(4)@Autowired存在问题

当同?类型存在多个bean时,使用@Autowired会存在问题。

解决方法

  1. @Primary:确定默认的实现
@Component
public class BeanConfig {
 	@Primary //指定该bean为默认bean的实现
	@Bean("u1")
 	public User user1(){
 		User user = new User();
 		user.setName("zhangsan");
 		user.setAge(18);
 		return user;
 	}
 	
 	@Bean
 	public User user2() {
 		User user = new User();
 		user.setName("lisi");
 		user.setAge(19);
 		return user;
 	}
}
  1. @Qualifier:指定当前要注入的bean对象。@Qualifier注解不能单独使用,必须配合@Autowired使用

```java
@Controller
public class UserController {
 	@Qualifier("user2") //指定bean名称
	@Autowired
 	private User user;
 	public void sayHi(){
 		System.out.println("hi,UserController...");
 		System.out.println(user);
 	}
}
  1. @Resource:按照bean的名称进行注入。通过name属性指定要注?的bean的名称
@Controller
public class UserController {
 	@Resource(name = "user2")
 	private User user;
 	public void sayHi(){
 		System.out.println("hi,UserController...");
 		System.out.println(user);
 	}
}

@Autowird 与 @Resource的区别

  • @Autowired 是spring框架提供的注解,?@Resource是JDK提供的注解
  • @Autowired 默认是按照类型注入,而@Resource是按照名称注?. 相比于 @Autowired 来说,@Resource 支持更多的参数设置,例如 name 设置,根据名称获取 Bean

2. 区别概述

Spring:简单来说,Spring 是?个开发应用框架,什么样的框架呢,有这么几个标签:轻量级、?站式、模块化,其目的是用于简化企业级应用程序开发

Spring的主要功能: 管理对象,以及对象之间的依赖关系, ?向切?编程, 数据库事务管理, 数据访问,web框架支持等
但是Spring具备高度可开放性, 并不强制依赖Spring, 开发者可以自由选择Spring的部分或者全
部,Spring可以无缝继承第三方框架,比如数据访问框架(Hibernate 、JPA),web框架(如Struts、JSF)

Spring MVC:Spring MVC是Spring的?个子框架, Spring诞生之后,?家觉得很好用,于是按照MVC模式设计了?个 MVC框架(一些用Spring 解耦的组件), 主要用于开发WEB应用和网络接口,所以,Spring MVC 是?个Web框架

Spring MVC基于Spring进行开发的, 天生的与Spring框架集成. 可以让我们更简洁的进行Web层开发, 支持灵活的 URL 到页面控制器的映射, 提供了强大的约定大于配置的契约式编程支持, 非常容易与其他视图框架集成,如 Velocity、FreeMarker等

Spring Boot:Spring Boot是对Spring的?个封装, 为了简化Spring应用的开发而出现的,中小型企业,没有成本研究自己的框架, 使用Spring Boot 可以更加快速的搭建框架, 降级开发成本, 让开发人员更加专注于Spring应用的开发,而无需过多关注XML的配置和?些底层的实现

Spring Boot 是个脚?架, 插拔式搭建项目, 可以快速的集成其他框架进来

Spring MVC和Spring Boot都属于Spring,Spring MVC 是基于Spring的?个MVC 框架,?Spring Boot 是基于Spring的?套快速开发整合包

文章来源:https://blog.csdn.net/wuweixiaoyue/article/details/134737735
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。