[JavaEE] Spring IoC&DI

目录

[1. IoC&DI入门](#1. IoC&DI入门)

[1.1. 什么是Spring](#1.1. 什么是Spring)

[1.2 什么是IoC](#1.2 什么是IoC)

[1.3 IoC的介绍](#1.3 IoC的介绍)

[1.4 IoC的优势](#1.4 IoC的优势)

[1.5 什么是DI](#1.5 什么是DI)

[2. IoC&DI的使用](#2. IoC&DI的使用)

[3. IoC详解](#3. IoC详解)

[3.1 Bean的存储](#3.1 Bean的存储)

[3.1.1 @Controller(控制器存储)](#3.1.1 @Controller(控制器存储))

[3.1.2 @Service(服务存储)](#3.1.2 @Service(服务存储))

[3.1.3 @Repository(仓库存储)](#3.1.3 @Repository(仓库存储))

[3.1.4 @Component(组件存储)](#3.1.4 @Component(组件存储))

[3.1.5 @Configuration(配置存储)](#3.1.5 @Configuration(配置存储))

[3.2 五个注解总结](#3.2 五个注解总结)

3.3.方法注解@Bean

[3.4 扫描路径](#3.4 扫描路径)

[4. DI详解](#4. DI详解)

[4.1 属性注入](#4.1 属性注入)

[4.2 构造方法注入](#4.2 构造方法注入)

[4.3 Setter方法注入](#4.3 Setter方法注入)

[4.4 三种注入方式的优缺点](#4.4 三种注入方式的优缺点)

[4.5 @Autowired注解存在的问题](#4.5 @Autowired注解存在的问题)

[5. 常见问题](#5. 常见问题)

[5.1 @Autowired注解和@Resource注解的区别](#5.1 @Autowired注解和@Resource注解的区别)

[5.2 Spring & Spring Boot & Spring MVC的区别](#5.2 Spring & Spring Boot & Spring MVC的区别)


1. IoC&DI入门

1.1. 什么是Spring

Spring在广义上来说是一个家族,包含了很多Spring的框架和项目。

在狭义上来说就是Spring Famework,是Spring家族的最底层的项目,它提供了IoC容器,AOP,事务管理,JDBC继承,MVC等功能,其他的项目都是基于Spirng来创建的。

1.2 什么是IoC

IoC是一种思想,就是控制反转的意思,之前我们使用对象时候,需要自己new一个对象去使用,这时候对象的创建和生命周期都受到类的控制,

而现在把创建对象的任务交给了容器,在Spring中就是IoC容器,通过IoC容器来创建对象,此时对象就受到容器的控制,让我们想要使用对象的时候,就可以等待被容器注入对象来使用。

1.3 IoC的介绍

传统情况下,我们想要开发一辆车,需要按照下面过程来制作:

代码如下:

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

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

public class Framework {
    private Bottom bottom;

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

public class Bottom {
    private Tire tire;

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

public class Tire {
    private int size;
    public Tire() {
        this.size = 17;
        System.out.println("tire init size = " + size);
    }
}

我们想要建造一辆车,需要先建造一个车身,建造车身需要先建造底盘,建造底盘需要先建造轮胎,这是传统的代码模式。

但是这种模式,如果我们想要修改轮胎的大小的话,我们就需要通过传参数来修改,并且每个类的构造方法都要添加参数,并且都要修改,如果增加的属性过多,此时参数就会很长,这种方式就很麻烦。

如何解决这种问题呢?

此时我们就可以换一种思路,不再是汽车依赖车身,车身依赖底盘,底盘依赖轮胎,而是反过来操作:

此时我们就不用在每个类里面创建子类,而是通过注入的方式,不同的类生产不同的产品,最后由一个类进行组装,就成为一个汽车,修改后的代码如下:

java 复制代码
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();
    }
}

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

public class Framework {
    private Bottom bottom;

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

public class Bottom {
    private Tire tire;

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

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

此时代码就会变得更加灵活,修改起来也会更加方便。

1.4 IoC的优势

传统造车的过程是:造车依赖车架,车架依赖底盘,底盘依赖轮胎,这种方法相当于是使用方对象创建并控制依赖对象,此时依赖对象是固定的,修改的话,需要经过使用方对象。相当于先造出轮胎,然后根据轮胎来造车,后面修改轮胎属性,此时所有的组件都需要修改。

IoC开发:轮胎依赖于底盘,底盘依赖于车架,车架依赖于车。此时这种方法就通过依赖注入的方法,将依赖对象注入到使用方的对象,此时的依赖对象就不受到使用方对象的控制,依赖类发生变化,不会影响到当前类。相当于先设计车的框架,需要什么型号的材料,就向生产该材料的工厂发布任务,此时改变了轮胎的属性,其他厂商不受影响。

IoC容器的优点:

  1. 资源集中管理,IoC容器会帮我们管理一些资源(对象),这样可以更好的进行资源的配置和管理。

  2. 可以降低调用资源和提供资源的两方对于资源的依赖,可以直接获取资源使用,不需要注意资源的细节。

1.5 什么是DI

DI就是注入**依赖注入,**相当于将容器里面的依赖,提供给需要次依赖的对象中,这个过程就是依赖注入。

2. IoC&DI的使用

Spring容器主要管理的是对象,用Bean来表示对象,由Spring来进行对象的创建和销毁,只需要告诉Spring那些对象需要存,需要使用那些对象。

我们使用**@Component**注解,来将类交给Spring管理。

使用**@Autowired**注解,来获取运行时所依赖的对象。

原来BookController类的代码,这里需要自己创建BookService对象。

java 复制代码
@RequestMapping("/book")
@RestController
public class BookController {

    @RequestMapping("/getList")
    public List<BookInfo> getList() {
        BookService bookService = new BookService();
        return bookService.getList();
    }
}

原来BookService类的代码,这里需要自己创建BookDao对象。

java 复制代码
public class BookService {
    public List<BookInfo> getList() {
        BookDao bookDao = new BookDao();
        //这里没有使用数据库,先mock数据(自己构造虚假数据)
        List<BookInfo> bookInfos = bookDao.mockData();
        //这里返回给前端的图书借阅状态设置成文字
        for(BookInfo book : bookInfos) {
            if(book.getStatus() == 1) {
                book.setStatusCN("可借阅");
            }else {
                book.setStatusCN("不可借阅");
            }
        }
        return bookInfos;
    }
}

原来的BookDao的代码。

java 复制代码
public class BookDao {
    //该方法从mock的数据中查数据
    public List<BookInfo> mockData() {
        List<BookInfo> bookInfos = new ArrayList<>();
        for (int i = 1; i <= 15 ; i++) {
            BookInfo book = new BookInfo();
            book.setId(i);
            book.setBookName("图书" + i);
            book.setAuthor("作者" + i);
            book.setCount(new Random().nextInt(80) + 21); //[20,100]
            book.setPrice(new BigDecimal(new Random().nextInt(80) + 21));
            book.setPublish("图书出版社" + i);
            book.setStatus(i % 5 == 0 ? 2 : 1); //1-可借阅 2-不可借阅
            bookInfos.add(book);
        }
        return bookInfos;
    }
}

我们将上面的对象统一交给Spring管理,修改后的代码:

BookController类的代码,此时我们在使用BookService对象时候,在该引用上面使用@Autowired注解,可以直接从Spring容器里面注入对象来使用。

java 复制代码
@RequestMapping("/book")
@RestController
public class BookController {
    @Autowired
    private BookService bookService;

    @RequestMapping("/getList")
    public List<BookInfo> getList() {
//        BookService bookService = new BookService();
        return bookService.getList();
    }
}

BookService类需要加上@Component注解才能将该类交给Spring来管理去创建对象,在调用BookDao对象时候,使用@Autowired注解来获得BookDao对象使用。

java 复制代码
@Component
public class BookService {
    @Autowired
    private BookDao bookDao;
    public List<BookInfo> getList() {
//        BookDao bookDao = new BookDao();

        //这里没有使用数据库,先mock数据(自己构造虚假数据)
        List<BookInfo> bookInfos = bookDao.mockData();
        //这里返回给前端的图书借阅状态设置成文字
        for(BookInfo book : bookInfos) {
            if(book.getStatus() == 1) {
                book.setStatusCN("可借阅");
            }else {
                book.setStatusCN("不可借阅");
            }
        }
        return bookInfos;
    }
}

这里需要在BookDao类上加@Component注解才能被Spring容器管理。

java 复制代码
@Component
public class BookDao {
    //该方法从mock的数据中查数据
    public List<BookInfo> mockData() {
        List<BookInfo> bookInfos = new ArrayList<>();
        for (int i = 1; i <= 15 ; i++) {
            BookInfo book = new BookInfo();
            book.setId(i);
            book.setBookName("图书" + i);
            book.setAuthor("作者" + i);
            book.setCount(new Random().nextInt(80) + 21); //[20,100]
            book.setPrice(new BigDecimal(new Random().nextInt(80) + 21));
            book.setPublish("图书出版社" + i);
            book.setStatus(i % 5 == 0 ? 2 : 1); //1-可借阅 2-不可借阅
            bookInfos.add(book);
        }
        return bookInfos;
    }
}

3. IoC详解

3.1 Bean的存储

这里的Bean表示的就是对象,在Spring中将对象交给IoC容器来进行管理,此时就需要将对象放到容器里,和从容器中取对象使用,此时Spring框架提供了两类注解:

类注解:@Controller @Service @Repository @Component @Configuration

方法注解:@Bean

3.1.1 @Controller(控制器存储)

此时我们创建一个类:

java 复制代码
@Controller
public class HelloController {
    public void print() {
        System.out.println("hello controller");
    }
}

此时我们如何确定该类被放在IoC容器里面呢?

下面是启动类的代码:

java 复制代码
@SpringBootApplication
public class SpringIocDemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringIocDemoApplication.class, args);
	}

}

上面类因为加了@SpringBootApplication注解,才是启动类,启动类会调用run方法,而run方法的返回值类型是:

返回类型的这个接口又继承于ApplicationContext类:

ApplicationContext类,也就是Spring上下文,存储的是当前代码的运行环境所需要的资源,也是Spring IoC核心的容器,也是用来管理应用程序中被控制反转的对象。

此时我们就可以通过这个类中的方法来获取对象:

这里获取对象的方式有五种,这里我会说明前三种的用法:

java 复制代码
@SpringBootApplication
public class SpringIocDemoApplication {

	public static void main(String[] args) {
		ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
		//通过对象的名字来获取对象
		HelloController bean1 = (HelloController)context.getBean("helloController");
		bean1.print();
		//通过对象的名字和类对象类获取对象
		HelloController bean2 = context.getBean("helloController", HelloController.class);
		bean2.print();
		//通过类对象来获取对象
		HelloController bean3 = context.getBean(HelloController.class);
		bean3.print();
	}
}

这三种方法都可以获取到容器里面的对象。

当我们把对象交给Spring容器进行管理,此时容器里面创建的对象的名字有下面规范:

  • 采用小驼峰的形式作为对象名字。
  • 如果有连续两个大写字母,就采用原来的类名。比如类名:USController,对象名:USController

3.1.2 @Service(服务存储)

创建一个类,使用@Service注解:

java 复制代码
@Service
public class UserService {
    public void print() {
        System.out.println("hello service");
    }
}

使用三种方法获取对象:

java 复制代码
		ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
        //service
		UserService bean4 = (UserService)context.getBean("userService");
		bean4.print();
		UserService bean5 = context.getBean("userService", UserService.class);
		bean5.print();
		UserService bean6 = context.getBean(UserService.class);
		bean6.print();

3.1.3 @Repository(仓库存储)

创建一个类,使用@Repository注解:

java 复制代码
@Repository
public class UserRepository {
    public void print() {
        System.out.println("hello repository");
    }
}

使用三种方法获取对象:

java 复制代码
		ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
		//repository
		UserRepository bean7 = (UserRepository)context.getBean("userRepository");
		bean7.print();
		UserRepository bean8 = context.getBean("userRepository", UserRepository.class);
		bean8.print();
		UserRepository bean9 = context.getBean(UserRepository.class);
		bean9.print();

3.1.4 @Component(组件存储)

创建一个类,使用@Component注解:

java 复制代码
@Component
public class UserComponent {
    public void print() {
        System.out.println("hello component");
    }
}

使用三种方法获取对象:

java 复制代码
		ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
		//component
		UserComponent bean10 = (UserComponent)context.getBean("userComponent");
		bean10.print();
		UserComponent bean11 = context.getBean("userComponent", UserComponent.class);
		bean11.print();
		UserComponent bean12 = context.getBean(UserComponent.class);
		bean12.print();

3.1.5 @Configuration(配置存储)

创建一个类,使用@Configuration注解:

java 复制代码
@Configuration
public class UserConfiguration {
    public void print() {
        System.out.println("hello configuration");
    }
}

使用三种方法获取对象:

java 复制代码
		ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
		//configuration
		UserConfiguration bean13 = (UserConfiguration)context.getBean("userConfiguration");
		bean13.print();
		UserConfiguration bean14 = context.getBean("userConfiguration", UserConfiguration.class);
		bean14.print();
		UserConfiguration bean15 = context.getBean(UserConfiguration.class);
		bean15.print();

3.2 五个注解总结

这五个注解可以在后面括号里设置对象的名字,比如:

这里创建一个类:

java 复制代码
@Controller("abc")
public class HController {
    public void print() {
        System.out.println("hello HController");
    }
}

此时我们就可以使用修改后的对象名字来访问:

java 复制代码
		HController bean16 = (HController)context.getBean("abc");
		bean16.print();

@Controller注解一般用于控制层,接收请求,返回响应,控制层必须使用@Controller注解。

@Service注解一般用于业务逻辑层。

@Repository注解一般用于数据层。

@Component注解一般用于组件层。

@Configuration注解一般用于配置层。

我们可以测试下@Controller和其他注解的区别:

编写一个类:

java 复制代码
@ResponseBody
@Controller
public class UserController {
    @RequestMapping("/h1")
    public String print() {
        return "测试Controller和Service的区别";
    }
}

上面的代码可以正常运行。

如果我们把@Controller注解换成其他注解,这里我用@Service注解举例:

java 复制代码
@ResponseBody
@Service
public class UserController {
    @RequestMapping("/h1")
    public String print() {
        return "测试Controller和Service的区别";
    }
}

此时通过网页访问该资源,会报404错误。

因为被@Controller注解标注的类会被标识为控制器,是用来处理http请求的。

3.3.方法注解@Bean

@Bean是一个方法注解,用来将方法返回的对象交给IoC容器来进行管理,需要和上面五个注解搭配使用,我们使用@Bean注解存入容器的对象的名字默认是方法名。

我们先创建一个Student类:

java 复制代码
@NoArgsConstructor
@AllArgsConstructor
@Component
@Data
public class Student {
    private String name;
    private Integer age;
}

创建一个具有多个student对象的类:

此时通过@Component和@Bean注解将s1和s2两个方法中返回的对象交给容器进行管理,此时容器里面就存在s1和s2两个Student类型的对象。

java 复制代码
@Component
public class StudentComponent {
    @Bean
    public Student s1() {
        return new Student("zhangsan",20);
    }
    @Bean
    public Student s2() {
        return new Student("lisi", 40);
    }
}

此时在启动类里面编写代码:

java 复制代码
		Student bean17 = (Student)context.getBean("student");
		System.out.println(bean17);

运行上面代码,此时会根据student这个名字在容器里面寻找对象,最终找到两个属性为null的对象。

如果修改启动类里面的代码:

java 复制代码
		Student bean18 = context.getBean(Student.class);
		System.out.println(bean18);

此时运行就会报错,因为上面代码是根据Student类型来查找对象的,此时容器里面有三个Student类型对象,此时就不知道选取那个对象来访问。

这里的@Bean注解也可以自己设置对象的名字:

我们修改StudentComponent类里面的代码,在@Bean注解里面添加value属性的值,可以是一个名字,也可以是多个名字。

java 复制代码
@Component
public class StudentComponent {
    @Bean({"s4","s5"})
    public Student s3() {
        return new Student("wangwu", 60);
    }
}

运行下面代码,此时访问的都是s3方法返回的对象:

java 复制代码
		Student bean19 = (Student) context.getBean("s4");
		System.out.println(bean19);
		Student bean20 = (Student) context.getBean("s5");
		System.out.println(bean20);

通过参数注入:

修改StudentComponent类里面的代码:

这里@Bean注解将String类型的属性放到容器里面,此时下面s6方法就会从容器里面找到String类型的对象,来赋值。

java 复制代码
    @Bean
    public String name() {
        return "zhouliu";
    }
    @Bean
    public Student s6(String name) {
        return new Student(name, 80);
    }

修改启动类里面的代码:

运行下面的代码,此时就可以成功运行:

java 复制代码
		Student bean21 = (Student)context.getBean("s6");
		System.out.println(bean21);

3.4 扫描路径

上面的@Bean注解搭配其他注解将对象放到容器里面一定会生效吗,这个是不一定的。

因为这里还涉及到一个Spring的扫描路径的概念,通过ComponentScan注解来设置Spring的扫描路径,比如在启动类加入该注解,此时的扫描路径就是设置的两个包下面的代码,这里也是可以设置一个路径,也可以设置多个路径。

java 复制代码
@ComponentScan({"com.sas.ioc.a","comm.sas.ioc.b"})
@SpringBootApplication
public class SpringIocDemoApplication {
	public static void main(String[] args) {
		ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
    }
}

但是上面我们都没有设置这个注解,但是代码照样正常运行,因为在@SpringBootApplication这个启动类注解中,已经包含了@ComponentScan这个注解,而启动类中的@ComponentScan注解的扫描路径默认是 当前这启动类所在的包下面的所有代码。

4. DI详解

DI就是依赖注入,依赖就相当于一个类需要正常运行,此时里面有个属性,这个属性就是这个类的依赖,如果属性没有赋值,此时该类就不能正常运行。而我们把对象放到IoC容器里面,此时就需要将对象从容器里面取出来进行使用,这个过程就是依赖注入的过程。

而Spring给我们提供了三种依赖注入的方法:

属性注入 构造方法注入 setter方法注入

4.1 属性注入

属性注入通过**@Autowired注解**来实现的。

我们定义一个类,使用@Service注解实现控制反转,把UserService这个类交给Spring管理。

java 复制代码
@Service
public class UserService {
    public void print() {
        System.out.println("hello service");
    }
}

定义一个类,使用@Autowired注解来实现对UserService对象的注入,此时会在Spring容器里面找UserService类型的对象,进行赋值。

java 复制代码
@Component
public class UserComponent {
    //属性注入
    @Autowired
    public UserService userService;
    
    public void print() {
        System.out.println("hello component");
        userService.print();
    }
}

我们在启动类里面编写代码,调用UserComponent类中的print()方法:

此时运行下面代码,就可以成功运行,说明此时@Autowired注解成功的给userService引用赋值:

java 复制代码
		UserComponent bean22 = context.getBean(UserComponent.class);
		bean22.print();

如果我们把@Autowired注解去掉,就会报空指针异常。

4.2 构造方法注入

这里我们创建一个UserCompinent类,里面设置了一个构造方法来进行依赖注入:

java 复制代码
@Component
public class UserComponent {

    //构造方法注入
    public UserService userService;
    
    public UserComponent(UserService us) {
        this.userService = us;
    }

    public void print() {
        System.out.println("hello component");
        userService.print();
    }
}

编写启动类里面的代码,此时代码可以正常运行:

java 复制代码
		UserComponent bean23 = context.getBean(UserComponent.class);
		bean23.print();

如果我们在UserComponent类中加入一个无参的构造方法,此时运行启动类里面的代码就会报错:

java 复制代码
@Component
public class UserComponent {
    //构造方法注入
    public UserService userService;
    
    public UserComponent() {
       
    }
    
    public UserComponent(UserService us) {
        this.userService = us;
    }

    public void print() {
        System.out.println("hello component");
        userService.print();
    }
}

因为Spring在管理对象时候利用的是反射机制来创建对象,如果存在一个构造方法,就会使用这个构造函数创建对象,此时的@Autowired注解可以省略,如果存在多个构造方法,此时创建对象默认使用的是无参的构造方法,此时我们可以在有参的构造方法上面加上@Autowired注解来修改默认的构造方法,此时就可以正常运行,修改成下面的代码:

java 复制代码
@Component
public class UserComponent {
    //构造方法注入
    public UserService userService;

    public UserComponent() {
    }

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

    public void print() {
        System.out.println("hello component");
        userService.print();
    }
}

4.3 Setter方法注入

我们修改UserComponent类里面的代码,这里的setter方法注入也是需要使用@Autowired注解的。

java 复制代码
@Component
public class UserComponent {
    //setter方法注入
    public UserService userService;

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

    public void print() {
        System.out.println("hello component");
        userService.print();
    }
}

4.4 三种注入方式的优缺点

属性注入:

编码比较简单,但是不能注入final修饰的属性,只能在Spring boot项目使用。

构造方法注入:

可以注入final修饰的属性,注入后的对象不能被修改,可以适用于多个框架使用,但是如果注入对象过多,代码就会繁琐。

setter方法注入:

类在实例化之后,可以重新对该对象进行注入,但是不能注入final修饰的属性,并且有可能被多次调用,依赖注入的对象容易变化。

4.5 @Autowired注解存在的问题

该注解是根据数据类型在容器里面寻找对象的,如果一个类有多个对象,那么这个注解就会找不到应该注入那个对象。

我们编写一个StudentComponent类,将s3方法返回的对象交给Spring容器管理。

java 复制代码
@Component
public class StudentComponent {
    @Bean({"s4","s5"})
    public Student s3() {
        return new Student("wangwu", 60);
    }
}

在另外一个类里面依赖注入,此时可以成功注入,因为此时容器里面只有一个Student类型的对象。

java 复制代码
@Controller
public class HelloController {
    @Autowired
    private Student s3;
    
    public void print() {
        System.out.println("hello controller");
        System.out.println(s3);
    }
}

如果我们修改StudentComponent类的代码,此时再进行依赖注入就会报错,因为此时容器里面有两个Student类型的对象,此时就不知道注入那个。

java 复制代码
@Component
public class StudentComponent {
    @Bean({"s4","s5"})
    public Student s3() {
        return new Student("wangwu", 60);
    }
    @Bean
    public String name() {
        return "zhouliu";
    }
    @Bean
    public Student s6(String name) {
        return new Student(name, 80);
    }
}

会有下面的报错:

里面的Action提供了两种解决方法:

第一种:使用@Primary注解,来设置默认的注入对象,此时就不会报错了

java 复制代码
@Component
public class StudentComponent {
    @Primary
    @Bean({"s4","s5"})
    public Student s3() {
        return new Student("wangwu", 60);
    }
    @Bean
    public String name() {
        return "zhouliu";
    }
    @Bean
    public Student s6(String name) {
        return new Student(name, 80);
    }
}

第二种:使用@Qualifier注解,这个注解是用来设需要注入的对象的名字的和@Autowired注解搭配使用,此时也是可以运行的。

java 复制代码
@Controller
public class HelloController {
    @Qualifier("s4")
    @Autowired
    private Student s3;

    public void print() {
        System.out.println("hello controller");
        System.out.println(s3);
    }
}

其实还有第三种解决方法:

我们可以使用jakarta包里面的一个注解@Resource来进行依赖注入,并且指定注入对象的名字的,这里要使用该注解里面的name属性。

java 复制代码
@Controller
public class HelloController {
    @Resource(name = "s4")
    private Student s3;

    public void print() {
        System.out.println("hello controller");
        System.out.println(s3);
    }
}

5. 常见问题

5.1 @Autowired注解和@Resource注解的区别

@Autowired注解是Spring提供的,@Resource注解是jdk提供的。

@Autowired注解是根据类型去注入的,@Resource注解是根据名称来注入的,@Resource注解包含的属性比@Autowired注解多,比如:name属性可以设置注入对象的名字。

@Resource注解实际上是根据类型+名字来进行注入的。

@Autowired注解的注入顺序:

首先按照类型查找bean,没有bean抛异常,只有一个bean就直接注入,有多个bean,再查看是否使用了@Qualifier注解,使用了就根据@Qualifier注解的属性来查找,找到就注入,找不到就抛异常,没有使用@Qualifier注解,就根据名称去查找,找到就注入,找不到就抛异常。

5.2 Spring & Spring Boot & Spring MVC的区别

Spring是一个开发应用的框架,Spring Boot和Spring MVC都属于Spring,Spring Boot是基于Spring实现的一个用于快速开发应用的框架,Spring MVC是Spring的一个子框架,用来提供Web服务的。

相关推荐
阿杰 AJie17 小时前
Token 管理工具
java·spring
czlczl2002092518 小时前
从 SSO 登录到跨系统资源访问:OAuth2 全链路交互详解
java·spring boot·后端·spring·架构
memgLIFE18 小时前
mybatis数据库查询
数据库·oracle·mybatis
我命由我1234518 小时前
Android Jetpack Compose - TopAppBar、BottomAppBar、Scaffold
android·java·java-ee·kotlin·android studio·android jetpack·android-studio
廋到被风吹走18 小时前
【Spring】IoC容器深度解析:Bean生命周期与循环依赖三级缓存
java·spring·缓存
我命由我1234519 小时前
Android Studio - Android Studio 去除 import 的未使用的类
android·java·ide·学习·java-ee·android studio·学习方法
drebander19 小时前
MyBatis-Plus saveBatch 在异步线程中事务未提交问题排查与修复
数据库·mybatis
用户02033886131421 小时前
手写Spring(2):实现AOP与JdbcTemplate
spring
用户02033886131421 小时前
手写Spring框架(3):实现MVC
spring