控制反转IOC
Spring环境搭建
本章节将在IDEA中开发,在Eclipse(STS)中创建项目基本是一样的,无此经验者,可以自行百度。
创建一个maven父项目
file-new-project
在父项目中导入jar包
我们选择已maven的方式导入,maven仓库地址:https://mvnrepository.com/。这里我们直接导入spring-webmvc的jar,它会自动将一些其他依赖的jar包导入,比如core,context,AOP,beans,expression,等。如果IDEA没有自动导包,则右击项目执行一下maven的Reload Project。关于Eclipse关于lombok的安装可参考https://blog.youkuaiyun.com/weixin_41549354/article/details/107801940,IDEA的安装请自行百度,方法非常简单,这里就不赘述了,如果不想装,就写上getter,setter,等方法。
<?xml version="1.0" encoding="UTF-8"?>
<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</groupId>
<artifactId>SpringFather</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
</project>
创建一个module
右击SpringFather-new-module,点击next
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bpA2umz5-1596901471078)(D:\我的文档\Pictures\31.PNG)]
项目截图
HelloSpring的编写
重点:IOC的思想
在使用Spring之前,我们先来稍微了解一些IOC是个什么东西。在未使用Spring的Dao-Service-Controller开发中,用户的需求可能会影响我们原始的代码,因为我们会根据用户的需求去修改源代码。
举个例子,我们在dao层有一个UserDao的接口,然后这个UserDao有好多个实现类,然后在service里面我们直接调用了UserDao接口的实现类对象,类似的直接在UserServiceImpl写:
private UserDao userDao = new UserDaoImpl1();
然后在控制器里面调用UserService对象(不接触我们的Dao层,由UserService的对象去调用UserDao的对象),但此时问题就来了,我们需要用不同UserDaoImpl的对象时,就要修改UserServiceImpl里面的代码。十分的麻烦。业务调用的主动权在Service里面写死了,
我们修改上面的例子。
修改代码为
private UserDao userDao;
public void setUserDao(User userDao) {
this.userDao = userDao;
}
利用Set进行动态实现值的注入。程序不再具有主动性了,而是被动的在控制器里面接收UserDao的对象。这种注入思想,系统耦合性大大的降低,能够更加专注于业务的实现,主动权交给Controller,这就是IOC的原型!Spring的底层也都是利用了这种思想。
在没有IOC的程序中,对象的创建与对象间的依赖关系完全在硬编码中,对象的创建由程序自己控制,控制反转之后将对象的创建转移给第三方Spring容器,在程序需要某个对象的地方,这个SpringIOC容器将根据我们的需求自动注入所依赖的对象,程序获取依赖对象的方式反转了。
IOC是Spring框架的核心内容,在实际开发中,我们可以通过XML配置,也可以通过注解,甚至可以零配置实现IOC。我们写完POJO,接口等代码之后,通过XML配置,或者注解,然后SpringIOC容器去读取配置文件或者解析注解,从而将这些类的对象组织存入到IOC容器中,程序要用的时候,IOC容器根据需求自动注入所需的对象
IOC的实现方法是依赖注入DI(denpendency Injection)
实体类
在src/main/java文件夹下,新建一个com.pojo包,然后新建Hello.java
package com.pojo;
import lombok.Data;
@Data
public class Hello {
private String name;
}
Spring配置文件
在资源文件夹下面新建一个beans.xml。可以参考https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#spring-core拷贝官方网站的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">
<!--
使用Spring来创建对象,在Spring这些都称为bean
该id属性是标识单个bean定义的字符串。
该class属性定义Bean的类型,并使用完全限定的类名。
下面的代码就相当于,
Hello hello = new Hello();
hello.setName("Spring");
-->
<bean id="hello" class="com.pojo.Hello">
<!--
ref:引用Spring中已经管理好的对象
value:基本数据类型
-->
<property name="name" value="Spring"/>
</bean>
</beans>
实例化容器
提供给ApplicationContext
构造函数的一个或多个位置路径是资源字符串,可让容器从各种外部资源(例如本地文件系统,Java等)中加载配置元数据CLASSPATH
。在test文件夹下面新建一个包com。然后建一个HelloTest.java
package com;
import com.pojo.Hello;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class HelloTest {
@Test
public void testHello() {
/**
* 官方示例
* ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
* 作用:获取Spring的上下文对象,即Spring的容器,我们的对象都在Spring中组织好了,要用直接取就行了
*/
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
/**
* 从Spring容器里面取出对象,方法getBean(在beans.xml中定义的id)
* 注意id的大小写
*/
Object hello = (Hello)applicationContext.getBean("hello");
System.out.println(hello.toString());
}
}
测试
在上述例子中,Hello对象已经不是通过我们去手动new出来了,而是在Spring的配置文件里面进行声明。SpringIOC容器就帮我们管理好了,我们要用的时候:
直接通过 ClassPathXmlApplicationContext(“beans.xml”)读取配置文件创建一个Spring的上下文对象
然后通过这个对象的getBean方法取出IOC管理的对象。自此就实现了Hello的控制反转,它的底层还是用的set方法来进行注入
Hello(name=Spring)
Process finished with exit code 0
简单点说说一下IOC,就好比我们要炒菜请客,已经买好了食材(类),然后请一个第三方餐厅(IOC)来帮我们做,把我们的食材跟列出来的菜单交给这个餐厅就好了(进行配置)并且把菜做好了(Singleton),等待客人过来吃饭。客人来了之后,要吃什么菜,直接根据菜单去点菜就行(使用IOC的对象)
IOC创建对象的方式
基于无参构造的注入
我们在Hello.java中加上无参构造方法。
package com.pojo;
import lombok.Data;
@Data
public class Hello {
private String name;
public Hello() {
System.out.println("调用了无参构造方法");
}
}
然后进行测试
调用了无参构造方法
Hello(name=Spring)
Process finished with exit code 0
基于有参构造的注入
我们先删除Hello的无参构造,然后写上一个有参构造,干掉默认的无参构造
注意:如果这时候,直接执行的话,程序会报错 No default constructor found;。即使我们修改测试类,不调用无参构造创建对象的方式,程序仍然会报错,因为SpringIOC容器在初始化的时候,会自动创建好我们注册的类的对象(默认在Singleton的情况下会创建)
package com.pojo;
import lombok.Data;
@Data
public class Hello {
private String name;
public Hello(String name) {
System.out.println("调用了有参构造方法");
this.name = name;
}
}
官方提供了三种方式进行配置。
第一种:
在配置文件里面加上代码:
<?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">
<!--
使用Spring来创建对象,在Spring这些都称为bean
该id属性是标识单个bean定义的字符串。
该class属性定义Bean的类型,并使用完全限定的类名。
下面的代码就相当于
Hello hello = new Hello();
hello.setName("Spring");
-->
<!-- <bean id="hello" class="com.pojo.Hello">-->
<!-- <!–-->
<!-- ref:引用Spring中已经管理好的对象-->
<!-- value:基本数据类型-->
<!-- –>-->
<!-- <property name="name" value="Spring"/>-->
<!-- </bean>-->
<!--
Hello hello = new Hello("有参构造,第一种方式:下标赋值")
-->
<bean id="hello" class="com.pojo.Hello">
<constructor-arg index="0" value="有参构造,第一种方式:下标赋值"/>
</bean>
</beans>
测试:
调用了有参构造方法
Hello(name=有参构造,第一种方式:下标赋值)
Process finished with exit code 0
第二种
先在配置文件里面注释掉刚才的代码,重新加上
<!--
Hello hello1 = new Hello("有参构造,第二种方式:类型赋值")
不建议使用
-->
<bean id="hello" class="com.pojo.Hello">
<constructor-arg type="java.lang.String" value="有参构造,第二种方式:类型赋值"/>
</bean>
调用了有参构造方法
Hello(name=有参构造,第二种方式:类型赋值)
Process finished with exit code 0
第三种
先在配置文件里面注释掉刚才的代码,重新加上
<!--
推荐使用
有参构造,第三种方式:参数名赋值
-->
<bean id="hello1" class="com.pojo.Hello">
<constructor-arg name="name" value="有参构造,第三种方式:参数名赋值"/>
</bean>
调用了有参构造方法
Hello(name=有参构造,第三种方式:参数名赋值)
Process finished with exit code 0