依赖注入(dependency injection)的意思为,给予调用方它所需要的事物。“依赖”是指可被方法调用的事物。依赖注入形式下,调用方不再直接指使用“依赖”,取而代之是“注入”。"注入”是指将“依赖”传递给调用方的过程。
要想搞清楚这个“依赖注入”就必须理解什么是“依赖”,依赖就是指一个业务功能执行期间所需要的前提,比如:做米饭以米为前提,则做米饭功能依赖为米,汽车需要汽油才能跑起来,则汽车跑依赖于汽油,再比如光头强砍树需要用到电锯,则光头强砍树依赖于电锯。
注入是指被依赖的前提对象获得方式,如果是自己去创建就是“主动获得”,如果是被动得到,就是“注入”得到,比如光头强砍树依赖的电锯是李老板给的,这就是李老板将“电锯”注入给光头强,光头强去砍树。
Spring IOC容器提供了依赖注入功能,可以将一个组件依赖的对象,在使用之前注入到合适位置。比如,如下关系模拟了光头强砍树时候依赖电锯:
Spring IOC解决依赖注入,在配置类Config中为Bean组件初始化方法增加参数,Spring IOC容器会在初始化时候自动根据类型注入Bean组件对象,解决“依赖注入”问题:
创建新的spring项目:
- 创建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; } }
- 创建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 + "砍树"); } }
- 创建配置文件
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; } }
- 测试案例
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.2.2 Spring IOC组件注入时候组件自动匹配规则:
- 首先按照注入参数类型查找相对应类型的Bean组件,如果没有直接报错
- 如果在Spring容器中能够匹配上唯一类型的Bean组件,如果没有直接报错
- 如果按照类型匹配到俩个Bean组件,则在查找组件ID和变量名是否匹配,如果匹配则注入成功
- 如果组件类型和组件ID都不能很好匹配则报错
- 更新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; } }
- 重新运行测试结果
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; } }
- 重新运行测试结果
光头强使用寒冰锯砍树2 @Autowired
Spring提供的组件扫描功能,在扫描时候也可以完成依赖注入,这样可以减少编码提高编程效率。
如何使用@Autowired:
- 创建一个新的Spring项目
- 电锯类Saw
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方法注入原理:
案例:
- 更新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 + "砍树"); } }
- 重新测试
setSaw() 光头强使用寒冰锯砍树
测试结果中出现setSaw则说明确实是执行了setSaw方法注入的,在实际工作中,可以在此过程中实现一些其他逻辑,而不是直接注入输出,可以使用此方法。
4 IOC/DI解耦 4.1 利用接口解耦IOC/DI和接口配合可以实现软件组件“解耦”。
要想理解“解耦”,就需要理解什么是耦合性,我们将一个组件依赖另外一个组件的现象称为耦合。组件连接紧密的称为“紧耦合”,反之松散连接关系称为“松耦合”。比如将手机和电池制造为一个整体,则手机和电池是紧密连接关系,是紧耦合;将手机和电池做成可拆卸组合的零件,则手机和电池是松散连接关系,是松耦合。松耦关系带来的好处是显而易见的,可以使组件之间可以重构,重组,重新搭配,就像手机和电池如果是松耦合关系,就可以在电池没有电的时候进行更换,生活中,松耦合关系比比皆是。
将紧密耦合关系改变为松散耦合关系称为“解耦”。
软件组件之间也存在耦合关系,将紧耦合转化为松耦合使软件之间能够更灵活的组合是很有必要的,上述的案例中“工人”只能依赖“电锯”砍树就是一种紧耦合关系,这种方式造成工人无法更换工具砍树。解决办法是利用接口进行抽象设计,这样组件依赖与接口就“解耦”了。
- 抽象设计工具接口,作为电锯和斧子的父类型
- 电锯和斧子实现工具接口
- 工人依赖于工具接口,工人不依赖于具体的工具
- 利用IOC管理组件,为工人注入适当的工具
利用Spring IOC容易提供的DI可以将工具对象注入给工人对象,继而解决对象之间的依赖关系,并且由于依赖接口,所以利用Spring IOC就可以控制组件的组合关系,实现松耦合。
案例:
- 创建一个新Spring项目
- 声明工具接口
package cn.tedu.demo; public interface Tool { }
- 声明工人类
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 + "砍树"); } }
- 声明电锯类
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; } }
- 声明斧子类
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; } }
- 配置类
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 { }
- 测试类
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(); } }
- 测试结果
光头强使用开天斧砍树
删除斧子类上的@Component注解,在电锯类上增加@Component注解,再次测试:
光头强使用寒冰锯砍树
4.2 @Autowired注入规则说明只需要简单更改配置就可以轻松解决注解之间的耦合关系
上述案例中,如果将斧子类和电锯类都加上@Component注解将出现运行错误,原因是违反类@Autowired注解的注解注规则,@Autowired注入规则与@Bean组件注入规则类似:
- 首先按照注入参数类型查找相应类型的Bean组件,如果没有直接报错误
- 如果在Spring容器中能够匹配上唯一类型的Bean组件,则进行注入成功
- 如果按照类型匹配到俩个bean组件,则在查找组件ID和变量名是否匹配,如果匹配则注入成功
- 如果组件类型的组件ID都不能很好匹配则报错
- 如何解决斧子类和电锯类都加上@Component注解将出现运行错误?
-
- 利用组件自定义的ID,当组件的ID设置为too时,@Autowired注解就会根据ID匹配成功
-
- 利用注解@Qualifier(“BeanID”)指定注入Bean组件的ID
案例:将斧子类和电锯类都加上@Component注解
- 在斧子类上设置自定义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; } }
测试结果:
光头强使用开天斧砍树
- 重构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声明的组件
- 利用@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(); } }
- 利用@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 + "]"; } }
- 测试案例
@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声明的组件
- 声明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 + "]"; } }
- 在配置类Config中利用@Bean声明Bean组件
@Bean public Dept dept(Employee employee) { Dept dept = new Dept(); dept.serManager(employee); return dept; }
- 测试案例
@Test public void testDept() { Dept dept = ctx.getBean("dept",Dept.class); System.out.println(dept); }
- 测试结果
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连接池的步骤是:
- 利用Maven导入Druid连接池API和MySql数据库驱动API
- 将连接池作为JavaBean在Config.java中配置
- 测试连接池是否可用
案例:
- 导入连接池
com.alibaba druid1.2.8 mysql mysql-connector-java8.0.27
- 配置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; }
- 测试
测试结果:
5.2 @PropertiesSource和Environment程序参数经常保存在Properties文件中,Spring提供了@PropertiesSource注解用于读取Properties文件,读取后保存到Environment对象中,可以在程序中注入Environment对象,就可以获得配置文件中的信息了。
案例:
-
在resource文件夹中编写jdbc.properties文件
-
配置Config.java
-
测试案例
测试结果:
5.3 @Value利用@Value可以读取当前系统环境Environment中的信息,注入到变量中,这个方式更加灵活方便。
${}是Spring提供的表达式,这里可以得到配置文件中的属性信息。
案例:
测试案例:
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)