自学内容网 自学内容网

SpringIoc&DI

SpringIoc&DI

一。SpringIOC

Spring是一个包含众多工具IOC容器

Spring的两个核心思想:IOC和AOP

Spring容器装的是对象

IOC:控制反转(控制权反转),其主要目的就是解耦合,就是把创建对象的控制权交给了Spring

就例如一下代码:完成一个汽车的制造(车->车身->底盘->轮子)

//main方法
public static void main(String[] args){
  Car car=new Car();
  car.run();
}
//Car方法
public class Car{
  private FrameWork frameWork;
  public Car(){
    frameWork=new FrameWork();
    System.out.println("car init....");
  }
  public void run{
    System.out.println("car run....");
  }
}
//车身(FrameWork)
public class FrameWork{
  private Bottom bottom;
  public FrameWork(){
    bottom=new Bottom();
    System.out.println("bottom init...")
  }
}
//底盘(Bottom)
public class Bottom{
  private Tire tire;
  public Bottom(){
    tire=new Tire();
    System.out.println("bottom init...");
  }
}
//轮子(Tire)
public class Tire{
  private int size=32;
  public Tire(){
    System.out,println("Tire init");
  }
}

这种是没用IOC思想的写法,它有很大的缺陷,假设这时候有一个新需求,要20中不同size的车轮,以这种写法就无法实现代码的复用,要创建20个这样的代码,然后手动更改size的值,当然也有办法让它实现复用

//main方法
public static void main(String[] args){
  Scanner sc=new Scanner(System.in);
  int size=sc.nextInt();
  Car car=new Car(size);
  car.run();
}
//Car方法
public class Car{
  private FrameWork frameWork;
  public Car(int size){
    frameWork=new FrameWork(size);
    System.out.println("car init....");
  }
  public void run{
    System.out.println("car run....");
  }
}
//车身(FrameWork)
public class FrameWork{
  private Bottom bottom;
  public FrameWork(int size){
    bottom=new Bottom(size);
    System.out.println("bottom init...")
  }
}
//底盘(Bottom)
public class Bottom{
  private Tire tire;
  public Bottom(int size){
    tire=new Tire(int size);
    System.out.println("bottom init...");
  }
}
//轮子(Tire)
public class Tire{
  private int size;
  public Tire(int size){
    System.out,println("Tire init");
  }
}

这个代码就比第一个好了很多,但是我们会发现,随着size这个参数加入到Tire这个类中的构造方法之中,那么其他的调用Tire类的方法就也要加上size这个参数,这样耦合性就会非常的高, 假如后面要加更多的参数,那么这个改动的地方就会非常多,因此有了IOC思想就可以降低耦合性

分析上面的代码,size起其实只作用在Tire(轮子)那个方法中,那么是否有办法让其他方法不变,只改变Tire这一个方法呢

就相当于这么一个逻辑,车能否不创建车身了,而是车身自己创建好,提供给车,同理车身能否不创建底盘了,而是底盘自己创建好提供给车,以此类推


//Car方法
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....");
  }
}
//车身(FrameWork)
public class FrameWork{
  private Bottom bottom;
  public FrameWork(Bottom bottom){
    this.bottom=bottom;
    System.out.println("bottom init...")
  }
}
//底盘(Bottom)
public class Bottom{
  private Tire tire;
  public Bottom(Tire tire){
    this.tire=tire;
    System.out.println("bottom init...");
  }
}
//轮子(Tire)
public class Tire{
  private int size;
  private String color;
  
  public Tire(int size,String color){
    this.size=size;
    this.color=color;
    System.out,println("Tire init...size:"+size+",color:"+color);
  }
}
//main方法
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();
  }
}

这个代码就相当于站在Bottom的角度上,不管Tire类发生什么变化,我只要Tire给我就行,你给我什么我用什么,相当于间接的封装

这样写的话如果哪个方法想加新元素,只需要改变main方法和目标方法,耦合性大大降低

Main方法就是被Spring管理,其他的方法我们自己写,告诉Spring帮我们创建哪些对象

Spring帮我们管理对象:

(1)告诉Spring,帮我们管理哪些对象(存)

(2)知道这些对象是怎么取出来的(取)

二。DI

IOC是一种思想, DI是一种实现方式

(1)取对象

@RequestMappint("/book")
@RestController
public class BookController{
  @Autowired
  private BookService bookservice;
  
  @RequestMapping("/getBookList")
  public List<BookInfo> getBookList(){
    //获取图书的数据
    //对图书的数据进行处理
    //返回数据
    List<BookInfo> bookInfo=bookService.getBookList();
    return bookInfo;
  }
}

@Autowired
private BookService bookservice;

告诉Spring,从容器中取出这个对象,赋值给当前对象的属性

@Autowired
  private BookService bookservice;
//其实上面这个注解是和下面这串代码是等价的
private BookService bookservice;
public BookService(){
  bookeService=new BookService();
}

(2)存对象

@Component
public class BookService{
  private BookDao bookdao=new BookDao();
  public List<BookInfo> getBookList(){
    List<BookInfo> bookInfos=bookdao.mockData();
    //......
  }
}

@Component注解就是存对象的写法

//同样上述代码中的private BookDao bookdao=new BookDao();也可写成
@Autowired
private BookDao bookDao;
并且在BookDao类前加上@Component注解

三。IOC详解

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

1.Bean存储:

在之前的案例中,要把某个对象交给IOC容器管理,要加上一个注解:@Component,而Spring框架为了更好的服务Web应用程序,提供了更丰富的注解

共有两类注解可以实现:

类注解:@Controller,@Service,@Repository,@Compont,@Configuration

方法注解:@Bean

1.1类注解:
(1)@Controller

DemoApplication类:

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

这里的getBean就是从context中拿到bean(对象)

UserController类:

@Controller
public class UserController {
    public void doController(){
        System.out.println("do controller");
    }
}
(2)@Service(服务存储)

UserService类:

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

DemoApplication类:

public class DemoApplication {
public static void main(String[] args) {
ApplicationContext context= SpringApplication.run(DemoApplication.class, args);
UserService bean=context.getBean(UserService.class);
bean.doService();
}
}

这只是一种获取bean的方式,其实还有很多种

//根据名称获取bean
UserService userService2=(UserService)context.getBean("userService");

getBean括号中的内容就是bean的名称,一般bean名称都是小驼峰的形式(首字母小写,中间拼接的单词大写),把类的大驼峰形式的名称改为小驼峰就成了bean的名称

//根据名称和类型获取bean
UserService userService3=context.getBean("userService",UserService.class);
userService3.doService();

注意:特殊情况:如果类名前两位都是大写,那么bean的名称为类名

无论使用哪种方法获取Bean,最底层调用的都是BeanFactory

(3)@Repository(仓库存储)

DemoApplication类:

public class DemoApplication {
public static void main(String[] args) {
ApplicationContext context= SpringApplication.run(DemoApplication.class, args);
UserService bean=context.getBean(UserService.class);
bean.doRepository();
}
}

UserService类:

@Repository
public class UserService {
    public void doRepository(){
        System.out.println("do Repository");
    }
}
(4)@Component(组件存储)

DemoApplication类:

public class DemoApplication {
public static void main(String[] args) {
ApplicationContext context= SpringApplication.run(DemoApplication.class, args);
UserService bean=context.getBean(UserService.class);
bean.doComponent();
}
}

UserService类:

@Component
public class UserService {
    public void doComponent(){
        System.out.println("do Component");
    }
}
(5)Configuration(配置存储)

DemoApplication类:

public class DemoApplication {
public static void main(String[] args) {
ApplicationContext context= SpringApplication.run(DemoApplication.class, args);
UserService bean=context.getBean(UserService.class);
bean.doConfiguration();
}
}

UserService类:

@Configuration
public class UserService {
    public void doConfiguration(){
        System.out.println("do Configuration");
    }
}

其实观察代码可以发现,这五个注解实际上写法都是一样的,无非就是换一个注解名,然后方法名换一下;实际上这些注解就相当于不同地区的车牌,用来区分不同地区的车辆;映射到代码层面,就是来区分这串代码是数据层还是控制层还是其他层,可以让程序员们清楚的知道这串代码是干什么的

五大注解只能加在类上,并且只能加在自己的代码上,如果引入第三方jar包,也希望交给Spring管理,是没法加五大注解的;想要解决这个问题就要引入方法注解:@Bean

1.2方法注解:

类注解是添加到某个类上的,但是存在两个问题

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

(2)一个类需要多个对象,比如多个数据源

这种场景就要用到方法注解:@Bean

1.一个类中有多个对象

创建UserInfo类:

public class UserInfo {
    private Integer id;
    private String name;
    private Integer age;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

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

创建BeanConfig类:

@Component
public class BeanConfig {
    @Bean
    public UserInfo userInfo(){
        UserInfo userInfo=new UserInfo();
        userInfo.setId(1);
        userInfo.setName("zhangsan");
        userInfo.setAge(12);
        return userInfo;
    }
  
    @Bean
    public UserInfo userInfo2(){
        UserInfo userInfo2=new UserInfo();
        userInfo2.setId(2);
        userInfo2.setName("lisi");
        userInfo2.setAge(20);
        return userInfo2;
    }
}

注意:@Bean必须和五大注解搭配使用才行

当一个类中存在多个Bean时,就不能使用类型来获取对象了

DemoApplication类:

@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
ApplicationContext context= SpringApplication.run(DemoApplication.class, args);
UserInfo userInfo= (UserInfo) context.getBean("userInfo2");
System.out.println(userInfo);
}
}

还一种获取对象的方法:

UserInfo userInfo=context.getBean("userInfo2",UserInfo.class);
2.@Bean传递参数
    @Bean
    public String name(){
        return "zhangsan";
    }
    @Bean
    public String name2(){
        return "lisi";
    }
    @Bean
    public UserInfo userInfo(String name){
        UserInfo userInfo=new UserInfo();
        userInfo.setId(1);
        userInfo.setName(name);
        userInfo.setAge(12);
        return userInfo;
    }

如果需要Bean的类型对应的对象只有一个时,就直接赋值,如果有多个时,通过名称去匹配

userInfo方法中括号的内容是从上面那个name方法拿的,规则就是根据名称来拿

四。DI详解

依赖注入是一个过程,是指IOC容器在创建Bean时,去提供运行中所依赖的资源,而资源就指的是对象,在前面DI部分,我们使用@Autowired这个注解完成依赖注入的操作

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

这里的“依赖注入”也可被称为“对象注入”或者“属性装配”

对于依赖注入,Spring提供了三种方法:

(1)属性注入 (2)构造方法注入 (3)Setter注入

1.属性注入:

创建一个UserController类

@Controller
public class UserController {
    @Autowired
    private UserService userService;
    public void doController(){
        userService.doRepository();
        System.out.println("do Controller");
    }
}

这里的@Autowired注解就是对线main这个UserService属性进行注入

UserService叫类型,userService叫名称

如果UserService这个类型中存在多个对象时,那么优先匹配名称与对象相同的,如果名称都对不上,就报错

UserService类

@Repository
public class UserService {
    public void doRepository(){
        System.out.println("do Repository");
    }
}

DemoApplication类

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

2.构造方法注入:

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

如果存在多个构造函数的时候,要加@Autowired,注明调用的哪个构造函数,如果只有一个构造函数,@Autowired可以省略掉

3.setter方法注入:

private UserService us;
@Autowired
public void setUs(UserService us){
  this.us=us;
}

4.@Autowired存在问题和解决方法:

就像上面属性注入的时候描述的那样,当程序中同一个类型有多个对象,使用@Autowired有可能会报错

解决方法:

(1)属性名和你需要的对象名保持一致

这种办法虽然事最简单的,但是实际上不好完成,因为两者耦合性太高,如果忘记了就会使整个程序报错

(2)使用@Primary来标识默认对象

字面上Primary是优先的意思,其实就是给对象进行优先修饰,如果名称不匹配,那么默认调这个对象

    @Primary
    @Bean
    public UserInfo userInfo2(){
        UserInfo userInfo2=new UserInfo();
        userInfo2.setId(2);
        userInfo2.setName("lisi");
        userInfo2.setAge(20);
        return userInfo2;
    }
(3)使用@Qualifier

UserController类:

@Controller
public class UserController {
    @Qualifier("userInfo1")
    @Autowired
    private UserInfo userInfos;
    public void doController(){
        System.out.println(userInfos.toString());
    }
}

UserInfo类:

public class UserInfo {
    private Integer id;
    private String name;
    private Integer age;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

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

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

BeanConfig类:

package com.example.demo;

import org.apache.catalina.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

@Component
public class BeanConfig {
    @Bean
    public String name(){
        return "zhangsan";
    }
    @Bean
    public String name2(){
        return "lisi";
    }
    @Bean
    public UserInfo userInfo1(){
        UserInfo userInfo=new UserInfo();
        userInfo.setId(1);
        userInfo.setName("zhangsan");
        userInfo.setAge(12);
        return userInfo;
    }
    @Bean
    public UserInfo userInfo2(){
        UserInfo userInfo2=new UserInfo();
        userInfo2.setId(2);
        userInfo2.setName("lisi");
        userInfo2.setAge(20);
        return userInfo2;
    }
}

这里的@Qualifier就是在指定使用,下面那行private UserInfo userInfos;实际上调用的是BeanConfig类中的userInfo1

这种@Qualifier原理就是指定使用对象,并且一定要和@Autowired一起使用,缺一个都会报错

(4)使用@Resource

UserController类:

@Controller
public class UserController {
    @Resource(name="userInfo2")
    private UserInfo userInfos;
    public void doController(){
        System.out.println(userInfos.toString());
    }
}

其他类不变,代码和上面的一样

这个@Resource注解实际上和@Qualifier一样,都是指定对象,只是它括号内是name=“xxx”的形式,而且不于@Autowired一起使用

补充:@Autowired和@Resource的区别:

(1)@Autowired是Spring框架的注解,而@Resource是JDK提供的注解

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

总结一句:上面IOC详解就是Bean(对象)的存,DI详解就是Bean(对象)的取


原文地址:https://blog.csdn.net/Eliaukqwe/article/details/143661028

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!