全面剖析Spring框架技术原理(非常全面)
Spring是一个全面的解决方案,但他坚持的原则:不重新发明轮子。
Spring框架由大约20个功能模块组成。这些模块被分成六个部分,分别是Core Container 、 data/access/Integration、Web、AOP(aspect Oriented Programming)、Instrumntaion及Test。
SpringCore是框架的最基础的部分,提供了IoC特性。Spring Context为企业级开发提供了便利和集成的工具。SpringAOP是基于Spring Core的符合规范的面向切面的编程实现。SpringJDBC提供了JDBC的抽象层,简化了jdbc编码,同时使用代码更健壮。SpringORM对市面上流行的ORM框架提供了支持。SpringWeb为Spring在web应用程序中的使用提供了支持。
Spirng框架的体现结构
①Spring是一个开源框架
②Spring为简化企业级开发而生,使用Spring开发可以将Bean对象,Dao组件对象,Service组件对象等交给Spring容器来管理,这样使得很多复杂的代码在Spring中开发却变得非常的优雅和简洁,有效的降低代码的耦合度,极大的方便项目的后期维护、升级和扩展。
③Spring是一个IOC(DI)和AOP容器框架。
④Spring的优良特性
[1]非侵入式:基于Spring开发的应用中的对象可以不依赖于Spring的API
[2]控制反转:IOC——Inversion of Control,指的是将对象的创建权交给Spring去创建。使用Spring之前,对象的创建都是由我们自己在代码中new创建。而使用Spring之后。对象的创建都是由给了Spring框架。
[3]依赖注入:DI——Dependency Injection,是指依赖的对象不需要手动调用setXX方法去设置,而是通过配置赋值。
[4]面向切面编程:Aspect Oriented Programming——AOP
[5]容器:Spring是一个容器,因为它包含并且管理应用对象的生命周期
[6]组件化:Spring实现了使用简单的组件配置组合成一个复杂的应用。在 Spring 中可以使用XML和Java注解组合这些对象。
[7]一站式:在IOC和AOP的基础上可以整合各种企业应用的开源框架和优秀的第三方类库(实际上Spring 自身也提供了表述层的SpringMVC和持久层的Spring JDBC)
SpringIoC
控制反转(Inversion of Control,IoC),也称依赖注入(Dependency Injection,DI),是面向对象编程中的一种设计理念,用来降低程序代码之间的耦合程度。
首先简单的说一下IOC(控制反转),IOC的基本思想就是反转资源获取的方向,传统的资源查找方式要求组件向容器发起请求,然后获取到所要请求的资源作为回应,应用了IOC之后,则是容器主动的将资源推送给它所管理的组件,组件只需要做的是选择一种合适的方式来接受资源,这种行为就被看做是组件被动的查找资源。DI(依赖注入),这个是对IOC的另一种表达方式,也就是组件以一些预先定义好的方式(例如setter方法)接受来自容器的资源注入,相对于IOC而言,这种表述更直接。
IoC(Inversion of Control)控制反转,对象创建责任的反转,在spring中BeanFacotory是IoC容器的核心接口,负责实例化,定位,配置应用程序中的对象及建立这些对象间的依赖。XmlBeanFacotory实现BeanFactory接口,通过获取xml配置文件数据,组成应用对象及对象间的依赖关系。
Spring框架分为四大模块:
Core核心模块。负责管理组件的Bean对象spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar
创建一个HelloWorld类
HelloWorld.java
public class HelloWorld {
private String user;
public HelloWorld() {
System.out.println("HelloWorld's constructor...");
}
public void setUser(String user) {
System.out.println("setUser:" + user);
this.user = user;
}
public HelloWorld(String user) {
this.user = user;
}
public void hello(){
System.out.println("Hello: " + user);
}
}
创建Spring容器的xml配置文件,在项目根目录下(Eclipse的src目录、idea中使用resources目录,如果没有,可以自己创建)
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"
xmlns:util="http://www.springframework.org/schema/util"
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/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
<!-- 配置一个 bean -->
<bean id="helloWorld" class="com.spring.helloworld.HelloWorld">
<!-- 为属性赋值 -->
<property name="user" value="Jerry"></property>
</bean>
</bean>
测试类
public static void main(String[] args) {
//1. 创建 Spring 的 IOC 容器
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//2. 从 IOC 容器中获取 bean 的实例
HelloWorld helloWorld = (HelloWorld) ctx.getBean("helloWorld");
//根据类型来获取 bean 的实例: 要求在 IOC 容器中只有一个与之类型匹配的 bean, 若有多个则会抛出异常.
//3. 使用 bean
helloWorld.hello();
}
除了ClassPathXmlApplicationContext,ApplicationContext接口还有他实现类。例如FileSystemXmlApplicationContext也可用于加载Spring配置文件。
除了ApplicationContext及其实现类,还可以通过BeanFactory接口及其实现类对Beran组件实施管理。
事实上,ApplicationContext就是建立在BeanFactory的基础上,BeanFactory是SpringIoc容器的核心,负责管理和它们之间的依赖关系,应用程序通过BeanFactory接口与springIoC容器交互。ApplicationContext是BeanFactory的子接口。
容器负责吧组件所**依赖的具体对象注入(赋值给组件),从而避免组件之间以硬编码的方式耦合在一起。
SpringAOP
什么是AOP
AOP(Aspect-OrientedProgramming,面向切面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此。这种散布在各处的无关的代码被称为横切(cross-cutting)代码,在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。
而**AOP**技术则恰恰相反,它利用一种称为“**横切**”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“**Aspect**”,即方面。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。**AOP**代表的是一个横向的关系,如果说“对象”是一个空心的圆柱体,其中封装的是对象的属性和行为;那么面向方面编程的方法,就仿佛一把利刃,将这些空心圆柱体剖开,以获得其内部的消息。而剖开的切面,也就是所谓的“方面”了。然后它又以巧夺天功的妙手将这些剖开的切面复原,不留痕迹。
AOP的基本概念
- (1)Aspect(切面):通常是一个类,里面可以定义切入点和通知
- (2)JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用
- (3)Advice(通知):AOP在特定的切入点上执行的增强处理,有before,after,afterReturning,afterThrowing,around
- (4)Pointcut(切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式
- (5)AOP代理:AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类
通知方法:
- 前置通知:在我们执行目标方法之前运行(@Before)
- 后置通知:在我们目标方法运行结束之后 ,不管有没有异常**(@After)**
- 返回通知:在我们的目标方法正常返回值后运行**(@AfterReturning)**
- 异常通知:在我们的目标方法出现异常后运行**(@AfterThrowing)**
- 环绕通知:动态代理, 需要手动执行joinPoint.procced()(其实就是执行我们的目标方法执行之前相当于前置通知, 执行之后就相当于我们后置通知**(@Around)**
启用@AspectJ支持
为了在Spring配置中使用@AspectJ切面,你首先必须启用Spring对@AspectJ切面配置的支持,并确保*自动代理(autoproxying)*的bean是否能被这些切面通知。自动代理是指Spring会判断一个bean是否使用了一个或多个切面通知,并据此自动生成相应的代理以拦截其方法调用,并且确保通知在需要时执行。
通过在你的Spring的配置中引入下列元素来启用Spring对@AspectJ的支持:
<aop:aspectj-autoproxy/>
SpringAOP实现
声明一个切面
切面使用aop:aspect来声明,backing bean(支持bean)通过 ref
属性来引用:
<aop:config>
<aop:aspect id="myAspect" ref="aBean">
...
</aop:aspect>
</aop:config>
<bean id="aBean" class="...">
...
</bean>
声明一个切入点
一个命名切入点可以在aop:config元素中定义,这样多个切面和通知就可以共享该切入点。
一个描述service层中所有service执行的切入点可以定义如下:
<aop:config>
<aop:pointcut id="businessService"
expression="execution(* com.xyz.myapp.service.*.*(..))"/>
</aop:config>
<aop:config>
<aop:aspect id="myAspect" ref="aBean">
<aop:pointcut id="businessService"
expression="execution(* com.xyz.myapp.service.*.*(..)) && this(service)"/>
<aop:before pointcut-ref="businessService" method="monitor"/>
...
</aop:aspect>
</aop:config>
前置通知
前置通知在匹配方法执行前运行。在<aop:aspect>
中使用aop:before 元素来声明它。
<aop:aspect id="beforeExample" ref="aBean">
<aop:before
pointcut-ref="dataAccessOperation"
method="doAccessCheck"/>
...
</aop:aspect>
后置通知
后置通知在匹配的方法完全执行后运行。和前置通知一样,可以在<aop:aspect>
里面声明它。例如:
<aop:aspect id="afterReturningExample" ref="aBean">
<aop:after-returning
pointcut-ref="dataAccessOperation"
method="doAccessCheck"/>
...
</aop:aspect>
异常通知
异常通知在匹配方法抛出异常退出时执行。在<aop:aspect>
中使用 after-throwing元素来声明:
<aop:aspect id="afterThrowingExample" ref="aBean">
<aop:after-throwing
pointcut-ref="dataAccessOperation"
method="doRecoveryActions"/>
...
</aop:aspect>
最终通知
最终通知无论如何都会在匹配方法退出后执行。使用after
元素来声明它:
<aop:aspect id="afterFinallyExample" ref="aBean">
<aop:after
pointcut-ref="dataAccessOperation"
method="doReleaseLock"/>
...
</aop:aspect>
环绕通知
环绕通知是最后一种通知类型。环绕通知在匹配方法运行期的“周围”执行。 它有机会在目标方法的前面和后面执行,并决定什么时候运行,怎么运行,甚至是否运行。 环绕通知经常在需要在一个方法执行前后共享状态信息,并且是在线程安全的情况下使用 (启动和停止一个计时器就是一个例子)。注意选择能满足你需求的最简单的通知类型; 如果简单的前置通知能做的事情就绝对不要使用环绕通知。
Around通知使用aop:around
元素来声明。通知方法的第一个参数的类型必须是 ProceedingJoinPoint
类型。在通知的主体中,调用 ProceedingJoinPoint
的proceed()
方法来执行真正的方法。 proceed
方法也可能会被调用并且传入一个Object[]
对象 - 该数组将作为方法执行时候的参数。参见Section 6.2.4.5, “环绕通知”中调用具有 Object[]
的proceed方法。
<aop:aspect id="aroundExample" ref="aBean">
<aop:around
pointcut-ref="businessService"
method="doBasicProfiling"/>
...
</aop:aspect>
<aop:config>
<aop:aspect id="concurrentOperationRetry" ref="concurrentOperationExecutor">
<aop:pointcut id="idempotentOperation"
expression="execution(* com.xyz.myapp.service.*.*(..))"/>
<aop:around
pointcut-ref="idempotentOperation"
method="doConcurrentOperation"/>
</aop:aspect>
</aop:config>
<bean id="concurrentOperationExecutor"
class="com.xyz.myapp.service.impl.ConcurrentOperationExecutor">
<property name="maxRetries" value="3"/>
<property name="order" value="100"/>
</bean>
Aspectj实现AOP
声明一个切面
启用@AspectJ支持后,在application context中定义的任意带有一个@Aspect切面(拥有@Aspect
注解)的bean都将被Spring自动识别并用于配置Spring AOP。以下例子展示了为完成一个不是非常有用的切面所需要的最小定义:
application context中一个常见的bean定义,它指向一个使用了@Aspect
注解的bean类:
<bean id="myAspect" class="org.xyz.NotVeryUsefulAspect">
<!-- configure properties of aspect here as normal -->
</bean>
切入点指示符(PCD)的支持
Spring AOP支持在切入点表达式中使用如下的AspectJ切入点指示符:
其他的切入点类型
完整的AspectJ切入点语言支持额外的切入点指示符,但是Spring并不支持。它们分别是call, get, set, preinitialization, staticinitialization, initialization, handler, adviceexecution, withincode, cflow,cflowbelow, if, @this
和@withincode
。在Spring AOP中使用这些指示符将会导致抛出IllegalArgumentException
异常。
Spring AOP支持的切入点指示符可能会在将来的版本中得到扩展,从而支持更多的AspectJ切入点指示符。
- execution - 匹配方法执行的连接点,这是你将会用到的Spring的最主要的切入点指示符。
- within - 限定匹配特定类型的连接点(在使用Spring AOP的时候,在匹配的类型中定义的方法的执行)。
- this - 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中bean reference(Spring AOP 代理)是指定类型的实例。
- target - 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中目标对象(被代理的应用对象)是指定类型的实例。
- args - 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中参数是指定类型的实例。
- @target - 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中正执行对象的类持有指定类型的注解。
- @args - 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中实际传入参数的运行时类型持有指定类型的注解。
- @within - 限定匹配特定的连接点,其中连接点所在类型已指定注解(在使用Spring AOP的时候,所执行的方法所在类型已指定注解)。
- @annotation - 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中连接点的主题持有指定的注解。
异常通知(After throwing advice)
抛出异常通知在一个方法抛出异常后执行。使用@AfterThrowing
注解来声明:
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;
@Aspect
public class AfterThrowingExample {
@AfterThrowing("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
public void doRecoveryActions() {
// ...
}
}
最终通知(After (finally) advice)
不论一个方法是如何结束的,最终通知都会运行。使用@After
注解来声明。最终通知必须准备处理正常返回和异常返回两种情况。通常用它来释放资源。
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.After;
@Aspect
public class AfterFinallyExample {
@After("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
public void doReleaseLock() {
// ...
}
}
环绕通知
最后一种通知是环绕通知。环绕通知在一个方法执行之前和之后执行。它使得通知有机会 在一个方法执行之前和执行之后运行。而且它可以决定这个方法在什么时候执行,如何执行,甚至是否执行。 环绕通知经常在某线程安全的环境下,你需要在一个方法执行之前和之后共享某种状态的时候使用。 请尽量使用最简单的满足你需求的通知。(比如如果简单的前置通知也可以适用的情况下不要使用环绕通知)。
环绕通知使用@Around
注解来声明。通知的第一个参数必须是 ProceedingJoinPoint
类型。在通知体内,调用 ProceedingJoinPoint
的proceed()
方法会导致 后台的连接点方法执行。proceed
方法也可能会被调用并且传入一个 Object[]
对象-该数组中的值将被作为方法执行时的参数。
*当传入一个Object[]对象的时候,处理的方法与通过AspectJ编译器处理环绕通知略有不同。 对于使用传统AspectJ语言写的环绕通知来说,传入参数的数量必须和传递给环绕通知的参数数量匹配 (不是后台的连接点接受的参数数量),并且特定顺序的传入参数代替了将要绑定给连接点的原始值 (如果你看不懂不用担心)。Spring采用的方法更加简单并且能更好匹配它基于代理(proxy-based)的执行语法, 如果你使用AspectJ的编译器和编织器来编译
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.ProceedingJoinPoint;
@Aspect
public class AroundExample {
@Around("com.xyz.myapp.SystemArchitecture.businessService()")
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
// start stopwatch
Object retVal = pjp.proceed();
// stop stopwatch
return retVal;
}
}
Spring整合Mybatis框架实现访问数据库
在spring整合Mybatis框架前我们需要下载spring 整合mybatis框架的jar包,使用方式有两种
通过maven的pox文件添加jar依赖
首先我们需要下载mybatis的jar包
由于在整合中 我们会用到事物和数据源的支持,因此还需要加入spirng-jdbc-.ReLEASE.jar包和spring-tx-.RELEASE.jar包
创建实体类User
User.java
package com.ssm04.pojo;
import java.util.Date;
/**
* @program: SSM
* @description: 用户
* @author: liu qing
* @create: 2019-09-17 21:28
*/
public class User {
private Integer id;
private String userCode;
private String userName;
private String userPassword;
private Integer gender;
private Date birthday;
private String phone;
private String address;
private Integer userRole;
private Integer createdBy;
private String creationDate;
private Integer modifyBy;
private String modifyDate;
private String userRloeName;
private Integer age;
/**
* 用户角色
*/
private Role role;
public Role getRole() {
return role;
}
public void setRole(Role role) {
this.role = role;
}
public String getUserRloeName() {
return userRloeName;
}
public void setUserRloeName(String userRloeName) {
this.userRloeName = userRloeName;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getUserRoleName() {
return userRloeName;
}
public void setUserRoleName(String userRoleName) {
this.userRloeName = userRoleName;
}
}
创建数据访问接口
UserMapper.java
package com.ssm04.mapper;
import com.ssm04.pojo.User;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
public interface UserMapper {
//示例2
List<User> getUserList(User user);
//示例6
/**
* 查询用户列表(参数:Map)
* @param userMap
* @return
*/
List<User> getUserListBymap(Map<String, String> userMap);
//示例10
List<User> getUserListp(User user);
//增加方法
int add(User user);
//修改
int modify(User user);
//使用@Param注解实现多参数入参
int updatePwd(@Param("id") Integer id, @Param("userPassword") String pwd);
//delete方法
int deleteUserById(@Param("id") Integer delId);
List<User> getUserListId(@Param("userRole") Integer roleId);
}
创建mybatis的xml映射文件
UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ssm04.mapper.UserMapper">
<select id="getUserListByUserName" resultType="User" parameterType="string">
select * from smbms_user where userName like concat('%',#{userName},'%')
</select>
<!-- //示例三-->
<select id="getUserList" resultType="User" parameterType="User">
select * from smbms_user where userName like concat('%',#{userName},'%') and userRole = #{userRole}
</select>
<!-- //示例7-->
<select id="getUserListBymap" resultType="User" parameterType="Map">
select * from smbms_user where userName like concat('%',#{uName},'%') and userRole = #{uRole}
</select>
<!-- //示例8-->
<select id="getUserListp" resultMap="userList" parameterType="User">
select u.*,r.rloeName
from smbms_user u,smbms_role r
where u.userName like concat('%',#{userName},'%') and u.userRole = #{userRole} and u.userRole = r.id
</select>
<!-- 增加用户-->
<insert id="add" parameterType="User">
insert into smbms_user (userCode,userName,userPassword,gender,birthday,phone,address,userRole,createdBy,creationDate)
values (#{userCode},#{userName},#{userPassword},#{gender},#{birthday},#{phone},#{address},#{userRole},#{createdBy},#{creationDate})
</insert>
<!-- 示例9-->
<resultMap type="User" id="userList">
<result property="id" column="id"/>
<result property="userName" column="userName"/>
<result property="userRloeName" column="rloeName"/>
</resultMap>
<!-- 示例13-->
<!-- !-- 修改用户–>-->
<update id="modify" parameterType="User">
update smbms_user set userCode=#{userCode},userName=#{userName},userPassword=#{userPassword},phone=#{phone} where id = #{id}
</update>
<!-- 示例15-->
<!-- //修改当用户密码-->
<update id="updatePwd">
update smbms_user set userPassword=#{userPassword} where id = #{id}
</update>
<!-- 示例17-->
<delete id="deleteUserById" parameterType="Integer">
delete from smbms_user where id=#{id}
</delete>
<resultMap id="userRoleResult" type="User">
<id property="id" column="id"/>
<result property="userCode" column="userCode" />
<result property="userName" column="userName"/>
<result property="userRole" column="userRole" />
<association property="role" javaType="com.ssm04.pojo.Role">
<id property="id" column="r_id"/>
<result property="roleCode" column="roleCode"/>
<result property="rloeName" column="rloeName"/>
</association>
</resultMap>
<select id="getUserListId" resultMap="userRoleResult">
select u.*,r.id as r_id,r.roleCode,r.rloeName
from smbms_user u,smbms_role r where u.userRole = #{userRole} and u.userRole = r.id
</select>
</mapper>
创建mybatis配置文件
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
</configuration>
建立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"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
配置数据源
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url">
<value><![CDATA[jdbc:mysql://127.0.0.1:3306/easybuy?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8]]></value>
</property>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
**在创建一个bean **id为dataSource
org.apache.commons.dbcp2.BasicDataSource:dbcp数据库连接池
driverClassName:mysql数据库驱动
url:数据库连接url
username :数据库用户名
password :数据库密码
<![CDATA[jdbc:mysql://127.0.0.1:3306/easybuy?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8]]>
因为url属性的值包含特殊符合“&”,所以**这里需要将数据连接url进行转义**
配置SqSessoionFactoryBean
<!--配置sqlSessionFactory工厂-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
org.mybatis.spring.SqlSessionFactoryBean:在mybatis中,SqlSessionFactory由SqlSessionFactoryBuilder创建,在mybatis-spring中,是由SqlSessionFactoryBean创建的。