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组件的实例化,通过被动构建的形式实现组件的实例化以及依赖关系的构建。在大型项目中,这种写法有以下毛病:

  1. 一个组件如果要应用另一个组件,必须先晓得如何正确地创立它,治理起来麻烦。
  2. 随着更多的组件被引入,组件的依赖层级变深,组织起来太简单。
  3. 有些组件须要销毁以便开释资源,但如果该组件被多个组件共享,如何确保它的应用方都曾经全副被销毁?
  4. 组件相互依赖,测试艰难。
如果一个零碎有大量的组件,其生命周期和相互之间的依赖关系如果由组件本身来保护,岂但大大增加了零碎的复杂度,而且会导致组件之间极为严密的耦合,继而给测试和保护带来了极大的艰难。

为解决这个问题,提出了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;@Configurationpublic 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;@Componentpublic 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;@Componentpublic 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;@Componentpublic 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...