Spring IoC和DI

前言

Spring是一个开源框架 可以让我们更简单的开发 用更具体的话说Spring就是一个包含了众多工具方法的IoC容器

什么是容器呢?

容器就是用来容纳某些物品的;

例如

List数据存储容器

Tomcat是web容器

什么是IoC?

在我们使用注解@RestController和@Controller时就是把这个对象交给了Spring管理Spring框架启动时就会加载这个类,我们把对象交给Spring管理这件事情就是Ioc思想

IoC:Inversion of Control(控制反转)就是Spring是一个控制反转的容器

控制反转就是控制权反转 指的是获取依赖对象的过程被反转了当某个对象在传统开发中需要自己new创建对象,现在就把创建对象的任务交给容器, 程序中只需要依赖注⼊(DependencyInjection,DI)就可以了然后我们就把这个容器称:IoC容器 Spring是一个IoC容器有时也称为Spring容器

通过一个例子来理解IoC容器

我们在现实造一辆车先设计轮⼦(Tire),然后根据轮⼦的⼤⼩设计底盘(Bottom),接着根据底盘设计⻋⾝(Framework),最后根据⻋⾝设计好整个汽⻋(Car)。这⾥就出现了⼀个"依赖"关系:汽⻋依赖⻋⾝,⻋⾝依赖底盘,底盘依赖轮⼦.

java 复制代码
package com.example.carioc.demos.web;

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

我们看起来代码没问题 但是要对程序进行修改的时候

比如我们要加一个int size 后面的代码都需要修改

以上程序的问题是:当最底层代码改动之后,整个调⽤链上的所有代码都需要修改.

为了解决这个方法

我们尝试换⼀种思路,我们先设计汽⻋的⼤概样⼦,然后根据汽⻋的样⼦来设计⻋⾝,根据⻋⾝来设计底盘,最后根据底盘来设计轮⼦.这时候,依赖关系就倒置过来了:轮⼦依赖底盘,底盘依赖⻋⾝,⻋⾝依赖汽⻋

IoC程序开发

传统的代码创建对象是:Car->Framework->Bottom->Tire

改进之后创建对象的顺序是:Tire->Bottom->Framework->Car

改进之后的控制权发生了反转不是使用方法对象创建控制依赖对象 而是把依赖对象注入当前对象之中

依赖对象的控制权不再由当前类控制 所以当依赖类发生任何改变 当前类都不受影响 这就是控制反转

具体代码如下

java 复制代码
package com.example.carioc.demos.web;

public class IoCNewCarExample {
    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("Framework 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容器

IoC容器的优点

(1) 资源集中管理:实现资源的可配置和易管理 IoC容器会帮我们管理⼀些资源(对象等),我们需要使⽤时,只需要从IoC容器中去取就可以了

(2)在创建实例的时候不用了解之中的细节 降低了依赖程度 实现了低耦合

什么是DI

DI:DependencyInjection(依赖注⼊)

容器在运行期间动态的为应用程序提供运行时所需要依赖的资源 称为依赖注入

程序运行时需要某个资源 此时容器就为程序提供这个资源

依赖注⼊(DI)和控制反转(IoC)是从不同的⻆度的描述的同⼀件事情,就是指通过引⼊IoC容器,利⽤依赖关系注⼊的⽅式,实现对象之间的解耦。

在上面的代码就是通过构造函数的方式把依赖对象注入到使用对象之中

这里的Botton对象需要依赖Tire类然后我们使用构造函数将依赖Tier类的对象注入到Botton的对象之中

IoC 是⼀种思想,也是"⽬标",⽽思想只是⼀种指导原则,最终还是要有可⾏的落地⽅案,⽽DI就属于

具体的实现。所以也可以说,DI是IoC的⼀种实现

IoC和DI的使用

对于spring是一个IoC容器 就具备两个基础功能
存和取

例子:

通过一个图书项目完成Controller层,Service层,Dao层的解耦

我们把 Service层及Dao层的实现类 给Spring管理使用注解 @Component

在Controller层和Service层 注入运行时依赖的对象 使用注解@Autowired

把BookDao交给Spring管理,由Spring来管理对象

java 复制代码
package com.example.book.dao;

import com.example.book.model.BookInfo;
import org.springframework.stereotype.Component;


import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

@Component
public class BookDao {
    //获取图书信息
    public List<BookInfo> mockData() {
        List<BookInfo> books = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            BookInfo book = new BookInfo();
            book.setId(i);
            book.setBookName("书籍" + i);
            book.setAuthor("作者" + i);
            book.setCount(i * 5 + 3);
            book.setPrice(new BigDecimal(new Random().nextInt(100)));
            book.setPublish("出版社" + i);
            book.setStatus(1);
            books.add(book);
        }
        return books;
    }
}

把BookService交给Spring管理,由Spring来管理对象

java 复制代码
package com.example.book.service;

import com.example.book.dao.BookDao;
import com.example.book.model.BookInfo;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
public class BookService {
    public List<BookInfo> getBookList(){
        BookDao bookDao = new BookDao();
        List<BookInfo> books = bookDao.mockData();
        //处理页面展示
        for(BookInfo book : books){
            if(book.getStatus() == 1){
                book.setStatusCN("可借阅");
            }else {
                book.setStatusCN("不可借阅");
            }
        }
        return books;
    }

}

删除创建BookDao的代码,从Spring中获取对象

java 复制代码
package com.example.book.service;

import com.example.book.dao.BookDao;
import com.example.book.model.BookInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
public class BookService {
    @Autowired
    private BookDao bookDao;
    public List<BookInfo> getBookList(){
        List<BookInfo> books = bookDao.mockData();
        //处理页面展示
        for(BookInfo book : books){
            if(book.getStatus() == 1){
                book.setStatusCN("可借阅");
            }else {
                book.setStatusCN("不可借阅");
            }
        }
        return books;
    }

}

删除创建BookService的代码,从Spring中获取对象

原先的代码

java 复制代码
package com.example.book.demos.web.controller;


import com.example.book.model.BookInfo;
import com.example.book.service.BookService;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


import java.util.List;


@RequestMapping("/book")
@RestController
public class BookController {
    @RequestMapping("/getList")
    public List<BookInfo> getList(){

        BookService bookService = new BookService();
        List<BookInfo> books = bookService.getBookList();
        return books;

    }




}

修改后的代码

java 复制代码
package com.example.book.demos.web.controller;


import com.example.book.model.BookInfo;
import com.example.book.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


import java.util.List;


@RequestMapping("/book")
@RestController
public class BookController {
    @Autowired
    private BookService bookService;
    @RequestMapping("/getList")
    public List<BookInfo> getList(){
        
        List<BookInfo> books = bookService.getBookList();
        return books;

    }

    

}

这样我们就完成了Controller层,Service层,Dao层的解耦

@Component 是对类的使用

@Autowired 是对注入运行时依赖的对象使用

IoC之Bean的存储

前面提到IoC控制反转将对象的控制权交给Spring的IOC容器由IOC容器创建及管理对象。这就是bean的存储

我们要把每个对象交给IoC容器管理 需要在类上加一个注解 @Component

Spring框架为了更好的服务web应⽤程序,提供了更丰富的注解

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

(1) 类注解:@Controller @Service @Repository @Component @Comfiguration

(2)方法注解 @Bean

@Controller

java 复制代码
package com.example.carioc.demos.web;

import org.springframework.stereotype.Controller;

@Controller //将对象存储到Spring中
public class UserController {
    public  void sayHi(){
        System.out.println("hi,UserController...");
    }
}

我们将这个对象存在Spring容器之中那么该如何获取对象呢?

java 复制代码
package com.example.carioc;

import com.example.carioc.demos.web.UserController;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

@SpringBootApplication
public class CarIoCApplication {

    public static void main(String[] args) {

        ApplicationContext context = SpringApplication.run(CarIoCApplication.class, args);
        UserController bean = context.getBean(UserController.class);
        bean.sayHi();
    }

}

ApplicationContext :Spring上下⽂

当我们运行时 就可以看到Spring获取Controller对象

然后指向Controller的sayHi方法

如果这时我们把@Controller去掉再看结果

它就会显示报错 表示:找不到类型 com.example.carioc.demos.web.UserController的bean

获取bean的其他方式

上面的代码是根据对象来查找对象的 如果Spring容器中同一个类型存在多个bean怎么办呢? ApplicationContext 提供了其他获取bean的方式ApplicationContext 获取bean对象的功能式

父类BeanFactory提供的功能 我们查看源码getBean可以发现

java 复制代码
public interface BeanFactory {
    String FACTORY_BEAN_PREFIX = "&";
	// 1. 根据bean名称获取bean
    Object getBean(String var1) throws BeansException;
	 // 2. 根据bean名称和类型获取bean
    <T> T getBean(String var1, Class<T> var2) throws BeansException;
	 // 3. 按bean名称和构造函数参数动态创建bean,只适⽤于具有原型(prototype)作⽤域的bean
    Object getBean(String var1, Object... var2) throws BeansException;
	 // 4. 根据类型获取bean
    <T> T getBean(Class<T> var1) throws BeansException;
	 // 5. 按bean类型和构造函数参数动态创建bean, 只适⽤于具有原型(prototype)作⽤域的bean
    <T> T getBean(Class<T> var1, Object... var2) throws BeansException;
    //下面省略....
 }

1 2 4 种获取的bean是一样的

1 2根据名称来获取对象
Bean命名约定

程序开发⼈员不需要为bean指定名称(BeanId),如果没有显式的提供名称(BeanId),Spring容器将为该

bean⽣成唯⼀的名称命名约定使⽤Java标准约定作为实例字段名.也就是说,bean名称以⼩写字⺟开头,然后使⽤驼峰式⼤⼩写.

例如:

类名 UserController Bean的名称为:userController

类名 AccountManager Bean的名称为:accountManager

类名:AccountService, Bean的名称为:accountService

特殊情况当第一个字和第二个字都是大写时

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

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

实例

java 复制代码
package com.example.carioc;

import com.example.carioc.demos.web.UserController;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

@SpringBootApplication
public class SpringIocDemoApplication {
    public static void main(String[] args) {
        //获取 Spring 上下⽂对象

        ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class);
        //根据bean类型 从Spring上下文获取
        //从 Spring 上下⽂中获取对象
        UserController userController = context.getBean(UserController.class);
        //根据bean名称 从Spring上下文获取
        UserController userController1 =(UserController) context.getBean("userController");
        //根据bean类型+名称 从Spring上下文获取
        UserController userController2 = context.getBean("userController", UserController.class);
        System.out.println(userController);
        System.out.println(userController1);
        System.out.println(userController2);
    }
}

我们运行发现它们的地址一样说明时同一个地址 所以是一个对象

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

ApplicationContext 和 BeanFactory

继承关系和功能⽅⾯来说:

Spring容器有两个顶级的接⼝:BeanFactory和ApplicationContext 属于 BeanFactory 的⼦类,它除了继承了BeanFactory的所有功能之外它还拥有独特的特性,还添加了对国际化⽀持、资源访问⽀持、以及事件传播等⽅⾯的⽀持

从性能⽅⾯来说:

ApplicationContext是⼀次性加载并初始化所有的Bean对象,而BeanFactory 是需要那个才去加载那个,因此更加轻量.(空间换时间)

注解@Service(服务存储)

使用@Service存储bean的代码

java 复制代码
package com.example.carioc.demos.web.Service;

import org.springframework.stereotype.Service;

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

运行结果

java 复制代码
package com.example.carioc;

import com.example.carioc.demos.web.Controller.UserController;
import com.example.carioc.demos.web.Service.UserService;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

@SpringBootApplication
public class SpringIocDemoApplication2 {
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(SpringIocDemoApplication2.class,args);
        UserController bean = context.getBean(UserController.class);
        bean.doController();

        UserService userService = context.getBean(UserService.class);
        userService.doService();

    }
}

其他几个注解

@Repository(仓库存储)

@Component(组件存储)

@Configuration(配置存储)

java 复制代码
package com.example.carioc.demos.web.Repo;

import org.springframework.stereotype.Repository;

@Repository
public class UserRepository {
    public void sayRepository(){
        System.out.println("Hi Repository");
    }
}
java 复制代码
package com.example.carioc.demos.web.Configuration;

import org.springframework.context.annotation.Configuration;

@Configuration
public class UserConfiguration {
    public void sayConfiguration(){
        System.out.println("Hi Configuration");
    }
}
java 复制代码
package com.example.carioc.demos.web.Component;

import org.springframework.stereotype.Component;

@Component
public class UserComponent {
    public void sayHi(){
        System.out.println("Hi Component");
    }
}

运行结果

java 复制代码
package com.example.carioc;

import com.example.carioc.demos.web.Component.UserComponent;
import com.example.carioc.demos.web.Configuration.UserConfiguration;
import com.example.carioc.demos.web.Controller.UserController;
import com.example.carioc.demos.web.Repo.UserRepository;
import com.example.carioc.demos.web.Service.UserService;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

@SpringBootApplication
public class SpringIocDemoApplication2 {
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(SpringIocDemoApplication2.class,args);
        UserController bean = context.getBean(UserController.class);
        bean.doController();

        UserService userService = context.getBean(UserService.class);
        userService.doService();

        UserComponent userComponent = context.getBean(UserComponent.class);
        userComponent.sayHi();

        UserRepository userRepository = context.getBean(UserRepository.class);
        userRepository.sayRepository();

        UserConfiguration userConfiguration = context.getBean(UserConfiguration.class);
        userConfiguration.sayConfiguration();
        
    }
}

结果

为什么要这么多注解?

目的就是让程序员看到注解能了解当前类的用途

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

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

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

@Configuration:配置层.处理项⽬中的⼀些配置信息

我们查看这些注解源码可以发现




这些注解都有注解@Component 这说明它们都是属于 @Component的子类 @Component是一个元注解可以注解其他类注解

@Controller @Service @Repository是@Component的衍生注解

方法注解@Bean

类注解是添加到某个类上面 但是有两个问题

(1)使用外部包⾥的类, 没办法添加类注解

(2)⼀个类,需要多个对象,⽐如多个数据源

这时我们就需要注解@Bean

@Bean的使用

java 复制代码
package com.example.carioc.demos.web.Component;

import com.example.carioc.demos.web.User;
import org.springframework.context.annotation.Bean;

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

但是我们想获取取的Bean的user时发现获取不到

java 复制代码
package com.example.carioc;

import com.example.carioc.demos.web.Controller.UserController;
import com.example.carioc.demos.web.Service.UserService;
import com.example.carioc.demos.web.User;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

@SpringBootApplication
public class CarIoCApplication {

    public static void main(String[] args) {

        ApplicationContext context = SpringApplication.run(CarIoCApplication.class, args);
        User user = context.getBean(User.class);
        System.out.println(user);
    }

}

这是因为方法类注解要配合类注解使用

在Spring框架的设计中,⽅法注解@Bean 要配合上面的五大类注解才能将对象正常的存储到Spring容器中

例如:

java 复制代码
package com.example.carioc.demos.web.Component;

import com.example.carioc.demos.web.User;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

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

结果

那么对于同一个类如何定义多个对象呢?

java 复制代码
package com.example.carioc.demos.web.Component;

import com.example.carioc.demos.web.User;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

@Component
public class BeanConfig {
    @Bean
    public User user(){
        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(20);
        return user;
    }
}

我们运行可以发现

期望结果只有一个匹配但是却发现了两个

user user2

我们可以根据名称来获取bean对象

例如

java 复制代码
package com.example.carioc;

import com.example.carioc.demos.web.Controller.UserController;
import com.example.carioc.demos.web.Service.UserService;
import com.example.carioc.demos.web.User;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

@SpringBootApplication
public class CarIoCApplication {

    public static void main(String[] args) {

        ApplicationContext context = SpringApplication.run(CarIoCApplication.class, args);
        User user = (User) context.getBean("user");
        User user2 = (User) context.getBean("user2");
        System.out.println(user);
        System.out.println(user2);
    }

}

然后根据名称就能获取多个对象

重命名Bean

可以通过属性name来对Bean对象进行重命名

java 复制代码
 @Bean(name = {"u1","user"})
    public User user(){
        User user = new User();
        user.setName("zhangsan");
        user.setAge(18);
        return user;
    }

此时使用u1就可以获取User对象

java 复制代码
package com.example.carioc;

import com.example.carioc.demos.web.Controller.UserController;
import com.example.carioc.demos.web.Service.UserService;
import com.example.carioc.demos.web.User;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

@SpringBootApplication
public class CarIoCApplication {

    public static void main(String[] args) {

        ApplicationContext context = SpringApplication.run(CarIoCApplication.class, args);
        User user = (User) context.getBean("u1");
        //User user2 = (User) context.getBean("user2");
        System.out.println(user);
        //System.out.println(user2);
    }

}

name={}可以省略写成

java 复制代码
   @Bean({"u1","user"})
    public User user(){
        User user = new User();
        user.setName("zhangsan");
        user.setAge(18);
        return user;
    }

扫描路径

使用四个注解声明的bean不一定会生效,想要生效还得需要Spring扫描

当我们修改项目工程的目录结构时

然后再运行代码

这时就会报错 被告知没有发现u1名称的bean

为什么会没有发现呢?

这是因为使用五大注解声明的bean要想生效就需要配置扫描路径 让Spring扫描这些注解

通过@ComponentScan 来配置扫描扫描路径

例如

java 复制代码
package com.demos.Controller;


import com.demos.User;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScans;


@ComponentScan("com.demos")
@SpringBootApplication
public class CarIoCApplication {

    public static void main(String[] args) {

        ApplicationContext context = SpringApplication.run(CarIoCApplication.class, args);
        User user = (User) context.getBean("u1");
        //User user2 = (User) context.getBean("user2");
        System.out.println(user);
        //System.out.println(user2);
    }

}

在通过扫描之后就可以执行成功了

为什么在之前没有配置@ComponentScan注解也可以成功呢?

这是因为 @ComponentScan 注解虽然没有显式配置,但是实际上已经包含在了启动类声明注解

我们查看注解源码@SpringBootApplication可以发现

注解@ComponentScan已经包含在其中了

然后默认扫描的范围是SpringBoot启动类所在包及其⼦包

在配置类上添加@ComponentScan注解,该注解默认会扫描该类所在的包下所有的配置类

所以我们推荐把启动类放在包路径之下 这样就可以让我们定义的bean都可以扫描到

DI 详解

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

简单来说就是把对象取出来放入某个类的属性之中

关于依赖注入有三种方法

属性注⼊(FieldInjection)

构造⽅法注⼊(ConstructorInjection)

Setter 注⼊(SetterInjection)

属性注⼊(FieldInjection)

使用注解@Autowired实现例如

例如

Service类注⼊到Controller类中

如下

java 复制代码
package com.demos.Controller;

import com.demos.Service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class UserController {
    //注入方法属性注入
    @Autowired
    private UserService userService;
    public  void doController(){
        System.out.println("hi,UserController...");
        userService.doService();
    }
}
java 复制代码
package com.demos.Service;

import org.springframework.stereotype.Service;

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

然后我们获取Controller中sayHi方法

java 复制代码
package com;

import com.demos.Controller.UserController;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

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

}

结果

构造方法注⼊

构造方法注⼊是在类的构造⽅法中实现注⼊

java 复制代码
package com.demos.Controller;

import com.demos.Service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class UserController2 {
    private  UserService userService;
    //注入方法2:构造方法
    @Autowired
    public UserController2(UserService userService){
        this.userService = userService;
    }
    public void sayHi(){
        System.out.println("hi,UserController2...");
        userService.doService();
    }
}
java 复制代码
 UserController2 userController2 = context.getBean("userController2",UserController2.class);
        userController2.sayHi();


Setter 注⼊

java 复制代码
package com.demos.Controller;

import com.demos.Service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class UserController3 {
    private UserService userService;
    @Autowired
    public void setUserService(UserService userService){
        this.userService = userService;
    }
    public void sayHi(){
        System.out.println("hi,UserController3...");
        userService.doService();
    }
}

结果

java 复制代码
  UserController3 userController3 = context.getBean("userController3", UserController3.class);
        userController3.sayHi();


三种注入的区别

属性注入

优点:简洁,使⽤⽅便;

缺点:只能⽤于IoC容器,如果是⾮IoC容器不可⽤,并且只有在使⽤的时候才会出现NPE 不能注⼊⼀个Final修饰的属性
构造函数注⼊(Spring4.X推荐)

优点:可以注⼊final修饰的属性

注⼊的对象不会被修改

缺点:注⼊多个对象时,代码会⽐较繁琐
Setter注⼊(Spring3.X推荐)

优点:⽅便在类实例之后,重新对该对象进⾏配置或者注⼊

缺点:不能注⼊⼀个Final修饰的属性

注⼊对象可能会被改变,因为setter⽅法可能会被多次调⽤,就有被修改的⻛险

当我们同一个类型出现多个bean是使用注解@Autowired就会出现问题

例如

java 复制代码
package com.demos.Component;

import com.demos.User;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

@Component
public class BeanConfig {
    @Bean("u1")
    public User user(){
        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(20);
        return user;
    }
}


这里表示了无法自动连接bean以为User类型有两个分别是"u1" 和"user"

这时我们如何做区分呢?

我们就需要使用到三个注解分别是

@Primary

@Qualifier

@Resource

使用@Primary当多个相同类型的Bean注入时 加上@Primary注解之后 就表明这个Bean是默认的获取bean会自动获取@Primary修饰的bean

例如:

java 复制代码
@Component
public class BeanConfig {
    @Primary
    @Bean("u1")
    public User user(){
        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(20);
        return user;
    }
}

然后我们使用启动类来查看运行结果就会发现它获取的bean是使用@Primary注解修饰的那一个

java 复制代码
@SpringBootApplication
public class CarIoCApplication {

    public static void main(String[] args) {

        ApplicationContext context = SpringApplication.run(CarIoCApplication.class, args);
        UserController userController = context.getBean(UserController.class);
        userController.doController();

    }

}

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

例如:

使用@Resource注解:是按照bean的名称进行注入通过name属性指定要注入的bean名称

注意:@Qualifier注解不能单独使⽤,必须配合@Autowired使⽤

例如

java 复制代码
@Controller
public class UserController {
    //注入方法属性注入
    @Autowired
    private UserService userService;
    //注入user
    @Qualifier(value = "user2")
    @Autowired
    private User user;
    public  void doController(){
        System.out.println("hi,UserController...");
        userService.doService();
        System.out.println(user);
    }
}

结果

java 复制代码
@Controller
public class UserController {
    //注入方法属性注入
    @Autowired
    private UserService userService;
    //注入user
    @Resource(name = "user2")
    private User user;
    public  void doController(){
        System.out.println("hi,UserController...");
        userService.doService();
        System.out.println(user);
    }
}

@Autowird与@Resource的区别

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

@Autowired是spring框架提供的注解,⽽@Resource是JDK提供的注解

相关推荐
小鑫记得努力1 分钟前
Java类和对象(下篇)
java
binishuaio5 分钟前
Java 第11天 (git版本控制器基础用法)
java·开发语言·git
zz.YE7 分钟前
【Java SE】StringBuffer
java·开发语言
老友@7 分钟前
aspose如何获取PPT放映页“切换”的“持续时间”值
java·powerpoint·aspose
颜淡慕潇16 分钟前
【K8S问题系列 |1 】Kubernetes 中 NodePort 类型的 Service 无法访问【已解决】
后端·云原生·容器·kubernetes·问题解决
wrx繁星点点22 分钟前
状态模式(State Pattern)详解
java·开发语言·ui·设计模式·状态模式
Upaaui25 分钟前
Aop+自定义注解实现数据字典映射
java
zzzgd81625 分钟前
easyexcel实现自定义的策略类, 最后追加错误提示列, 自适应列宽,自动合并重复单元格, 美化表头
java·excel·表格·easyexcel·导入导出
友善的鸡蛋26 分钟前
解决:使用EasyExcel导入Excel模板时出现数据导入不进去的问题
java·easyexcel·excel导入
星沁城27 分钟前
240. 搜索二维矩阵 II
java·线性代数·算法·leetcode·矩阵