Spring IOC

个人主页♡喜欢做梦

欢迎 👍点赞 ➕关注 ❤️收藏 💬评论


目录

🍑一、Lombok

🍓定义

🍓常用注解

🍓使用

🍑二、IoC

🍓什么是IoC?

🍓IoC的实现方式:依赖注入(DI)

🍉传统方式

🍉ioc方式

🍓Bean的存储:将类纳入Spring容器管理

🍉类注解

@Controller(控制器存储)

@Service(服务层类)

@Respository(数据访问层类)

@Component(通用主键类)

@Configuration(配置类)

🍉对象的获取

写法一:通过Bean的类型(class)获取

写法二:通过Bean的名称获取(需强制类型转换)

写法三:通Bean的名称+类型获取

🍉方法注解@Bean

1.@Bean的使用

2.定义多个对象

3.多个对象的获取

4.重命名Bean

常用写法:通过name属性指定

简化写法:省略name

4.bean的获取

🍉扫描路径@

🍑三、@Autowired

🍓定义

🍓属性注入

🍓构造注入

🍓setter注入


🍑一、Lombok

🍓定义

Lombok是一个Java库,他通过注解的方式来简化Java代码的编写,减少冗余代码,提高开发效率。

🍓常用注解

|-------------------------|-----------------------------------------------------------------------------|
| 注解 | 作用 |
| @Data | 组合注解,包含@Getter,@Setter,@ToString、@EqualsAndHashCode、@RequireArgsConstructor |
| @Getter/@Setter | 自动添加getter/setter方法 |
| @ToString | 自动添加toString |
| @EqualsAndHashCode | 自动添加equals和hashCode方法 |
| @NoArgsConstructor | 自动添加无参构造方法 |
| @AllArgsConstructor | 自动添加全属性构造方法,顺序按照属性的定义顺序 |
| @NonNull | 属性不能为null |
| @RequireArgsConstructor | 自动添加必需属性的构造方法,final和@NonNull的属性为必需 |
| @Slf4j | 生成日志对象,用于日志输出 |

在Settings,点击Plugins,在Marketplace中,搜索EditStarters,进行下载

在pox.xml文件中,右键点击Generate

点击后出现 Edit Starters,再次点击

出现一下结果,点击ok即可

🍓使用

java 复制代码
@Data
@Getter@Setter
public class UserInfo2 {
    @Getter
    private String name;
    @Setter
    private int age;
    @Getter@Setter
    private String number;
}
  • 可以同时使用,可以放在类里面,也可以放在类外,如果放在类外,作用于该类的全部,类里面就不用在使用这种方法。而放在类里面就只作用于当前方法的下面一个成员变量。

🍑二、IoC

🍓什么是IoC?

IoC(Inversion of Controller,控制反转)是软件工程中的一种设计原则,用于降低代码间的耦合度。他的核心思想是将对象的创建、依赖关系的管理等控制权从应用程序代码本身转移到外部容器或框架,从而实现"控制反转"。

简单来说,传统编程 中,对象通常由自身或直接依赖的其他对象主动创建(例如用new关键字实例化),而在IoC模式 下,这种创建和管理的权力被"反转"给了第三方。Spring是包含众多工具方法的IoC容器,对对象交给Spring管理,就是IoC思想。

🍓IoC的实现方式:依赖注入(DI)

依赖注入是IoC最常见的实现方式,指容器在创建对象时,自动将依赖的其他对象注入进来,而无需自己创建对象。

🍉传统方式

如果我们要设计一个汽车,首先我们设计他的车轮(tire),在根据轮子的大小来设计底盘(bottom),在依据底盘来设计车身(framework),最终形成一个完整的汽车。

修改前:

java 复制代码
//车轮
public class Tire {
   private int size;
    public Tire(){
        this.size=10;
        System.out.println("size="+size);
    }
}
//底盘
public class Bottom {
    private Tire tire;
    public Bottom(){
        this.tire=new Tire();
        System.out.println("Bottom init...");
    }
}
//车身
public class Framework {
    private Bottom bottom;
    public Framework(){
        this.bottom=new Bottom();
        System.out.println("Framework init...");
    }
}
public class Car {
    private Framework framework;
    public Car(){
        this.framework=new Framework();
        System.out.println("Car init....");
    }
}
java 复制代码
public class Main {
    public static void main(String[] args) {
        Car car=new Car();
    }
}

这些代码看着没有什么问题,但是这属于硬编码,使用无参的构造方法固定了轮胎的尺寸大小。随着需求的逐渐加大,每个车轮的大小都可能需要动态变化,我们不能仅满足于一种轮胎固定大小的需求,所以我们需要将上面的代码进行修改。

修改后

java 复制代码
//车轮
public class Tire {
    private int size;
    public Tire(int size){
        this.size=size;
        System.out.println("size="+size);
    }
}
public class Bottom {
    private Tire tire;
    public Bottom(int size){
        this.tire=new Tire(size);
        System.out.println("Bottom init...");
    }
}
public class Framework {
    private Bottom bottom;
    public Framework(int size){
        this.bottom=new Bottom(size);
        System.out.println("Framework init...");
    }
}
public class Car {
    private Framework framework;
    public Car(int size){
        this.framework=new Framework(size);
        System.out.println("Car init....");
    }
}

测试:

java 复制代码
 @Test
	void TestCar(){
		Car car=new Car(25);
	}

改代码通过有参的构造函数和参数传递,让整条依赖链的配置可以动态调整。但仍然存在问题,当轮胎的尺寸得到修改,其底盘,车身也要随之改变。

那我们可不可以将思路倒过来,让轮胎的尺寸依赖于底盘,让底盘依赖于车身,车身依赖于汽车

如图:

那我们要如何实现呢?这里我们需要使用到注入的方法。

🍉ioc方式

java 复制代码
//车轮
public class Tire {
    private int size;
    public Tire(int size){
        this.size=size;
        System.out.println("size="+size);
    }
}
//底盘
public class Bottom {
    private Tire tire;
    public Bottom(Tire tire){
        this.tire=tire;
        System.out.println("Bottom init...");
    }
}
//车身
public class Framework {
    private Bottom bottom;
    public Framework(Bottom bottom){
        this.bottom=bottom;
        System.out.println("Framework init...");
    }
}
//汽车
public class Car {
    private Framework framework;
    public Car(Framework framework){
        this.framework=framework;
        System.out.println("Car init....");
    }
}

测试

java 复制代码
 @Test
    void TestCar3(){
        Tire tire=new Tire(20);
        Bottom bottom=new Bottom(tire);
        Framework framework=new Framework(bottom);
        Car car=new Car(framework);
    }

这个代码依赖由外部传递进来,对象只需关心使用依赖,不用关心如何创建依赖。也就是依赖注入(DI)的核心思想------"将依赖的创建交给外部,而非自己创建"。


🍓Bean的存储:将类纳入Spring容器管理

要让Spring容器管理一个类,需要通过注解XML配置 将其标记为**"Bean"** ,也就是bean的存储。

要把某个对象交给IOC容器管理,需要在类上添加一个注解:@Component

除此之外,Spring还提供了一系列注解来标识不同角色丰富的注解。

共用两类注解类型可以实现:

类注解:

  • @Controller(控制层类):表示控制层类,处理请求、返回响应。例如,接收http请求,表单的提交。
  • @Service(服务层类):表示服务层类,封装业务逻辑。例如,处理订单创建、支付等业务流程。
  • @Respository(数据访问层类):标识数据访问层类,操作数据库并转换异常。例如,执行数据库的增删查改。
  • @Component(通用主键类):通用注解主键,用于无明确分层的类。例如,工具类、辅助类等通用注解。
  • @Configuration(配置类):标识配置类,用于定义bean的配置。例如,自定义bean创建逻辑,全局配置。

这些注解加类上,Spring会自动扫描并实例化为bean

方法注解:

  • @Bean:标注在配置类方法上,注册方法返回对象为bean。例如第三库对象、创建复杂逻辑的bean对象。通常加在配置类的方法上,方法返回的对象会被注册为bean。

🍉类注解

就是在类上面加上相应的注解。

@Controller(控制器存储)
java 复制代码
@Controller
public class UserController {
    public void ctl(){
        System.out.println("UserController...");
    }
}
@Service(服务层类)
java 复制代码
@Service
public class UserService {
    public void service(){
        System.out.println("UserService...");
    }
}
@Respository(数据访问层类)
java 复制代码
@Repository
public class UserRepository {
    public void repository(){
        System.out.println("Userepository...");
    }
}
@Component(通用主键类)
java 复制代码
@Component
public class UserComponent {
    public void component(){
        System.out.println("UserComponent...");
    }
}
@Configuration(配置类)
java 复制代码
@Configuration
public class UserConfiguration {
    public void configuration(){
        System.out.println("UserConfiguration...");
    }
}

那么我们要怎么从Spring容器中获取对象呢?

🍉对象的获取

Spring容器中对象的获取

我们原本就有这个类,无需在创建

该类是Spring Boot应用的启动类,是项目的核心入口,我们再次基础上进行修改就好

写法一:通过Bean的类型(class)获取
javascript 复制代码
@SpringBootApplication
public class SpringIocDemo2Application {
	//从Spring容器中获取存储的对象
	public static void main(String[] args) {
		//适合对象只有一个
		//获取Spring上下文对象:初始化IOC容器,扫描并创建所有被Spring管理的Bean
		ApplicationContext context=SpringApplication.run(SpringIocDemo2Application.class, args);
		//写法1
		//@Controller
		//从Spring上下文获取对象:从Spring容器中获取UserController实例
		UserController userController=context.getBean(UserController.class);
		//调用对象
		userController.controller();
		System.out.println(userController);
}
}
  • 使用只获取一个bean对象;
写法二:通过Bean的名称获取(需强制类型转换)
javascript 复制代码
@SpringBootApplication
public class SpringIocDemo2Application {
	//从Spring容器中获取存储的对象
	public static void main(String[] args) {
		//适合对象只有一个
		//获取Spring上下文对象:初始化IOC容器,扫描并创建所有被Spring管理的Bean
		ApplicationContext context=SpringApplication.run(SpringIocDemo2Application.class, args);
		//写法2:
		UserController userController2=(UserController) context.getBean("userController");
		System.out.println(userController2);
}
}
  • 因为getBean()返回的类型是object类型,所以需要强制转换为UserController;
  • bean名称以小写 字母开头,使用驼峰式大小写。例如,类名:UserController,Bean的名称:userController,写错则报错;
  • 一些特殊情况,第一个和第二个都是大写,将保留原始的大小写。例如,类名:UController,Bean的名称:UController。
写法三:通Bean的名称+类型获取
javascript 复制代码
@SpringBootApplication
public class SpringIocDemo2Application {
	//从Spring容器中获取存储的对象
	public static void main(String[] args) {
		//适合对象只有一个
		//获取Spring上下文对象:初始化IOC容器,扫描并创建所有被Spring管理的Bean
		ApplicationContext context=SpringApplication.run(SpringIocDemo2Application.class, args);
		//写法3:
		UserController userController3=context.getBean("userController",UserController.class);
		System.out.println(userController3);
}
}

可以发现他们三个打印的内容都一样,因为他们获取的是Spring容器中的同一个Bean实例。(Spring默认Bean是单例的,即容器中的同一类型的Bean只有一个实例)

如果去掉@Controller或者其他注解,会出现什么样的结果?

不出所料,出现报错现象

java 复制代码
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.likedreams.ioc2.controller.UserController' available
  • 意思为在"main"线程中出现异常:org.springframework.beans.factory.NoSuchBeanDefinitionException(没有此类Bean定义异常),没有符合条件为 "com.likedreams.ioc2.controller.UserController"的Bean可以使用

🍉方法注解@Bean

为什么要使用方法注解@Bean?

类注解有两个局限:

  • 外部包类无法添加类注解:如果我们要使用的类来自外部,也就是不是我们自己项目里面的类,就是没有办法添加这些注解,Spring无法自动扫描并创建bean;
  • 一个类需要多个对象:我们知道类注解加在类上,只能被扫描注册成一个Bean,无法配置多个对象。

@Bean的作用:

  • 在上面两种场景下,就需要使用@Bean注解。他可以标记在配置类的方法上,方法返回的对象被注册为Bean。可以解决类注解的局限------既可以对外部类对象进行Bean注册,也可以生成同一个类的多个不同对象。

1.@Bean的使用

java 复制代码
@Configuration
public class UserConfiguration {
    @Bean
    public String configuration(){
        System.out.println("UserConfiguration...");
        return "111";
    }
}
java 复制代码
@SpringBootApplication
public class SpringIocDemo2Application {
	//从Spring容器中获取存储的对象
	public static void main(String[] args) {
		//适合对象只有一个
		//获取Spring上下文对象:初始化IOC容器,扫描并创建所有被Spring管理的Bean
		ApplicationContext context=SpringApplication.run(SpringIocDemo2Application.class, args);
		//@Configuration
		String userConfiguration=context.getBean(String.class);
		System.out.println(userConfiguration);
	}
}
  • @Bean一般与@Configuration配合使用。

结果:

如果不加@Bean会出现什么样的结果?加@Bean和不加有什么区别?

如果不加@Bean则会出现报错,无法获取到bean对象。

就好比登记信息,加@Bean就相当于登记信息,我们可以查找到自己的信息。不加,就相当于我们没有登记信息,Spring不知道他的存在,想要查找也无法查找到,需要自己手动拎出来。

2.定义多个对象

java 复制代码
@Repository
public class UserRepository {
    @Bean
    public String repository1(){
        return "111";
    }
    @Bean
    public String repository2(){
        return "222";
    }
}

这是定义多个对象,那么我们对对象的获取应该怎么写?

3.多个对象的获取

我们上面也有写到对象的获取,写法一只适用于获取一个对象,我们可以选择写法二和写法三。

javascript 复制代码
	public static void main(String[] args) {
		ApplicationContext context=SpringApplication.run(SpringIocDemo2Application.class, args);
		String userRepository1=(String) context.getBean("repository1");
		System.out.println(userRepository1);
		String userRepository2=(String) context.getBean("repository2");
		System.out.println(userRepository2);
		System.out.println("---");
		String userRepository3= context.getBean("repository1",String.class);
		System.out.println(userRepository3);
		String userRepository4= context.getBean("repository2",String.class);
		System.out.println(userRepository4);
	}
}

结果

4.重命名Bean

常用写法:通过name属性指定
javascript 复制代码
@Configuration
public class UserConfiguration {
    @Bean(name = {"config","configuration"})
    public String configuration(){
        System.out.println("UserConfiguration...");
        return "111";
    }
}
简化写法:省略name
javascript 复制代码
@Configuration
public class UserConfiguration {

    //可定义多个名称,多个可以使用,原来的名称不可再用
    @Bean(name={"config","configuration0"})
    public String configuration(){
        System.out.println("UserConfiguration...");
        return "111";
    }
    //省略name
    @Bean({"config1","configuration1"})
    public String configuration1(){
        System.out.println("UserConfiguration...");
        return "222";
    }
    //只重命名一个名称时,可以省略{}
    @Bean("config3")
    public String configuration3(){
        System.out.println("UserConfiguration...");
        return "333";
    }
}

4.bean的获取

javascript 复制代码
	public static void main(String[] args) {
		ApplicationContext context=SpringApplication.run(SpringIocDemo2Application.class, args);
		//@Configuration
		//重命名名称
		//多个重命名名称可以同时使用
		String userConfiguration1=context.getBean("config",String.class);
		System.out.println(userConfiguration1);
		String userConfiguration2=context.getBean("configuration0",String.class);
		System.out.println(userConfiguration2);
		
		String userConfiguration3=context.getBean("config3",String.class);
		System.out.println(userConfiguration3);
		
		//原来的名称:发生报错
		String userConfiguration4=context.getBean("configuration",String.class);
		System.out.println(userConfiguration4);
	}
}

结果:

  • 可以重命名多个名称,也可以同时使用;
  • 重命名之后,旧的名称不可以在使用;
  • name可以省略,重命名只有一个名称时{}可以省略。
  • 不要不同方法重命名成相同的名称,会出现冲突

🍉扫描路径@

有时候我们也可能出现Spring扫描的问题

当我们要调用的是config下的包的时候,然而SpringIocDemo2Application类却在controller包下,那么久无法扫描到config。会出现下面的情况:

那我们应该怎么做?

我们可以使用@@ComponentScan来配置扫描路径

这样就可以扫描到,当然我们也可以放到外面


🍑三、@Autowired

🍓定义

DI是Spring框架核心思想"控制反转(IoC)"的具体实现方式。其核心是让容器自动管理对象之间的依赖关系。我们需要使用@Autowired这个注解,完成依赖注入。

@Autowired 作用:@Autowire是Spring提供的注解,用于告诉Spring容器:需要被注解的属性、方法或构造器注入对应的依赖对象。

🍓属性注入

使用

javascript 复制代码
//添加@Autowired
@Service
public class UserService {
    public void service(){
        System.out.println("UserService...");
    }
}
@Controller
public class UserController {
    @Autowired
    private UserService userService;
    public void controller(){
        System.out.println("UserController...");
        userService.service();
    }
}
//不添加@Autowired相当于
@Controller
public class UserController {
    private UserService userService;

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

    public void controller(){
        System.out.println("UserController...");
        userService.service();
    }
}
javascript 复制代码
public static void main(String[] args) {
		ApplicationContext context=SpringApplication.run(SpringIocDemo2Application.class, args);
        UserController userController=context.getBean(UserController.class);
		userController.controller();
		System.out.println(userController);
	}

添加@Autowired和不添加@Autowired有什么区别

  • 添加@Autowired:Spring会在启动时自动在容器中寻找相对应的bean,并赋值属性
  • 不添加@Autowired:调用时则会发生空指针异常

简单来说,@Autowired的核心作用就是让Spring帮我们"自动装配"依赖对象(属性赋值),没有他,依赖对象就不会被赋值,代码执行时会因为对象为null而报错。

🍓构造注入

但一个类有多个构造方法 ,这时候,Spring无法判断应该使用哪个构造方法。此时就需要再构造方法添加@Autowired 。当如果只有一个构造方法 ,那么可以省略

java 复制代码
@Controller
public class UserController {
    private UserService userService;
    private UserRepository userRepository;
    
    public UserController(){}

    @Autowired
    public UserController(UserService userService, UserRepository userRepository) {
        this.userService = userService;
        this.userRepository = userRepository;
    }

    public void controller(){
        System.out.println("UserController...");
        userService.service();
        userRepository.repository1();
    }
}
javascript 复制代码
	public static void main(String[] args) {
		ApplicationContext context=SpringApplication.run(SpringIocDemo2Application.class, args);
        UserController userController=context.getBean(UserController.class);
		userController.controller();
		System.out.println(userController);
	}

如果有多个构造方法没有添加@Autowired,那么会出现下面的情况:

🍓setter注入

javascript 复制代码
@Controller
public class UserController {
    private UserService userService;
    private UserRepository userRepository;
    public UserController(){}
    @Autowired(required = false) //可选依赖
    public void setUserService(UserService userService) {
        this.userService = userService;
    }
    @Autowired
    public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
    
    public UserController(UserService userService, UserRepository userRepository) {
        this.userService = userService;
        this.userRepository = userRepository;
    }

    public void controller(){
        System.out.println("UserController...");
        userService.service();
        userRepository.repository1();
    }
}
  • 适合处理"可选依赖",可注入也可以不注入;
  • 允许对象在创建后动态修改依赖。
相关推荐
拾忆,想起2 小时前
TCP滑动窗口:网络世界的“智能流量阀门”
java·网络·数据库·网络协议·tcp/ip·php·哈希算法
IT_陈寒2 小时前
Vue 3性能优化实战:7个关键技巧让我的应用加载速度提升50%
前端·人工智能·后端
摇滚侠2 小时前
Spring Boot3零基础教程,Reactive-Stream 发布订阅写法,笔记104 笔记105
java·spring boot·笔记
laplace01235 小时前
Java八股—MySQL
java·mysql·oracle
熙客6 小时前
TiDB:分布式关系型数据库
java·数据库·分布式·tidb
你想考研啊7 小时前
linux安装jdk和tomcat和并自启动
java·linux·tomcat
星释7 小时前
Rust 练习册 :Leap与日期计算
开发语言·后端·rust
悟能不能悟8 小时前
java的java.sql.Date和java.util.Date的区别,应该怎么使用
java·开发语言
高山上有一只小老虎9 小时前
java 正则表达式大全
java·正则表达式