在上次更新完《Spring中IOC思想(1)》博客后,也有段时间了,结合我对IOC更深层次的理解,我再写一篇关于IOC的博客
IOC
Spring框架的一个特点之一就是IOC思想
IOC思想其实就是将创建对象的权利进行转变,也就是控制反转,另外一个名称是依赖注入
在我们之前的博客讲解中,我们讲解到了个别案例,使用的是三层dao–>service–>前端
在service层中我们需要对dao层的中的类进行实例化,只有这样,我们才能在service层中通过调用dao层中的类的方法来对数据库进行增删改查、获得我们想要的数据。这也就需要我们在一个类中对另外一个类进行实例化,这样程序员在无意中就增加了程序的耦合性,而Java编程的思想其实是高内聚、低耦合,对项目的后期升级维护不利,那么有没有改进的方法?
这就需要说到Spring的作用
我在IDE中新建一个普通Maven项目,用于IOC的讲解
实体类的编写
这是新建的项目自动生成的目录,在java目录下创建com目录,在com目录下创建westos目录,在westos目录下创建pojo类,在pojo类中进行实体类的编写
在pojo中创建实体类HelloSpring
package com.westos.pojo;
public class HelloSpring {
private String username;
public void setUsername(String username) {
this.username = username;
}
public void show(){
System.out.println(username + ",你好,欢迎使用Spring");
}
}
在创建好这个实体类后,和之前的将类使用关键字new
进行实例化不同,我们使用Spring自带的方法来进行
因为我们需要使用Spring,所以我们需要导入Spring的一些依赖
依赖的导入
首先,为了防止资源在导入和导出时候发生问题,我们需要在pom.xml文件中新建<build>
标签,在该标签中写入
<!--解决资源导出问题-->
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
之后我们在导入Spring需要的依赖
<!--junit包单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!-- Spring MVC 及 Spring系列包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.24.RELEASE</version>
</dependency>
因为我们现在创建的项目是个比较简单的项目,没有用到前端和web,主要用来对IOC思想进行讲解,所以只导入上面的两个依赖
查看项目结构,可以看到导包成功
applicationContext.xml文件的编写
在resources目录下创建applicationContext.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="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
<!-- more bean definitions go here -->
</beans>
这是我们在官网上面找到的,官网上面直接给好了<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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--将HelloSpring类进行实例化-->
<bean id="hellospring" class="com.westos.pojo.HelloSpring">
<!--对实例化的对象中的成员变量进行赋值-->
<property name="username" value="强静州"></property>
</bean>
</beans>
在Spring中我们使用xml配置文件来对类进行实例化
Spring的另外一个特点就是容器,在这个容器中可以创建对象、建立对象之间的依赖关系以及管理对象的生命周期
也就是说,我们将类交给Spring容器进行保管,在Spring容器中对类进行实例化,这样我们在需要一个类的对象时不需要自己使用代码来进行创建,直接从容器中取出来就行
在使用Spring框架后,类的实例不再由调用者创建,而是由Spring容器来进行创建,Spring容器会负责控制程序之间的关系,而不是由调用者的程序代码直接控制,这样降低了对象之间的耦合性,与此同时,对象的创建 控制权由应用程序代码交给了Spring容器,控制权发生了反转,这就是我们上面讲解到控制反转
测试
我们在test目录下创建测试类
对我们之前的代码进行测试,我们先初始化Spring容器,并加载applicationContext.xml文件;之后从容器中取出已经实例化的对象,调用对象的成员方法
代码实现:
package com.westos;
import com.westos.pojo.HelloSpring;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
@org.junit.Test
public void HelloSppring(){
//初始化Spring容器并且并加载applicationContext.xml文件
ApplicationContext apc = new ClassPathXmlApplicationContext("applicationContext.xml");
//通过Spring容器获取HelloSpring类的实例hellospring
HelloSpring hellospring = (HelloSpring)apc.getBean("hellospring");
//调用该实例的成员方法
hellospring.show();
}
}
运行结果
运行成功
依赖注入概念
通过上面的例子主要是想讲解控制反转的概念,上面又提到IOC的另外一个名称是依赖注入,为什么呢?
这其实是一个东西从不同的概念来看待,从程序员角度来看的话,IOC是控制反转;但是从Spring角度来看的话,其实是依赖注入
在编写applicationContext.xml文件时为了能让Spring容器将我们需要使用的实体类进行实例化,存在下面的代码
<!--将HelloSpring类进行实例化-->
<bean id="hellospring" class="com.westos.pojo.HelloSpring">
<!--对实例化的对象中的成员变量进行赋值-->
<property name="username" value="强静州"></property>
</bean>
<bean>
标签的作用是将指定类配置给Spring,让Spring创建该类的实例<property>
标签的作用是为实例中的属性赋值
通过上面的两个标签的结合使用,可以将类进行实例化并且将类中的成员变量赋值,而给成员变量进行赋值的这个过程其实就可以看做是依赖注入,因为成员变量如果是个其他类的实例的话,我们相当于将这个类的实例所以依赖的其他类进行了注入入,这就是依赖注入
IOC从不同的角度来看的话就有两种结果:控制反转和依赖注入
依赖注入详解
依赖注入又分为两种setter方法注入和构造方法注入
setter方法注入
在我们之前的applicationContext.xml文件中使用的依赖注入方式为setter方法注入
这个方法其实是调用实体类的无参构造将实体类进行实例化,之后根据这个实例的反射来调用相关成员变量的set方法进行依赖的注入,所以在我们编写的实体类中有username的set方法,而且必须要有无参构造
package com.westos.pojo;
public class HelloSpring {
private String username;
public void setUsername(String username) {
this.username = username;
}
public void show(){
System.out.println(username + ",你好,欢迎使用Spring");
}
}
setter方法注入的基本格式为
<property name="username" value="强静州"></property>
我们使用到了<property>
标签,在次之前我们先讲解一下<bean>
标签的作用,之前讲解过,它的作用是将类进行实例化
<bean>
标签的主要属性为:
- id属性用来标识实例名,也就是类的对象名
- calss属性指定待实例化的类的全路径名
<property>
标签的主要属性为:
- name属性用来指定实体类的实例中的待赋值属性,在我上面举的例子中,name指定
hellospring
对象的username属性 - value属性就是给username属性赋的值
setter注入随着实例的属性的类型不同,也有多种注入方式:
常量注入
<!--普通字段-->
<property name="name" value="小明"/>
Bean注入
<!--引用其他bean使用ref-->
<!--上面一行的注释中的bean是什么意思呢?在Spring中将被Spring容器托管的类称为bean-->
<property name="address" ref="addr"/>
<!--addr是个实例名-->
数组注入
<!--数组的注入-->
<property name="books">
<array>
<value>西游记</value>
<value>水浒传</value>
<value>红楼梦</value>
<value>三国演义</value>
</array>
</property>
List注入
<!--List注入-->
<property name="hobbys">
<list>
<value>女孩</value>
<value>代码</value>
<value>电影</value>
<value>音乐</value>
</list>
</property>
Map注入
标签:<entry>
键:使用key
值: 使用value
<!--Map的注入-->
<property name="card">
<map>
<entry key="IdCard" value="666666888888884444"/>
<entry key="银行卡" value="111122223333444"/>
</map>
</property>
Set注入
<!--Set注入-->
<property name="games">
<set>
<value>王者荣耀</value>
<value>贪玩蓝月</value>
<value>绝地求生</value>
<value>LOL</value>
</set>
</property>
空值注入
<!--Null空值注入-->
<property name="girlFriend">
<null/>
</property>
Properties注入
<props>
标签
键使用key
值,在标签中间;
<!--Properties注入-->
<property name="info">
<props>
<prop key="学号">201932301</prop>
<prop key="性别">男</prop>
<prop key="姓名">小明</prop>
</props>
</property>
在使用常规setter方法注入时,如果实例有多个属性也就是成员变量,我们要对属性一一赋值就非常麻烦
p空间注入方法
这里讲到一种注入方法是p空间注入方法
我们在applicationContext.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"
<!--添加p命名空间的声明-->
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--将HelloSpring类进行实例化-->
<bean id="hellospring" class="com.westos.pojo.HelloSpring">
<!--对实例化的对象中的成员变量进行赋值-->
<property name="username" value="强静州"></property>
</bean>
</beans>
在添加完成后,我们就能在<bean>
标签中直接使用p属性来对实例的属性进行注入
我们先对pojo目录中的实体类进行修改
package com.westos.pojo;
public class HelloSpring {
private String username;
private String sex;
public void setUsername(String username) {
this.username = username;
}
public void setSex(String sex) {
this.sex = sex;
}
public void show(){
System.out.println(username + ",你好,欢迎使用Spring");
System.out.println(username + ",你好,你的性别是" + sex);
}
}
修改完成后我们重写一遍applicationContext.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"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--将HelloSpring类进行实例化-->
<bean id="hellospring" class="com.westos.pojo.HelloSpring">
<!--对实例化的对象中的成员变量进行赋值-->
<property name="username" value="强静州"></property>
<property name="sex" value="男"></property>
</bean>
<bean id="hellospring1" class="com.westos.pojo.HelloSpring" p:username="强东刘" p:sex="男"/>
</beans>
我们新创建了一个对象叫hellospring1
之后进行测试
package com.westos;
import com.westos.pojo.HelloSpring;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
@org.junit.Test
public void HelloSppring(){
//初始化Spring容器并且并加载applicationContext.xml文件
ApplicationContext apc = new ClassPathXmlApplicationContext("applicationContext.xml");
//通过Spring容器获取HelloSpring类的实例hellospring
HelloSpring hellospring = (HelloSpring)apc.getBean("hellospring");
HelloSpring hellospring1 = (HelloSpring)apc.getBean("hellospring1");
//调用该实例的成员方法
hellospring.show();
hellospring1.show();
}
}
运行结果
运行成功,从中也可以看出使用p空间注入的优点:当有实例有多个属性时,比<property>
标签更加方便快捷