网站首页 > 博客文章 正文
Spring-基础笔记(一)
一、Spring核心
Spring是一个轻量级控制反转(IOC)和面向切面(AP)的容器框架。
Spring是一个开源框架,它由Rod Johnson创建。它是为了解决企业应用开发的复杂性而创建的。Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。
- 目标: 解决企业应用开发的复杂性
- 功能: 使用基本的JavaBean代替EJB,并提供了更多的企业应用功能
- 范围: 任何Java应用
Spring的特性
- 轻量级——从大小和开销两方面而言Spring都是轻量级的,更重要的是,Spring是非侵入式的;
- 控制反转——通过控制反转技术实现组件的解耦,容器在对象初始化时主动将该对象所依赖的对象传递给它;
- 面向切面——提供了面向切面编程的丰富支持,运行通过分离应用的业务逻辑和系统服务进行内聚性的开发;
- 容器——包含并管理应用对象的配置和生命周期,从这个意义上说,Spring是一个容器;
- 框架——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容器实现:
- BeanFactory : IOC容器的基本实现,是Spring框架的基础设施,面向Spring本身;
- ApplicationContext : 是BeanFactory的子接口,提供了更多的高级特性,面向Spring框架的开发者;
注意:几乎所有的应用场合都直接使用ApplicationContext,而非底层的BeanFactory。
2.1 关于ApplicationContext
ApplicationContext 在初始化上下文时就实例化所有单例的Bean;
- ApplicationContext的2个主要扩展接口:
- ConfigurableApplicationContext : 新增了2个方法 refresh() 和 close(),让ApplicationContext具有启动、刷新和关闭上下文的功能;
- WebApplicationContext : 专门用于WEB应用,允许从相对于WEB根目录的路径中完成初始化工作;
- ApplicationContext的主要实现类:
- ClassPathXmlApplicationContext : 从类路径下加载配置问价;
- FileSystemXmlApplicationContext :从文件系统下加载配置文件。
3. 依赖注入的方式
Spring支持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个:
- singleton :单例模式,整个IOC容器范围内只存在一个Bean实例,Bean以单实例存在;
- prototype : 多例模式,每次调用getBean()方法都会创建一个Bean实例;
- request : 每一个HTTP 请求都会创建一个Bean,仅适用于WEB环境;
- session : 同一个HTTP Session共享一个Bean,不同Session使用不同的Bean,仅适用于WEB环境;
需要注意的是:request 和 session 仅适用于WEB环境,即 WebApplicationContext环境。
5. Bean的生命周期
Spring IOC 容器可以管理Bean的生命周期,Spring 允许在Bean 生命周期的特定节点执行定制的任务。
5.1 一般生命周期
Spring IOC 容器对Bean的生命周期的管理过程:
- 通过构造器或者工厂方法创建Bean实例;
- 为Bean的属性设置值或者设置对其它Bean的引用;
- 调用Bean的初始化方法;
- 正常获取Bean实例(Bean可以使用了~~~);
- 当关闭容器时,调用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的生命周期如下:
- 通过构造器或工厂方法创建实例;
- 为Bean的属性设置值或其他Bean的引用;
- 将Bean实例传递给Bean的后置处理器: postProcessBeforeInitialization 方法;
- 调用Bean的初始化方法;
- 将Bean实例传递给Bean的后置处理器: postProcessAfterInitialization 方法;
- 正常获取Bean实例;
- 当关闭容器时,调用Bean的销毁方法。
特别注意的是:
- Bean后置处理器不是对特定Bean实例的操作,而是对所有Bean的拦截处理,这个过程中可以根据类型判断,只对某些类型Bean处理;
- Bean后置处理器的2个方法,权限非常大,可以”偷天换日“,比如new 一个对象再返回,将传过来的Bean彻底改变。
- Bean后置处理器的2个方法,都有2个参数:Object bean, String beanName;
- bean : 第一个参数是bean实例,
- beanName : IOC容器中配置的bean名称
6. Bean的配置方式
Spring IOC 容器实例化Bean时允许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实例,调用的工厂方法分为两类:
- 静态工厂方法:将对象创建过程封装到静态方法中;
- 实例工厂方法:将对象创建过程封装到另外一个对象实例的方法中。
将对象创建过程封装到工厂的方法中,当客户端需要请求对象时,只需要简单地调用静态方法或实例方法就行,不需要关心对象创建的细节。
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种模式:
- byType :根据类型自动装配,若存在多个类型,则无法装配
- byName :根据名称自动装配,根据Setter风格的属性名装配
- constructor :根据构造器自动装配,不推荐使用,因为存在多个构造器时此方式很复杂。
7.1.2 缺点
自动装配是Bean级别的装配,导致自动装配不够灵活,自动装配的缺点:
(1)若使用了自动装配,则所有属性都必须使用该方式,若只希望装配个别属性,则不适用;
(2)自动装配的模式只能选择其中一种,不能同时使用多种方式;
一般情况下,在实际项目中很少使用自动装配,相比自动装配带来的好处,明确清晰的配置更有说服力。
7.2 Bean的继承及依赖
此处的关系不是指对象的继承或依赖关系,而是指<bean>配置上的继承或依赖关系。
7.2.1 继承
Spring 运行继承 Bean的配置,被继承的Bean称为父Bean,继承父Bean的称为子Bean;
子Bean与父Bean的关系如下:
- 子Bean从父Bean中继承配置,包括Bean的属性配置,但不包括 autowire / abstract 属性;
- 子Bean也可以覆盖从父Bean继承过来的配置;
- 父Bean可以作为配置模板,也可以作为Bean实例;
- 若Bean设置属性abstract=true时,则Bean作为模板存在,Spring将不会实例化这个Bean;
- 可以忽略父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种类型的持久化类:
- 定义泛型基类:BaseRepository<T>
- 定义继承该基类的业务子类: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类:
- 定义泛型基类:BaseService<T>;
- 定义继承该基类的业务子类: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
猜你喜欢
- 2024-09-26 基于 redis 和 ehcache 的两级缓存组件 uncode-cache
- 2024-09-26 melon-idfactory主键工厂,提供ID生成服务,保证ID的唯一性。
- 2024-09-26 Spring Boot中的Properties(springboot property)
- 2024-09-26 带你读源码2——Spring源码分析之容器的刷新 - refresh()
- 2024-09-26 super-diamond 结合springboot的使用经验(用过的都说好)
- 2024-09-26 还在为数据同步而苦恼吗?手把手教你实现canal数据同步(一)
- 2024-09-26 源码分析:Spring是如何获取容器中的Bean?
- 2024-09-26 五种方式让你在java中读取properties文件内容
- 2024-09-26 图表显示日志离线信息(图表显示日志离线信息什么意思)
- 2024-09-26 Java程序员 必须掌握的 20+ 种 Spring 常用注解
你 发表评论:
欢迎- 最近发表
-
- 给3D Slicer添加Python第三方插件库
- Python自动化——pytest常用插件详解
- Pycharm下安装MicroPython Tools插件(ESP32开发板)
- IntelliJ IDEA 2025.1.3 发布(idea 2020)
- IDEA+Continue插件+DeepSeek:开发者效率飙升的「三体组合」!
- Cursor:提升Python开发效率的必备IDE及插件安装指南
- 日本旅行时想借厕所、买香烟怎么办?便利商店里能解决大问题!
- 11天!日本史上最长黄金周来了!旅游万金句总结!
- 北川景子&DAIGO缘定1.11 召开记者会宣布结婚
- PIKO‘PPAP’ 洗脑歌登上美国告示牌
- 标签列表
-
- ifneq (61)
- messagesource (56)
- aspose.pdf破解版 (56)
- promise.race (63)
- 2019cad序列号和密钥激活码 (62)
- window.performance (66)
- qt删除文件夹 (72)
- mysqlcaching_sha2_password (64)
- ubuntu升级gcc (58)
- nacos启动失败 (64)
- ssh-add (70)
- jwt漏洞 (58)
- macos14下载 (58)
- yarnnode (62)
- abstractqueuedsynchronizer (64)
- source~/.bashrc没有那个文件或目录 (65)
- springboot整合activiti工作流 (70)
- jmeter插件下载 (61)
- 抓包分析 (60)
- idea创建mavenweb项目 (65)
- vue回到顶部 (57)
- qcombobox样式表 (68)
- vue数组concat (56)
- tomcatundertow (58)
- pastemac (61)
本文暂时没有评论,来添加一个吧(●'◡'●)