[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服务的。

相关推荐
落霞的思绪3 小时前
Mybatis读取PostGIS生成矢量瓦片实现大数据量图层的“快显”
linux·运维·mybatis·gis
diudiu96283 小时前
Maven配置阿里云镜像
java·spring·阿里云·servlet·eclipse·tomcat·maven
222you7 小时前
SpringAOP的介绍和入门
java·开发语言·spring
CodeAmaz7 小时前
Spring编程式事务详解
java·数据库·spring
谷哥的小弟8 小时前
Spring Framework源码解析——RequestContext
java·后端·spring·框架·源码
那我掉的头发算什么8 小时前
【javaEE】UDP与TCP核心原理深度解析:从“不可靠”到“稳如老狗”的进化之路
网络协议·tcp/ip·udp·java-ee·传输层协议
程序员阿鹏9 小时前
SpringBoot自动装配原理
java·开发语言·spring boot·后端·spring·tomcat·maven
老华带你飞9 小时前
工会管理|基于springboot 工会管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·spring
自在极意功。9 小时前
MyBatis配置文件详解:environments、transactionManager与dataSource全面解析
java·数据库·tomcat·mybatis
⑩-9 小时前
SpringCloud-Feign客户端实战
后端·spring·spring cloud