Spring-IoC&DI

一.什么是Spring

我们知道Spring是一个生态,里面包含众多的工具,可以在我们进行Java项目开发中,帮助我们完成一些功能,给我们带来一些便利。

总的一句话来说:Spring其实就是一个包含众多工具的IoC容器。

二.什么是容器

可以盛放东西的物品就是容器。

例如:

list/map 是装数据的容器

tomcat是装web的容器

既然是容器就要管理这些存放的东西,也就是容器内存放的这些东西的存和去。

Spring是一个管理对象的容器。

三.什么是IoC

IoC(控制反转,Inversion of Control)是一种编程思想。

所谓控制反转,实质上是指控制权的转移:

举个例子,自动驾驶技术便是一种控制反转的体现。在传统的汽车驾驶中,驾驶控制权掌握在驾驶员手中,而在自动驾驶模式下,控制权则交由车辆的自动化系统。这种控制权的转移便是控制反转的体现------本来由司机掌控的驾驶权,现在转交给了车辆系统。

四.Spring其实就是一个包含众多工具的IoC容器

Spring其实就是一个包含众多工具的IoC容器。

也就是说Spring是一个包含众多工具的一个实现了控制反转思想的管理对象的容器。

我们知道当在类中需要使用某一个类型的对象的时候,我们直接new一个就好了:

但是现在不用了,我们直接找Spring要就可以。 在类上⾯添加 @RestController 和 @Controller 注解, 就是把这个类交给Spring管理, Spring 框架启动时就会加载该类. 将这个类的对象交给Spring管理, 这就是IoC思想,在Spring中的体现。

五.传统的程序开发

在传统的程序开发模式中,对于一个汽车对象我们的设计思路是:

先设计轮子(Tire),然后根据轮子的大小设计地盘(Bottom),接着根据地盘设计车身(Framework),最后根据车身设计好整个汽车(Car)。

代码实现如下:

java 复制代码
public class NewCarExample {
    public static void main(String[] args) {
        Car car = new Car();
        car.run();
    }

    /**
     * 汽⻋对象
     */
    static class Car {
        private Framework framework;

        public Car() {
            framework = new Framework();
            System.out.println("Car init....");
        }
        public void run(){
            System.out.println("Car run...");
        }
    }

    /**
     * ⻋⾝类
     */
    static class Framework {
        private Bottom bottom;

        public Framework() {
            bottom = new Bottom();
            System.out.println("Framework init...");
        }
    }

    /**
     * 底盘类
     */
    static class Bottom {
        private Tire tire;

        public Bottom() {
            this.tire = new Tire();
            System.out.println("Bottom init...");
        }
    }

    /**
     * 轮胎类
     */
    static class Tire {
        // 尺⼨
        private int size;

        public Tire(){
            this.size = 17;
            System.out.println("轮胎尺⼨:" + size);
        }
    }
}

这样的设计看起来没有问题,但是可维护性却很低:

此时如果对轮子进行更改,那么地盘车身汽车的整体都要随着轮子的改变发生改变。这显然是非常反人类的设计。

完整代码如下:

java 复制代码
public class NewCarExample {
    public static void main(String[] args) {
        Car car = new Car(20);
        car.run();
    }

    /**
     * 汽车对象
     */
    static class Car {
        private Framework framework;

        public Car(int size) {
            framework = new Framework(size);
            System.out.println("Car init....");
        }

        public void run(){
            System.out.println("Car run...");
        }
    }

    /**
     * 车身类
     */
    static class Framework {
        private Bottom bottom;

        public Framework(int size) {
            bottom = new Bottom(size);
            System.out.println("Framework init...");
        }
    }

    /**
     * 底盘类
     */
    static class Bottom {
        private Tire tire;

        public Bottom(int size) {
            this.tire = new Tire(size);
            System.out.println("Bottom init...");
        }
    }

    /**
     * 轮胎类
     */
    static class Tire {
        // 尺寸
        private int size;

        public Tire(int size){
            this.size = size;
            System.out.println("轮胎尺寸:" + size);
        }
    }
}

从以上代码可以看出,以上程序的问题是:当最底层代码改动之后,整个调用链上的所有代码都需要跟着进行更改,这样的程序的耦合度非常高(修改一处diamond,影响其他处的代码进行修改)。

六.解决方案

在上面的程序中,我们是根据轮子的尺寸设计的底盘,轮子的尺寸一改,底盘的设计就得修改。同样因为我们是根据地盘设计的车身,那么车身也得改,同理汽车的设计也得修改,也就是因为车轮的更改,整个汽车的设计都跟着修改了一遍。

此时我们不妨换一种思路,我们先设计汽车的大概样子,然后根据汽车的样子来设计车身,根据车身来设计地盘,根据地盘来设计轮子的尺寸。

这时候,依赖关系就倒置过来了:轮子依赖地盘,地盘依赖车身,车身依赖汽车。

这就类似我们打造一辆完整的汽车,如果所有的配件都是自己造的,那么当客户需求发生改变的时候,比如轮胎的尺寸不再是原来的尺寸,那我们就要自己动手来改,如果我们是把轮胎外包出去,那么即使轮胎的尺寸发生改变了,我们只需要向代理工厂下订单就行了,我们自身是不需要出力的。

如何来实现呢:

我们可以尝试不在每个类中自己创建下级类,如果自己创建下级列就会出现当下级类发生改变操作,自己也要跟着修改。

此时,我们只需要将原来由自己创建的下级类,改为传递的方式(也就是注入的方式),因为我们不需要在当前类中创建下级类了,所以下级类即使发生变化(创建或减少参数),当前类本身也无需修改任何代码,这样就完成了程序的解耦。

七.IoC程序

基于以上思路,我们把调用汽车的程序实例改造一下,把创建子类的方式,改为注入传递的方式。

具体代码实现如下:

java 复制代码
public class IocCarExample {
    public static void main(String[] args) {
        Tire tire = new Tire(20);
        Bottom bottom = new Bottom(tire);
        Framework framework = new Framework(bottom);
        Car car = new Car(framework);
        car.run();
    }

    static 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...");
        }
    }

    static class Framework {
        private Bottom bottom;

        public Framework(Bottom bottom) {
            this.bottom = bottom;
            System.out.println("Framework init...");
        }
    }

    static class Bottom {
        private Tire tire;

        public Bottom(Tire tire) {
            this.tire = tire;
            System.out.println("Bottom init...");
        }
    }

    static class Tire {
        private int size;

        public Tire(int size) {
            this.size = size;
            System.out.println("轮胎尺寸:" + size);
        }
    }
}

代码经过以上调整,无论底层类如何变化,整个调用链是不用做任何改变的,这样就完成了代码之间的解耦,从而实现了更加灵活、通用的程序设计了。

八.IoC的优势

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

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


我们发现了⼀个规律,通⽤程序的实现代码,类的创建顺序是反的,传统代码是 Car 控制并创建了Framework,Framework 创建并创建了 Bottom,依次往下,而改进之后的控制权发生的反转,不再是使用⽅对象创建并控制依赖对象了,而是把依赖对象注⼊将当前对象中,依赖对象的控制权不再由当前类控制了。这样的话, 即使依赖类发⽣任何改变,当前类都是不受影响的,这就是典型的控制反转,也就是 IoC 的实现思想。


这部分代码, 就是IoC容器做的⼯作。
从上⾯也可以看出来,IoC容器具备以下优点:
资源不由使⽤资源的双⽅管理,⽽由不使⽤资源的第三⽅管理,这可以带来很多好处。第⼀,资源集 中管理,实现资源的可配置和易管理。第⼆,降低了使⽤资源双⽅的依赖程度,也就是我们说的耦合度。
资源集中管理: IoC容器会帮我们管理⼀些资源(对象等), 我们需要使⽤时, 只需要从IoC容器中去取 就可以了
我们在创建实例的时候不需要了解其中的细节, 降低了使⽤资源双⽅的依赖程度, 也就是耦合度。Spring 就是⼀种IoC容器, 帮助我们来做了这些资源管理。

九.什么是DI

IoC是一种思想,DI是一种实现方式(Spring实现IoC思想的方式)。
DI:依赖注入,将所需要的资源,注入到等待使用的方法中


IoC 是⼀种思想,也是"⽬标", 而思想只是⼀种指导原则,最终还是要有可⾏的落地⽅案,⽽ DI 就属于具体的实现。所以也可以说, DI 是IoC的⼀种实现。
比如说我今天心情比较好,吃⼀顿好的犒劳犒劳⾃⼰,那么"吃⼀顿好的"是思想和目标(是
IoC),但最后我是吃海底捞还是杨国福?这就是具体的实现,就是 DI。

十.IoC & DI 使用

既然Spring是一个IoC容器,那么作为容器,那么它就具备两个最基础的功能:

存和取

Spring容器管理的主要是对象,这些对象,我们称之为"Bean"。我们把这些对象交给Spring管理,由Spring来负责对象的创建和销毁。我们程序只需要告诉Spring,那些需要存,以及如何从Spring中取出对象。

十一.IoC容器(存)

前⾯我们提到IoC控制反转,就是将对象的控制权交给Spring的IOC容器,由IOC容器创建及管理对象。

也就是bean的存储。

对于一个类我们想让Spring帮我们管理这个类的对象需要给这个类打注释:

共有两类注释类型可以实现:

类注解:@Controller、@Service、@Reponsitory、@Component、@Configuration

方法注解:@Bean

其中类注解又称五大注解。

1.@Controller

通过加@Controller注解的方式告诉Spring容器来帮我们管理这个对象。

java 复制代码
import org.springframework.stereotype.Controller;

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

在启动类中获取这个被Spring容器管理的Bean:

java 复制代码
@SpringBootApplication
public class IoCDiApplication {

    public static void main(String[] args) {

       ApplicationContext applicationContext =  SpringApplication.run(IoCDiApplication.class, args);

       UserController userController = applicationContext.getBean(UserController.class);
       
       userController.doController();
    }

}

@SpringBootApplication :

默认的@SpringBootApplication中的@ComponentScan是会去找启动方法所在的包中的类及其子包中的类:

2.@Service

java 复制代码
import org.springframework.stereotype.Service;

@Service
public class UserService {
    public void doService(){
        System.out.println("doService.....");
    }
}
java 复制代码
 UserService userService = applicationContext.getBean(UserService.class);

        userService.doService();

通过名称获取Bean:

上面的获取Spring容器中的Bean的方式是通过Bean的类型来获取,但是如果在 Spring容器中同一类型,存在多个Bean,此时这种方法就行不通了。

观察获取Bean的方式:

通过上面的观察,我们发现还可以通过名称的方式来获取Bean:

普通类型:

java 复制代码
//根据名称来获取Bean
UserService userService2 = (UserService) applicationContext.getBean("userService");
        userService2.doService();

两个大写字母开头的类型:

java 复制代码
@Controller
public class UController {
    public void doController(){
        System.out.println("doUController....");
    }
}
java 复制代码
UController uController = (UController) applicationContext.getBean("UController");
        uController.doController();

通过上面不同方式获取的Bean都是相同的,是同一个对象。

ApplicationContext VS BeanFactory:

从继承关系和功能方面来说:Spring容器有两个顶级的接口:BeanFactory和ApplicationContext。其中BeanFactory提供了基础的访问容器的能力,而ApplicationContext属于BeanFactory的子类,它除了继承了BeanFactory的所有功能之外,它还拥有独特的特性,还添加了对国际化的支持、资源访问支持、以及事件传播等方面的支持。

从性能方面来说:ApplictionContext是一次性加载并初始化所有Bean对象,而BeanFactory是需要哪个才去加载哪个,因此更加轻量。(空间换时间)

3.@Repository

4.@Component

5.Configuration

6.为什么需要这么多注解

让程序员在看到相应的注解后就知道这个部分功能是干什么的:

@Controller:控制层,接收请求,对请求进行处理,并进行响应。

@Service:业务逻辑层,处理具体的业务逻辑。

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

@Configuration:配置层,处理项目中的一些配置信息。

通过观察@Controller/@Service/@Repository/@Configuration,发现这些注释中都包含了@Component ,也就说明这些注释均是@Component的衍生类。

IoC的五大注解功能是类似的,但是也不尽相同,例如Controller还被赋予了其他功能,如果想被外界获取只能使用@Controller注释,也就是通过HTTP请求获取。

后端在返回数据的时候尽量避免返回中文。

7.@Bean

从上面我们发现使用五大注解来管理对象,不论什么时候怎么样取,相同类型下,取出的都是同一对象。

而且对于五大注解来说,在使用的时候只能在我们自己的代码中使用,如果我们让Spring帮我们管理外部包中的类,五大注解是办不到的。

所以在上面两种情况下,我们就需要使用@Bean。

我们试着写一些@Bean的方法:

首先先定义一个对象:

java 复制代码
public class User {
    private String name;
    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

编写@Bean注释的方法:

@Bean表示这个方法返回的是一个Bean,Spring容器会托管它;

方法名user()对应的Bean的名字就是name;

方法的返回值是一个User对象,会被注册为一个类型为User的Bean;

此时Spring容器中就相当于有一个user对象;

java 复制代码
import org.example.ioc_di.model.User;
import org.springframework.context.annotation.Bean;

public class BeanConfig {
    @Bean
    public User user (){
        User user = new User();
        user.setName("张三");
        user.setAge(30);
        return user;
    }
}

在启动类中获取User对象:

java 复制代码
@SpringBootApplication
public class IoCDiApplication {

    public static void main(String[] args) {

        ApplicationContext applicationContext = SpringApplication.run(IoCDiApplication.class, args);

        User user = (User) applicationContext.getBean("user");
        System.out.println("user = " + user);
    }
}

启动后发现Spring容器找不到这个对象:

出现这种情况的原因是,在Spring框架的设计中,方法注解@Bean要配合类注解才能将对象正常的存储到Spring容器中:

java 复制代码
import org.example.ioc_di.model.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class BeanConfig {
    @Bean
    public User user (){
        User user = new User();
        user.setName("张三");
        user.setAge(30);
        return user;
    }
}

此时还有一个问题:

java 复制代码
import org.example.ioc_di.model.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class BeanConfig {
    @Bean
    public User user (){
        User user = new User();
        user.setName("张三");
        user.setAge(30);
        return user;
    }

    @Bean
    public User userOne (){
        User user = new User();
        user.setName("李四");
        user.setAge(30);
        return user;
    }
}
java 复制代码
@SpringBootApplication
public class IoCDiApplication {

    public static void main(String[] args) {

        ApplicationContext applicationContext = SpringApplication.run(IoCDiApplication.class, args);

        User user = (User) applicationContext.getBean(User.class);
        System.out.println("user = " + user);
    }
}

如果有多个对象的时候使用对象的类型来获取对象时,Spring就不知道我们到底想要获取那个了,所以此时应该使用Bean的名称来获取,使用@Bean注解时Bean的名称是方法名,或者使用类型加名称的方式也可以获取。

java 复制代码
@SpringBootApplication
public class IoCDiApplication {

    public static void main(String[] args) {

        ApplicationContext applicationContext = SpringApplication.run(IoCDiApplication.class, args);

        User user = (User) applicationContext.getBean("user");
        System.out.println("user = " + user);
    }
}

如何进行参数传递:

参数 String name 会自动注入 Spring 容器中名为 name 的 Bean(就是上面那个字符串 "张三");这是 Spring 的一种 方法参数注入方式 (基于方法参数类型匹配或名称匹配);方法内部创建了一个 User 实例,设置了名字(来自 name Bean)和年龄 30;最终返回并注册这个 User Bean 到 Spring 容器中。

java 复制代码
@Configuration
public class BeanConfig {

    @Bean
    public String name(){
        return "张三";
    }

    @Bean
    public User user (String name){
        User user = new User();
        user.setName(name);
        user.setAge(30);
        return user;
    }
}

@Bean重命名

可以通过设置name属性给Bean对象进行重命名操作,代码如下:

java 复制代码
@Configuration
public class BeanConfig {

    @Bean
    public String name(){
        return "张三";
    }

    @Bean(name = {"u1","user1"})
    public User user (String name){
        User user = new User();
        user.setName(name);
        user.setAge(30);
        return user;
    }
}
java 复制代码
@SpringBootApplication
public class IoCDiApplication {

    public static void main(String[] args) {

        ApplicationContext applicationContext = SpringApplication.run(IoCDiApplication.class, args);

        User user = (User) applicationContext.getBean("user1");
        System.out.println("user = " + user);
    }
}

其中name是可以省去的:

java 复制代码
@Configuration
public class BeanConfig {

    @Bean
    public String name(){
        return "张三";
    }

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

如果名称只有一个{}也可以省去:

java 复制代码
@Configuration
public class BeanConfig {

    @Bean
    public String name(){
        return "张三";
    }

    @Bean("u1")
    public User user (String name){
        User user = new User();
        user.setName(name);
        user.setAge(30);
        return user;
    }
}

十二.DI(取)依赖注入

三种方式:

1.属性注入

@Autowired

属性注入以类型进行匹配,与注入的属性名称无关,但是如果一个类型存在多个对象时,优先名称匹配,如果名称都匹配不上,就会报错。

属性注入无法注入Final修饰的属性,因为Fianl是定义时赋值或者构造方法中进行赋值。

java 复制代码
import org.springframework.stereotype.Component;

@Component
public class User {
    private String name;
    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
java 复制代码
import org.example.ioc_di.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    @Autowired
    private User user;
    public String doService(){
        user.setName("张三");
        user.setAge(30);
        return  user.toString();
    }
}
java 复制代码
import org.example.ioc_di.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;


@RequestMapping("/UserController")
@Controller
public class UserController {


    @Autowired
    private UserService userService;

    @RequestMapping("/doController")
    @ResponseBody
    public String doController(){
        return userService.doService();
    }
}

2.构造方法注入

它通过构造方法将所需的依赖项传递给类的属性。

java 复制代码
@RequestMapping("/UserController")
@Controller
public class UserController {

    private UserService userService;

    public UserController(){}

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

    @RequestMapping("/doController")
    @ResponseBody
    public String doController(){
        return userService.doService();
    }
}

注意:当类中只有一个构造方法时,@Autowired 可以省略;但当类中有多个构造方法时,必须使用 @Autowired 来明确指定 Spring 使用哪个构造方法进行依赖注入。如果没有明确指定,Spring 将抛出异常。

3. Setter注入(方法注入)

java 复制代码
@RequestMapping("/UserController")
@Controller
public class UserController {

    private UserService userService;

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

    @RequestMapping("/doController")
    @ResponseBody
    public String doController(){
        return userService.doService();
    }
}

4.三种注入方式的优点和缺点

属性注入(Field Injection)

优点:

简洁,使用方便

缺点:

只能用于 IoC 容器中,如果是非 IoC 容器不可用

并且只有在使用时才会出现空指针异常(NPE)

不能注入一个 final 修饰的属性

构造函数注入(Constructor Injection,Spring 4.x 推荐)

优点:

可以注入 final 修饰的属性

注入的对象不会被修改

依赖对象在使用前一定会被完全初始化(构造方法在类加载阶段执行)

通用性好,构造方法是 JDK 支持的,换框架也通用

缺点:

注入多个对象时,代码会比较繁琐

Setter 注入(Spring 3.x 推荐)

优点:

方便在类实例化之后,重新对该对象进行配置或注入

缺点:

不能注入 final 修饰的属性

注入对象可能会被改动,因为 setter 方法可能会被多次调用,存在被修改的风险

十三.@Autowired存在的问题

存在的问题:

java 复制代码
package org.example.ioc_di.controller;


import org.example.ioc_di.model.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class BeanConfig {

    @Bean
    public String name(){
        return "李四";
    }

    @Bean("u1")
    public User userOne (String name){
        User user = new User();
        user.setName(name);
        user.setAge(30);
        return user;
    }

    @Bean
    public User userTwo (String name){
        User user = new User();
        user.setName(name);
        user.setAge(30);
        return user;
    }
}
java 复制代码
package org.example.ioc_di.service;

import org.example.ioc_di.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    @Autowired
    private User user;

    public String doService(){
        user.setName("张三");
        user.setAge(30);
        return  user.toString();
    }
}
java 复制代码
package org.example.ioc_di.controller;

import org.example.ioc_di.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;


@RequestMapping("/UserController")
@Controller
public class UserController {

    private UserService userService;

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

    @RequestMapping("/doController")
    @ResponseBody
    public String doController(){
        return userService.doService();
    }
}

此时运行代码会报错:

因为此时同一个类型的bean在我们的项目中有多个,我们交给Spring托管,但是当我们和Spring要Bean的时候,Spring蒙了它并不知道此时到底给我们那个,所以此时就需要我们指定Spring给我们那个。

四种解决方法:

1.属性名和需要使用的对象名保持一致:

java 复制代码
import org.example.ioc_di.model.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class BeanConfig {

    @Bean
    public String name(){
        return "李四";
    }

    @Bean("u1")
    public User userOne (String name){
        User user = new User();
        user.setName(name);
        user.setAge(30);
        return user;
    }

    @Bean
    public User userTwo (String name){
        User user = new User();
        user.setName(name);
        user.setAge(30);
        return user;
    }
}
java 复制代码
import org.example.ioc_di.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    @Autowired
    private User userTwo;

    public String doService(){
        userTwo.setName("张三");
        userTwo.setAge(30);
        return  userTwo.toString();
    }
}
java 复制代码
import org.example.ioc_di.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;


@RequestMapping("/UserController")
@Controller
public class UserController {

    private UserService userService;

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

    @RequestMapping("/doController")
    @ResponseBody
    public String doController(){
        return userService.doService();
    }
}

2.使用@Primary注解标识默认的对象

java 复制代码
import org.example.ioc_di.model.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

@Configuration
public class BeanConfig {

    @Bean
    public String name(){
        return "李四";
    }

    @Primary
    @Bean("u1")
    public User userOne (String name){
        User user = new User();
        user.setName(name);
        user.setAge(30);
        return user;
    }

    @Bean
    public User userTwo (String name){
        User user = new User();
        user.setName(name);
        user.setAge(30);
        return user;
    }
}
java 复制代码
import org.example.ioc_di.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    @Autowired
    private User user;

    public String doService(){
        user.setName("张三");
        user.setAge(30);
        return  user.toString();
    }
}
java 复制代码
import org.example.ioc_di.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;


@RequestMapping("/UserController")
@Controller
public class UserController {

    private UserService userService;

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

    @RequestMapping("/doController")
    @ResponseBody
    public String doController(){
        return userService.doService();
    }
}

3.使用@Qualifier注解:指定当前要注入的bean对象。在@Qualifier的value属性中,指定注入的bean的名称。

@Qualifier注解不能单独使用,必须配合@Autowired使用。

java 复制代码
import org.example.ioc_di.model.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

@Configuration
public class BeanConfig {

    @Bean
    public String name(){
        return "李四";
    }

    @Primary
    @Bean("u1")
    public User userOne (String name){
        User user = new User();
        user.setName(name);
        user.setAge(30);
        return user;
    }

    @Bean
    public User userTwo (String name){
        User user = new User();
        user.setName("wuhu");
        user.setAge(30);
        return user;
    }
}
java 复制代码
import org.example.ioc_di.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    @Autowired
    @Qualifier("userTwo")
    private User user;

    public String doService(){
        //user.setName("张三");
        user.setAge(30);
        return  user.toString();
    }
}
java 复制代码
import org.example.ioc_di.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;


@RequestMapping("/UserController")
@Controller
public class UserController {

    private UserService userService;

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

    @RequestMapping("/doController")
    @ResponseBody
    public String doController(){
        return userService.doService();
    }
}

4.使用@Resource注解

java 复制代码
import org.example.ioc_di.model.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

@Configuration
public class BeanConfig {

    @Bean
    public String name(){
        return "李四";
    }

    @Primary
    @Bean("u1")
    public User userOne (String name){
        User user = new User();
        user.setName(name);
        user.setAge(30);
        return user;
    }

    @Bean
    public User userTwo (String name){
        User user = new User();
        user.setName("wuhu");
        user.setAge(30);
        return user;
    }
}
java 复制代码
import org.example.ioc_di.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service
public class UserService {

   @Resource(name = "userTwo")
    private User user;

    public String doService(){
        //user.setName("张三");
        user.setAge(30);
        return  user.toString();
    }
}
java 复制代码
import org.example.ioc_di.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;


@RequestMapping("/UserController")
@Controller
public class UserController {

    private UserService userService;

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

    @RequestMapping("/doController")
    @ResponseBody
    public String doController(){
        return userService.doService();
    }
}

还有一种情况就是,如果存在方法重载的情况,此时Spring给我们Bean是随机的:

java 复制代码
import org.example.ioc_di.model.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

@Configuration
public class BeanConfig {

    @Bean
    public String name(){
        return "李四";
    }

    //@Primary
    //@Bean("u1")
    @Bean
    public User userOne (String name){
        User user = new User();
        user.setName(name);
        user.setAge(30);
        return user;
    }

    @Bean
    public User userOne (){
        User user = new User();
        user.setName("wuhu");
        user.setAge(30);
        return user;
    }
}
java 复制代码
import org.example.ioc_di.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service
public class UserService {

   //@Resource(name = "userTwo")
    @Autowired
    private User user;

    public String doService(){
        //user.setName("张三");
        user.setAge(30);
        return  user.toString();
    }
}
java 复制代码
import org.example.ioc_di.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;


@RequestMapping("/UserController")
@Controller
public class UserController {

    private UserService userService;

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

    @RequestMapping("/doController")
    @ResponseBody
    public String doController(){
        return userService.doService();
    }
}

此时这种情况可以通过给Bean重命名的方法解决。

java 复制代码
import org.example.ioc_di.model.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

@Configuration
public class BeanConfig {

    @Bean
    public String name(){
        return "李四";
    }

    //@Primary
    @Bean("u1")
    //@Bean
    public User userOne (String name){
        User user = new User();
        user.setName(name);
        user.setAge(30);
        return user;
    }

    @Bean
    public User userOne (){
        User user = new User();
        user.setName("wuhu");
        user.setAge(30);
        return user;
    }
}
java 复制代码
import org.example.ioc_di.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service
public class UserService {

   @Resource(name = "userOne")
    private User user;

    public String doService(){
        //user.setName("张三");
        user.setAge(30);
        return  user.toString();
    }
}

@Autowired和 @Resource的区别

@Autowired是Spring框架提供的注释,而@Resource是JDK提供注释

@Autowired默认是按照类型注入,而@Resource是按照名称注入。相比于@Autowired来说,@Resource支持更多的参数设置,例如name设置,根据名称获取Bean。

相关推荐
葫芦和十三4 小时前
图解 MongoDB 21|选举与 failover:Primary 是怎么选出来的
后端·mongodb·agent
GetcharZp5 小时前
26k Star 开源内网穿透神器 NetBird,一分钟实现全球设备互联!
后端
考虑考虑6 小时前
Mybatis实现批量插入
java·后端·mybatis
咖啡八杯6 小时前
GoF设计模式——中介者模式
java·后端·spring·设计模式
lizhongxuan9 小时前
多Agent之间的区别
后端
青石路10 小时前
记一次多JDK版本问题的排查,一坑套一坑,差点没爬上来
java
杨充11 小时前
1.面向对象设计思想
后端
IT_陈寒11 小时前
Java的Date类又坑了我一次,改用时间戳真香
前端·人工智能·后端
systemPro12 小时前
2.6亿条设备数据,历史查询从超时到50ms,我做了什么
后端
要阿尔卑斯吗12 小时前
提示词优化启示:为什么“按顺序输出“比“关键度评分“更有效
后端