Spring技术(框架)
一、Spring引言
Spring框架:
集众多优秀的设计模式为一体的开源、轻量级项目管理框架。JavaEE轻量级解决方案。
工厂、单例、代理、策略、模板。。。。。 Spring 3.X
轻量级项目管理框架:
轻量级:代码的侵入性比较小。
struts、mybatis框架 ---> MVC分层中某一层技术存在明显的不足,具体的业务替换。
Spring项目管理框架:单层框架进行优化,使用优秀的设计模式揉合现有的单层框架进行整合管理。
Spring框架: 管理 项目中组件的创建使用以及销毁。 对象---> dao service action 组件对象
Spring是一个容器。
Spring通常不对实体类进行管理创建。
工厂设计模式
概念:通过工厂类,完成对象的创建或者生产实例化。
好处:替代原始的直接new对象,通过反射来动态创建类的对象,系统–解耦和。 扩展性,开闭原则。
步骤:
1. 完成基础类的开发 (接口 实现类 工厂类)
2. 使用配置文件配置类信息 key=value
3. 调用工厂来创建对象 避免了new关键字 解耦和
Spring工厂类 + 配置文件 来完成用户自定义对象或者组件的创建。 (解耦和)
二、第一个Spring程序(Hello World)
1.搭建环境
引入jar包 Spring不提供三方jar 详见资料
2.配置文件
Spring: 位置随意,名称随意。 学习阶段建议:applicationContextxml
3.初始化配置信息
不需要任何初始化配置。
Spring程序 — 核心API
1.Spring的工厂类
ApplicationContext 工厂类 接口!!!
工厂类实现:(了解)
ClassPathXmlApplicationContext("配置文件路径"); 使用类路径完成配置信息的载入
WebXmlApplicationContext(路径); 应用才Web环境下读取xml配置文件
FileSystemXmlApplicationContext(路径) 应用在文件系统中
工厂一定是一个重量级的资源,工厂只有一个。 线程安全,占用内存较多。
创建:ApplicationContext ctx = new ClassPathXmlApplicationContext("文件路径");
Spring代码步骤
- 基础代码书写
- 配置文件配置
//id唯一标示 class 权限定名
<bean id="user" class="first.User"></bean>
<bean id="userDAO" class="first.UserDAOImpl"></bean>
- 通过Spring的工厂类来创建对象
@Test
public void test1(){
//工厂类的创建
ApplicationContext ctx =
new ClassPathXmlApplicationContext("/first/applicationContext.xml"); //路径
User user = (User) ctx.getBean("user"); //user 来源于配置文件的 id
UserDAO userDAO = (UserDAO) ctx.getBean("userDAO"); //userDAO 来源于配置文件的 id
System.out.println(user);
System.out.println(userDAO);
}
三、注入(Injection)
可以使用配置文件来为创建出来的对象进行赋值 — 注入
关注语法如何实现,并不关系项目如何使用。
<!-- 可以通过配置文件为创建的对象 赋值 语法讲解 -->
<bean id="user" class="first.User">
<property name="id" value="1"></property>
<property name="name">
<value>jinqiu</value>
</property>
</bean>
1. Set注入
1. JDK中所存在的数据类型
1.1 八种基本类型 + String 详细见上图
1.2 数组
User : private String s[];
<property name="s">
<array>
<value>1</value>
<value>2</value>
<value>3</value>
</array>
</property>
1.3 集合 Set List Map
User : private Set<Object> set;
<property name="set">
<set>
<value>1</value>
<value>2</value>
<value>3</value>
</set>
</property>
User : private List<Object> list;
<property name="list">
<list>
<value>1</value>
<set>
<value>1</value>
</set>
</list>
</property>
注意: 所有的数据的存储不一定都是 <value></value> 具体由实际存储的数据本身来决定
User : private Map<String,String> maps;
<key></key> 只是标识了里面的数据是键 键具体是什么样的数据值要视情况而定(基本<value> 其他对应的标签)
<property name="maps">
<map>
<entry>
<key><value>1</value></key>
<value>1</value>
</entry>
<entry>
<key><value>2</value></key>
<value>2</value>
</entry>
</map>
</property>
特殊 : Properties类型 : Map<String,String>
<property name="maps">
<props>
<prop key="1">1</prop>
<prop key="2">2</prop>
</props>
</property>
用户自定义类型
通常所指的实体类 —> UserDAOImpl UserServiceImpl
遵循之前的赋值思想:
UserServiceImpl 中去使用 UserDAOImpl的实现对象 注入 的赋值思想
(先创建UserDAO 将其注入到UserService)
在UserServiceImpl 定义成员变量 UserDAO set/get方法 ,使用配置文件对于成员变量进行赋值。
<!-- 需要去创建UserDAOImpl的实现 -->
<bean id="uDAO" class="first.UserDAOImpl"></bean>
<!-- 需要去创建UserServiceImpl的实现 Service中需要使用 UserDAOImpl的实现-->
<!-- UserDAO定义为成员变量 使用set注入赋值 -->
<bean id="userService" class="first.UserServiceImpl">
<property name="userDAO">
<ref local="uDAO"></ref> <!-- 注入赋值 uDAO 就是 UserDAOImpl 中id值 -->
</property>
</bean>
注入的应用:解耦和 — 使用配置文件严格遵循开闭原则
Spring框架如何实现注入
1. 使用bean标签完成声明
<bean id="uDAO" class="first.UserDAOImpl"></bean>
<bean id="userService" class="first.UserServiceImpl"></bean>
2. 定义成员变量
private UserDAO userDAO; set、get
Spring工厂: 读取配置文件中class,使用反射获得类对象Class,先创建类的对象。
3. Spring调用类中的Set方法为成员变量进行赋值操作。
<bean id="userService" class="first.UserServiceImpl">
<property name="userDAO">
<ref local="uDAO"></ref>
</property>
</bean>
2. 构造注入(了解)
使用构造方法完成对于成员变量的赋值操作
- 定义构造方法(有参构造)
- 配置文件的配置
<!-- 使用构造方法注入 -->
<bean id="user" class="first.User">
<!-- 调用指定的构造方法 -->
<constructor-arg type="String">
<value>1</value>
</constructor-arg>
<constructor-arg type="int">
<value>2</value>
</constructor-arg>
</bean>
<constructor-arg></constructor-arg> 按照参数列表的顺序一一赋值
type="参数类型"
根据配置文件自动去匹配对应的构造方法
四、Spring工厂 FactoryBean技术
1、使用Spring工厂核心目的:创建对象!
1. 简单对象
1.1 书写对应类
1.2 配置文件的配置
1.3 配置文件中为对象赋值
注入: Set注入、构造注入
JDK类型的数据 或者 用户自定义对象类型。
2. 复杂对象
不能使用new关键字或者通过构造方法直接创建,创建过程中往往需要多个参数配置或者多个复杂步骤。
例如: Connection SqlSessionFactory 接口对象
1、 复杂对象创建的代码类 实现 FactoryBean接口
public class ConnectionFactoryBean implements FactoryBean<Connection>{
@Override
//获得对象 创建复杂对象的代码
public Connection getObject() throws Exception {
Class.forName("oracle.jdbc.OracleDriver");
Connection connection =
DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:XE", "hr", "hr");
return connection;
}
@Override
//获得复杂对象的class对象
public Class getObjectType() {
return Connection.class;
}
@Override
//是否是单例 true单例 只创建一次 false不是单例可以多次创建
public boolean isSingleton() {
return false;
}
}
2、完成Spring applicationContext.xml的配置
<bean id="conn" class="factorybean.ConnectionFactoryBean"></bean>
3、测试
@Test
public void test1(){
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
//如果实现了 FactoryBean接口 类的创建并不是接口实现类对象(ConnectionFactoryBean)
//而是其中的getObject方法的返回值对象 Connection
Connection conn = (Connection) ctx.getBean("conn"); //复杂对象
//获取 ConnectionFactoryBean 基本没人用
ConnectionFactoryBean cfb = (ConnectionFactoryBean) ctx.getBean("&conn"); //具体的实现类对象
System.out.println(conn);
System.out.println(cfb);
}
注意:
1. 创建简单对象的时候,getBean("id") 返回值是配置文件中 class 的具体实例对象
2. 复杂对象中使用getBean("id") 获得的是类中 getObject方法的返回值(复杂对象)
3. 如果真想获取class中的实例对象 getBean("&id"). --- 很少使用
第二讲
一、控制简单对象的创建次数
Spring工厂创建简单对象,由配置文件指定对象是否是单例
默认单例 : 节省资源 !!!
公用资源对象: DAO实现类对象、Service实现类对象
私有资源对象: Struts2(Action)、Cart
由对象本身的特性决定
多例: <bean id="userDAO" class="first.UserDAOImpl" scope="prototype"></bean>
单例: <bean id="userDAO" class="first.UserDAOImpl" scope="singleton"></bean>
二、IOC 和 DI
1. IOC (Inversion of Control) 控制反转
面向对象编程中一种设计原则,降低计算机系统中的耦合度。
控制: 在对象的创建的过程中,对于成员变量的赋值权的控制。
控制权反转 : 由代码的直接书写赋值,交由了Spring的配置文件。
工厂+反射
<bean id="uDAO" class="first.UserDAOImpl"></bean>
<bean id="userService" class="first.UserServiceImpl">
<property name="userDAO">
<ref local="uDAO"></ref>
</property>
</bean>
2. DI(Dependency Injection) 依赖注入
一个类的实例对象需要使用另一个类的实例对象,将依赖对象转换成成员变量使用Spring的配置文件进行赋值创建。
1. Service层需要使用DAO对象,Service依赖于DAO对象,将DAO定义成Service中的成员变量使用配置文件进行赋值创建。
2. 复杂对象创建 例如 Connection 常用的参数 driver url ... 遵循 IOC DI 将其定义成成员变量使用配置文件依赖注入赋值。
三、Spring容器的特性
1. 工厂创建对象的生命周期
1.1 创建
Spring工厂创建的同时会将配置文件中所有的对象全部创建。
ApplicationContext ctx = new ClassPathXmlApplicationContext("路径");
使用工厂.getBean其他对象的时候,所有对象的构造方法都会被调用。
饿汉式: 当首次运行会创建所有的对象,提升系统的运行效率。(减少对象的创建时间)
1.2 销毁
close(); 具体实现类中存在。 Spring销毁所有对象伴随工厂。
1.3 用户可以进行初始化以及销毁方法的自定义
<bean id="userDAO" class="first.UserDAOImpl" scope="singleton"
init-method="MyInit" destroy-method="MyDestroy"></bean>
用户自定义:MyInit MyDestroy
在配置文件中进行对应的信息配置。
2.配置文件参数化 PropertyPlaceHolder
将配置文件中有可能需要经常改变的内容(字符串)转移至更小的配置文件中。
<context:property-placeholder location="classpath:/jdbc.properties" />
<bean id="conn" class="first.ConnectionFactoryBean">
<property name="driverClassName" value="${driverClassName}"></property>
<property name="url" value="${url}"></property>
<property name="user" value="${user}"></property>
<property name="password" value="${password}"></property>
</bean>
1. context 全新的标签 引入标签库
参考beans中的bean的配置
修改五个地方
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
* *
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-3.2.xsd
http://www.springframework.org/schema/context
*
http://www.springframework.org/schema/context/spring-context-3.2.xsd
* * ">
2. location="classpath:/jdbc.properties"
classpath: 只要用来屏蔽路径差异
四、AOP 面向切面编程
代理设计模式
1. 静态代理设计模式
将核心业务代码 和 额外功能代码 分离开发。
概念: 通过代理类(Proxy)为原始类(Impl)增加额外功能代码。
目的:拆分核心功能和额外功能,保证不去频繁修改核心功能代码,提高代码的维护性。
注意: 代理类 同样需要 实现 原始接口。
代理类 = 使用原始类 + 额外功能 + 实现和原始类相同的接口
静态代理存在一些设计上没有避免的问题:
1. 代码的冗余 ,一个实现类最少有一个代理类。 ---> 实现类数量的增加
2. 代理类功能冗余。 一个相同的功能使用并不灵活。
3. 本质开发繁琐,替换额外功能开发新的代理类。
2. Spring中的动态代理设计模式
概念:使用代理类为原始类添加额外功能。
避免对于代码的频繁修改,冗余问题。
准备: 引入Jar包
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
1. 创建开发原始对象
1. 创建UserService UserServiceImpl
2. 配置
<bean id="userService" class="dynamicproxy.UserServiceImpl"></bean>
2. 额外功能 Advice接口(通知) (开发代理类)
使用Advice的子接口
1. MethodBeforeAdvice 在执行原始方法之前 (前置通知)
2. AfterReturningAdvice 在执行原始方法之后 (后置通知)
3. MethodInterceptor 在执行原始方法之前之后执行 (环绕通知)
4. ThrowAdvice 在原始方法运行发生异常时候处理 (异常通知)
完成了代理类的开发:
<bean id="userServiceProxy" class="dynamicproxy.UserServiceProxy"></bean>
3. 定义切入点 PointCut
决定额外功能所添加的位置
<aop:config>
<!-- 定义切入点 id pointCut标示
expression 切入的位置 execution(* *(..)) 所有声明内容都添加-->
<aop:pointcut id="userServicePc" expression="execution(* *(..))"></aop:pointcut>
</aop:config>
execution() * *(..) 所有的方法都添加额外功能(前置通知)
1. 方法切入点
表达式 execution() 函数
* *(..) 所有
修饰符 返回值类型 方法名(参数表){实现}
public void login(){}
* login(..)
//寻找一个参数是User的方法 自定义对象
* login(dynamicproxy.User) 权限定名
* login(String)
* login(String,String)
* login(String,..)
2. 类切入点
方法来源于类
public void dynamicproxy.UserServiceImpl.login(){}
修饰符返回值 包.类.方法(参数)
* *.UserServiceImpl.*(..) //所有UserServiceImpl下的方法
* *.UserServiceImpl.login(..) // 类下的所有login方法
3. 包切入点
public void dynamicproxy.UserServiceImpl.login(){}
修饰符返回值 包.类.方法(参数)
* dynamicproxy.*.*(..) //包下的所有类的所有方法
* dynamicproxy..*.*(..) //包后多了一个. 子包
// dynamicproxy.sub 可以的 dynamicproxy.sub.xx 不可以
4. 组装 (整合 切入点 + 代理通知)
<aop:config>
<!-- 定义切入点 id pointCut标示
expression 切入的位置 execution(* *(..)) 所有声明内容都添加-->
<aop:pointcut id="userServicePc" expression="execution(* *(..))"></aop:pointcut>
<!-- 切入点 + 代理通知 -->
<aop:advisor advice-ref="userServiceProxy" pointcut-ref="userServicePc"></aop:advisor>
</aop:config>
注意:
1.Spring会运行时动态创建代理对象。
2.Spring创建对象 原始类的id 还是 代理类的id?
原始类的id Spring工厂会在创建对象的时候为原始类根据配置信息动态生成代理类。
ApplicationContext ctx = new ClassPathXmlApplicationContext("路径");
UserService userService = (UserService) ctx.getBean("userService");
第三讲
一、切入点
1. 切入点函数
1. execution()
2. args()
作用: 主要用于匹配函数中的参数作为切入点
语法: args(String) ----> execution(* *(String))
3. within()
作用: 匹配包或者类作为切入点
execution(* 包.类.方法(..))
within(*.UserServiceImpl)
within(dynamicproxy.*)
within(dynamicproxy..*)
4. @annotation()
作用:判断方法是否存在某个特定注解,如果存在则加入切入点。
需要一个注解:自定义注解!!!
@Target(ElementType.METHOD) //注解应用在方法上
@Retention(RetentionPolicy.RUNTIME) //RUNTIME 运行时依然有效
public @interface MyAnn {
//String aaa() default ""; //属性 default 默认值
}
使用: 对应方法添加自定义注解 @MyAnn
配置: @annotation(注解权限定名)
补充: 支持逻辑运算—切入点函数 and or not
函数之间的运算
expression="args(String) or @annotation(dynamicproxy.MyAnn)"
注意:相同函数之间不可以使用 and (类似于数据库中的and or)
2. 额外功能通知 Adivce接口
1. MethodBeforeAdivce 前置通知
@Override
public void before(Method method, Object[] args, Object obj)
throws Throwable {
System.out.println("-----Proxy------"); //会在原始类之前执行
}
method:原始方法声明
args:原始类方法中的参数数组
obj:原始类对象
2. AfterReturningAdvice 后置通知
@Override
public void afterReturning(Object ref, Method method, Object[] args,
Object obj) throws Throwable {
// TODO Auto-generated method stub
}
其他对象和上一个方法没有区别
Object ref: 原始类方法的返回值 注意:如果原始方法返回值 void ref=null;
3. MethodInterceptor 环绕通知
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
System.out.println("----前----");
Object proceed = mi.proceed(); //执行后续的原始类方法 获得方法的返回值
System.out.println("----后----");
return proceed;
}
4. ThrowsAdvice
ThrowsAdvice:标示性接口 接口中没有必须实现的方法。 (Seriazable对象序列化)
public void afterThrowing(Exception ex){
System.out.println(ex);
}
//ex 产生的异常
二、AOP的实现原理
面向对象编程:类—>对象,通过对象之间的相互调用。
面向切面编程:
切面 : 切入点 + 额外功能 的 组合。
如何实现:Spring 动态代理实现。 代理类为原始类添加额外功能
动态代理
代理类 = 原始对象 + 额外功能 + 实现相同接口
直接写出来代理类 = 静态代理
使用方法在程序运行时动态生成代理类 = 动态代理
JDK 动态代理(接口)
代理类 = Proxy.newProxyInstance( 类加载器 ,原始对象的接口 , 额外功能 );
类加载器: ClassLoader .class—> JVM中运行, 代理类是程序运行的时候动态生成,代理类是没有.class就没有默认的类加载器。(随便借一个)
@Test
public void test1(){
final UserService uS = new UserServiceImpl();
//额外功能 InvocationHandler接口
InvocationHandler ih = new InvocationHandler(){
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("-----InvocationHandler-----");
//调用UserServiceImpl 中的方法
//uS 原始类对象 method原始类对象方法
Object invoke = method.invoke(uS, args);
return invoke;
}
};
UserService userService = (UserService)
Proxy.newProxyInstance(TestJDKProxy.class.getClassLoader(),
uS.getClass().getInterfaces() , ih);
userService.login();
}
Cglib动态代理(动态字节码添加)(实现类)
public class UserService{
public void login(){
//代码
}
}
new UserService().login();
代理类中的方法 和 接口 以及原始类中的方法完全一样,代理类要去继承原始类,
继承:同样可以保证代理类和原始类中的方法一致。
final UserService1 userService1 = new UserService1();
//cglib核心对象
Enhancer enhancer = new Enhancer();
//代理类继承原始类保证方法一致
enhancer.setSuperclass(userService1.getClass());
//额外方法 import org.springframework.cglib.proxy.InvocationHandler;
InvocationHandler ih = new InvocationHandler(){};
//添加额外功能
enhancer.setCallback(ih);
UserService1 us = (UserService1) enhancer.create();
us.login();
BeanPostProcessor 为什么可以通过id获取代理类对象
BeanPostProcessor:Spring工厂对于原始对象再加工。
Spring + MyBatis 整合
1.整合思路
1. 环境Jar包
Spring --- jar Mybatis --- jar JDBC --- jar Spring+MyBatis 整合时使用的jar
2. 整合
Spring 轻量级的项目管理框架 项目管理 组件的创建
MyBatis 持久层框架 替换JDBC
核心对象的创建交由Spring进行管理
3. MyBatis核心对象
DAO实例对象
SqlSession
SqlSessionFactory
创建SqlSessionFactory ---> 依赖对象 MyBatis配置文件
1. 数据的连接 dataSource 数据源
2. mapper.xml注册
4. 提取核心内容交由Spring的配置文件来完对于SqlSessionFactory的创建
SqlSessionFactory实现类 实现 FactoryBean<SqlSessionFactory> 大家都一样
Spring配置 ---> Spring 和 MyBatis整合jar包
<bean id="sqlSessionFactory"
class="Spring 和 MyBatis整合jar包帮你定义好的SqlSessionFactory的创建"/>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"></bean>
配置文件:
<!-- 配置文件参数化 -->
<context:property-placeholder location="classpath:/jdbc.properties" />
<!-- dataSource :sqlSessionFactory的数据源 数据库连接 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${driverClassName}"></property>
<property name="url" value="${url}"></property>
<property name="username" value="${user}"></property>
<property name="password" value="${password}"></property>
</bean>
<!-- 创建MyBatis中 sqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--dataSource 连接 -->
<property name="dataSource" ref="dataSource"></property>
<!-- Mapper.xml的注册 -->
<property name="mapperLocations" value="classpath:com/baizhi/mapper/*Mapper.xml"></property>
<!-- typeAliasesPackage 包下所有的实体类自动别名 类名 User / user -->
<property name="typeAliasesPackage" value="com.baizhi.entity"></property>
</bean>
<!-- DAO对象依然使用 Spring工厂创建 IOC DAO创建依赖于sqlSessionFactory-->
<!-- mapper.xml 转换 java对象 -->
<!-- mapperScanner创建所有配置过的mapper.xml对象 -->
<bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- DAO创建依赖于sqlSessionFactory-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
<!-- sqlSessionFactory Mapper注册的是所有 包中的所有 value=“包名”-->
<property name="basePackage" value="com.baizhi.dao"></property>
</bean>
<!-- UserDAO Spring默认接口首字字母小写 bean userDAO-->
<bean id="userService" class="com.baizhi.service.UserServiceImpl">
<!-- 依赖注入思想 userDAO id 所有的DAO都在mapperScanner
命名规范:UserDAO Spring默认接口首字字母小写 bean id="userDAO"
ref="userDAO" 从mybatisScanner获取已经创建好的对象
-->
<property name="userDAO" ref="userDAO"></property>
</bean>
注意: Spring允许配置文件的拆分
<import resource="classpath:/Spring_MyBatis.xml"></import>
2. MyBatis + Spring 事务控制
事务:控制原子操作要么一起彻底成功,要么一起彻底失败。
JDBC : Connection MyBatis: SqlSession
整合中连接在哪?
dataSource 数据源—>连接池 Service需要使用连接控制事务
- 将dataSource 定义成成员变量 使用依赖注入赋值
- 使用dataSource 获取连接 控制事务
Service 和 DAO 需要获取相同的连接对象?
JDBC :线程绑定。
Spring帮你去完成 ----> 同步标记
<!-- 事务管理 : 保证Service 和 DAO 获取相同连接对象 类似于线程邦定 使用同步标记 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
dataSource 交给 DataSourceTransactionManager 管理
获取连接不再是使用原始的dataSource,使用管理DataSourceTransactionManager
Service中定义成员变量
private DataSourceTransactionManager transactionManager;
Set/Get 方法
Connection conn = DataSourceTransactionManager.getDataSource().getConnection();
AOP面向切面编程 — 解决Service中的冗余代码(额外功能)
处理事务的环绕通知不需要自己开发,使用Spring中的配置。
AOP开发步骤
1.原始类接口 2.Adivce <tx:advice> 3.定义切点<aop:pointcut> 4.组装
<!-- tx事务 advice通知接口 额外方法 环绕通知控制事务 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 细粒度的声明 ServiceImpl中哪些具体方法需要使用事务环绕通知 -->
<tx:attributes>
<tx:method name="r*"></tx:method>
</tx:attributes>
</tx:advice>
<!-- 定义切入点 组装切面 -->
<aop:config>
<!-- 定义切点 -->
<aop:pointcut id="pc" expression="within(com.baizhi.service.*ServiceImpl)"></aop:pointcut>
<!-- 组装切面 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pc"></aop:advisor>
</aop:config>
3. 事务的传播属性
现有的业务场景过于简单,Service就是调用DAO中的方法,Service调用Service中的方法。
业务方法在相互调用的时候如何保持一致?有时候需要明确区分独立事务?
事务的传播属性: 内外方法调用时候事务是否相互影响
<tx:method name="r*" propagation=""></tx:method>
REQUIRED : 表示需要事务 (默认值 相同事务)
如果外部没有事务,则内部开启新的事务,如果外部存在事务,则内外事务融合。
REQUIRES_NEW : 每次开启新的事务 (各自独立事务)
外部没有事务,内部需要开启新事务,如果外部存在事务则挂起外部事务,开启内部新事务,执行完毕再次唤 醒外部的事务。
SUPPORTS : 表示支持事务(外部是一个DML操作嵌套一个查询但是查询再次嵌套DML)
外部没有事务则内部独立,如果外部存在事务可以和内部(第三次嵌套)的事务融合
NOT_SUPPORTED : 内部一定不会和外部事务融合。
NEVER : 不支持事务
ManDatory : 强制事务
NESTED(听说过有这么个属性就行)
4. 事务的隔离属性
脏读 : 一个事务读取到了另一个事务还未提交的数据。
幻读 : T1读取了两次数据,在前后读取的过程中T2对数据进行了操作(DML),T1前后数据不一致。
不可重复读 : 以为读取的是对的,T1事务读取数据,T2事务对数据进行了修改,实际数据已经发生了改变。
数据库存在默认隔离级别:避免以上错误读取。 关系型
四个: Read uncommitted (读未提交) 所有的都可能发生
Read committed (读提交) 避免脏读
Repeatable Read (重复读) 避免脏读、不可重复读
Serializeable (序列化) 避免脏读、不可重复读、幻读
Oracle 默认 Read committed 可以改变 最高允许Serializeable
MySQL 默认 Repeatable Read
isolation="Default" 默认使用数据库自己的隔离级别
5.事务中的其他属性
1. timeout="-1" 超时属性 默认-1 无限等待 (秒)
事务的超时:事务的挂起时间
2. read-only=true / false 默认false
read-only=true : 该方法就是查询方法不存在DML操作 建议如果是单纯的查询操作最好添加
3. 参数:异常的权限定名 填父类包括子类 Spring默认提交策略
rollback-for="" 发生对应异常直接回滚数据
no-rollback-for="" 发生对应异常直接提交数据
Spring + Struts2 整合
1.整合思路
1. 导入 jar包 struts.xml文件
2. 核心对象的创建交由Spring完成
自定义Action
XXXAction组件 ---> Spring 完成创建
核心过滤器 web.xml中的配置的Filter (是由服务器创建)
还是使用web.xml配置即可(不发生改变)
3. 交由Spring创建的核心对象只有Action
Spring的配置文件创建 Action对象
<bean id="XXX" class="" scope="Action多例对象">
<Service业务对象的依赖注入 ref="">
</bean>
localhost:8989/项目名/namespace/action_name ---> class="Spring Bean id"
Struts.xml
<package name="" extends="" namespace="">
<action name="" class="XXX" method=""></action>
</package>
jar包:struts-spring-plugin.jar 引入jar包 默认class中直接去寻找Spring配置中的bean id
2.如何去创建Action对象? — 项目启动的时候创建Spring工厂
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
Action action = (Action) ctx.getBean("action bean id");
原来从未手动创建过Action对象
Action由Spring创建, 需要在启动服务器的时候去创建Spring工厂。
谁能在服务器启动的时候去创建工厂呢? ---- 监听 ServletContext 全局唯一。
传你的配置文件给Spring的工厂创建方法即可。
写在哪? ---web.xml中
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:/applicationContext.xml</param-value>
</context-param>
3. Spring + Struts2+MyBatis 步骤
1. 环境 jar包 以及配置文件
applicationContext.xml struts.xml
2. Spring + MyBatis
2.1 建表
2.2 实体类
2.3 DAO接口定义
2.4 Mapper.xml的开发
2.5 Service业务接口的定义
2.6 Service业务方法的实现
2.7 配置Spring的xml文件
a. DataSource的数据源
b. 配置文件参数化 <context:property-placeholder/>
c. sqlSessionFactory 核心工厂的创建
注入属性
DataSource 数据源
mapperLocations 注册所有的mapper
typeAliasesPackage 包中所有实体类别名
d. MapperScannerConfigurer 创建所有的UserDAO实现类
注入属性
sqlSessionFactoryBeanName 工厂
basePackage 接口包 --所有的对象都是接口首字母小写
e. transactionManager 事务管理器 保证连接相同
注入属性
DataSource
f. 声明 事务的环绕通知
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--细粒度的声明 ServiceImpl中哪些具体方法需要使用事务环绕通知 -->
<tx:attributes>
<!-- 添加事务的属性 传播属性 内外相互影响吗? -->
<tx:method name="r*" read-only="true"></tx:method>
</tx:attributes>
</tx:advice>
g. 定义切入点 组装切面 ---- 事务
h. 使用bean创建Service对象 使用属性就是依赖注入DAO
3. struts2 + Spring
3.1 配置 web.xml
Struts中的核心过滤器 StrutsPrepareAndExecuteFilter
Listener监听---> 工厂的启动创建
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:/applicationContext.xml</param-value>
</context-param>
3.2 Action的开发
3.3 Spring配置文件 创建Action对象 <bean scope="prototype">多例对象
3.4 配置struts.xml <action name="" class="spring bean id">
4. Spring的注解开发
Spring配置文件中依然存在大量的配置冗余,2.5X建议使用注解开发简化配置文件。
在配置文件开启注解扫描
<context:component-scan base-package="com"></context:component-scan>
扫描的包
1、@Component 实例化相关注解
替换bean标签配置
类上添加了 @Component Spring工厂会创建该类的对象 默认 id 类名首字母小写
@Component("userService") 加给类(组件) Action DAO Service (如果集成MyBatis DAO就可以忽略)
@Component(value="userService")
子分支
DAO : @Repository()
Service : @Service("userService")
Action : @Controller("userAction")
@Scope("prototype") 多例
默认 singleton 单例
2、注入相关
@Autowired : Spring 使用在成员变量或者set方法上
一般都是用在成员变量上 用来替换配置中的set注入 set、get可以省略
@Autowired 根据成员变量的类型自动选择注入
private UserDAO userDAO;
@Autowired
@Qualifier(value="") 指定注入数据的内容
private UserDAO userDAO;
@Resource : JavaEE规范中 先根据成员变量的名称,再根据成员变量的类型匹配。
3、事务相关
@Transactional
类 或者 方法上 控制事务 声明的环绕通知以及切点 组装切面可以省略
注解生效
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
事务存在传播属性 注解中的属性
@Transactional(propagation=Propagation.SUPPORTS)
4、测试相关
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestMyBatis {
@Autowired
private UserService userService;
@Test
public void test3(){
//创建工厂
//bean id 获取对象
List<User> queryAllUser = userService.queryAllUser();
for (User user : queryAllUser) {
System.out.println(user);
}
}
}