Spring学习之路 第一篇
- 我们开始学习Spring框架。这篇主要讲解入门的介绍和用法,配置均采用的xml方式。
- 本文涉及以下内容:
1. 使用Spring的优点。
2. Spring的IOC解决程序耦合
3. Spring的依赖注入DI
1. Spring的优点 |
1. 方便解耦,简化开发
- 通过 Spring 提供的 IoC 容器,可以将对象间的依赖关系交由 Spring 进行控制,避免硬编码所造成的过度程序耦合。用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。
2. AOP 编程的支持
- 通过 Spring 的 AOP 功能,方便进行面向切面的编程,许多不容易用传统 OOP 实现的功能可以通过 AOP 轻松应付。
3. 声明式事务的支持
- 可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务的管理,提高开发效率和质量。
2. Spring的IOC解决程序耦合 |
- 明确IOC的作用:消减计算机程序的耦合(降低我们代码中的依赖关系)
2.1 入门案例搭建
- 我们先看看整体的项目结构图:
- 在pom.xml中添加spring的包:
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.2.RELEASE</version> </dependency> </dependencies>
- 编写账户持久层接口IAccountDao:
//账户的持久层接口 public interface IAccountDao { //模拟保存账户 void saveAccount(); }
- 编写账户持久层的实现类AccountDaoImpl:
//账户的持久层实现类 public class AccountDaoImpl implements IAccountDao { public void saveAccount(){ System.out.println("保存了账户"); } }
- 编写账户业务层接口IAccountService:
//账户业务层的接口 public interface IAccountService { //模拟保存账户 void saveAccount(); }
- 编写账户业务层的实现类AccountServiceImpl:
//账户的业务层实现类 public class AccountServiceImpl implements IAccountService { private IAccountDao accountDao = new AccountDaoImpl();//此处的依赖关系有待解决 public AccountServiceImpl(){ System.out.println("对象创建了"); } public void saveAccount(){ accountDao.saveAccount(); } }
- 编写配置文件bean.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"> <!--把对象的创建交给spring来管理--> <bean id="accountService" class="fang.service.impl.AccountServiceImpl"></bean> <bean id="accountDao" class="fang.dao.impl.AccountDaoImpl"></bean> </beans>
- 编写表现层代码Client:
public class Client { public static void main(String[] args) { //1.获取核心容器对象 ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); //2.根据id获取Bean对象 IAccountService as = (IAccountService) ac.getBean("accountService"); IAccountDao adao = ac.getBean("accountDao", IAccountDao.class); System.out.println(as); System.out.println(adao); as.saveAccount(); } }
- 到这里IOC的入门案例就完成啦。
2.2 入门案例中涉及的一些内容:
- ApplicationContext的三个常用实现类:
- ClassPathXmlApplicationContext:它可以加载类路径下的配置文件,要求配置文件必须在类路径下。不在的话,加载不了。(更常用)
- FileSystemXmlApplicationContext:它可以加载磁盘任意路径下的配置文件(必须有访问权限)
- AnnotationConfigApplicationContext:它是用于读取注解创建容器的。
2.3 bean标签
- 作用:
- 配置对象让 spring 来创建的。
- 情况下它调用的是类中的无参构造函数。如果没有无参构造函数则不能创建成功。
- 属性:
- id:给对象在容器中提供一个唯一标识。用于获取对象。
- class:指定类的全限定类名。用于反射创建对象。默认情况下调用无参构造函数。
- scope:指定对象的作用范围。
- singleton :默认值,单例的.
- prototype :多例的.
- request :WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中.
- session :WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中.
- global session :WEB 项目中,应用在 Portlet 环境.如果没有 Portlet 环境那么globalSession 相当于 session.
- init-method:指定类中的初始化方法名称。
- stroy-method:指定类中销毁方法名称。
2.4 bean的作用范围和生命周期
- 单例对象:scope=“singleton”
- 一个应用只有一个对象的实例。它的作用范围就是整个引用。
- 生命周期:
- 对象出生:当应用加载,创建容器时,对象就被创建了。
- 对象活着:只要容器在,对象一直活着。
- 对象死亡:当应用卸载,销毁容器时,对象就被销毁了。
- 多例对象:scope=“prototype”
- 访问对象时,都会重新创建对象实例。
- 生命周期:
- 对象出生:当使用对象时,创建新的对象实例。
- 对象活着:只要对象在使用中,就一直活着。
- 对象死亡:当对象长时间不用时,被 java 的垃圾回收器回收了。
2.5 实例化Bean的三种方式:
- 使用默认构造函数
<!--在默认情况下: 它会根据默认无参构造函数来创建类对象。如果 bean 中没有默认无参构造函数,将会创建失败。--> <bean id="accountService" class="fang.service.impl.AccountServiceImpl"/>
- spring 管理静态工厂-使用静态工厂的方法创建对象
- 模拟一个静态工厂,创建业务层实现类
public class StaticFactory { public static IAccountService createAccountService() { return new AccountServiceImpl(); } }
- 此种方式是:
使用 StaticFactory 类中的静态方法 createAccountService 创建对象,并存入 spring 容器
id 属性:指定 bean 的 id,用于从容器中获取
class 属性:指定静态工厂的全限定类名
factory-method 属性:指定生产对象的静态方法<bean id="accountService" class="fang.factory.StaticFactory" factory-method="createAccountService"></bean>
- spring 管理实例工厂-使用实例工厂的方法创建对象
- 模拟一个实例工厂,创建业务层实现类
此工厂创建对象,必须现有工厂实例对象,再调用方法public class InstanceFactory { public IAccountService createAccountService(){ return new AccountServiceImpl(); } }
- 此种方式是:先把工厂的创建交给 spring 来管理。然后在使用工厂的 bean 来调用里面的方法
factory-bean 属性:用于指定实例工厂 bean 的 id。
factory-method 属性:用于指定实例工厂中创建对象的方法。<bean id="instancFactory" class="fang.factory.InstanceFactory"></bean> <bean id="accountService" factory-bean="instancFactory" factory-method="createAccountService"></bean>
3. Spring的依赖注入DI |
- 依赖注入:Dependency Injection。它是 spring 框架核心 ioc 的具体实现。这种业务层和持久层的依赖关系,在使用 spring 之后,就让 spring 来维护了。简单的说,就是坐等框架把持久层对象传入业务层,而不用我们自己去获取。
- 依赖注入能注入的三种方式:
- 基本类型和String
- 其他bean类型(在配置文件中或者注解配置过的bean)
- 复杂类型/集合类型
- 注入的方式有三种:
- 第一种:使用构造函数提供
- 第二种:使用set方法提供
- 第三种:使用注解提供
3.1 构造函数注入
- 就是使用类中的构造函数,给成员变量赋值。注意,赋值的操作不是我们自己做的,而是通过配置的方式,让 spring 框架来为我们注入。
- 编写AccountServiceImpl类:
public class AccountServiceImpl implements IAccountService { private String name; private Integer age; private Date birthday; public AccountServiceImpl(String name, Integer age, Date birthday) { this.name = name; this.age = age; this.birthday = birthday; } @Override public void saveAccount() { System.out.println(name + "," + age + "," + birthday); } }
- 在bean.xml中编写如下:
- 使用构造函数的方式,给 service 中的属性传值,涉及的标签:
- constructor-arg,属性如下:
- index:指定参数在构造函数参数列表的索引位置
- type:指定参数在构造函数中的数据类型
- name:指定参数在构造函数中的名称
上面三个都是找给谁赋值,下面两个指的是赋什么 - value:它能赋的值是基本数据类型和 String 类型
- ref:它能赋的值是其他 bean 类型,也就是说,必须得是在配置文件中配置过的 bean.
<bean id="accountService" class="fang.service.impl.AccountServiceImpl"> <constructor-arg name="name" value="泰斯特"></constructor-arg> <constructor-arg name="age" value="18"></constructor-arg> <constructor-arg name="birthday" ref="now"></constructor-arg> </bean> <!-- 配置一个日期对象 --> <bean id="now" class="java.util.Date"></bean>
3.2 set方法注入
- 就是在类中提供需要注入成员的 set 方法。具体代码如下:
- AccountServiceImpl代码如下:
public class AccountServiceImpl implements IAccountService { private String name; private Integer age; private Date birthday; public void setName(String name) { this.name = name; } public void setAge(Integer age) { this.age = age; } public void setBirthday(Date birthday) { this.birthday = birthday; } @Override public void saveAccount() { System.out.println(name + "," + age + "," + birthday); } }
- bean.xml添加如下:
- 通过配置文件给 bean 中的属性传值:使用 set 方法的方式,涉及的标签:
- property属性:
- name:找的是类中 set 方法后面的部分
- ref:给属性赋值是其他 bean 类型的
- value:给属性赋值是基本数据类型和 string 类型的
- 实际开发中,此种方式用的较多。
<bean id="accountService" class="fang.service.impl.AccountServiceImpl"> <property name="name" value="test"></property> <property name="age" value="21"></property> <property name="birthday" ref="now"></property> </bean> <bean id="now" class="java.util.Date"></bean>
- 复杂类型的注入/集合类型的注入
- 注入集合数据(同一结构的数据标签可以互换):
- List结构的:
- array, list, set
- Map结构的
- map, entry, props, prop
<bean id="accountService" class="fang.service.impl.AccountServiceImpl"> <!-- 给数组注入数据 array, list, set三个标签都可用--> <property name="myStrs"> <set> <value>AAA</value> <value>BBB</value> <value>CCC</value> </set> </property> <!-- 注入 Map 数据 --> <property name="myMap"> <props> <prop key="testC">ccc</prop> <prop key="testD">ddd</prop> </props> </property> <!-- 注入 map 数据 --> <property name="myProps"> <map> <!--两种赋值方式均可--> <entry key="testA" value="aaa"></entry> <entry key="testB"> <value>BBB</value> </entry> </map> </property>
- List结构的:
- 例如在AccountServiceImpl中调用了AccountDaoImpl类
- 需要在AccountServiceImpl类中添加AccountDao的set方法
- bean.xml添加如下:
<bean id="accountDao" class="fang.dao.impl.AccountDaoImpl"/> <bean id="accountService" class="fang.service.impl.AccountServiceImpl"> <property name="accountDao" ref="accountDao"/> </bean>