上一篇Blog我们大致了解了Spring的历史由来,以及Spring的核心思想:IOC和AOP,以及IOC的实现手段DI,并且了解了Spring的分层结构体系,本篇Blog就来快速上手一个Spring框架程序,看看Spring的这些核心思想和理论基础是如何在实践中发挥作用的。
初始化一个Spring程序
接下来我们就按照步骤初始化一个Spring程序:
1 新建Java Web项目
因为我们是搞Web开发么,所以这次依然选择构建一个Web程序
选择配件模块时依然跳过,在学习的过程中依次向pom.xml中加才更能领会配置意义。
2 pom.xml中引入Spring依赖
在maven的pom.xml文件中我们引入Spring的依赖,从网址Maven坐标搜索查看要引入的坐标:
找到Maven的坐标后加入依赖即可:
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.9</version> </dependency>
上篇Blog我们提到spring-context模块建立在由 core和 beans 模块基础上,我们一般只需要在Maven中配置spring-context
,就会自动将它的其它依赖,例如beans和core导入到项目中,如下图所示:
3 创建HelloSpring类
接下来我们在代码中创建HelloSpring类来测试如何通过Spring的IOC进行bean的注入:
package com.example.Spring.model; import lombok.Data; @Data public class HelloSpring { private String message; private String from; public void show(){ System.out.println(message+" "+ from); } }
这里使用了lombook的注解@Data
,这个注解帮我们实现了属性的get和set方法,注入时是依赖这些方法的,所以这个注解必须加上。
4 新建applicationContext.xml文件
在resources文件夹下新建applicationContext.xml.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就是java对象 , 由Spring创建和管理--> <bean id="helloProperty" class="com.example.Spring.model.HelloSpring"> <property name="message" value="tml first Spring from helloProperty"/> <property name="from" value="by property"/> </bean> </beans>
5 创建测试类并进行测试
接下来我们就来测试下Spring的实现方式:
import com.example.Spring.model.HelloSpring; import org.junit.jupiter.api.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class HelloTest { @Test public void HelloSpringTest(){ //解析beans.xml文件,生成管理相应的bean对象 ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); //getBean : 参数即为spring配置文件中bean的id System.out.println("属性注入方式==========================================================="); HelloSpring helloProperty = (HelloSpring) context.getBean("helloProperty");//强制转换为Hello类型的 helloProperty.show(); } }
打印结果如下:
Bean的注入方式
Bean的依赖注入方式有两种,一种是属性注入,一种是构造方法注入,其中构造方法注入又可以分为按照属性的索引、属性的类型和属性的名称三种方式。
1 beans.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就是java对象 , 由Spring创建和管理--> <bean id="helloProperty" class="com.example.Spring.model.HelloSpring"> <property name="message" value="tml first Spring from helloProperty"/> <property name="from" value="by property"/> </bean> <bean id="helloCIndex" class="com.example.Spring.model.HelloSpring"> <!-- index指构造方法 , 下标从0开始 --> <constructor-arg index="1" value="by constructor-index"/> <constructor-arg index="0" value="tml first Spring from hellCIndex"/> </bean> <bean id="helloCName" class="com.example.Spring.model.HelloSpring "> <!-- name指参数名 --> <constructor-arg name="message" value="tml first Spring from hellCName"/> <constructor-arg name="from" value="by constructor-name"/> </bean> <bean id="helloCType" class="com.example.Spring.model.HelloSpring "> <!-- type指参数类型,如果不指定索引顺序,那么按照配置顺序对应属性,所以最好也指定好索引顺序 --> <constructor-arg type="java.lang.String" index="0" value="tml first Spring from hellCType"/> <constructor-arg type="java.lang.String" index="1" value="by constructor-type"/> </bean> </beans>
2 HelloSpring类增加注解
属性注入必须要求有无参的构造函数,而构造器注入则要求必须有有参的构造函数【且参数必须和构造函数的参数一一对应,不能多也不能少否则会报找不到这样的构造函数】,本质是因为SpringContext利用无参的构造函数创建一个对象,然后利用setter方法赋值。所以如果无参构造函数不存在,Spring上下文创建对象的时候便会报错。所以如果想两个都用,那么必须显式的实现两种构造函数:
package com.example.Spring.model; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @AllArgsConstructor @NoArgsConstructor public class HelloSpring { private String message; private String from; public void show(){ System.out.println(message+" "+ from); } }
所以这三个注解我们必须都加上:@Data,@AllArgsConstructor,@NoArgsConstructor
3 实现方式整体测试
那么接下来我们用单元测试看下执行结果:
import com.example.Spring.model.HelloSpring; import org.junit.jupiter.api.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class HelloTest { @Test public void HelloSpringTest(){ //解析beans.xml文件,生成管理相应的bean对象 ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); //getBean : 参数即为spring配置文件中bean的id System.out.println("属性注入方式==========================================================="); HelloSpring helloProperty = (HelloSpring) context.getBean("helloProperty");//强制转换为Hello类型的 helloProperty.show(); System.out.println("构造器-属性索引顺序 注入方式==========================================================="); HelloSpring helloCIndex = (HelloSpring) context.getBean("helloCIndex");//强制转换为Hello类型的 helloCIndex.show(); System.out.println("构造器-属性名 注入方式==========================================================="); HelloSpring helloCName = (HelloSpring) context.getBean("helloCName");//强制转换为Hello类型的 helloCName.show(); System.out.println("构造器-属性类型 注入方式==========================================================="); HelloSpring helloCType = (HelloSpring) context.getBean("helloCType");//强制转换为Hello类型的 helloCType.show(); } }
执行结果如下:
Sping的方式更换实现类
我们说IOC的实现方式可以更便捷的为我们更换实现类,接下来我们测试下:
1 创建Service和Dao相关类
我们沿用三层架构的方式,先把相关的Service和Dao创建完毕,整体目录结构如下
PersonService
package com.example.Spring.service; public interface PersonService { void showPersonMessage(); }
PersonServiceImpl
package com.example.Spring.serviceImpl; import com.example.Spring.dao.PersonDao; import com.example.Spring.service.PersonService; import lombok.Data; @Data public class PersonServiceImpl implements PersonService { private PersonDao personDao; @Override public void showPersonMessage() { personDao.showPersonMessage(); } }
PersonDao
package com.example.Spring.dao; public interface PersonDao { void showPersonMessage(); }
PersonDaoFirstImpl
package com.example.Spring.daoImpl; import com.example.Spring.dao.PersonDao; import lombok.Data; @Data public class PersonDaoFirstImpl implements PersonDao { private String message; @Override public void showPersonMessage() { System.out.println("PersonDaoFirstImpl 实现当前方法"); } }
PersonDaoSecondImpl
package com.example.Spring.daoImpl; import com.example.Spring.dao.PersonDao; import lombok.Data; @Data public class PersonDaoSecondImpl implements PersonDao { private String message; @Override public void showPersonMessage() { System.out.println("PersonDaoSecondImpl 实现当前方法"); } }
2 配置beans.xml文件
我们对这几个实现类分别进行配置,并配置他们的依赖关系,其中ref表示依赖的类型id。
<bean id="personServiceImpl" class="com.example.Spring.serviceImpl.PersonServiceImpl"> <property name="personDao" ref="personDaoSecondImpl"/> </bean> <bean id="personDaoFirstImpl" class="com.example.Spring.daoImpl.PersonDaoFirstImpl"> <property name="message" value="PersonDaoFirstImpl 实现当前方法"/> </bean> <bean id="personDaoSecondImpl" class="com.example.Spring.daoImpl.PersonDaoSecondImpl"> <property name="message" value="PersonDaoSecondImpl 实现当前方法"/> </bean>
3 编写测试类进行测试
然后我们编写测试类进行测试:
import com.example.Spring.serviceImpl.PersonServiceImpl; import org.junit.jupiter.api.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class PersonServiceTest { @Test public void PersonServiceTest(){ //解析beans.xml文件,生成管理相应的bean对象 ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); //getBean : 参数即为spring配置文件中bean的id PersonServiceImpl personServiceImpl = (PersonServiceImpl) context.getBean("personServiceImpl"); personServiceImpl.showPersonMessage(); } }
打印结果如下:
所以当我们想要更换实现的时候,不需要在每个用到PersonDao的地方重新构建替换实现,而仅仅在配置文件上的ref进行以下修改即可,调用方再也不需要关系依赖对象的实现方式,也不再需要费力的构建它们了。
总结一下
这篇博客详细的实践了Spring的IOC思想,对这部分内容有了一个较深的体会,事实上,正是因为有了IOC,我们发现调用方再也不需要关心细节而只需要关心业务逻辑和需求了,这一切都可以通过配置和描述进行统一管理,这就是框架的美妙之处吧。