概述
- 轻量级/开源JavaEE框架
- 解决企业应用开发复杂性
- 两核心:IOC、AOP
- 方便解耦,简化开发
- AOP编程
- 方便测试
- 降低API使用难度
- 支持事务
- 方便整合其他框架
Spring依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
Junit依赖
创建普通类,接口
创建spring配置文件
ClassPathXmlApplicationContext
IOC
创建对象交给Spring
注入方式
- Setter
- 构造注入
- P命名空间
<bean id="student" class="org.example.pojo.Student" autowire="byName|byType">
多配置文件
<!--包含多个配置文件-->
<!-- <import resource="classpath:base03/applicationContext_school.xml"></import>-->
<!-- <import resource="classpath:base03/applicationContext_student.xml"></import>-->
<!--通配符配置多个-->
<import resource="classpath:base03/applicationContext_*.xml"></import>
注解
- 加入spring注解依赖(spring-context时会把注解依赖自动加入spring-aop)
<!-- 组件扫描器,1.可指定多个包用,隔开,2.可以多次使用标签引入多个包3.可以指定父包,会自动扫描子包-->
<context:component-scan base-package="org.example"></context:component-scan>
- 类中加入注解
//@Component(value = "myStudent")
//@Repository 持久层
//@Service 业务层
//@Controller 控制器
@Component("myStudent")
//@Component
成员变量或者setter方法注解
// @Autowired引用类型注解,required默认true找不到报错
// @Autowired(required = false)
// @Qualifier("mySchool-1")
// @Resource默认byName,没有则byType
@Resource(name="school")
- 在spring配置文件中加入组件扫描器
AOP
动态代理
- 创建目标类,增强
- JDK,创建InvocationHandler接口的实现类,在这个类实现给目标类方法增强
public class InvocationHandlerImpl implements InvocationHandler {
private Object target;//SomeServiceImpl
public InvocationHandlerImpl(Object target){
this.target=target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object res=null;
ServiceTool.doLog();
// 执行目标对象方法
method.invoke(target,args);//doSome()doOther()
ServiceTool.doTran();
return res;
}
}
B.使用Proxy类,创建代理对象,实现创建对象的能力
// 创建目标对象
SomeService target = new SomeServiceImpl();
// 创建InvocationHandlerImpl
InvocationHandler handler=new InvocationHandlerImpl(target);
//创建Proxy代理对象
SomeService proxy = (SomeService)Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),handler);
//通过代理执行方法
proxy.doSome();
Spring面向切面
Aspectj、Spring
@Before(value = "execution(* *..*.do*(..))")//前置通知
public void myBefore(JoinPoint jp) {
System.out.println(jp.getSignature());
System.out.println("切面功能,目标方法执行之前输入时间:" + new Date());
}
JDBCTemplate
事务管理
- 什么是事务
事务是指一组sql语句的集合,集合有多条sql语句可能是insert,update,select,delete,我们希望这些多个sql语句都能成功,或者失败,这些sql的执行是一致的,作为一个整体执行。
- 什么时候使用
当我的操作涉及得到多个表,或者多个sql语句的insert,update,delete。需要保证这些语句都是成功才能完成我的功能,或者都是失败,保证操作符合要求的。
在java中哪里写事务控制事务?放在service业务方法中,因为业务方法会调用多个dao方法执行多个sql。
- 通常使用JDBC访问数据库,还是mybatis访问数据库是怎么处理事务
Jdbc:conn.startTransaction() conn.commit() conn.rollback()
Mybatis:sqlSession.commit() sqlSession.rollback()
- 上面事务处理方式有什么不足
A不同数据库访问技术,处理事务对象方法不同,需要了解不同数据库访问技术使用事务的原理
B掌握多种数据库访问技术的事务处理逻辑,什么时候提交事务,什么时候回滚事务
C处理事务的多种方法
总结:就是多种数据库访问技术,有不同的事务处理机制,对象,方法
- 怎么解决
Spring提供一种处理事务的统一模型,能使用统一步骤,完成多种不同数据库访问技术的事务处理,使用spring的事务处理机制,可以完成hibernate,mybatis访问数据库的事务处理
- 处理事务,需要怎么做,做什么
Commit close 回滚事务
接口:PlatformTransactionManager,定义了事务的重要方法,commit,close ,rollback
实现类:mybatis访问数据库,spring创建的是DataSourceTransactionManager
Hibernate:HibernateTransactonManager
怎么使用:告诉spring使用的是哪种数据库访问技术,使用<bean>声明数据库访问技术对应的实现类
<bean id=”dataSource” class=”xx.xx.DataSourceTransactionManager”>
业务需要什么样的事务,说明事务的类型
- 事务隔离级别,4个值
read_uncommited读未提交,未解决并发问题,
READ_Commited读已提交,解决脏读,存在不可重复读与幻读
Reapeat——read:可重复读,解决脏读,不可重复读,存在幻读
Serializable:串行化,不存在并发问题
- 事务超时时间,表示一个方法最长执行时间,如果方法执行超过了这个时间,事务回滚,单位秒,默认-1
- 事务传播行为,7个:
PROPAGATION_REQUIRED:指定的方法在事务中执行,若当前存在事务,就加入到当前事务中,若当前没有事务就创建一个事务,最常见,也是spring默认事务传播行为
PROPAGATION_REQUIRES_NEW:方法任何情况都创建新的事务执行,当前存在就挂起,直到新事务完成
PROPAGATION_SUPPORTS:方法有事务就当前事务中执行,也可以非事务方式执行
当业务执行成功,没有异常抛出,当方法执行完毕,spring咋方法执行后提交事务
当业务方法执行时异常,spring执行回滚,调用事务管理器的rollback
运行时异常:RuntimeException和他的子类都是运行时异常,例如:NullPointer、NumberFormat
当业务抛出非运行异常,主要是受查异常时,提交事务
- 管理事务,事务管理和它的实现类
- Spring的事务时一个统一模型
- 指定要使用的事务管理器实现类,使用<bean>
- 指定哪些类,哪些方法需要加入事务的功能
- 指定方法需要的隔离级别,传播行为,超时时间
我们要做的是:告诉spring项目类的信息,方法名称,方法事务传播行为
Spring提供事务处理方式
- 适合中小项目,注解方案,spring框架自己用aop实现给业务方法增加事务的功能@Transactional放在public方法上面,表示当前方法有事务,给注解属性赋值,隔离级别isolation=Isolation.DEFAULT,传播行为propagation=Propagation.REQUIRED,只读readOnly=false超时,rollbackFor异常类数组,rollbackForClassName异常类名,noRollbackFor
- 声明事务管理器对象<bean DataSourceTransactionManager>
- 开启事务注解驱动,告诉spring我要使用注解的方式管理事务,spring使用aop机制,创建@Transactional所在的类的代理对象,给方法加入事务功能
- 方法上使用@Transactional
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--指定数据源,数据库-->
<property name="dataSource" ref="druidDataSource"></property>
</bean>
<!--开启事务注解驱动,告诉spring使用注解管理事务,创建代理对象-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
大型项目
<tx:advice id="myAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="buy" propagation="REQUIRED" isolation="DEFAULT"
rollback-for="org.example.exception.NotEnoughException,java.lang.NullPointerException"/>
<tx:method name="add*" propagation="REQUIRES_NEW"/>
<tx:method name="modify*" propagation="REQUIRES_NEW"/>
<tx:method name="remove*" propagation="REQUIRES_NEW"/>
<!--统配,SUPPORTS事务可以有可以么有-->
<tx:method name="*" propagation="SUPPORTS" read-only="true"/>
</tx:attributes>
</tx:advice>
<!-- 配置AOP-->
<aop:config>
<!-- 配置切入点-->
<aop:pointcut id="pointcutTX" expression="execution(* *..service..*.*(..))"></aop:pointcut>
<!-- 配置增强-->
<aop:advisor advice-ref="myAdvice" pointcut-ref="pointcutTX"></aop:advisor>
</aop:config>
脏读:读取了另一事务修改尚未提交的修改
不可重复读:执行一个事务两次或以上读取数据数据,但每次读取得到的数据不同
幻读:执行一个事务两次或以上,读取的数据记录不一样
WEB中使用容器对象
只需创建一次,放入全局作用域,servletContext
监听器,当全局作用域对象时,创建容器
ServletContext。setAttribute(key,value),可以使用框架的ContextLoaderListener
<!-- 监听器对象依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!-- 注册监听器,默认读取/WEB-INF/applicationContext.xml-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
// 创建spring容器对象
// ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
WebApplicationContext context = null;
// 获取ServletContext中的容器对象
// String key="ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE";
// Object obj=getServletContext().getAttribute(key);
// if(obj!=null){
// context=(WebApplicationContext)obj;
// }
// 使用框架的方法获取容器对象
ServletContext sc = getServletContext();
context = WebApplicationContextUtils.getRequiredWebApplicationContext(sc);