SpringBoot学习笔记——第二节,依赖注入与控制反转

 Spring框架有四大原则:

       ·使用POJO进行轻量级与最小侵入式开发

       ·通过依赖注入和基于接口编程实现松耦合

       ·通过AOP和默认习惯进行声明式编程

       ·通过AOP和模板减少模式化代码

 

    那么什么是依赖注入与控制反转呢?我们先通过一个例子来理解一下

    首先我们有一个类叫做Student,里面有两个成员变量分别是id和name,并提供给他们get和set方法

public class Student {
	private int id;                                                                    //学生ID
	private String name;                                                               //学生姓名
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	
}

然后我们有另外一个类叫做StudentManager

public class StudentManager {
	
	Student student;                                                    //StudentManager管理了一个Student的对象
	
	public void setStudent(Student student) {                           //提供SetStudent方法添加依赖
		this.student = student;
	}
	
	public void show() {
		System.out.println(student.getId());
		System.out.println(student.getName());
	}
}

    这个StudentManager的类有个成员是Student的一个对象,然后它的Show方法能够打印这个student的 id 以及 name,并提供了setStudent方法初始化Student对象。我们可以说,StudentManager是依赖于Student的。

    但是一个问题就出现了,StudentManager与Student之间的耦合非常紧密,假如我们还没有来的及对StudentManager的student绑定对象,却调用了show方法的话,那么程序将会抛出空指针异常。

    这是我们开发过程中最不希望看到的结果,所以Spring提供了一套叫做控制反转与依赖注入这套机制,目的就是为了解耦。

    在Spring中,你不需要自己创建对象,你只需要告诉Spring,哪些类我需要创建出对象,然后在启动项目的时候Spring就会自动帮你创建出该对象,并且只存在一个类的实例。这个类的实例在Spring中被称为Bean。而这种模式,我们称之为“单例模式”。也就是一个类只有一个实例的意思。

    那么Spring是靠什么来了解究竟哪些类需要帮我们创建呢,这里介绍最常用的两种方式——JAVA注解配置,JAVA代码配置。之前的还有XML配置,groovy配置等,但是我们现在已经不推荐使用了。

    首先我们要介绍的,是JAVA注解配置,这是最简单也是最常用的一种方法。

    我们先来看看声明注解的语法:

                声明                                    含义        

       @Component                            当前类是组件,没有明确的意思

       @Service                                    当前类在业务逻辑层使用

       @Repositorty                             当前类在数据访问层使用

       @Controller                               当前类在展现层(MVC)使用    

    以上四种声明方式效果完全一致,使用不同的关键词是为了给阅读的人能够快速了解该类属于哪一层

    使用方法为:在定义类前使用该注解。让我们看下面一段代码

import org.springframework.stereotype.Component;    //使用了@Component了以后eclipse会自动帮我们导入该包

@Component                                          //这是在告诉Spring,在创建项目时要自动帮忙创建这个类的Bean
public class Student {
	private int id;
	private String name;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	
}

    我们在刚才的Student类前面,加上了@Component注解,成功告诉Spring:你要在项目创建运行时帮我创建Student类的Bean(对象)。

    OK,添加“依赖”我们已经做完了,是不是很简单呢。   

     但是还没完,我们虽然让Spring帮我们创建了对象,但是StudentManager怎么知道这个对象在哪呢?它根本就不知道你有这么一个Bean(对象)啊。

    所以接下来,我们要告诉StudentManager刚才Spring帮我们创建的Bean(对象)到底在哪,也就是“注入”这个Bean。

    我们来看看注入注解的语法:

                声明                                    含义       

        @Autowired                    Spring为这个变量注入一个Bean

        @Inject                            翻译为“注入”最易懂的注入注解

        @Resource                      翻译为“资源”,可以理解为Spring往里面注入的时一个资源

    以上四种声明方式效果完全一致,使用不同的关键词是为了给阅读的人能够快速了解注入的Bean到底是个什么东西。

    使用方法为:在我们需要注入依赖的成员变量前使用该注解。让我们看下面一段代码

import org.springframework.beans.factory.annotation.Autowired;    //这是使用@Autowired自动导入的包
import org.springframework.stereotype.Component;                  //这是使用@Component自动导入的包  

@Component
public class StudentManager {
	@Autowired                                                //要求Spring帮我们注入一个Bean
	Student student;

	public void show() {
		System.out.println(student.getId());
		System.out.println(student.getName());
	}
}

    我们可以看到,在声明成员变量Student的前面我们使用了@Autowired,所以Spring会自动帮我们注入一个Bean,我们就再也不用担心忘记绑定对象而出现空指针啦。

    但是这还没完,虽然我们告诉了Spring哪些类是需要添加依赖,哪些类是需要注入Bean,但是Spring还需要我们做一次配置,来真正完成这样一个操作。

    下面我们将介绍Spring的第一种配置方式:JAVA注解配置

    创建配置类的方法为:在该类前使用@ComponentScan语句,声明这个类是一个配置类。一般来说,一个Spring项目只有一个配置类。然后我们要用@ComponentScan("包路径")来扫描需要注入Bean的包。

    我们看下面一段代码:

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration                        //告诉Spring这是一个配置类
@ComponentScan("com.example.demo")    //配置com.example.demo包

public class DIConfig {

}

    这样就万事具备只欠东风啦,让我们让这个Spring项目真正跑起来吧

    我们都知道,要让项目跑起来我们需要一个主类,并运行Main函数,我们这里直接使用java/main下自动创建的VadaskiSpring1Application类来作为主函数

    

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

@SpringBootApplication
public class VadaskiSpring1Application {

	public static void main(String[] args) {
		
		AnnotationConfigApplicationContext context = 
				new AnnotationConfigApplicationContext(DIConfig.class);     //获取Spring项目的对象
		StudentManager studentManager = context.getBean(StudentManager.class);
		studentManager.student.setId(0);                                            //我们给这个Student对象id属性设置为0
		studentManager.student.setName("LitaVadaski");                              //我们给这个Student对象name属性设置为LitaVadaski
		studentManager.show();                                                      //我们让StudentManager去Show出来
		context.close();
	}
}

    context就是Spring项目的一个对象,我们将测试类的类名告诉Spring,所以这个对象创建的时候就会自动帮我们注入Bean。

    既然Spring已经帮我们创建了Bean,那我们怎么获得每个类的Bean(对象)呢

    我们这里使用了Spring对象的getBean方法,这个方法就能获取Spring帮我们创建的Bean,之后我们就可以用这个对象来做一些事情啦!

    我这里干了三件事:

        ·给这个Student对象id属性设置为0

        ·给这个Student对象name属性设置为LitaVadaski

        ·让StudentManager去Show出来

    我们来看看运行截图

SpringBoot学习笔记——第二节,依赖注入与控制反转_第1张图片

我们看到成功Show出来我们的 id 以及  name!Spring成功跑起来啦~

是不是巨简单呢?

第二种配置方法:使用Spring自动注入Bean的第二种配置方式:JAVA代码配置

我们刚才通过在类名前面添加注解

 @Component  @Service   @Repositorty  @Controller   来告诉Spring这个类需要你来帮我们创建Bean

那么假如我们这个Bean想要初始化该怎么办呢,Spring怎么知道你要初始化哪些值呢?

所以接下来我们要介绍的是Spring的第二种配置方法——JAVA代码配置,它的优势在于更加灵活,我们可以定义创建Bean的时候到底要创建什么样的Bean,由我们在 配置类 中手工定义。

首先我们创建类的时候,就不用再定义类前添加注解

//取消掉了自动加入依赖的注解
public class Student {
	private int id;
	private String name;
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}
//取消掉了自动加入依赖的注解
public class StudentManager {
	//取消了自动注入Bean的注解
	Student student;
	//手工创造一个注入依赖的方法
	public void setStudent(Student student) {
		this.student = student;
	}
	
	public void show() {
		System.out.println(student.getId());
		System.out.println(student.getName());
	}
}
    我们看到,在Student类中我们取消了自动加入依赖的注解

    在StudentManager类中我们取消了自动加入依赖的注解,并取消了自动注入Bean的注解

    但是StudentManager类却新增加了一个手工注入Student的Bean的方法——setStudent()

    然后我们来看配置类应该如何配置

    我们在配置类中,手动写入需要注入的类的构造方法,并使用@Bean告诉Spring要添加这个

    

@Configuration
public class DIConfig {
	@Bean
	public Student student() {
		return new Student();
	}
	
	@Bean
	public StudentManager studentManager() {
		StudentManager SM = new StudentManager();			//先创建出一个需要的StudentManager对象
		Student student = student();					//再创建我们需要注入的Bean,这是并不是调用Student的构造方法
		student.setId(-1);  					        //自定义要注入的Bean的属性
		student.setName("Undefined");
		SM.setStudent(student);						//注入Bean
		return SM;							//返回该对象,由@Bean注解将该返回值加入到IOC容器中
	}
}
@Bean注解的实质是将方法的返回值添加到IOC容器中,并且该实例的id就是方法名。

·单例模式

    我们是通过写构造方法,并在构造方法中注入Bean来实现Spring的自动依赖注入,但是我们注意这行代码

Student student = Student();

    看上去我们是调用了Student的构造方法,我们来看刚才写的构造方法


@Bean
	public Student student() {
		return new Student();
	}

    好像每次调这个方法都会new一个新的Student对象,但是实际上并不是这样。

    Spring实现了一种机制,叫做单例模式

    什么叫单例呢?

    单例其实就是一个类,只创建一个实例,也就是一个对象

    而我们刚才调用这个Student()构造方法的时候,实际上并不会真正去调用Student的构造方法,而是去绑定了Spring为我们创建出来的Student的Bean。Spring实现了一种机制, 能保证这种单例模式存在。

    也就是说,在Spring中,构造方法只能被调用一次,以后的构造方法调用都会自动绑定Spring创建的Bean,而不是new新对象。

    我们可以看到我们在构造StudentManager对象的时候注入的是一个 id=-1 且 name = Undefined 的Student对象 

    我们在Main中输出一下

    

@SpringBootApplication
public class VadaskiSpring1Application {

	public static void main(String[] args) {
		
		AnnotationConfigApplicationContext context = 
				new AnnotationConfigApplicationContext(DIConfig.class);
		StudentManager studentManager = context.getBean(StudentManager.class);    //我们让Spring给我们一个StudentManager的Bean
		studentManager.show();//我们用Show去输出StudentManager管理的的name和id
		Student student = context.getBean(Student.class);                         //我们让Spring给我们一个Student的Bean
		System.out.println(student.getId());                                      //让这个Student的Bean输出一下你的id
		System.out.println(student.getName());                                    //让这个Student的Bean输出一下你的name
		student.setId(1);					                  //我们修改一下Student的Bean的id
		studentManager.show();				                          //让StudentManager去Show一下你依赖的Student
		context.close();
	}
}


我们来看看输出结果

SpringBoot学习笔记——第二节,依赖注入与控制反转_第2张图片

注意:我修改的是Studnt的Bean的id为1,然后让StudentManager的Bean去Show

          这里可以看到,StudentManager的Bean依赖的Student Bean的id已经由 -1 变成了 1

          那么我们可以证明,Student管理的Bean 和 StudentManager依赖的Bean是同一个Bean

单例模式得以证明。

    以上内容就是Spring得依赖注入,那什么是控制反转呢

    控制反转得意思是指,调用者不再手动创建一个类得实例(对象/Bean),而是由Spring来帮我们创建。

    我们得控制反转其实就是依赖于依赖注入得以实现。我们在上面得Main中,直接使用得getBean(类名.class)来获得一个实例,而这个实例是由Spring来帮我们创建的。这,就是控制反转。

预告:

    下一节我们将讲解Spring中的AOP思想,以及如何在Spring中使用AOP


你可能感兴趣的:(JAVA,Spring)