02 Spring-DI

02 Spring-DI,第1张

02 Spring-DI 1 依赖注入 1.1 什么是依赖注入

依赖注入(dependency injection)的意思为,给予调用方它所需要的事物。“依赖”是指可被方法调用的事物。依赖注入形式下,调用方不再直接指使用“依赖”,取而代之是“注入”。"注入”是指将“依赖”传递给调用方的过程。
要想搞清楚这个“依赖注入”就必须理解什么是“依赖”,依赖就是指一个业务功能执行期间所需要的前提,比如:做米饭以米为前提,则做米饭功能依赖为米,汽车需要汽油才能跑起来,则汽车跑依赖于汽油,再比如光头强砍树需要用到电锯,则光头强砍树依赖于电锯。
注入是指被依赖的前提对象获得方式,如果是自己去创建就是“主动获得”,如果是被动得到,就是“注入”得到,比如光头强砍树依赖的电锯是李老板给的,这就是李老板将“电锯”注入给光头强,光头强去砍树。

1.2 @Bean注入

Spring IOC容器提供了依赖注入功能,可以将一个组件依赖的对象,在使用之前注入到合适位置。比如,如下关系模拟了光头强砍树时候依赖电锯:

Spring IOC解决依赖注入,在配置类Config中为Bean组件初始化方法增加参数,Spring IOC容器会在初始化时候自动根据类型注入Bean组件对象,解决“依赖注入”问题:

1.2.1 案例:

创建新的spring项目:

  1. 创建Saw
package cn.tedu.demo;

import java.io.Serializable;

public class Saw implements Serializable{
	
	private String name = "寒冰锯";
	
	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return name;
	}

}
  1. 创建Worker
package cn.tedu.demo;

import java.io.Serializable;

public class Worker implements Serializable{
	
	private String name = "光头强";
	
	public Saw saw;
	
	public void work() {
		System.out.println(name + "使用" + saw + "砍树");
	}

}
  1. 创建配置文件
package cn.tedu.context;

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

import cn.tedu.demo.Saw;
import cn.tedu.demo.Worker;

@Configuration
public class config {
	
	@Bean
	public Saw saw() {
		return new Saw();
	}
	
	@Bean
	
	public Worker worker(Saw s) {
		Worker worker = new Worker();
		worker.saw = s;
		return worker;
	}
}
  1. 测试案例
package cn.tedu.test;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import cn.tedu.context.config;
import cn.tedu.demo.Worker;

public class TestCase {

	AnnotationConfigApplicationContext ctx;

	@Before
	public void init() {
		ctx = new AnnotationConfigApplicationContext(config.class);
	}

	@After
	public void destroy() {
		ctx.close();
	}

	
	@Test
	public void testWorker() {
		Worker worker = ctx.getBean("worker",Worker.class);
		worker.work();
	}
}
  1. 运行结果
光头强使用寒冰锯砍树
1.2.2 Spring IOC组件注入时候组件自动匹配规则:
  1. 首先按照注入参数类型查找相对应类型的Bean组件,如果没有直接报错
  2. 如果在Spring容器中能够匹配上唯一类型的Bean组件,如果没有直接报错
  3. 如果按照类型匹配到俩个Bean组件,则在查找组件ID和变量名是否匹配,如果匹配则注入成功
  4. 如果组件类型和组件ID都不能很好匹配则报错
1.2.3 注入失败案例:
  1. 更新Config.java
package cn.tedu.context;

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

import cn.tedu.demo.Saw;
import cn.tedu.demo.Worker;

@Configuration
public class config {
	
	@Bean
	public Saw saw() {
		return new Saw();
	}
	
	@Bean
	public Saw saw2() {
		return new Saw();
	}
	
	@Bean
	
	public Worker worker(Saw s) {
		Worker worker = new Worker();
		worker.saw = s;
		return worker;
	}
}
  1. 重新运行测试结果
No qualifying bean of type 'cn.tedu.demo.Saw' available: expected single matching bean but found 2: saw,saw2

利用名字匹配案例:

package cn.tedu.context;

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

import cn.tedu.demo.Saw;
import cn.tedu.demo.Worker;

@Configuration
public class config {
	
	@Bean
	public Saw saw1() {
		return new Saw();
	}
	
	@Bean
	public Saw saw2() {
		return new Saw();
	}
	
	@Bean
	
	public Worker worker(Saw saw1) {
		Worker worker = new Worker();
		worker.saw = saw;
		return worker;
	}
}
  1. 重新运行测试结果
光头强使用寒冰锯砍树
2 @Autowired

Spring提供的组件扫描功能,在扫描时候也可以完成依赖注入,这样可以减少编码提高编程效率。
如何使用@Autowired:

2.1 案例: 2.1.1 创建项目
  1. 创建一个新的Spring项目
  2. 电锯类Saw
2.1.2 创建Worker类
package cn.tedu.demo;

import java.io.Serializable;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;


@Component
public class Worker implements Serializable{
	
	private String name = "光头强";
	
	@Autowired
	public Saw saw;
	
	public void work() {
		System.out.println(name + "使用" + saw + "砍树");
	}

}
2.1.3 创建Saw类
package cn.tedu.demo;

import java.io.Serializable;

import org.springframework.stereotype.Component;

@Component
public class Saw implements Serializable{
	
	private String name = "寒冰锯";
	
	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return name;
	}

}
2.1.4 创建配置类
package cn.tedu.context;

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

import cn.tedu.demo.Saw;
import cn.tedu.demo.Worker;

@Configuration
@ComponentScan(basePackages = "cn.tedu.demo")
public class config {

}
2.1.5 测试案例
package cn.tedu.test;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import cn.tedu.context.config;
import cn.tedu.demo.Worker;

public class TestCase {

	AnnotationConfigApplicationContext ctx;

	@Before
	public void init() {
		ctx = new AnnotationConfigApplicationContext(config.class);
	}

	@After
	public void destroy() {
		ctx.close();
	}

	
	@Test
	public void testWorker() {
		Worker worker = ctx.getBean("worker",Worker.class);
		worker.work();
	}
}
2.1.6 测试结果运行
光头强使用寒冰锯砍树
3 set方法注入

刚刚学过@Autowired可以实现对象属性注入,@Autowired也可以标注在set方法上实现set方法注入。set方法也称为Bean属性访问方法,所以set方法注入也称为Bean属性注入。
Spring提供set方法注入的目的是给程序员以更多可以选择的注入方式,程序员可以根据实际情况下选择一种方式注入对象。
使用set方法注入原理:

案例:

  1. 更新Worker
package cn.tedu.demo;

import java.io.Serializable;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;


@Component
public class Worker implements Serializable{
	
	private String name = "光头强";
	public Saw saw;
	@Autowired
	public void setSaw(Saw saw) {
		this.saw = saw;
		System.out.println("setSaw()");
	}
	
	public void work() {
		System.out.println(name + "使用" + saw + "砍树");
	}

}
  1. 重新测试
setSaw()
光头强使用寒冰锯砍树

测试结果中出现setSaw则说明确实是执行了setSaw方法注入的,在实际工作中,可以在此过程中实现一些其他逻辑,而不是直接注入输出,可以使用此方法。

4 IOC/DI解耦 4.1 利用接口解耦

IOC/DI和接口配合可以实现软件组件“解耦”。
要想理解“解耦”,就需要理解什么是耦合性,我们将一个组件依赖另外一个组件的现象称为耦合。组件连接紧密的称为“紧耦合”,反之松散连接关系称为“松耦合”。比如将手机和电池制造为一个整体,则手机和电池是紧密连接关系,是紧耦合;将手机和电池做成可拆卸组合的零件,则手机和电池是松散连接关系,是松耦合。松耦关系带来的好处是显而易见的,可以使组件之间可以重构,重组,重新搭配,就像手机和电池如果是松耦合关系,就可以在电池没有电的时候进行更换,生活中,松耦合关系比比皆是。
将紧密耦合关系改变为松散耦合关系称为“解耦”。
软件组件之间也存在耦合关系,将紧耦合转化为松耦合使软件之间能够更灵活的组合是很有必要的,上述的案例中“工人”只能依赖“电锯”砍树就是一种紧耦合关系,这种方式造成工人无法更换工具砍树。解决办法是利用接口进行抽象设计,这样组件依赖与接口就“解耦”了。

  1. 抽象设计工具接口,作为电锯和斧子的父类型
  2. 电锯和斧子实现工具接口
  3. 工人依赖于工具接口,工人不依赖于具体的工具
  4. 利用IOC管理组件,为工人注入适当的工具

    利用Spring IOC容易提供的DI可以将工具对象注入给工人对象,继而解决对象之间的依赖关系,并且由于依赖接口,所以利用Spring IOC就可以控制组件的组合关系,实现松耦合。

案例:

  1. 创建一个新Spring项目
  2. 声明工具接口
package cn.tedu.demo;

public interface Tool {

}
  1. 声明工人类
package cn.tedu.demo;

import java.io.Serializable;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class Worker implements Serializable{
	
	private String name = "光头强";
	
	@Autowired
	private Tool tool;
	
	public void work() {
		System.out.println(name + "使用" + tool + "砍树");
	}

}
  1. 声明电锯类
package cn.tedu.demo;

import java.io.Serializable;

import org.springframework.stereotype.Component;

//@Component
public class Saw implements Tool{
	
	private String name = "寒冰锯";
	
	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return name;
	}

}
  1. 声明斧子类
package cn.tedu.demo;

import org.springframework.stereotype.Component;

@Component
public class Axe implements Tool{
	
	private String name = "开天斧";
	
	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return name;
	}

}
  1. 配置类
package cn.tedu.context;

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

@Configuration
@ComponentScan(basePackages = "cn.tedu.demo")
public class Config {

}
  1. 测试类
package cn.tedu.text;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import cn.tedu.context.Config;
import cn.tedu.demo.Worker;

public class TestCase {
	
	AnnotationConfigApplicationContext ctx;
	
	@Before
	public void init() {
		ctx = new AnnotationConfigApplicationContext(Config.class);
	}
	
	@After
	public void destroy() {
		ctx.close();
	}
	
	@Test
	public void testWorker() {
		Worker worker = ctx.getBean("worker",Worker.class);
		worker.work();
	}

}
  1. 测试结果
光头强使用开天斧砍树

删除斧子类上的@Component注解,在电锯类上增加@Component注解,再次测试:

光头强使用寒冰锯砍树

说明只需要简单更改配置就可以轻松解决注解之间的耦合关系

4.2 @Autowired注入规则

上述案例中,如果将斧子类和电锯类都加上@Component注解将出现运行错误,原因是违反类@Autowired注解的注解注规则,@Autowired注入规则与@Bean组件注入规则类似:

  1. 首先按照注入参数类型查找相应类型的Bean组件,如果没有直接报错误
  2. 如果在Spring容器中能够匹配上唯一类型的Bean组件,则进行注入成功
  3. 如果按照类型匹配到俩个bean组件,则在查找组件ID和变量名是否匹配,如果匹配则注入成功
  4. 如果组件类型的组件ID都不能很好匹配则报错
    • 如何解决斧子类和电锯类都加上@Component注解将出现运行错误?
      1. 利用组件自定义的ID,当组件的ID设置为too时,@Autowired注解就会根据ID匹配成功
      1. 利用注解@Qualifier(“BeanID”)指定注入Bean组件的ID

案例:将斧子类和电锯类都加上@Component注解

  1. 在斧子类上设置自定义BeanID
package cn.tedu.demo;

import org.springframework.stereotype.Component;

@Component("tool")
public class Axe implements Tool{
	
	private String name = "开天斧";
	
	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return name;
	}

}

测试结果:

光头强使用开天斧砍树
  1. 重构Worker类,设定@Qualifier(“saw”)
package cn.tedu.text;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import cn.tedu.context.Config;
import cn.tedu.demo.Worker;

public class TestCase {
	
	AnnotationConfigApplicationContext ctx;
	
	@Before
	public void init() {
		ctx = new AnnotationConfigApplicationContext(Config.class);
	}
	
	@After
	public void destroy() {
		ctx.close();
	}
	
	@Test
	public void testWorker() {
		Worker worker = ctx.getBean("worker",Worker.class);
		worker.work();
	}

}

测试结果:

光头强使用寒冰锯砍树

以上方式都可以解决同时注入冲突问题,日常使用中根据实际情况使用即可。

4.3 同时使用@Bean和@Component

@Bean组件和@Component注解可以同时使用,都可以在spring容器中创建Bean组件。运行结果没有差异,并且注入时候俩种方式创建的组件也可以相互注入,@Bean声明组件可以注入到@Component声明的组件,反之也可以。

案例:@Bean声明的组件注入到@Component声明的组件

  1. 利用@Bean在配置类Config中声明组件
package cn.tedu.context;



import java.util.Date;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

@Configuration
@ComponentScan(basePackages = "cn.tedu.demo")
public class Config {
	
	@Bean
	public Date currentDate() {
		return new Date();
	}

}
  1. 利用@Component声明组件,注入Date类型组件
package cn.tedu.demo;

import java.util.Date;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class Employee {
	
	private String name = "Tom";
	
	@Autowired
	private Date date;
	
	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return "Employee [name = "+ name + ",date =" + date + "]";
	}

}
  1. 测试案例
	@Test
	public void testEmployee() {
		
		Employee employee = ctx.getBean("employee",Employee.class);
		System.out.println(employee);
	}

测试结果:输出当前系统时间,表示Date注入成功

Employee [name = Tom,date =Mon Jan 03 22:46:10 CST 2022]

案例:@Component声明的组件注入到@Bean声明的组件

  1. 声明Bean类型
package cn.tedu.demo;

public class Dept {
	
	private String name = "教研部";
	
	private Employee manager;
	
	public void serManager(Employee manager) {
		
		this.manager = manager;
		
	}

	
	@Override
	public String toString() {
		return "Dept [name=" + name + ", manager=" + manager + "]";
	}
	
}
  1. 在配置类Config中利用@Bean声明Bean组件
	@Bean
	public Dept dept(Employee employee) {
		Dept dept = new Dept();
		dept.serManager(employee);
		return dept;
	}
  1. 测试案例
	@Test
	public void testDept() {
		
		Dept dept = ctx.getBean("dept",Dept.class);
		System.out.println(dept);
	}
  1. 测试结果
Dept [name=教研部, manager=Employee [name = Tom,date =Mon Jan 03 22:59:11 CST 2022]]
5 使用Properties 5.1 Druid连接池

Druid连接池是阿里巴巴提供开源数据库连接池(https://github.com/alibaba/druid/wiki/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98),是目前被广泛使用的数据库连接池,官方网站是https://github.com/alibaba/druid。
连接池本身也是对象,可以利用Spring IOC容器进行管理,利用Spring IOC管理Druid连接池的步骤是:

  1. 利用Maven导入Druid连接池API和MySql数据库驱动API
  2. 将连接池作为JavaBean在Config.java中配置
  3. 测试连接池是否可用

案例:

  1. 导入连接池
		
			com.alibaba
			druid
			1.2.8
		
		
			mysql
			mysql-connector-java
			8.0.27
		
  1. 配置Config.java
	@Bean(initMethod = "init",destroyMethod = "close")
	public DataSource dataSource() {
		DruidDataSource ds = new DruidDataSource();
		ds.setDriverClassName("com.mysql.jdbc.Driver");
		ds.setUrl("jdbc:mysql://localhost:3306/mysql?characterEncoding=utf8&useSSL=false&rewriteBatchedStatements=true");
		ds.setUsername("root");
		ds.setPassword("root");
		ds.setMaxActive(10);
		ds.setInitialSize(2);
		return ds;
	}
  1. 测试

测试结果:

5.2 @PropertiesSource和Environment

程序参数经常保存在Properties文件中,Spring提供了@PropertiesSource注解用于读取Properties文件,读取后保存到Environment对象中,可以在程序中注入Environment对象,就可以获得配置文件中的信息了。

案例:

  1. 在resource文件夹中编写jdbc.properties文件

  2. 配置Config.java

  3. 测试案例

测试结果:

5.3 @Value

利用@Value可以读取当前系统环境Environment中的信息,注入到变量中,这个方式更加灵活方便。

${}是Spring提供的表达式,这里可以得到配置文件中的属性信息。
案例:

测试案例:

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5695603.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存