专业的编程技术博客社区

网站首页 > 博客文章 正文

Spring-基础笔记(一)(spring详细教程)

baijin 2024-09-26 07:03:33 博客文章 3 ℃ 0 评论

Spring-基础笔记(一)

一、Spring核心

Spring是一个轻量级控制反转(IOC)和面向切面(AP)的容器框架。

Spring是一个开源框架,它由Rod Johnson创建。它是为了解决企业应用开发的复杂性而创建的。Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。

  • 目标: 解决企业应用开发的复杂性
  • 功能: 使用基本的JavaBean代替EJB,并提供了更多的企业应用功能
  • 范围: 任何Java应用

Spring的特性

  1. 轻量级——从大小和开销两方面而言Spring都是轻量级的,更重要的是,Spring是非侵入式的;
  2. 控制反转——通过控制反转技术实现组件的解耦,容器在对象初始化时主动将该对象所依赖的对象传递给它;
  3. 面向切面——提供了面向切面编程的丰富支持,运行通过分离应用的业务逻辑和系统服务进行内聚性的开发;
  4. 容器——包含并管理应用对象的配置和生命周期,从这个意义上说,Spring是一个容器;
  5. 框架——Spring可以通过将简单的组件配置、组合成为复杂的应用,提供了很多基础功能,可整合其它框架;

二、控制反转(IOC)

1. 核心概念

  • 控制反转(Inversion Of Control,IOC),其核心思想是反转资源获取的方向,容器主动地将资源推送给它所管理的组件,组件所要做的仅是选择一种合适的方式来接收资源。
  • 依赖注入(Dependency Injection,DI),是IOC模式的一种扩展解释,也是IOC技术实现的一种方式,IOC容器在其运行期间动态地将将某种关系注入到对象中,对象以一种预定义好的方式(如Setter方法)接收来自容器的资源注入。

控制反转(IOC)和依赖注入(DI)是从不同的角度描述同一件事情,就是指通过引入IOC容器,利用依赖关系注入的方式,实现对象之间的解耦。

Class A中用到了Class B的对象b,一般情况下,需要在A的代码中显式的new一个B的对象。

采用依赖注入技术之后,A的代码只需要定义一个私有的B对象,不需要直接new来获得这个对象,而是通过相关的容器控制程序来将B对象在外部new出来并注入到A类里的引用中。而具体获取的方法、对象被获取时的状态由配置文件(如XML)来指定。

2. IOC容器

在Spring IOC 容器读取Bean配置创建Bean之前,必须对它进行实例化,只有在容器实例化后,才可以从IOC容器里获取Bean实例并使用。

Spring提供了2种类型的IOC容器实现:

  1. BeanFactory : IOC容器的基本实现,是Spring框架的基础设施,面向Spring本身;
  2. ApplicationContext : 是BeanFactory的子接口,提供了更多的高级特性,面向Spring框架的开发者;

注意:几乎所有的应用场合都直接使用ApplicationContext,而非底层的BeanFactory。

2.1 关于ApplicationContext

ApplicationContext 在初始化上下文时就实例化所有单例的Bean;

  • ApplicationContext的2个主要扩展接口:
  1. ConfigurableApplicationContext : 新增了2个方法 refresh() 和 close(),让ApplicationContext具有启动、刷新和关闭上下文的功能;
  2. WebApplicationContext : 专门用于WEB应用,允许从相对于WEB根目录的路径中完成初始化工作;
  • ApplicationContext的主要实现类:
  1. ClassPathXmlApplicationContext : 从类路径下加载配置问价;
  2. FileSystemXmlApplicationContext :从文件系统下加载配置文件。

3. 依赖注入的方式

Spring支持3种依赖注入的方式:

  1. 属性注入 : 实际工作中最常使用的方式;
  2. 构造器注入 : 适用于初始化固定值;
  3. 工厂方法注入 : 很少使用,不推荐;

3.1 属性注入

属性注入:通过Setter方法注入Bean的属性或者依赖的对象。

 <!-- 属性注入方式,必须有对应Setter方法 -->
 <bean id="user3" class="com.tengol.demo.spring.basic.domain.User">
 <property name="id" value="23"/>
 <property name="username" value="user_03"/>
 </bean>

3.2 构造器注入

构造器注入: 使用构造方法注入Bean的属性值或者依赖的对象。

 <!-- 构造器注入方式,必须有对应的构造器方法 -->
 <bean id="car" class="com.tengol.demo.spring.domain.Car">
 <constructor-arg name="brand" value="Audi"/>
 <constructor-arg name="price" value="223000"/>
 </bean>

当有多个构造器方法并且构造器方法入参较多时,可通过以下方式匹配构造器方法和入参:

(1)按索引匹配入参

 <!-- 按索引匹配构造方法的入参 -->
 <bean id="car" class="com.tengol.demo.spring.domain.Car">
 <constructor-arg value="Audi" index="0"/>
 <constructor-arg value="223000" index="1"/>
 </bean>

(2)按类型匹配入参

 <!-- 按配型匹配构造方法的入参 -->
 <bean id="car" class="com.tengol.demo.spring.domain.Car">
 <constructor-arg value="Audi" type="java.lang.String"/>
 <constructor-arg value="223000" type="java.lang.Double"/>
 </bean>

3.3 注入属性值的细节

注入属性值的细节:

(1)普通属性值

字面值、基本类型及其封装类、String类 等类型的注入,使用value属性或者 <value> 元素标签注入。

 <!-- 使用value标签元素注入字面值 -->
 <bean id="user" class="com.tengol.demo.spring.basic.domain.User">
 <constructor-arg type="int">
 <value>22</value>
 </constructor-arg>
 </bean>

(2)特殊字符的注入

若字面值中包含特殊字符,则可以使用标签 <![CDATA[]]> 包裹字面值。

 <constructor-arg type="java.lang.String">
 <value><![CDATA[<Beijing>]]></value>
 </constructor-arg>

(3)Bean之间的引用关系

组成程序的Bean之间经常会相互协作,可以通过ref属性或者<ref>元素指定对Bean的引用。

 <bean id="car" class="com.tengol.demo.spring.domain.Car">
 <constructor-arg name="brand" value="Audi"/>
 <constructor-arg name="producer" ref="producer"/>
 </bean>

(4)内部Bean

也可以通过在属性或构造器里包含Bean的声明,这种Bean成为内部Bean,不能被外部引用。

 <property name="user">
 <!-- 内部bean,不能被外部bean引用-->
 <bean class="com.tengol.demo.spring.basic.domain.User">
 <constructor-arg name="age" value="22"/>
 <constructor-arg name="username" value="superman"/>
 </bean>
 </property>

(5)赋值为null

可以使用<null/>标签元素为元素赋值为null,和不赋值效果相同,故不常用。

 <!-- 赋值为null -->
 <constructor-arg><null/></constructor-arg>

(6)级联属性赋值

若一个Bean的属性是一个对象,可以使用xx.属性值的方式为级联属性赋值,特别注意的是,前提是先为该对象属性赋值,例如:

 <!-- 对象属性必须先创建 -->
 <bean id="owner" class="com.tengol.demo.spring.domain.Owner">
 <property name="username" value="zhangsan"/>
 </bean>
 <bean id="car" class="com.tengol.demo.spring.domain.Car">
 <property name="brand" value="Audi"/>
 <property name="owner" ref="owner"/>
 <!--级联属性赋值,注意:要求owner必须先创建并赋值-->
 <property name="owner.nickname" value="Zhang"/>
 </bean>

(7)集合属性赋值

若属性为List集合private List<String> addressList; 则

 <property name="addressList">
 <list>
 <value>Beijing</value>
 <value>Shanghai</value>
 </list>
 </property>

若属性为Map集合 private Map<String,String> addressMap;,则

 <property name="addressMap">
 <map>
 <entry key="BJ" value="Beijing"/>
 <entry key="SH" value="Shanghai"/>
 </map>
 </property>

(8)属性集合Properties

若属性为Properties类型的,则

 public class DataSource {
 private Properties properties;
 //省略Setter、Getter
 }
 ?
 <bean id="dataSource" class="com.tengol.demo.spring.basic.domain.DataSource">
 <property name="properties">
 <!-- 使用props 和 prop 为类型为java.util.Properties 的属性赋值-->
 <props>
 <prop key="url">jdbc:mysql://localhost:3306/mydb</prop>
 <prop key="username">root</prop>
 <prop key="password">root123</prop>
 </props>
 </property>
 </bean>

运行结果:

 {
 "properties":
 {
 "password":"root123",
 "url":"jdbc:mysql://localhost:3306/mydb",
 "username":"root"
 }
 }

(9)独立的集合Bean

很多时候会频繁引用某些集合,这时可以配置独立的集合Bean,方便其它地方引用。

下面以List集合为例,同理可定义:<util:map> <util:properties> <util:constant> 。

第一步:导入util 命名空间

 xmlns:util="http://www.springframework.org/schema/util"

第二步:定义集合

 <util:list id="addressList" list-class="java.util.List">
 <value>Beijing</value>
 <value>Shanghai</value>
 </util:list>

第三步:引用

 <property name="addressList" ref="cityList"/>

(10)p命名空间

Spring 2.5 版本开始引入了p命名空间,能够简化属性配置:

 <bean id="user4" class="com.tengol.demo.spring.basic.domain.User" 
 p:username="user_04" p:id="24"/>

4. Bean的作用域

在Spring中,可以在<bean>标签元素的scope属性设置Bean的作用域。

Bean的默认作用域是:singleton,即整个容器只存在一个Bean实例,共享使用该单例Bean。

Bean的作用域有3个:

  1. singleton :单例模式,整个IOC容器范围内只存在一个Bean实例,Bean以单实例存在;
  2. prototype : 多例模式,每次调用getBean()方法都会创建一个Bean实例;
  3. request : 每一个HTTP 请求都会创建一个Bean,仅适用于WEB环境;
  4. session : 同一个HTTP Session共享一个Bean,不同Session使用不同的Bean,仅适用于WEB环境;

需要注意的是:request 和 session 仅适用于WEB环境,即 WebApplicationContext环境。

5. Bean的生命周期

Spring IOC 容器可以管理Bean的生命周期,Spring 允许在Bean 生命周期的特定节点执行定制的任务。

5.1 一般生命周期

Spring IOC 容器对Bean的生命周期的管理过程:

  1. 通过构造器或者工厂方法创建Bean实例;
  2. 为Bean的属性设置值或者设置对其它Bean的引用;
  3. 调用Bean的初始化方法;
  4. 正常获取Bean实例(Bean可以使用了~~~);
  5. 当关闭容器时,调用Bean的销毁方法;

在Bean的声明里设置属性init-method 和 destroy-method 分别指定初始化和销毁方法。

简单示例:验证Bean的生命周期

第一步:定义测试类CycleDemo

 public class CycleDemo {
 private String name;
 //构造方法
 public CycleDemo() {
 System.out.println("CycleDemo : Constructor ...");
 }
 //初始化方法
 public void init(){
 System.out.println("CycleDemo : init ...");
 }
 //销毁方法
 public void destory(){
 System.out.println("CycleDemo : destory ...");
 }
 //普通Getter方法
 public String getName() {
 return name;
 }
 //普通Setter方法
 public void setName(String name) {
 this.name = name;
 System.out.println("CycleDemo : set name ...");
 }
 }

第二步:配置Bean的init-method 和 destroy-method 属性

 <bean id="demoCycle" class="com.tengol.demo.spring.basic.domain.CycleDemo"
 init-method="init" destroy-method="destory">
 <property name="name" value="demo for cycle"/>
 </bean>

第三步:编写测试用例

 CycleDemo demo = (CycleDemo) context.getBean("demoCycle");
 System.out.println("context.getBean : " + gson.toJson(demo));
 //关闭容器,调用销毁方法
 context.close();

运行结果如下:

 CycleDemo : Constructor ...
 CycleDemo : set name ...
 CycleDemo : init ...
 context.getBean : {"name":"demo for cycle"}
 CycleDemo : destory ...

5.2 后置处理器

Bean后置处理器,允许在调用初始化方法前后对Bean进行额外处理,该处理是针对IOC容器中所有Bean的,而非只对特定Bean处理。

典型应用:检查 Bean 属性的正确性 或 根据特定的标准更改 Bean 的属性。

定义Bean后置处理器,需要实现接口org.springframework.beans.factory.config.BeanPostProcessor:

  • postProcessBeforeInitialization : 在初始化方法之前执行的操作;
  • postProcessAfterInitialization : 在初始化方法之后执行的操作。

简单示例:带后置处理器的生命周期

第一步:定义Bean后置处理器

 public class MyBeanPostProcessor implements BeanPostProcessor {
 //初始化前调用
 public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
 System.out.println("postProcessBeforeInitialization ...");
 return bean;
 }
 //初始化后调用
 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
 System.out.println("postProcessAfterInitialization ...");
 return bean;
 }
 }

第二步:声明Bean后置处理器

 <bean class="com.tengol.demo.spring.basic.domain.MyBeanPostProcessor"/>

第三步:编写测试用例

 @Test
 public void testCycle(){
 CycleDemo demo = (CycleDemo) context.getBean("demoCycle");
 System.out.println("context.getBean : " + gson.toJson(demo));
 //关闭容器
 context.close();
 }

运行结果:

 CycleDemo : Constructor ...
 CycleDemo : set name ...
 postProcessBeforeInitialization ...
 CycleDemo : init ...
 postProcessAfterInitialization ...
 context.getBean : {"name":"demo for cycle"}
 CycleDemo : destory ...

5.3 带后置处理器的生命周期

增加Bean后置处理器后,Bean的生命周期如下:

  1. 通过构造器或工厂方法创建实例;
  2. 为Bean的属性设置值或其他Bean的引用;
  3. 将Bean实例传递给Bean的后置处理器: postProcessBeforeInitialization 方法;
  4. 调用Bean的初始化方法;
  5. 将Bean实例传递给Bean的后置处理器: postProcessAfterInitialization 方法;
  6. 正常获取Bean实例;
  7. 当关闭容器时,调用Bean的销毁方法。

特别注意的是:

  1. Bean后置处理器不是对特定Bean实例的操作,而是对所有Bean的拦截处理,这个过程中可以根据类型判断,只对某些类型Bean处理;
  2. Bean后置处理器的2个方法,权限非常大,可以”偷天换日“,比如new 一个对象再返回,将传过来的Bean彻底改变。
  3. Bean后置处理器的2个方法,都有2个参数:Object bean, String beanName;
  • bean : 第一个参数是bean实例,
  • beanName : IOC容器中配置的bean名称

6. Bean的配置方式

Spring IOC 容器实例化Bean时允许3种方式:

  1. 通过配置全类名:底层原理是通过反射技术创建实例;
  2. 通过工厂方法:分为静态工厂方法和实例工厂方法,将对象创建过程封装到工厂方法中,再调用该工厂方法;
  3. 通过FactoryBean:实现该接口的特定方法,可以完成Bean的实例化;

6.1 通过全类名

在XML配置文件中,使用class属性配置全类名,底层通过反射技术将Bean实例化到IOC容器。

 <bean id="user" class="com.tengol.demo.spring.generic.domain.User"/>

6.2 通过工厂方法

6.2.1 简介

Spring IOC 容器允许通过调用工厂方法创建Bean实例,调用的工厂方法分为两类:

  1. 静态工厂方法:将对象创建过程封装到静态方法中;
  2. 实例工厂方法:将对象创建过程封装到另外一个对象实例的方法中。

将对象创建过程封装到工厂的方法中,当客户端需要请求对象时,只需要简单地调用静态方法或实例方法就行,不需要关心对象创建的细节。

6.2.2 示例-静态工厂方法

在XML配置文件中,在Bean的配置属性中指定如下信息:

  • class : 指定拥有工厂方法的类名
  • factory-method : 指定工厂方法的名称
  • constructory-arg: 为方法传递参数

第一步:创建静态工厂方法

 /**
 * 静态工厂方法创建Car实例
 */
 public class StaticCarFactory {
 ?
 private static Map<String, Car> carMap = new HashMap<String, Car>();
 ?
 static {
 carMap.put("Audi", new Car("Audi",400000));
 carMap.put("Ford", new Car("Ford",300000));
 }
 //静态工厂方法,以静态方法形式封装对象Car创建过程
 public static Car getCar(String brand){
 return carMap.get(brand);
 }
 }

第二步:配置Bean (关键步骤)

 <!--通过静态方法工厂创建Bean,注意:不是创建工厂的Bean-->
 <bean id="car" class="com.tengol.demo.spring.factory.StaticCarFactory"
 factory-method="getCar">
 <constructor-arg value="Audi"/>
 </bean>

第三步:编写测试用例

 @Test
 public void testStaticFactory(){
 Car car = (Car) context.getBean("car");
 System.out.println(car);
 }

程序运行结果:

 Car{brand='Audi', price=400000, weight=null, owner=null}

6.2.3 示例-实例工厂方法

在XML配置文件中,通过如下信息配置Bean:

  • bean 的 factory-bean 属性 : 指定拥有该工厂方法的 Bean
  • bean 的 factory-method 属性 : 指定该工厂方法的名称
  • 使用 construtor-arg 元素为工厂方法传递方法参数

示例:

第一步:定义实例工厂方法

 /**
 * 实例工厂方法
 */
 public class InstanceCarFactory {
 private Map<String, Car> carMap;
 
 public InstanceCarFactory() {
 carMap = new HashMap<String, Car>();
 carMap.put("Audi", new Car("Audi", 400000));
 carMap.put("Ford", new Car("Ford", 300000));
 System.out.println("Instance Car Factory ....");
 }
 //实例工厂方法,封装对象创建过程
 public Car getCar(String brand) {
 System.out.println("Get Car by Brand : " + brand);
 return carMap.get(brand);
 }
 ?
 }

第二步:在XML文件配置Bean

 <!-- 通过实例工厂方法创建Bean -->
 <!-- 1.声明实例工厂 -->
 <bean id="carFactory" class="com.tengol.demo.spring.factory.InstanceCarFactory"/>
 ?
 <!-- 2.引用实例工厂方法创建Bean -->
 <bean id="car2" factory-bean="carFactory" factory-method="getCar">
 <constructor-arg value="Ford"/>
 </bean>

第三步:编写测试用例

 @Test
 public void testInstanceFactory(){
 Car newCar = (Car) context.getBean("car2");
 System.out.println(newCar);
 }

程序运行结果

 Instance Car Factory ....
 Get Car by Brand : Ford
 Car{brand='Ford', price=300000, weight=null, owner=null}

(3)通过FactoryBean

Spring中有两种类型的Bean,一种是普通的Bean,另外一种是工厂Bean,即FactoryBean。

工厂Bean与普通Bean不同,它返回的对象不是指定类的实例,而是其方法getObject()所返回的特定对象。

示例-通过FactoryBean创建Bean

第一步:定义FactoryBean

 public class CarFactoryBean implements FactoryBean<Car> {
 //定义 FactoryBean 返回的实例对象
 public Car getObject() throws Exception {
 return new Car("Audi",400000);
 }
 //定义 FactoryBean 返回的实例的类型
 public Class<?> getObjectType() {
 return Car.class;
 }
 //定义 FactoryBean 返回的实例是否为单例
 public boolean isSingleton() {
 return true;
 }
 }

第二步:配置Bean,与普通Bean配置方式相同

 <bean id="carFb" class="com.tengol.demo.spring.factory.CarFactoryBean"/>

第三步:编写测试用例

 @Test
 public void testFactoryBean(){
 Car car = (Car) context.getBean("carFb");
 System.out.println(car);
 }

程序运行结果:

 Car{brand='Audi', price=400000, weight=null, owner=null}

7. 其它

7.1 Bean的自动装配

Spring IOC容器可以实现Bean的自动装配(不需要手动在XML配置中指定),需要做的就是:

在<bean>元素的autowire属性里指定自动装配的模式

7.1.1 自动装配模式

自动装配有3种模式:

  1. byType :根据类型自动装配,若存在多个类型,则无法装配
  2. byName :根据名称自动装配,根据Setter风格的属性名装配
  3. constructor :根据构造器自动装配,不推荐使用,因为存在多个构造器时此方式很复杂。

7.1.2 缺点

自动装配是Bean级别的装配,导致自动装配不够灵活,自动装配的缺点:

(1)若使用了自动装配,则所有属性都必须使用该方式,若只希望装配个别属性,则不适用;

(2)自动装配的模式只能选择其中一种,不能同时使用多种方式;

一般情况下,在实际项目中很少使用自动装配,相比自动装配带来的好处,明确清晰的配置更有说服力。

7.2 Bean的继承及依赖

此处的关系不是指对象的继承或依赖关系,而是指<bean>配置上的继承或依赖关系。

7.2.1 继承

Spring 运行继承 Bean的配置,被继承的Bean称为父Bean,继承父Bean的称为子Bean;

子Bean与父Bean的关系如下:

  1. 子Bean从父Bean中继承配置,包括Bean的属性配置,但不包括 autowire / abstract 属性;
  2. 子Bean也可以覆盖从父Bean继承过来的配置;
  3. 父Bean可以作为配置模板,也可以作为Bean实例;
  4. 若Bean设置属性abstract=true时,则Bean作为模板存在,Spring将不会实例化这个Bean;
  5. 可以忽略父Bean的class属性,让子Bean指定自己的class属性,前提是父Bean必须设置abstract=true

使用属性parent表示继承关系,如下所示:

 <bean id="hero" class="com.tengol.demo.spring.basic.domain.User"
 p:id="22" p:username="hero" p:nickname="marvel"/>
 <bean id="iron-man" parent="hero"
 p:id="23" p:username="iron-man"/>

运行结果:

 {"id":22,"username":"hero","nickname":"marvel"}
 {"id":23,"username":"iron-man","nickname":"marvel"}

7.2.2 依赖

Spring 运行用户通过depends-on属性设定Bean前置依赖的Bean,即必须该依赖Bean存在时才能创建当前Bean,前置依赖Bean必须在本Bean实例化之前创建好。

如果前置依赖于多个Bean,则可以通过逗号、空格等方式配置Bean的名称;

使用属性depends-on表示前置依赖关系,如下所示:

第一步:声明前置依赖Bean

 <!-- 定义前置依赖Bean,此处为String类型 -->
 <bean id="uname" class="java.lang.String">
 <constructor-arg value="Banner"/>
 </bean>

第二步:设置前置依赖的Bean

 <!-- 该Bean的实例化依赖于另一个Bean,也就是uname -->
 <bean id="hulk" class="com.tengol.demo.spring.basic.domain.User"
 p:id="24" p:nickname="marvel" depends-on="uname"/>

若前置依赖Bean未创建,则报错如下:

 org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'hulk' defined in class path resource [spring.xml]: 'hulk' depends on missing bean 'uname'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'uname' available
 ......
 Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'uname' available
 ......

7.3 使用外部配置文件

Spring 允许将Bean配置的部分内容(比如文件路径、数据库连接信息等系统部署细节信息)外移到属性文件中,可以在Bean配置文件中通过${var}形式写法的变量引用配置文件中的属性,方便应用部署。

原理:Spring提供了一个名为PropertyPlaceholderConfigurer的BeanFactory后置处理器,该处理器从属性文件中加载属性,并使用这些属性替换Bean配置文件中对应的${var}形式的变量。

操作过程如下:

第一步:定义属性文件db.properties

 jdbcUrl=jdbc:mysql://localhost:3306/mydb
 driverClass=com.mysql.jdbc.Driver
 username=root
 password=root123

第二步:Bean配置文件中引用属性

 <!-- 加载外部属性配置文件 -->
 <context:property-placeholder location="classpath:db.properties"/>
 <!-- 读取外部属性文件中的值,提高灵活性 -->
 <bean id="dataSourceC3p0" class="com.mchange.v2.c3p0.ComboPooledDataSource">
 <property name="driverClass" value="${driverClass}"/>
 <property name="jdbcUrl" value="${jdbcUrl}"/>
 <property name="user" value="${username}"/>
 <property name="password" value="${password}"/>
 </bean>

7.4 泛型依赖注入

7.4.1 简介

Spring 4.0 版本开始支持对带泛型的Bean进行依赖注入,这个特性允许我们在Spring注入时应用泛型的特性,将公共代码抽离到泛型基类,方便维护和修改。

Spring4的新特性就是把泛型的具体类型(如下文业务层中BaseRepository<User>中的User)也作为类的一种分类方法(Qualifier).这样我们的userRepository和departmentRepository虽然是同一个类BaseRepository,但是因为泛型的具体类型不同,也会被区分开。

在Spring 4.0之前,BaseRepository<User>和BaseRepository<Department>会被认为是同一类型,一般需要用名字等其他方式加以区分。从Spring 4.0 开始,Spring会正确的识别声明的泛型类别,并且根据泛型给持久层Bean进行分类,User和Department的持久层Bean可以被正确的区分,并且注入到上一层,这为我们在代码中使用泛型提供了极大的便利。

7.4.2 简单示例:泛型依赖注入

第一步:定义实体User 和 Department

 //用户实体类
 public class User {
 private Integer id;
 private String userName;
 private String nickName;
 //省略Setter和Getter方法
 }
 //部门实体类
 public class Department {
 private Integer id;
 private String deptNo;
 private String deptName;
 //省略Setter和Getter方法
 }

第二步:定义持久化层

定义2种类型的持久化类:

  1. 定义泛型基类:BaseRepository<T>
  2. 定义继承该基类的业务子类:UserRepository 和 DepartmentRepository
 //泛型基类
 public abstract class BaseRepository<T> {
 public void add(T t) {
 System.out.println("Save : " + t);
 }
 }
 //继承该基类的2个业务子类
 @Repository
 public class UserRepository extends BaseRepository<User> {
 }
 ?
 @Repository
 public class DepartmentRepository extends BaseRepository<Department> {
 }

第三步:定义Service层

定义2种类型的Service类:

  1. 定义泛型基类:BaseService<T>;
  2. 定义继承该基类的业务子类:UserService 和 DepartmentService;
 //泛型基类
 public abstract class BaseService<T> {
 
 @Autowired
 private BaseRepository<T> baseRepository;
 ?
 public void add(T t) {
 System.out.println("add with : " + baseRepository);
 baseRepository.add(t);
 System.out.println("add ok!");
 }
 }
 //继承该基类的2个业务子类
 @Service
 public class UserService extends BaseService<User>{
 }
 ?
 @Service
 public class DepartmentService extends BaseService<Department> {
 }

第四步:配置注解自动扫描包

 <?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:context="http://www.springframework.org/schema/context"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
 http://www.springframework.org/schema/beans/spring-beans.xsd
 http://www.springframework.org/schema/context
 http://www.springframework.org/schema/context/spring-context.xsd">
 <!-- 配置自动扫描的包 -->
 <context:component-scan base-package="com.tengol.demo.spring.generic"/>
 </beans>

第五步:编写测试用例

 public class GenericBeanTest {
 @Test
 public void testGenericBean(){
 ApplicationContext context = new ClassPathXmlApplicationContext("spring-generic.xml");
 UserService userService = context.getBean(UserService.class);
 userService.add(new User());
 System.out.println("------");
 DepartmentService departmentService = context.getBean(DepartmentService.class);
 departmentService.add(new Department());
 }
 }

运行结果如下:

 add with : com.tengol.demo.spring.generic.dao.UserRepository@6f3b5d16
 Save : com.tengol.demo.spring.generic.domain.User@78b1cc93
 add ok!
 ------
 add with : com.tengol.demo.spring.generic.dao.DepartmentRepository@6646153
 Save : com.tengol.demo.spring.generic.domain.Department@21507a04
 add ok!

(未完待续......)

附录-参考资料:

本文档参考了尚硅谷Spring教程和网上相关参考资料,感谢互联网的分享。

尚硅谷

[尚硅谷-官网] http://www.atguigu.com/

关于Spring IOC容器及AOP

[Spring-百度百科] https://baike.baidu.com/item/spring/85061?fr=aladdin

[Spring-官方文档] https://spring.io/projects/spring-framework#learn

[控制反转-百度百科] https://baike.baidu.com/item/控制反转/1158025?fr=aladdin

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表