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)。这⾥就出现了⼀个"依赖"关系:汽⻋依赖⻋⾝,⻋⾝依赖底盘,底盘依赖轮⼦.
在这里插入图片描述

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
改进之后的控制权发生了反转不是使用方法对象创建控制依赖对象 而是把依赖对象注入当前对象之中
依赖对象的控制权不再由当前类控制 所以当依赖类发生任何改变 当前类都不受影响 这就是控制反转

在这里插入图片描述
具体代码如下

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来管理对象

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来管理对象

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中获取对象

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中获取对象
原先的代码

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;

    }




}

修改后的代码

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

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容器之中那么该如何获取对象呢?

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可以发现

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

实例
在这里插入图片描述

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的代码

package com.example.carioc.demos.web.Service;

import org.springframework.stereotype.Service;

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

运行结果

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(配置存储)

package com.example.carioc.demos.web.Repo;

import org.springframework.stereotype.Repository;

@Repository
public class UserRepository {
    public void sayRepository(){
        System.out.println("Hi Repository");
    }
}

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

package com.example.carioc.demos.web.Component;

import org.springframework.stereotype.Component;

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

运行结果

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的使用

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时发现获取不到

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容器中
例如:

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

结果
在这里插入图片描述

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

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对象

例如

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对象进行重命名

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

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

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={}可以省略写成

   @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 来配置扫描扫描路径
例如

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类中
如下

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

package com.demos.Service;

import org.springframework.stereotype.Service;

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

然后我们获取Controller中sayHi方法

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

}

结果
在这里插入图片描述
构造方法注⼊
构造方法注⼊是在类的构造⽅法中实现注⼊

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

 UserController2 userController2 = context.getBean("userController2",UserController2.class);
        userController2.sayHi();

在这里插入图片描述
Setter 注⼊

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

结果

  UserController3 userController3 = context.getBean("userController3", UserController3.class);
        userController3.sayHi();

在这里插入图片描述
三种注入的区别

属性注入
优点:简洁,使⽤⽅便;
缺点:只能⽤于IoC容器,如果是⾮IoC容器不可⽤,并且只有在使⽤的时候才会出现NPE 不能注⼊⼀个Final修饰的属性

构造函数注⼊(Spring4.X推荐)
优点:可以注⼊final修饰的属性
注⼊的对象不会被修改
缺点:注⼊多个对象时,代码会⽐较繁琐

Setter注⼊(Spring3.X推荐)
优点:⽅便在类实例之后,重新对该对象进⾏配置或者注⼊
缺点:不能注⼊⼀个Final修饰的属性
注⼊对象可能会被改变,因为setter⽅法可能会被多次调⽤,就有被修改的⻛险

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

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

例如:

@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注解修饰的那一个

@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使⽤
例如

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

结果
在这里插入图片描述

@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提供的注解

### IntelliJ IDEA 中通义 AI 功能介绍 IntelliJ IDEA 提供了一系列强大的工具来增强开发体验,其中包括与通义 AI 相关的功能。这些功能可以帮助开发者更高效地编写代并提高生产力。 #### 安装通义插件 为了使用通义的相关特性,在 IntelliJ IDEA 中需要先安装对应的插件: 1. 打开 **Settings/Preferences** 对话框 (Ctrl+Alt+S 或 Cmd+, on macOS)。 2. 导航到 `Plugins` 页面[^1]。 3. 在 Marketplace 中搜索 "通义" 并点击安装按钮。 4. 完成安装后重启 IDE 使更改生效。 #### 配置通义服务 成功安装插件之后,还需要配置通义的服务连接信息以便正常使用其提供的各项能力: - 进入设置中的 `Tools | Qwen Coding Assistant` 菜单项[^2]。 - 填写 API Key 和其他必要的认证参数。 - 测试连接以确认配置无误。 #### 使用通义辅助编程 一旦完成上述准备工作,就可以利用通义来进行智能编支持了。具体操作如下所示: ##### 自动补全代片段 当输入部分语句时,IDE 将自动提示可能的后续逻辑,并允许一键插入完整的实现方案[^3]。 ```java // 输入 while 循环条件前半部分... while (!list.isEmpty()) { // 激活建议列表选择合适的循环体内容 } ``` ##### 解释现有代含义 选中某段复杂的表达式或函数调用,右键菜单里会有选项可以请求通义解析这段代的作用以及优化意见。 ##### 生产测试案例 对于已有的业务逻辑模块,借助于通义能够快速生成单元测试框架及初始断言集,减少手动构建的成本。 ```python def test_addition(): result = add(2, 3) assert result == 5, f"Expected 5 but got {result}" ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值