慕课网学习spring入门篇-专题一 IOC
2017-06-25 07:31
471 查看
专题一 IOC
接口及面向接口编程什么是IOC
Spring的Bean配置
Bean的初始化
Spring的常用注入方式
接口
用于沟通的中介化物的抽象化实体把自己提供给外界的一种抽象化说明,用以由内部操作分离出外部沟通方法,使其能被修改内部而不影响外界其他实体与其交互的方式。接口对外提供了功能,内部的实现对外是不公开的,比如说提供了注册的功能,只需把用户名等信息传递给接口即可,至于具体怎么实现的,逻辑是什么样的,怎么判断,数据存储在哪等等,所有的逻辑是对外不公开的,这就是接口。
对应Java接口即声明,声明了哪些方法是对外公开提供的。
在Java8中,接口可以拥有方法体。Java中有几种类型:接口、实现的类和抽象类。接口只能有声明,不能有实现;抽象类是既可以有声明也可以有实现;在Java8中接口也可以拥有方法体。也就是说,接口和抽象类一定程度上具有相同的功能。这样做,会有一定的好处,因为接口是可以多重实现的,类只能单继承。
面向接口编程
- 结构设计中,分清层次及调用关系,每层只向外(上层)提供一组功能接口,各层间仅依赖接口而非实现类 - 接口实现的变动不影响各层间的调用,这一点在公共服务中尤其重要 - 面向接口编程中的接口是用于隐藏具体实现和实现多态性的组件
例子
把视频中的Spring前四章源码、Junit jar包、spring jar包 1和spring jar包 2下载下来并解压,使用Eclipse等IDE工具,把项目导入到工作空间。File->Import->General->Existing Projects into Workspace。找到解压好的源码,导入。导入后会发现工程上有一个红色的叹号。原因是工程中Spring的jar包找不到了。只要重写buildpath就可以了。选中Spirng这个工程,右键->Properties->Java Build Path,把missing的21个jar包移除。
然后把前面下载的Spring的jar包添加进去,两个目录下也一共是21个jar。
这样,我们的工程就导入完成了。
首先,声明一个接口,接口中有一个say方法,接受字符串输入,不返回结果。
package com.imooc.ioc.interfaces; public interface OneInterface { public void say(String arg); }
然后是实现类,将输入的字符串和输出语句中的字符拼装起来输出。具体的实现可以根据业务变化来调整
package com.imooc.ioc.interfaces; public class OneInterfaceImpl implements OneInterface { public void say(String arg) { System.out.println("ServiceImpl say: " + arg); } }
使用接口,输出:ServiceImpl say: hello.
package com.imooc.ioc.interfaces; public class TestInterface { public static void main(String[] args) { OneInterface oif = new OneInterfaceImpl(); oif.say("hello."); } }
用接口进行声明,用实现类赋值给对象的声明,然后进行调用。这就是面向接口编程的简单实现。这个例子中看不到面向接口编程的好处,反而会感觉为什么要搞接口和实现类,这样很麻烦。在下面的IOC中将了解面向接口编程的好处。
IOC
IOC:控制反转,控制权的转移,应用程序本身不负责依赖对象的创建和维护,而是由外部容器创建和维护。就是说,当需要一个对象的时候,不自己new一个,而是由外部容器创建。当需要一个房子,通常并不会自己去盖一个,而是找开发商买一套,不是自己创建,只负责使用。DI(依赖注入)是控制反转实现的一种方式
IOC的目的:创建对象,并组装对象之间的依赖关系。IOC容器在初始化时会创建一系列对象,同时,它能够把对象之间的关系通过依赖注入的方式组织起来。当一个类A,它里面持有另外一个类B,当实例化的时候,先实例化出A的对象,然后实例化B,然后把B的对象赋值给A。
IOC类比房屋租借
房屋租借 | IOC |
---|---|
找中介 | 找IOC容器 |
中介介绍房子 | 容器返回对象 |
租房、入住 | 使用对象 |
Spring的Bean配置
刚才的接口在Spring中的配置方式<?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 id="OneInterface" class="com.imooc.ioc.interfaces.OneInterfaceImpl" ></bean> </beans>
在IOC容器中,把所有的对象都称为bean。Spring对于bean有两种使用方式:xml和注解。
bean id=”OneInterface” class=”com.imooc.ioc.interfaces.OneInterfaceImpl”
id=”OneInterface”是bean的唯一标识,class=”com.imooc.ioc.interfaces.OneInterfaceImpl” 这个bean对应于哪一个具体的类。
使用例子:
package com.imooc.test.ioc.interfaces; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.BlockJUnit4ClassRunner; //import org.springframework.test.context.ContextConfiguration; //import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.imooc.ioc.interfaces.OneInterface; import com.imooc.test.base.UnitTestBase; @RunWith(BlockJUnit4ClassRunner.class) public class TestOneInterface extends UnitTestBase { public TestOneInterface() { super("classpath*:spring-ioc.xml"); } @Test public void testSay() { OneInterface oneInterface = super.getBean("oneInterface"); oneInterface.say("This is a test."); } }
这是一个JUnit单元测试,输出结果:ServiceImpl say: This is a test.
单元测试
下载junit-4.10.jar并引入工程创建UnitTestBase类,完成对Spring配置文件的加载、销毁
所有的单元测试类都继承自UnitTestBase,通过它的getBean方法获取想要得到的对象
子类(具体执行单元测试的类)加注解:@RunWith(BlockJUnit4ClassRunner.class)
单元测试方法加注解:@Test
右键选择要执行的单元测试方法执行或者执行一个类的全部单元测试方法
UnitTestBase类
package com.imooc.test.base; import org.junit.After; import org.junit.Before; import org.springframework.beans.BeansException; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.util.StringUtils; public class UnitTestBase { private ClassPathXmlApplicationContext context; private String springXmlpath; public UnitTestBase() {} public UnitTestBase(String springXmlpath) { this.springXmlpath = springXmlpath; } @Before public void before() { if (StringUtils.isEmpty(springXmlpath)) { springXmlpath = "classpath*:spring-*.xml"; } try { context = new ClassPathXmlApplicationContext(springXmlpath.split("[,\\s]+")); context.start(); } catch (BeansException e) { e.printStackTrace(); } } @After public void after() { context.destroy(); } @SuppressWarnings("unchecked") protected <T extends Object> T getBean(String beanId) { try { return (T)context.getBean(beanId); } catch (BeansException e) { e.printStackTrace(); return null; } } protected <T extends Object> T getBean(Class<T> clazz) { try { return context.getBean(clazz); } catch (BeansException e) { e.printStackTrace(); return null; } } }
首先执行构造方法,把springXmlpath传入,在TestOneInterface例子中就传入了classpath*:spring-ioc.xml。
接着执行@Before的方法,判断springXmlpath是否为空。
然后加载文件,创建上下文context = new ClassPathXmlApplicationContext(springXmlpath.split(“[,\s]+”));
public String[] split(String regex)根据给定的正则表达式的匹配来拆分此字符串。 []表示方括号内的任意字符。\s表示任何Unicode空白符,如空格,回车,换行。+表示一个或多个的意思。ClassPathXmlApplicationContext中的参数是String[]类型,springXmlpath可以使用逗号、空格等方式传入多个xml文件。context就是Spring的容器,当context.start()之后会查找配置文件中配置的信息,并把这些信息解析装载到Spring的上下文中。
在使用的过程中通过context.getBean()方法来获取相应的对象。在TestOneInterface例子中OneInterface oneInterface = super.getBean(“oneInterface”);
在执行之后@After关闭上下文context.destroy();
Bean容器初始化
基础:两个包org.springframework.beans
org.springframework.context
BeanFactory提供配置结构和基本功能,加载并初始化Bean
ApplicationContext保存了Bean对象(就是容器)并在Spring中被广泛使用
方式,ApplicationContext
本地文件
Classpath
Web应用中依赖servlet或Listener
例子:
//本地文件 FileSystemXmlApplicationContext context1 = new FileSystemXmlApplicationContext("F:/workspace/appcontext.xml"); //Classpath ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring-context.xml"); //Web应用,使用servlet或监听器来初始化bean容器 <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>context</servlet-name> <servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet>
Spring注入
Spring注入是指在启动Spring容器加载bean配置的时候,完成对变量的赋值行为常用的两种注入方式
设值注入
构造注入
Spring的常用注入方式
设值注入
<?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 id="injectionService" class="com.imooc.ioc.injection.service.InjectionServiceImpl"> <property name="injectionDAO" ref="injectionDAO"></property> </bean> <bean id="injectionDAO" class="com.imooc.ioc.injection.dao.InjectionDAOImpl"></bean> </beans>
声明两个bean,在IOC容器初始化加载当前XML文件的时候去创建这两个对象,id为injectionService这个bean中,有个property标签。是说,在InjectionServiceImpl类中会有一个叫“injectionDAO”成员变量,类型就是InjectionDAO这个类型,这个bean有个引用,指向id=”injectionDAO”这个bean。id=”injectionDAO”的这个bean代表InjectionDAOImpl的一个实例,然后把这个实例赋值给上面的属性:injectionDAO。
这种方式就是设值注入,通过set的方式去注入。可以把设值注入理解为自动的调用类的set方法。在InjectionServiceImpl类中也必须有setInjectionDAO方法。
注意:property name=”injectionDAO”,name要和InjectionServiceImpl中的变量名相同。
构造注入
其他的都相同,只是在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 id="injectionService" class="com.imooc.ioc.injection.service.InjectionServiceImpl"> <constructor-arg name="injectionDAO" ref="injectionDAO"></constructor-arg> </bean> <bean id="injectionDAO" class="com.imooc.ioc.injection.dao.InjectionDAOImpl"></bean> </beans>
调用InjectionServiceImpl的构造方法创建InjectionServiceImpl类的实例的时候,就会把ref中的实例赋值给InjectionServiceImpl在构造器中声明的injectionDAO这个属性,从而完成创建InjectionServiceImpl的时候对injectionDAO也就是InjectionServiceImpl类的成员变量的赋值。
例子
/Dao层接口 package com.imooc.ioc.injection.dao; public interface InjectionDAO { public void save(String arg); } //Dao层实现类 package com.imooc.ioc.injection.dao; public class InjectionDAOImpl implements InjectionDAO { public void save(String arg) { //模拟数据库保存操作 System.out.println("保存数据:" + arg); } } //Service层接口 package com.imooc.ioc.injection.service; public interface InjectionService { public void save(String arg); } //Service层实现类 package com.imooc.ioc.injection.service; import com.imooc.ioc.injection.dao.InjectionDAO; public class InjectionServiceImpl implements InjectionService { private InjectionDAO injectionDAO; //构造器注入 public InjectionServiceImpl(InjectionDAO injectionDAO) { this.injectionDAO = injectionDAO; } //设值注入 public void setInjectionDAO(InjectionDAO injectionDAO) { this.injectionDAO = injectionDAO; } public void save(String arg) { //模拟业务操作 System.out.println("Service接收参数:" + arg); arg = arg + ":" + this.hashCode(); injectionDAO.save(arg); } } //spring-injection.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 http://www.springframework.org/schema/beans/spring-beans.xsd" > <bean id="injectionService" class="com.imooc.ioc.injection.service.InjectionServiceImpl"> <property name="injectionDAO" ref="injectionDAO"></property> </bean> <!-- <bean id="injectionService" class="com.imooc.ioc.injection.service.InjectionServiceImpl"> <constructor-arg name="injectionDAO" ref="injectionDAO"></constructor-arg> </bean> --> <bean id="injectionDAO" class="com.imooc.ioc.injection.dao.InjectionDAOImpl"></bean> </beans> //测试类 package com.imooc.test.ioc.interfaces; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.BlockJUnit4ClassRunner; import com.imooc.ioc.injection.service.InjectionService; import com.imooc.test.base.UnitTestBase; @RunWith(BlockJUnit4ClassRunner.class) public class TestInjection extends UnitTestBase { public TestInjection() { super("classpath:spring-injection.xml"); } @Test public void testSetter() { InjectionService service = super.getBean("injectionService"); service.save("这是要保存的数据"); } @Test public void testCons() { InjectionService service = super.getBean("injectionService"); service.save("这是要保存的数据"); } }
测试类中两个测试方法其实是相同的,有一个就行了。发现一个问题,就是当InjectionServiceImpl类写了构造方法以后,再使用设值注入时,测试案例不会通过。原因是No default constructor found;所以在InjectionServiceImpl中加一个默认构造方法就好了。
相关文章推荐
- [spring入门学习笔记][spring的IoC原理]
- Spring学习笔记:第二章 Spring中IoC的入门实例
- [spring入门学习笔记][spring的IoC原理]
- Spring学习(1):控制反转(IoC)和依赖注入(DI)的详解以及注解(annotation)开发入门案例
- 慕课网学习spring入门篇-Spring Bean装配(下)
- spring 学习(1.1) 入门基础- IoC/依赖注入
- 慕课网学习spring入门篇-Spring Bean装配(上)
- Spring学习笔记[2] -- IOC快速入门
- 慕课网学习spring入门篇-AOP基本概念
- 【慕课网Spring系列学习攻略】IOC及Bean容器篇
- Spring学习笔记----01. 入门知识,IoC/DI
- Spring学习笔记:Spring中IoC的入门实例
- Spring学习笔记:2-Spring中IoC的入门实例
- Spring中IoC的入门实例
- Spring IOC 学习笔记(一) IoC和DI概述
- 用代码学习Spring:IoC、AOP
- 用代码学习Spring:IoC、AOP
- 学习spring的入门好书:Professional Java Development with the Spring Framework
- 学习Spring.NET - IoC对象管理容器(1)
- 用代码学习Spring:IoC、AOP(下)