共计 8467 个字符,预计需要花费 22 分钟才能阅读完成。
0 前言
Spring 的外围就是提供了一个 IoC(Inversion of Control)容器,它能够治理所有轻量级的 JavaBean 组件,提供的底层服务包含组件的生命周期治理、配置和组装服务、AOP 反对,以及建设在 AOP 根底上的申明式事务服务等。
本本次要展现 IoC 容器对 JavaBean 拆卸,以及依赖的注入的几种形式。
看本文之前请务必学习 JAVA 根底。
1 传统的实现形式
先来看上面这个例子:
class Person {
private String name;
public Person(){};
public Person(String name) {this.name = name;}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
}
class Dance {public void do_dance(Person person){System.out.println(person.getName()+"is dancing!");
}
}
class Sing {public void do_sing(Person person){System.out.println(person.getName()+"is sing!");
}
}
public class Test {public static void main(String[] args) {Person person=new Person("Ming");
new Sing().do_sing(person);
new Dance().do_dance(person);
}
}
Dance 与 Sing 组件的实例化都依赖于 Person 组件的实例化,通过被动构建的形式实现组件的实例化以及依赖关系的构建。在大型项目中,这种写法有以下毛病:
- 一个组件如果要应用另一个组件,必须先晓得如何正确地创立它,治理起来麻烦。
- 随着更多的组件被引入,组件的依赖层级变深,组织起来太简单。
- 有些组件须要销毁以便开释资源,但如果该组件被多个组件共享,如何确保它的应用方都曾经全副被销毁?
- 组件相互依赖,测试艰难。
如果一个零碎有大量的组件,其生命周期和相互之间的依赖关系如果由组件本身来保护,岂但大大增加了零碎的复杂度,而且会导致组件之间极为严密的耦合,继而给测试和保护带来了极大的艰难。
为解决这个问题,提出了 IoC 模式,在 IoC 模式下,控制权产生了反转,即从应用程序转移到了 IoC 容器,所有组件不再由应用程序本人创立和配置,而是由 IoC 容器负责,这样,应用程序只须要间接应用曾经创立好并且配置好的组件。为了能让组件在 IoC 容器中被“拆卸”进去,须要某种“注入”机制,依据操作的不同能够分为下文的几种。
2 基于 XML 配置的依赖注入
在 XML 中定义 Bean 并交代组件之间的依赖关系。
Pom 配置如下:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.itranswarp.learnjava</groupId>
<artifactId>spring-ioc-appcontext</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<java.version>11</java.version>
<spring.version>5.2.3.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
</project>
代码构造如下:
对应的代码如下:
Person
package model;
public class Person {
private String name;
private int age;
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public int getAge() {return age;}
public void setAge(int age) {this.age = age;}
}
Dance
package model;
public class Dance {
Person person;
public void setPerson(Person person){this.person=person;}
public void do_dance(){System.out.println(person.getName()+"is dancing!");
}
}
Sing
package model;
public class Sing {
Person person;
public void setPerson(Person person) {this.person = person;}
public void do_sing(){System.out.println(person.getName()+"is sing!");
}
}
Dance、Sing
本人并不会创立 Person
,而是期待内部通过set
办法来注入一个Person
:
Bean 的定义交给 XML
<?xml version="1.0" encoding="utf-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="dance" class="model.Dance">
<property name="person" ref="person"/>
</bean>
<bean id="sing" class="model.Sing">
<property name="person" ref="person"/>
</bean>
<bean id="person" class="model.Person">
<property name="name" value="Ming"/>
</bean>
</beans>
Main 中通过 context 来拜访 XML 文件
import model.Dance;
import model.Person;
import model.Sing;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {public static void main(String[] args) {ApplicationContext context=new ClassPathXmlApplicationContext("Application.xml");
Dance dance=context.getBean(Dance.class);
dance.do_dance();
Sing sing=context.getBean(Sing.class);
sing.do_sing();
Person person=context.getBean(Person.class);
person.setName("Tom");
Dance dance_=context.getBean(Dance.class);
dance_.do_dance();
Sing sing_=context.getBean(Sing.class);
sing_.do_sing();}
}
运行后果
Ming is dancing!
Ming is sing!
Tom is dancing!
Tom is sing!
应用 XML 配置的长处是所有的 Bean 都能高深莫测地列出来,并通过配置注入能直观地看到每个 Bean 的依赖。它的毛病是写起来十分繁琐,每减少一个组件,就必须把新的 Bean 配置到 XML 中。
3 基于 Java 配置的依赖注入
文件构造如下:
基于 Java 的配置文件(AppConfig),代替了 XML
Person
package model;
public class Person {
private String name;
private int age;
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public int getAge() {return age;}
public void setAge(int age) {this.age = age;}
}
Dance
package model;
public class Dance {
Person person;
public Dance(Person person) {this.person=person;}
public void do_dance(){System.out.println(person.getName()+"is dancing!");
}
}
Sing
package model;
public class Sing {Person person=new Person();
public void setPerson(Person person){this.person=person;}
public void do_sing(){System.out.println(person.getName()+"is sing!");
}
}
APPConfig
import model.Dance;
import model.Person;
import model.Sing;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class APPConfig {
@Bean
public Person person(){Person person=new Person();
person.setName("Ming");
return person;
}
@Bean // 通过结构器的注入依赖
public Dance dance(){return new Dance(person());
}
@Bean // 通过 set 注入依赖
public Sing sing(){Sing sing= new Sing();
sing.setPerson(person());
return sing;
}
}
这里的 APPConfig 相当于 XML,定义了每个 bean,以及各个 bean 之间的依赖关系。
@Bean
用于标记 JavaBean 对象@Configuration
用于标记这个类是个配置类,主函数中的AnnotationConfigApplicationContext
必须传入一个标注了@Configuration
的类名
留神:代码中同时展现了两种依赖注入的形式,一种是 set 办法,一种是带参结构器。
Main 办法
import model.Dance;
import model.Person;
import model.Sing;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {public static void main(String[] args) {ApplicationContext context = new AnnotationConfigApplicationContext(APPConfig.class);
Dance dance=context.getBean(Dance.class);
dance.do_dance();
Sing sing=context.getBean(Sing.class);
sing.do_sing();
Person person=context.getBean(Person.class);
person.setName("Tom");
Dance dance_=context.getBean(Dance.class);
dance_.do_dance();
Sing sing_=context.getBean(Sing.class);
sing_.do_sing();}
}
运行后果
Ming is dancing!
Ming is sing!
Tom is dancing!
Tom is sing!
从运行后果能够看出,大家应用的是同一个 Person 对象。
4 基于注解搜寻的依赖注入
不再须要独自的 XML 或者 APPConfig 来通知容器哪些是 Bean 以及他们之间的关系,能够通过注解在程序中定义,让程序主动搜寻辨认。
Person
package model;
import org.springframework.stereotype.Component;
@Component
public class Person {
private String name;
private int age;
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public int getAge() {return age;}
public void setAge(int age) {this.age = age;}
}
Dance
package model;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Dance {
@Autowired // 代替了 set
Person person;
public void do_dance(){System.out.println(person.getName()+"is dancing!");
}
}
Sing
package model;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Sing {
@Autowired
Person person;
public void do_sing(){System.out.println(person.getName()+"is sing!");
}
}
@Component
注解就相当于定义了一个 Bean,它有一个可选的名称,默认是mailService
,即小写结尾的类名。@Autowired
就相当于把指定类型的 Bean 注入到指定的字段中。@Autowired
大幅简化了注入,因为它岂但能够写在set()
办法上,还能够间接写在字段上,甚至能够写在构造方法中
Main 的代码如下:
import model.Dance;
import model.Person;
import model.Sing;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(value = "model")
public class Main {public static void main(String[] args) {ApplicationContext context = new AnnotationConfigApplicationContext(Main.class);
Dance dance=context.getBean(Dance.class);
dance.do_dance();
Sing sing=context.getBean(Sing.class);
sing.do_sing();
Person person=context.getBean(Person.class);
person.setName("Tom");
Dance dance_=context.getBean(Dance.class);
dance_.do_dance();
Sing sing_=context.getBean(Sing.class);
sing_.do_sing();}
}
运行后果:
null is dancing!
null is sing!
Tom is dancing!
Tom is sing!
留神:因为是主动拆卸,person 的 name 为 null。
AppConfig
标注了 @ComponentScan
,它通知容器,主动搜寻以后类所在的包以及子包,把所有标注为@Component
的 Bean 主动创立进去,并依据 @Autowired
进行拆卸。间接在 Main 中通过调用 @ComponentScan(value = "model")
来实现 bean 的主动搜寻与拆卸,防止了配置文件的书写。
应用 Annotation 配合主动扫描能大幅简化 Spring 的配置,咱们只须要保障:
- 每个 Bean 被标注为
@Component
并正确应用@Autowired
注入; - 配置类被标注为
@Configuration
和@ComponentScan
; - 所有 Bean 均在指定包以及子包内。
这个办法尽管不便,但不适用于第三方包退出时。
比方当须要调用某个第三方包里的某个办法,该办法在第三方包中没有用 @Component` 注解,如何通知 IoC 容器创立并配置?
或者换个说法,如何创立并配置一个第三方 Bean?
待续。。。
5 参考文献
【1】廖雪峰的官方网站:https://www.liaoxuefeng.com/w…