认识Spring
Spring框架有两个核心思想,一个是IoC控制反转,一个是AOP面向切面编程。
控制反转的意思就是,对象交给IoC container创建、管理,不再需要自己去new一个对象,如果这个对象依赖其他对象或资源,那么由IOC容器帮忙注入。DI依赖注入就是IoC思想的一种实现方式。原本是上层依赖下层,通过依赖注入的方式,将下层作为参数注入给上层,实现上层控制下层的功能。
举个例子,A类有个B类的成员变量,B类中有一个C类的成员变量,那么如果在C类增加了一个新的元素,导致的结果就是,A、B、C三个类的代码都要修改(构造方法上添加一个新的参数)。在使用上,构建一个A类的对象的时候,通过把新的参数传递给A,继而传给B,最后传给C,达到给C类对象的新参数赋值或者初始化的目的。
而控制反转以后,过程是初始化C类对象,把C类对象作为参数传给B类,B类再传给A类。这个过程中,A类接受的对象永远都是B类对象,B类对象接受的参数是C类对象,所以对C类对象内部的修改不影响B类和A类对C类的依赖。这样每次修改,只需要修改相应的对象就行,不需要改动原本那些依赖它的对象。
那么由谁来做控制反转这件事呢?答案是"IoC容器" 。IoC容器在这个过程中就是起到把下层传递给上层的作用,由它负责给你产生一个“控制反转过的对象”,也就是上述的 不需要自己去new一个对象。
IoC容器具体是什么? 粗略地说,它是一个工厂。在实现上,需要一个xml来配置这个容器,这个xml文件里需要把类注册成"bean",隐藏细节,这样在使用时就不需要关注细节了(比如上个例子,你只需要知道自己需要一个A对象,不用去了解生成一个A对象需要一个传入一个B和一个C,也不用知道去传入什么参数,这些在xml里面配置好了)。bean 就是那个被“控制反转”了的类。你调用它,它返回一个bean。
前面说到的注入,就是具体实现上层控制下层的方式,主要包含有两种方式:构造方法注入 和 setter方法注入。
而IoC容器,包含了 BeanFactory 和 ApplicationContext 两种。后者是前者的子类。
那么接下来在实践中看看以上概念到底是什么东西,——idea启动!
一、建立项目,手动或者用maven导入需要的包(core、context、expressions、beans和common-logging、log4j)
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
二、建立类似结构
三、beans组成
1、Eyes 接口
package cn.ldy.beans;
public interface Eyes {
void show();
}
2、BlueEyes 和 RedEyes 实现类
//BlueEyes.java
package cn.ldy.beans;
public class BlueEyes implements Eyes{
@Override
public void show() {
System.out.println("Blue eyes!");
}
}
//RedEyes.java
package cn.ldy.beans;
public class RedEyes implements Eyes {
@Override
public void show() {
System.out.println("Red Eyes!");
}
}
以及要注册的两个bean
3、BlueDragon 和 RedDragon
//BlueDragon.java
package cn.ldy.beans;
import java.util.List;
public class BlueDragon {
private Eyes eyes;
private int atk;
private int def;
private List<String> others;
public BlueDragon(){
}
public BlueDragon(Eyes eyes, int atk, int def, List<String> others) {
this.eyes = eyes;
this.atk = atk;
this.def = def;
this.others = others;
}
public Eyes getEyes() {
return eyes;
}
public void setEyes(Eyes eyes) {
this.eyes = eyes;
}
public int getAtk() {
return atk;
}
public void setAtk(int atk) {
this.atk = atk;
}
public int getDef() {
return def;
}
public void setDef(int def) {
this.def = def;
}
public List<String> getOthers() {
return others;
}
public void setOthers(List<String> others) {
this.others = others;
}
public void summon(){
System.out.println("Blue Dragon Summon!");
eyes.show();
}
}
//RedDragon.java
package cn.ldy.beans;
import java.util.List;
public class RedDragon {
private Eyes eyes;
private int atk;
private int def;
private List<String> others;
public RedDragon(Eyes eyes, int atk, int def, List<String> others) {
this.eyes = eyes;
this.atk = atk;
this.def = def;
this.others = others;
}
public RedDragon() {
}
public Eyes getEyes() {
return eyes;
}
public void setEyes(Eyes eyes) {
this.eyes = eyes;
}
public int getAtk() {
return atk;
}
public void setAtk(int atk) {
this.atk = atk;
}
public int getDef() {
return def;
}
public void setDef(int def) {
this.def = def;
}
public List<String> getOthers() {
return others;
}
public void setOthers(List<String> others) {
this.others = others;
}
public void summon(){
System.out.println("Red Dragon Summon!");
eyes.show();
}
}
接下来,就是把bean注册到配置文件里。
四、在beans.xml配置文件中注册bean
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="blueeyes" class="cn.ldy.beans.BlueEyes"></bean>
<bean name="redeyes" class="cn.ldy.beans.RedEyes"></bean>
<bean name="bluedragon" class="cn.ldy.beans.BlueDragon">
<property name="atk" value="3000"></property>
<property name="def" value="2500"></property>
<property name="eyes" ref="blueeyes"></property>
<property name="others">
<list>
<value>8 stars</value>
<value>dragon race</value>
</list>
</property>
</bean>
<bean name="reddragon" class="cn.ldy.beans.RedDragon">
<constructor-arg name="atk" value="2400"></constructor-arg>
<constructor-arg name="def" value="2000"></constructor-arg>
<constructor-arg name="eyes" ref="redeyes"></constructor-arg>
<constructor-arg name="others">
<list>
<value>7 stars</value>
<value>dragon race</value>
</list>
</constructor-arg>
</bean>
</beans>
如上,在 beans 标签里的 bean就是注册的类。在此例中,我们把cn.ldy.beans.RedDragon(class属性中的值)这个类注册成bean,并且这个bean的name属性,也就是用来标识它的属性命名为reddragon(其他也是类似)。
最简单的,对于BlueEyes和RedEyes两个接口实现类来说,注册了一下,填了name和class就好了。
对于RedDragon和BlueDragon这两个bean来说,它们分别使用到了文章开头所说的constructor和set注入方式。
观察一下可以发现,bluedragon使用的set注入,就是利用property标签来设置bean中的属性的值。对比一下,可以看到,对于eyes属性,使用是的ref,也就是引用,从而给它注入了配置文件里的blueeyes这个bean;而对于基本类型,直接用value标签传递值即可。另外举了一个list的例子可以参看一下,这是细节上的问题,不再多说(数组、set等都是类似的形式)
reddragon使用的则是constructor注入,构造器注入,跟前者不一样的是,这个是在构造bean对象时就初始化,而set注入是构造一个默认bean对象,再用set方法注入。所以set方法需要默认构造器和set方法,而构造器注入需要完整的构造方法。注入的形式二者类似,亦不多说。
接下来,测试一下。
五、运行
//Test.java
package cn.ldy.test;
import cn.ldy.beans.BlueDragon;
import cn.ldy.beans.RedDragon;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
BlueDragon blueDragon = (BlueDragon) context.getBean("bluedragon");
blueDragon.summon();
RedDragon redDragon = (RedDragon) context.getBean("reddragon");
redDragon.summon();
}
}
在这里,我们得到BlueDragon和RedDragon的对象的方式就不再是new一个出来了,而是利用一个IoC容器也就是ApplicationContext对象来替我们生成,具体需要一个什么对象,就是靠配置里的name属性来识别。它的生成过程就满足我们一开始讨论的,上层控制下层。现在就可以理解了,一个Dragon对象,它不需要自己在初始化的时候,给自己的Eyes赋值;而是反过来,在生成一个Dragon的过程中,我们是给Dragon传递了一个Eyes对象,这个Eyes你后续如何更改都无所谓,因为Dragon只管你是不是一个Eyes。而这反过来的这个生成过程,由我们配置在xml文件里,从而让IoC容器替我们生成对象。
看看结果: