1、Spring 框架的历史由来
Spring 框架是一个轻量级的企业级应用框架,兴起于 2003 年。当时流行的传统 Java EE 框架均为过于臃肿的“重量级”构架体系,其开发效率,开发难度和实际的性能都不能满足人的需求。 Spring 中文含义“春天”,正如它的名称一样,Spring 框架的诞生给人一种格外清新的感觉,蕴意着勃勃生机。
目前,Spring框架已经发展为一个功能丰富且易用的集成框架,其核心是一个完整的基于控制反转的轻量级容器,用户可以使用它建立增加的应用程序。在此容器之上,Spring 框架提供了大量实用的服务,并将很多高质量的开源项目集成到统一的框架之上。
2、Spring IoC
IoC 的全称为 Inversion of Control,即控制反转,意为把对资源的控制权反转过来。IoC 不是一项开发技术,也不是具体功能,而是面向对象编程中的一种设计理念。
2.1 IoC 和依赖注入
Dependency Injection(DI),翻译为依赖注入。其实,IoC 和 DI 所表达的意思基本相同,IoC 表示创建对象的控制权转移,是一种程序 设计思路;而 DI 表示将依赖的对象注入需要的类中,是 IoC 设计思想的一种具体实现方式。
2.2 第一个 Spring 程序
1、下载依赖 jar 包
<!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.3.9</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.9</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.9</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-expression -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.3.9</version>
</dependency>
2、编写 Spring 配置文件
创建 applicationContext.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 元素声明需要 Spring 创建的实例,该实例的类型通过 class 属性指定,并通过 id 属性为该实例指定一个名称,以便在程序中使用-->
<bean id="helloSpring" class="cn.springdemo.HelloSpring">
<!--property 元素用来为实例的属性赋值,此处调用 setHello() 方法实现赋值操作-->
<property name="hello" value="反转的人生,如此惊艳"/>
</bean>
</beans>
bean 标签中有两个常用属性,分别是 id 和 class。
id :定义的 Bean 实例的唯一标识;
class:定义的 Bean 实例的全类名,即类型。
ref:把对象注入依赖它的 Bean 中
property 标签用来定义 Bean 实例中的属性,其中的 name 属性并不对应 Bean 实例中的属性名称,而是与 setter 方法对应。
扩展知识
使用 bean 标签定义一个组件时,通常需要使用 id 属性为其指定一个用来访问的唯一的名称。
如果想为 Bean 指定更多的别名,可以通过 name 属性指定,名称之间使用逗、分号或空格进行分割。
3、编写测试代码通过 Spring 进行属性注入
@Test
public void testHelloSpring(){
/*通过 ClassPathXmlApplicationContext 实例化 Spring 的上下文*/
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContex.xml");
/*通过 ApplicationContext 的 getBean() 方法,根据 id 来获取 bean 的实例*/
HelloSpring helloSpring = context.getBean("helloSpring", HelloSpring.class);
/*执行 print() 方法*/
helloSpring.print();
}
ApplicationContext 是一个接口,负责读取 Spring 配置文件,管理对象的加载、生成、维护 Bean 对象与 Bean 对象之间的依赖关系,负责 Bean 的生命周期等。
ClassPathXmlAplicationContext 是 ApplicationContext 接口的实现类,用于从 classpath 路径中读取 Spring 配置文件。
总结
Spring 会根据配置文件中的信息。自动创建对应的实例对象,并通过 setter 方法为其属性进行赋值。实例的属性值将不再由程序中的代码来主动创建和管理,改为被动接受 Spring 的注入,使组件之间以配置文件而不是影编码的方式组织在一起。这就是依赖注入的工作原理。
5.3 Spring AOP
AOP 的全称是 Aspect Oriented Prgramming。中文含义为面向切面编程,是相对面向对象而言的。切面是横切面的意思,切开的切口就是切面,即 AOP 是把对象或固定的流程“切开”,找到其中的公共行为,并将其进行提取统一处理的一种思想。
AOP 认为是对 OOP的一种补充。这种横向式的编程方式更容易处理不同的对象、不同模块之间的共同业务,如访问控制、事务管理、性能监测等。
AOP 是依赖动态代理实现的,具体步骤:先把业务代码中的共性内容抽离出来,然后通过动态代理的方式把抽离出来的共性代码织入业务代码中来实现对原有代码的增强处理。
在代理模式中可以为原对象设置一个代理对象,被代理的对象也可称为目标对象,代理对象为目标对象的方法提供一个代理方法。
提供声明式事务;允许用户自定义切面
-
横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志、安全、缓存、事务等等……
-
切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。
-
通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。
-
目标(Target):被通知对象。
-
代理(Proxy):向目标对象应用通知之后创建的对象
-
切入点(PointCut):切面通知 执行的”地点“的定义。
-
连接点(JoinPoint):与切入点匹配的执行点。
AOP面向切面编程,是为了提高程序的灵活性,是一个可插拔的技术,和hibernate的集成没有关系
5.3.1 Spring AOP 在项目中的运用
1、引入依赖的 jar 包
Spring AOP 还依赖 AOP Alliance 和 AspectJ 项目中的组件
<!-- https://mvnrepository.com/artifact/aopalliance/aopalliance -->
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
2、编写保存用户的业务代码及用于增强处理的代码
/**
* 用户模块业务层实现
*/
public class UserServiceImpl implements UserService{
/**
* 声明接口类型的引用,和具体实现类解耦合
*/
private UserDao dao;
/**
* 保存用户信息
*/
@Override
public void save(SysUser user) {
//调用 UserDao 的方法保存用户信息
dao.save(user);
}
public void setDao(UserDao dao) {
this.dao = dao;
}
}
3、编写 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"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userDaoImpl" class="Train3.dao.SysUserDaoImpl"/>
<bean class="Train3.service.SysUserServiceImpl" id="service">
<property name="userDao" ref="userDaoImpl"/>
</bean>
<!--声明增强方法所在的Bean-->
<bean id="theLogger" class="aop.UserServiceLogger"/>
<!--配置切面-->
<aop:config>
<!--定义切入点-->
<aop:pointcut id="pointcut" expression="execution(public void save(cvs.pojo.SysUser))"/>
<!--引用包含增强方法的Bean-->
<aop:aspect ref="theLogger">
<!-- 将before() 方法定义为前置增强并引用pointcut切入点-->
<aop:before method="before" pointcut-ref="pointcut"/>
<!--将afterReturning()方法定义为后置增强并引用pointcut切入点-->
<!--通过returning属性指定为名为result的参数注入返回值-->
<aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="result"/>
</aop:aspect>
</aop:config>
</beans>
注意包含以下几项内容
1、在 beans 元素中需要添加 aop 命名空间,以导入与 AOP 相关的标签
2、与 AOP 相关的配置都放在 aop:config 标签中
3、< aop:pointcut> 表示切入点,id 属性为该切入点的名称,expression 属性为该切入点的表达式,execution 是切入点指示符,它的括号中是一个切入点表达式,可以配置需要切入增强处理的方法的特征,切入点表达式支持模糊匹配。
- public * addNewUser(entity.User):“*” 表示匹配所有类型的返回值
- public void *(entity.User):" * " 表示匹配所有方法名
- public void addNewUser(…) :“ * ” 表示匹配任意参数个数和类型
- *com.service. *. * (…):匹配 com.service 包下所有类的所有方法
- *com.service… *. * (…):匹配 com.service 包及其子包下所有类的所有方法
4、< aop:aspect> 引用增强的 Bean,ref 属性指定增强 Bean 的名称
- < aop:before> 声明前置增强,属性 pointcut-ref,表示引用的切入点
- < aop:after-returning>声明后置增强,pointcut-ref 属性表示引用的切入点,returning 属性表示需要注入返回值的属性名
5、把增强处理插入切入点的过程称为织入。配置文件所涉及的工作可以总结为,把日志功能抽离取为单独的类,并交由 Spring管理,声明需要插入日志功能的位置,然后将日志功能代码织入指定位置
3、增强类型扩展
3.1、异常抛出增强
指当目标对象方法抛出异常时进行织入操作的一种增强方式。
语法
<aop:config>
<aop:aspect ref="增强方
ter-throwing method="增强处理方法" pointcut-ref="切入点" throwing="e"/>
</aop:aspect>
</aop:config>
< aop:after-throwing > 标签表示增强处理类型为异常抛出增强。method 属性表示增强处理最终调用的方法,用以捕获异常。
如果需要获取抛出的异常信息,可以为增强方法声明相关类型的参数,并通过 throwing 属性指定该参数名称。
示例
public class ErrorLogger {
private static final Logger log = Logger.getLogger(ErrorLogger.class);
public void afterThrowing(JoinPoint jp, RuntimeException e){
System.out.println("注解:");
log.error(jp.getSignature().getName() + " 方法发生异常:" + e);
}
}
3.2、最终增强
无论目标对象的方法正常运行还是抛出异常,该增强处理都会被执行的一种增强方式。
与 Java 中 finally 代码块的作用相似,通常用于释放资源等操作,具有可灵活插拔的特点。
语法
<aop:config>
<aop:aspect ref="增强方法所在的 Bean ">
<aop:after method="增强处理方法" pointcut-ref="切入点"/>
</aop:aspect>
</aop:config>
< aop:after> 标签表示增强处理类型为最终增强。method 属性表示增强处理最终调用的方法。
示例
public class AfterLogger {
private static final Logger log = Logger.getLogger(AfterLogger.class);
public void afterLogger(JoinPoint jp){
log.info(jp.getSignature().getName() + " 方法结束执行。");
}
}
3.3、环绕增强 – 功能最强大
在目标对象方法前后都可以进行织入的一种增强处理方式。
可以获取或修改方法的参数,返回值,可以对它进行异常处理,甚至可以决定目标方法是否被执行。
语法
<aop:config>
<aop:aspect ref="增强方法所在的 Bean ">
<aop:around method="增强处理方法" pointcut-ref="切入点"/>
</aop:aspect>
</aop:config>
< aop:around > 标签表示增强处理类型为环绕增强。method 属性表示增强处理最终调用的方法。
示例
public class AroundLogger {
private static final Logger log = Logger.getLogger(AroundLogger.class);
public Object aroundLogger(ProceedingJoinPoint jp)throws Throwable{
log.info("调用 " + jp.getTarget() + " 的 " + jp.getSignature().getName() + " 方法。方法入参:" + Arrays.toString(jp.getArgs()));
try {
Object result = jp.proceed(); //执行目标方法并获得其返回值
log.info("调用 " + jp.getTarget() + " 的 "+ jp.getSignature().getName() + " 方法,方法返回值:" + result);
return result;
}catch (Throwable e){
log.error(jp.getSignature().getName() + " 方法发生异常:" + e);
throw e;
}finally {
log.info(jp.getSignature().getName() + " 方法结束执行。");
}
}
}
通过增强方法声明 ProceedingJoinPoint 类型的参数,可以获取连接点信息,方法与 JoinPoint 相同。
ProceedingJoinPoint 是 JoinPoint 的子接口,不仅封装目标方法及其入参数组,还封装了被代理的目标对象,通过它的 proceed() 方法
可以调用真正的目标方法,从而达到对连接点的完全控制。
4、 依赖注入方式扩展
4.1、构造注入
Spring 框架通过构造方法为属性赋值的一种注入方式。
构造注入可以在对象初始化时为属性赋值,具有良好的时效性。
4.1.1、语法
<bean id="唯一标识" class="类的全路径">
<constructor-arg name="参数名称" ref="依赖对象"/>
</bean>
< constructor-arg >标签表示使用构造器注入的方式向当前 Bean 注入依赖,其内部的 ref 标签用来配置被注入的 Bean 名称。
4.1.2、扩展
- 如果有多个参数,可以在当前 bean 标签下配置多个 < constructor-arg >标签。
- 除使用 name 属性区别多个参数外,还可以使用 < constructor-arg > 标签中的 index 属性指定参数的位置索引,位置从 0 开始。
- < constructor-arg > 标签提供了 type 属性,用以标识参数的类型。
4.1.3、总结
- 构造注入方式具有良好的时效性,在对象实例化时就能得到所依赖的对象,便于在对象初始化方法中使用所依赖的对象。但是,其受限于方法重载的形式,使用灵活不足。相比而言,设值注入使用灵活,但时效性不足,并且大量的 set() 方法增加了类的复杂性。Spring 框架并不倾向于某种注入方式,用户应该根据实际情况进行合理的选择
4.2、p 命名空间注入
特点是使用属性而不是元素的形式配置 Bean 的属性,从而简化了 Bean 的配置。
4.2.1、语法
<bean id="唯一标识" class="类的全路径"
p:"属性1"="注入的值" p:"属性2"="注入的值"/>
<bean id="唯一标识" class="类的全路径"
p:user-ref="注入的 Bean"/>
使用 p 标签注入属性时,如果有多个属性,写多个 p 标签即可。
4.2.2、使用 p 标签命名空间改造配置文件
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
4.3、不同数据类型的注入
4.3.1、空字符串和 null
当参数是空字符串或 null 时,可以使用< value ></ value >注入字符串值,使用 < null/> 注入 null 值
<bean id="entity" class="cn.pojo.TestEntity">
<!--注入空字符串值-->
<property name="emptyValue">
<value></value>
</property>
<!--注入 null 值-->
<property name="nullValue">
<null/>
</property>
</bean>
4.3.2、基本数据类型、字符串
当属性值中包含 &、<、>、’、"等 XML 中的特殊字符时,需要对配置文件做相应的处理。
<bean id="entity" class="cn.pojo.TestEntity">
<!--使用<![CDATA[]]>标记处理 XML 特殊字符-->
<property name="specialCharacter1">
<value><![CDATA[诸葛亮&路飞]]></value>
</property>
<!--使用实体引用方式处理 XML 特殊字符-->
<property name="specialCharacter2" value="诸葛亮&路飞"/>
</bean>
XML 文件中特殊符号于实体引用的对应关系
符号 | 实体引用 |
---|---|
& | & amp; |
< | & lt; |
> | & gt; |
’ | & apos; |
" | & quot; |
4.3.3、集合和数组
<bean id="entity" class="cn.pojo.TestEntity">
<property name="list">
<list>
<!--定义 List 或数组中的元素-->
<value>青龙偃月刀</value>
<value>丈八点钢矛</value>
</list>
</property>
</bean>
<bean id="entity" class="cn.pojo.TestEntity">
<property name="set">
<set>
<!--定义 set 集合的元素-->
<value>青龙偃月刀</value>
<value>丈八点钢矛</value>
</set>
</property>
</bean>
<bean id="entity" class="cn.pojo.TestEntity">
<property name="map">
<map>
<!--定义 map 中的键值对-->
<entry key="knife" value="青龙偃月刀"/>
<entry key="spear" value="丈八点纲矛"/>
</map>
</property>
</bean>
4.3.4、Properties 类型
<bean id="entity" class="cn.pojo.TestEntity">
<property name="props">
<props>
<!--定义 Properties 中的键值对-->
<prop key="knife">青龙偃月刀</prop>
<prop key="spear">丈八点钢矛</prop>
</props>
</property>
</bean>
4.3.5、Bean
<bean id="user" class="cn.pojo.SysUser"/>
<bean class="cn.pojo.TestEntity">
<!--定义 Bean 引用-->
<property name="user" ref="user"/>
</bean>
5、使用注解实现 Spring IoC
需要依赖 spring-aop.jar
步骤1
使用注解定义 Bean
import org.springframework.stereotype.Component;
/**
* 用户模块 DAO 业务层实现
*/
@Component("userDao")
public class UserDaoImpl implements UserDao {
public int save(SysUser user) {
System.out.println("保存用户信息到数据库");
}
}
@Component 注解将 UserDaoImpl 定义为 Spring 的 Bean 组件,并且命名为 userDao。
除 @Component 注解外,Spring 还提供了三个特殊的注解分别实现不同层级 Bean 的定义。
- @Controller:用于标注控制器类。
- @Service:用于标注业务类。
- @Repository:用于标注 DAO 类。
步骤2
使用注解实现 Bean 的注入功能
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Servic
/**
* 用户模块业务层实现
*/
@Service("userService")
public class UserServiceImpl implements UserService {
@Autowired
private UserDao user;
。。。。。
}
@Service 注解将 UserServiceImpl 的实例注册到 Spring 容器中,通过 @Autowired 注解将 userDao 注入当前类中。
@Autowired 注解采用了按类型匹配的方式为属性自动装配合适的依赖对象,即容器会查找和属性类型相匹配的 Bean 组件,并自动为属性注入。
知识扩展
如果想要根据 Bean 的名称进行注入,可以使用 @Qualifier 注解实现,使用方法如下所示。
//为 dao 属性注入名为 userDao 的 Bean
@Autowired
@Qualifier("userDao")
private UserDao dao;
当容器中有一个以上类型相同 Bean 需要被注入时,可以使用 @Qualifier 注解分别指定对应 Bean 名称的方式来区分。
步骤3
使用注解完成 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"
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="cn.service, cn.dao"/>
</beans>
base-package 属性用于指定需要被 Spring 扫描的基准包范围,Spring 会自动扫描这些包中所有的类,将被注解定义的 Bean 自动注册到 Spring 的容器中。
知识扩展
使用 @Autowired 注解进行 Bean 的注入时,如果找不到相匹配的 Bean 组件,Spring 容器会抛出异常。
如果依赖不是必须的,为避免抛出异常,可以将 required 属性设置为 false
@Service("userService")
public class UserServiceImpl implements UserService {
@Autowired(required = false)
private UserDao user;
…………
5.1、使用 Java 标准注解完成装配
@Resource 注解是 Java 规范提案——JSR-250中定义的
@Service("userService")
public class UserServiceImpl implements UserService {
@Resource
private UserDao user;
…………
@Resource 注解有一个 name 属性,默认情况下,spring 将这个属性的值解释为要注入的 Bean 的名称。
@Service("userService")
public class UserServiceImpl implements UserService {
@Resource(name = "userDao")
private UserDao user;
…………
如果使用 name 属性指定了 Bean 的名称,Spring 会以指定的名称查找依赖然后注入,否则,会按字段名进行匹配。
@Autowired 注解和 @Resource注解对比
- @Autowired 是 Spring 提供的注解,@Resource 是 java 提供的注解。
- 在不知道任何参数且不配合其他注解时,@Autowired 注解会优先按 Bean 的类型进行装配;@Resource 注解则优先按 Bean 的名称进行装配。
- 当需要使用 Bean 的名称进行装配时,@Autowired 注解是通过搭配 @Qualifier 注解实现,@Resource 注解则是通过自身的 name 参数实现的。
- @Resource 提供了通过 type 参数指定 Bean 类型的方式进行 Bean 的装配。
6、使用注解实现 Spring AOP
使用注解实现 Spring AOP 需要用到 AspectJ 框架。
6.1、使用注解方式标注切面
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import java.util.Arrays;
/**
* 使用注解定义切面
*/
@Aspect
public class UserServiceLogger {
private static final Logger log = Logger.getLogger(UserServiceLogger.class);
/**
* 前置增强
* @param jp
*/
@Before("execution(* cn.service.UserService.*(..))")
public void before(JoinPoint jp){
log.info("调用 " + jp.getTarget() + " 的 " + jp.getSignature().getName() + " 方法。方法入参:" + Arrays.toString(jp.getArgs()));
}
/**
* 后置增强
* @param jp
* @param returnValue
*/
@AfterReturning(pointcut = "execution(* cn.service.UserService.*(..))", returning = "returnValue")
public void afterReturning(JoinPoint jp, Object returnValue){
log.info("调用 " + jp.getTarget() + " 的 " + jp.getSignature().getName() + " 方法。方法入参:" + returnValue);
}
}
@Aspect :将 UserServiceLogger 类定义为切面
@Before :将 before() 方法定义为前置增强
@AfterReturning :将 afterReturning() 方法定义为后置增强
后置增强,可以定义一个参数用于接收目标方法的返回值,注意,必须在 @AfterReturning 注解中通过 returning 属性指定该参数的名称,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"
xmlns:aop="http://www.springframework.org/schema/aop"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--注解扫描-->
<context:component-scan base-package="cn.service, cn.dao"/>
<bean class="cn.aop.AroundLogger"/>
<aop:aspectj-autoproxy/>
</beans>
首先需要导入 aop 命名空间,然后将定义好的切面注册到 Spring 容器中,最后只需在配置文件中添加 <aop:aspectj-autoproxy /> 标签。
如果不需要被其他 Bean 引用,可以不指定 id 属性。
6.2、知识扩展
对于相同的切入点要求,可以统一定义,以便于重用和维护
/**
* 使用注解定义切面
*/
@Aspect
public class UserServiceLogger {
private static final Logger log = Logger.getLogger(UserServiceLogger.class);
@Pointcut("execution(* shili28.service.UserService.*(..))")
public void pointcut(){}
/**
* 前置增强
* @param jp
*/
@Before("pointcut()")
public void before(JoinPoint jp){
log.info("调用 " + jp.getTarget() + " 的 " + jp.getSignature().getName() + " 方法。方法入参:" + Arrays.toString(jp.getArgs()));
}
/**
* 后置增强
* @param jp
* @param returnValue
*/
@AfterReturning(pointcut = "pointcut()", returning = "returnValue")
public void afterReturning(JoinPoint jp, Object returnValue){
log.info("调用 " + jp.getTarget() + " 的 " + jp.getSignature().getName() + " 方法。方法入参:" + returnValue);
}
}
@Pointcut 注解用来声明切入点的表达式,而切入点签名通过定义一个普通的方法提供。
此方法返回值必须是 void 类型。切入点定义好后,就可以在 @Before、@AfterReturning 等注解中以 @Pointcut 的方式进行引用
7、小结
Spring 框架在同一个功能上提供了注解和配置文件两种实现方式以供选择。
通常情况下,优先选择注解方式,如果项目采用的 JDK 版本较低无法使用注解也可以使用配置文件的方式。
Spring 框架常用的增强处理类型
抛出异常增强 | <aop:after-throwing > | @AfterThrowing |
---|---|---|
前置增强 | <aop:before > | @Before |
后置增强 | <aop:after-returning > | @AfterReturning |
环绕增强 | <aop:around > | @Around |
最终增强 | <aop:after > | @After |
8、基本整合方式
MyBatis 框架构建 SqlSession 实例时的主要流程
- 需要自行编码通过 SqlSessionFactoryBuilder 读取配置文件并构建 SqlSessionFactory 实例,
- 通过 SqlSessionFactory 实例创建 SqlSession 实例,
- 通过 SqlSession 实例完成对数据的操作
以上流程可以全部给 Spring 框架来处理,从读取配置文件到组件的创建,再到组件之间的依赖关系甚至整个框架的生命周期都由 Spring 容器统一管理。
8.1、整合所需的依赖及配置
8.1.1、添加 jar 包
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
//Spring 框架与 MyBatis 框架的整合 jar 包
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-tx -->
//提供了对事务的支持
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.3.9</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
//Spring 框架对数据源支持所需的 jar 包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.9</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-pool/commons-pool -->
// 具体的数据源操作使用的技术
<dependency>
<groupId>commons-pool</groupId>
<artifactId>commons-pool</artifactId>
<version>1.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-dbcp/commons-dbcp -->
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
8.1.2、使用 Spring 配置文件配置数据源
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://127.0.0.1:3306/mybatis_1?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC&useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
8.1.3、通过 Spring 配置文件创建 SqlSessionFactory
<!--配置 SqlSessionFactoryBean-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--引用数据源组件-->
<property name="dataSource" ref="dataSource"/>
<!--引用 MyBatis 配置文件中的配置-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!--配置 SQL 映射文件信息-->
<property name="mapperLocations">
<list>
<value>classpath:cn/cvs/dao/**/*.xml</value>
</list>
</property>
</bean>
表示使用 mapperLocations 属性扫描加载 SQL 映射文件,
classpath:cn/cvs/dao/**/*.xml :表示扫描 cn.cvs.dao 下的任意层级子包中扩展名为 xml 的文件。
8.1.4、通过 SqlSessionTemplate 操作数据库
import org.mybatis.spring.SqlSessionTemplate;
import java.util.List;
public class SysUserMapperImpl implements SysUserMapper{
@Override
public List<SysUser> selectSysUserList(SysUser sysUser) {
return sqlSession.selectList("cn.cvs.dao.sysuser.SysUserMapper.selectSysUserList",sysUser);
}
private SqlSessionTemplate sqlSession;
public SqlSessionTemplate getSqlSession() {
return sqlSession;
}
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
}
使用 SqlSessionTemplate 可以更好地与 Spring 服务融合并简化部分流程化的工作;
可以保证和当前 Spring 事务相关联,自动管理会话的生命周期,包括必要的关闭、提交和回滚操作。
<!--配置 SqlSessionTemplate -->
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
<!--配置 DAO-->
<bean id="sysUserMapper" class="cn.cvs.dao.sysUser.SysUserMapperImpl">
<property name="sqlSession" ref="sqlSessionTemplate"/>
</bean>
sqlSessionTemplate 是线程安全的,可以以单例模式配置并被多个 DAO 对象共用,
而不必为每个 DAO 单独配置一个sqlSessionTemplate。
业务层
public interface SysUserService {
public List<SysUser> getList(SysUser sysUser);
}
public class SysUserServiceImpl implements SysUserService {
private SysUserMapper sysUserMapper;
@Override
public List<SysUser> getList(SysUser sysUser) {
try {
return sysUserMapper.selectSysUserList(sysUser);
}catch (RuntimeException e){
e.printStackTrace();
throw e;
}
}
public SysUserMapper getSysUserMapper() {
return sysUserMapper;
}
public void setSysUserMapper(SysUserMapper sysUserMapper) {
this.sysUserMapper = sysUserMapper;
}
}
<!--配置业务 Bean-->
<bean id="sysUserService" class="cn.cvs.service.sysUser.SysUserServiceImpl" p:sysUserMapper-ref="sysUserMapper"/>
<property name="sysUserMapper" ref="sysUserMapper"/>
</bean>
利用 Spring 框架和 MyBatis-spring 整合资源包提供的组件实现了 Spring 框架对 MyBatis 框架的整合工作;
通过 Spring 配置文件得到数据源,SqlSessionTemplate 等组件,并在此基础上完成 DAO 模块和业务模块的开发和装配,简化了开发过程且便于维护。
测试
public class SysUserTest {
private Logger logger = Logger.getLogger(SysUserTest.class);
@Before
public void setUp()throws Exception{}
@Test
public void testGetUserList(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContex.xml");
SysUserService sysUser = context.getBean("sysUserService", SysUserService.class);
SysUser userCondition = new SysUser();
userCondition.setRealName("丽");
userCondition.setRoleId(1);
List<SysUser> userList = sysUser.getList(userCondition);
for (SysUser user : userList) {
logger.debug("testGetUserList account:"
+ user.getAccount() + " and realName:"
+ user.getRealName() + " and roleId:"
+ user.getRoleId() + " and roleName:"
+ user.getRoleIdName() + " and address:"
+ user.getAddress());
}
}
}
8.2、使用 SqlSessionDaoSupport 简化代码
8.2.1、数据层
public class SysUserMapperImpl extends SqlSessionDaoSupport implements SysUserMapper {
@Override
public List<SysUser> selectSysUserList(SysUser sysUser) {
return this.getSqlSession().selectList("cn.cvs.dao.sysUser.SysUserMapper.selectSysUserList",sysUser);
}
}
8.2.2、配置文件
<bean id="sysUserMapper" class="cn.cvs.dao.sysUser.SysUserMapperImpl">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
SqlSessionDaoSupport 类中提供了 sqlSessionFactory() 方法用来注入 sqlSessionFactory 并创建 sqlSessionTemplate 实例。
DAO 实现类只需继承 SqlSessionDaoSupport 类即可通过 getSqlSession() 方法获得创建好的 sqlSessionTemplate 实例,无须额外定义
sqlSession 属性和 setter 方法。
9、映射器整合方式
9.1、使用 MapperFactoryBean 注入映射器
首先需删除原有的 DAO 层实现类 SysUserMapperImpl。
<!--配置 SqlSessionFactoryBean-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--引用数据源组件-->
<property name="dataSource" ref="dataSource"/>
<!--引用 MyBatis 配置文件中的配置-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!--配置 SQL 映射文件信息-->
<!-- <property name="mapperLocations">-->
<!-- <list>-->
<!-- <value>classpath:cn/cvs/dao/**/*.xml</value>-->
<!-- </list>-->
<!-- </property>-->
</bean>
<!--配置 DAO-->
<bean class="org.mybatis.spring.mapper.MapperFactoryBean" id="recoedMapper">
<property name="mapperInterface" value="cn.cvs.dao.storageRecoed.StorageRecoedMapper"/>
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
<bean id="storageRecoedService" class="cn.cvs.service.storageRecoed.StorageRecoedServiceImpl" p:storageRecoedMapper-ref="recoedMapper"/>
MapperFactoryBean 注入映射器时有以下特点:
- 配置 DAO 组件,class 属性不是某个实现类,而是 MapperFactoryBean。
- 通过 mapperInterface 属性指定映射器,并且其值只能是接口类型,不能是某个实现类。
- MapperFactoryBean 是 SqlSessionDaoSupport 的子类,使用时需要通过 setSqlSessionFactory() 方法注入 sqlSessionFactory 实例以创建 SqlSessionTemplate 实例。
- 如果映射器对应的 SQL 映射文件与映射器的类路径相同。该映射文件可以自动被 MapperFactoryBean 解析。在此情况下,配置 sqlSessionFactory 时可以不必指定 SQL 映射文件的位置。反之,如果映射器与映射文件的类路径不同,则仍需在配置 sqlSessionFactory 时明确指定映射文件的位置。
- SQL 映射文件中的命名空间和映射器接口的名称需相同,映射元素的 id 和映射器接口的方法名称相同
9.2、使用 MapperScannerConfigurer 注入映射器
MyBatis-Spring 中提供了 MapperScannerConfigurer,它可以扫描指定包中的接口并将它们直接注册为 MapperFactoryBean。
<!--配置 SqlSessionFactoryBean-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--引用数据源组件-->
<property name="dataSource" ref="dataSource"/>
<!--引用 MyBatis 配置文件中的配置-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
<!--配置 DAO-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer" p:basePackage="cn.cvs.dao"/>
- basePackage:指定了需要扫描的基准包,MapperScannerConfigurer 将递归扫描基准包下所有接口。如果它们在 SQL 映射文件中定义过,则将它们动态注册为映射器实现类,即可批量生成映射器的实现类。
- MapperScannerConfigurer 会为所有由他创建的映射器实现类开启自动装配。这些实现类都会自动注入 sqlSessionFactory 实例。
知识扩展
- basePackage 属性中可以配置多个包名,多个包名之间使用逗号或分号隔开。
- 如果环境中出于不同目的配置了多个 sqlSessionFactory 实例,自动装配将无法进行,此时应显示指定所依赖的 sqlSessionFactory 实例。
<!--配置 DAO-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer" p:basePackage="cn.cvs.dao" >
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
**注意 **
使用的是 sqlSessionFactoryBeanName 属性而不是 sqlSessionFactory 属性。
这个属性关注的是 Bean 的名称,所有为其赋值使用的是 value 属性而不是 ref。
映射器被注册到 Spring 容器时,Spring 框架会根据其接口名称为其命名,默认是首字母小写的非完全限定类名。
在开发中,可以使用 @Autowired 或 @Resource 注解实现对业务组件的依赖注入,以简化业务组件的配置。
@Service("sysUserService")
public class SysUserServiceImpl implements SysUserService {
@Autowired
private SysUserMapper sysUserMapper;
@Override
public List<SysUser> getList(SysUser sysUser) {
try {
return sysUserMapper.selectSysUserList(sysUser);
}catch (RuntimeException e){
e.printStackTrace();
throw e;
}
}
}
配置文件
<!--配置 DAO-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer" p:basePackage="cn.cvs.dao" >
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
<!--配置扫描注解定义的业务 Bean-->
<context:component-scan base-package="cn.cvs.service"/>
MapperScannerConfigurer 与 @Autowired 注解或 @Resource 注解配合使用,可以自动创建映射器实现并注入给业务组件,能够最大限度地减少 DAO 组件与业务组件的编码和配置工作。
10、声明式事务
10.1、配置声明式事务
步骤
10.1.1、导入命名空间
Spring 框架的声明式事务需要 tx 命名空间的标签,所有需要导入 tx 命名框架,且声明式事务是依赖面向切面思想实现的,所有还需要导入 aop 命名空间。
<?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:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
……………………
10.1.2、定义事务管理器
事务管理器提供了对事务处理的全面支持和统一管理,相当于 AOP 中增强处理的角色。
<!--定义事务管理器-->
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager" p:dataSource-ref="dataSource"/>
使用 Spring 框架提供的事务管理器类 DataSourceTransactionManager。需要注意的是,配置 DataSourceTransactionManager 时,要为其注入事先定义好的数据源组件。
10.1.3、设置事务属性
事务管理器可以通过设定事务的属性为不同业务方法指定具体的事务规则。
<tx:advice transaction-manager="transactionManager" id="txAdvice">
<tx:attributes>
<tx:method name="get*" propagation="SUPPORTS"/>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="del*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<tx:advice > 标签配置事务增强,标签内可以设置 id 属性和 transaction-manager 属性。
transaction-manager 属性需引用一个事务管理器 Bean。
注意
transaction-manager 属性的默认值是 transactionManager。
可以在 <tx:advice > 标签中配置事务的传播机制,隔离级别等属性。
Spring 框架支持对不同的方法设置不同的事务属性,可以在一个 <tx:advice > 标签中设置多个 <tx:method > 标签, <tx:method > 标签中的 name 属性是必须的, 用于指定相匹配的方法。
事务属性说明
事务属性 | 说明 |
---|---|
propagation | 事务传播机制 REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。 SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。 MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。 REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。 NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。 NESTED:支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务。 |
isolation | 事务隔离级别 |
timeout | 事务超时的时间(以秒为单位) |
read-only | 事务是否只读? |
rollback-for | 将被触发进行回滚的 Exception(s);以逗号分开。 如:‘com.foo.MyBusinessException,ServletException’ |
no-rollback-for | 不被触发进行回滚的 Exception(s);以逗号分开。 如:'com.foo.MyBusinessException‘ |
10.1.4、定义事务切面
可以将事务规则应用到指定的方法上
<!--定义切面-->
<aop:config>
<aop:pointcut id="serviceMethod" expression="execution(* cn.cvs.service..*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethod"/>
</aop:config>
<aop:advisor > 的 advice-ref 属性引用的是通过 <tx:advice > 标签设定了事务属性的组件。
10.1.5、测试
@Test
public void testAddUser(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContex.xml");
SysUserService sysUserService = context.getBean("sysUserService", SysUserService.class);
SysUser user = new SysUser("101", "888888888888", "测试trim标签修改用户名称", 2, new Date(), "现在才阿斯顿", "0000011111", 1, 1, new Date());
boolean result = sysUserService.add(user);
Log4jUtil.print("testAdd result : " + result);
}
10.2、使用注解实现声明式事务
使用注解处理事务仍然需要在 Spring 配置文件中配置事务管理类,并开启注解处理事务的功能。
<!--定义事务管理器-->
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager" p:dataSource-ref="dataSource"/>
<tx:annotation-driven/>
注意
事务管理器的 id 被定义为 transactionManager,则 <tx:annotation-driven /> 不需要指定 transaction-manager 属性的值。
配置文件编写完成之后,就可以在代码中使用 @Transactional 注解处理事务。
@Transactional
@Service("sysUserService")
public class SysUserServiceImpl implements SysUserService {
@Autowired
private SysUserMapper sysUserMapper;
@Override
public List<SysUser> getList(SysUser sysUser) {
………… // 省略实现代码
}
@Override
public boolean add(SysUser sysUser) {
………… // 省略实现代码
}
}
@Transactional 注解的使用方式,只需在业务实现类上添加 @Transactional 注解则可以为该类的所有业务方法统一添加事务处理。
如果某一业务方法需要采用不同的事务规则,可以在该业务方法上添加 @Transactional 注解单独进行设置。
@Transactional 注解也可以设置事务属性的值,默认的 @Transactional 设置如下
- 事务传播特性是 REQUIRED
- 事务隔离级别是 DEFAULT
- 事务是读/写
- 事务超时规则默认是依赖于事务系统的
- 所有的 RuntimException 都会触发事务回滚,但是所有的 checked Exception 都不会触发事务回滚。
@Transactional 注解的属性
事务属性 | 类型 | 说明 |
---|---|---|
propagation | 枚举型:Propagation | 可选的传播性设置 |
isolation | 枚举型:Isolation | 该属性用于设置底层数据库的事务隔离级别 |
timeout | int 型(以 s 为单位) | 该属性用于设置事务的超时秒数,默认值为-1表示永不超时 |
readOnly | 布尔型 | 该属性用于设置当前事务是否为只读事务 |
rollbackFor | 一组 Class 类的实例,必须是 Throwable 的子类 | 该属性用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。 |
rollbackForClassName | 一组 Class 类的名称,必须是 Throwable 的子类 | 该属性用于设置需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,则进行事务回滚。 |
noRollbackFor | 一组 Class 类的实例,必须是 Throwable 的子类 | 该属性用于设置不需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,不进行事务回滚。 |
noRollbackForClassName | 一组 Class 类的名称,必须是 Throwable 的子类 | 该属性用于设置不需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,不进行事务回滚。 |
11、Spring框架配置扩展
11.1、配置数据源
使用 properties 文件配置数据源时,可以把数据源的相关配置信息单独放到 properties 文件中进行维护。
jdbc.driver = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/mybatis_1
jdbc.username = root
jdbc.password = root
Spring 框架提供了 propertySourcesPlaceholderConfigurer 类加载 properties 文件。
在 Spring 配置文件中可以采用 ${x x x} 的方式引用 properties 文件中的键值对数据。
<!--引入 properties 文件-->
<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer" p:location="classpath:database.properties"/>
<!-- 配置 DataSource -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
知识扩展
在 Spring 配置文件中加载 properties 文件的方式很多,比较常用的有使用 context 标签进行加载
<context:property-placeholder ignore-unresolvable="true" location="classpath:database.properties"/>
使用 context 标签加载数据源配置文件时,数据源的 Bean 配置方式相同,不需要修改。
注意
- 表达式 ${x x x} 前后是没有空格的,如果开发者在其中键入了空格,这些空格字符将与变量合并后作为属性的值,最终将引发异常。
- 单独维护数据源配置信息可以在切换应用程序运行环境时,只对数据源配置文件做修改,避免修改 Spring 配置文件。、
11.2、使用 JNDI 配置数据源
Spring框架整合MyBatis框架-使用jndi的方式从服务器中获取datasource资源 - dongyaotou - 博客园 (cnblogs.com)
- 需要把数据库驱动文件放到 Tomcat 的 lib 目录下
- 把数据源信息配置到 Tomcat 的 conf 目录下的 context.xml 文件中,并修改 Spring 配置文件为通过 JNDI 方式配置数据源。
Tomcat 中 conf 目录下的 context.xml 文件
<Context>
<Resource
name="jdbc/cvs_db"
auth="Container"
type="javax.sql.DataSource"
maxActive="100"
maxIdle="30"
maxWait="10000"
username="root"
password="root"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://127.0.0.1:3306/mybatis_1?useUnicode=true&characterEncoding=utf-8"/>
</Context>
Spring 配置文件
<!--通过 JNDI 配置 DataSource -->
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean" p:jndiName="java:comp/env/jdbc/cvs_db"/>
Resource 标签的 name 属性用于指定数据源名称,此属性的值需要与 Spring 配置文件中 jndiName 属性值 java:comp/env/ 后的数据源名称保持一致。
JNDI 方式配置数据源功能,需在 Web 环境下进行。
11.3、拆分 Spring 配置文件
Spring 框架提供了可以将配置文件拆分处理的功能,可以将一个大的配置文件分解成多个小配置文件。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NlMQ4POy-1643083966545)(C:\Users\32912\AppData\Roaming\Typora\typora-user-images\image-20210818222530143.png)]
Spring 框架的 ClassPathXmlApplicationContext 类提供了多种重载形式,便于从多个配置文件中读取配置信息。
-
可传入动态参数的构造方法
-
ApplicationContext context = new ClassPathXmlApplicationContext( "applicationContext2.xml", "applicationContext.xml", "applicationContext-dao.xml");
-
数组参数方式
-
String[] config = {"applicationContext2.xml", "applicationContext.xml", "applicationContext-dao.xml"}; ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config);
-
通配符参数方式
-
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext*.xml");
Spring 还提供了在配置文件中通过 import 标签直接加载其他配置文件的功能
<?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
default-autowire="byName">
<import resource="applicationContext-dao.xml"/>
<import resource="applicationContext-service.xml"/>
</beans>
12、Bean 的自动装配
Spring 框架的自动装配是指在没有显示指定所依赖的 Bean 组件 id 的情况下,可以自动地将与属性类型相符的 Bean 注入想应属性的功能。
在 bean 标签中添加一个 autowire 属性。
autowire=”byName“ 表示根据属性名自动进行组件的装配,由 BeanFactory 检查 XML 配置文件内容,为 Bean 自动注入依赖关系。
Spring 框架提供了多种自动装配方式
- no:不使用自动装配。Bean 的依赖关系必须通过 property 元素定义。
- byType:根据属性类型自动装配。
- byName:根据属性名自动装配。
- constructor:与 byType 的方式类似,不同之处在于,它应用于构造器参数。如果在容器中没有找到与构造器参数类型一致的 Bean,将会抛出异常。
Spring 框架可以通过 beans 的 default-autowire 属性设置全局自动装配功能。配置 default-autowire 后,仍然可以在其下的 bean 节点中设置 autowire 属性,这时当前 bean 节点上的自动装配策略将覆盖全局设置,成为该 Bean 的自动装配策略。
12.1、Bean 的作用域
Bean 的作用域被划分为以下五种
- singleton:默认值。以单例模式创建 Bean 的实例,即容器中该 Bean 的实例只会被创建一个。
- prototype:每次从容器中获取 Bean 时,都会创建一个新的实例。
- request:用于 Web 应用环境,针对每次 HTTP 请求都会创建一个实例。
- session:用于 Web 应用环境,同一个会话共享同一个实例,不同的会话使用不同的实例。
- global session:仅在 Portlet 的 Web 应用中使用,同一个全局会话共享一个实例。对于非 Portlet 环境,等同于 session。
singleton 是默认的作用域,表示在默认情况下 Spring 框架为每个 Bean 仅创建一个实例,采用这种方式可以大大减少创建对象的开销,提高运行效率。对于存在线程安全问题的组件,可以使用 prototype 作用域代替单例模式。
<!--配置 DAO-->
<bean id="sysUserMapper" class="cn.cvs.dao.sysUser.SysUserMapperImpl" autowire="byType" scope="prototype">
<property name="sqlSession" ref="sqlSessionTemplate"/>
</bean>
12.1.1、使用注解指定 Bean 的作用域
@Scope("prototype")
@Service
public class SupplierServiceImpl implements SupperService {
…………
}
通过在添加 @Scope 注解可以设置当前 Bean 的作用域,参数的取值范围与使用配置文件时相同。