Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson 在其著作Expert One-On-One J2EE Development and Design中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架。Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。Spring的核心是控制反转(IOC)和面向切面(AOP)。简单来说,Spring是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架
Spring的优点:
- 方便解耦,简化开发 (高内聚低耦合) Spring就是一个大工厂(容器), Spring工厂是用于生成bean,可以将所有对象创建和依赖关系维护,交给Spring管理
- AOP编程的支持
Spring提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能- 声明式事务的支持 只需要通过配置就可以完成对事务的管理,而无需手动编程
- 方便程序的测试
Spring对Junit4支持,可以通过注解方便的测试Spring程序- 方便集成各种优秀框架
Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如:Struts、Hibernate、MyBatis等)的直接支持,方便整合其他框架- 降低JavaEE API的使用难度
Spring 对JavaEE开发中非常难用的一些API(JDBC、JavaMail、远程调用等),都提供了封装,使这些API应用难度大大降低
Inversion of Control 又称为 依赖注入 (DI) Dependency Injection 把对象的创建和方法的调用的控制权(反转)交由给容器管理,由容器控制程序之间的依赖关系 IOC就是一个生产和管理bean的容器就行了,原来需要在调用类中new的东西,现在都是通过容器生成 同时,要是产生的是单例的bean,他还可以给管理bean的生命周期
它并不是一种技术实现,而是一种设计思想。在任何一个有实际开发意义的程序项目中,我们会使用很多类来描述它们特有的功能,并且通过类与类之间的相互协作来完成特定的业务逻辑。这个时候,每个类都需要负责管理与自己有交互的类的引用和依赖,代码将会变的异常难以维护和极度的高耦合。而IOC的出现正是用来解决这个问题,我们通过IOC将这些相互依赖对象的创建、协调工作交给Spring容器去处理,每个对象只需要关注其自身的业务逻辑关系就可以了。在这样的角度上来看,获得依赖的对象的方式,进行了反转,变成了由spring容器控制对象如何获取外部资源(包括其他对象和文件资料等等)
将程序中的交叉业务逻辑(比如安全,日志,事务等),封装成一个切面,然后注入到目标对象(具体业务逻辑)中去。 比如: 很多方法可能会抛异常,你要记录这个异常到日志中去,可以写个拦截器类,在这个类中记录日志,在spring.xml中配置一个对这些要记录日志的方法的aop拦截器 在这个方法执行后调用这个拦截器,记录日志。这样就不用每次抛异常都要手动记录日志。 spring的事务管理用到的就是aop 这样也可以提高程序的内聚性
1)用maven创建一个项目,在 pom.xml 文件中加入 spring的依赖(jar包)
2)在 resource 目录下,编写 spring.xml 配置文件,并定义要控制反转的类
***注意***:
- bean 的 id是严格区分大小写的 如果id不存在会报错: NoSuchBeanDefinitionException
- 如果容器中同一个类型的bean有多个,再根据类型获取的话就会报错: NoUniqueBeanDefinitionException
- bean一般需要无参构造,spring会调用它来创建对象
如果没有无参构造,需要给构造方法的参数赋值:- 默认情况下,每个类型的bean在容器中只有一个对象(单例)
singleton "> 多例,每用一次,创建一个新的对象, 如果配置多例prototype"> - 初始化方法 和 销毁方法:
init-method="初始化方法名()" destory-method="销毁方法名()" > 单例:单例对象既会调用初始化方法,也会调用销毁方法
多例:每使用一个多例对象都会调用初始化方法,但所有的多例对象都不会调用销毁方法
3)在测试类中使用 spring容器 进行测试
1) set注入: property 配合 setXXX() 方法
测试代码:
/**
* 1) set注入
*/
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
spring.xml 配置:
2) 构造注入: constructor-arg 配合构造函数
测试代码:
/**
* 2) 构造注入
*/
public UserService(UserDao userDao) {
this.userDao = userDao;
}
spring.xml 配置:
3) 注解注入: @AutoWired | @Resource 配合
测试代码:
/**
* 3) 注解注入 : 利用反射,找到该变量的类型,自动赋值
*/
@Autowired | @Resource
private UserDao userDao;
spring.xml 配置:
方便以后修改,只修改 *.properties 中的内容,不改动 spring.xml 中的配置
注解方式 @Value 也可以完成这种 值注入
@Value("${properties中的key}")
1)配置 pom.xml 文件,加入所需要的依赖
org.springframework
spring-context
4.3.18.RELEASE
junit
junit
4.12
mysql
mysql-connector-java
8.0.12
ch.qos.logback
logback-classic
1.2.3
org.mybatis
mybatis
3.4.5
com.alibaba
druid
1.1.7
org.mybatis
mybatis-spring
1.3.1
org.springframework
spring-jdbc
4.3.17.RELEASE
2)把关键对象的创建交给 spring 控制反转(IOC),生成
3)配置 XXXMapper.xml 映射文件
4) 使用 SqlSession 进行操作
public class SpringMybatisTest {
/*
* 1. 创建spring容器
根据xml文件应用程序Context容器(上下文)
classpath指配置文件的位置, 起点有java, resources. 写路径时相对这个起点去写
* */
static ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring.xml");
@Test
public void test1(){
/* 得到 SqlSession 对象*/
SqlSession sqlSession = (SqlSession) context.getBean("sqlSession");
Map map = new HashMap();
map.put("start",0);
map.put("size",5);
// 调用 Mapper映射文件里的方法
List list = sqlSession.selectList("com.chen.mapper.ProductMapper.selectPage", map);
for (Product product : list) {
System.out.println(product);
}
}
}
1) 在配置文件 pom.xml 中加入依赖
org.springframework
spring-context
4.3.17.RELEASE
org.springframework
spring-jdbc
4.3.17.RELEASE
org.mybatis
mybatis
3.4.5
org.mybatis
mybatis-spring
1.3.1
mysql
mysql-connector-java
8.0.12
com.alibaba
druid
1.1.7
ch.qos.logback
logback-classic
1.2.3
org.slf4j
jcl-over-slf4j
1.7.25
junit
junit
4.12
2)在 spring.xml 配置文件中
3)编写 mapper接口
public interface ProductMapper {
/**
* 增
*/
@Insert("insert into product(name,price) values(#{name},#{price})")
void insert(Product product);
/**
* 删
*/
@Delete("delete from product where id=#{id}")
void delete(int id);
/**
* 改
*/
@Update("update product set name=#{name},price=#{price} where id=#{id}")
void update(Product product);
/**
* 查: 根据id查
*/
@Select("select id,name,price from product where id=#{id}")
Product findById(int id);
/**
* 分页查询(物理分页)
*/
@Select("select id,name,price from product limit #{start},#{size}")
List findByPage(Map map);
/**
* 分页查询(逻辑分页)
*/
@Select("select id, name,price from product")
List PageLogical(RowBounds rowBounds);
/**
* 当 *.java -> *.class时,方法的参数名信息会丢失,所以再按m名称去找,则无法找到该参数名
* mybatis的mapper映射中,默认方法最多只接收一个参数, 多个参数需要用map或list等集合包装
* 要突破这个限制需要使用@Param注解,把它加在方法参数上,建立方法参数和sql中#{}之间的联系:
* @param m
* @param n
* @return
*/
@Select("select id, name,price from product limit #{start},#{size}")
List Page(@Param("start")int m,@Param("size")int n);
/**
* 动态sql,配合 XXXMapper.xml 映射文件进行 动态sql拼接
* 注意: 1) 目录结构要一致
* 2) xml中 namespace的取值与接口的包名类名要一致
* 3) 接口中的方法名与xml中标签id对应
* 4) 接口中的方法名不能重复(不支持重载)
*/
void deleteByIds(List ids);
}
4)使用 XXXMapper.xml 配合 Mapper接口 进行 动态 SQL 的拼接
delete from product where id in
#{i}
5) 测试Mapper接口
public class MapperTest {
//获得spring容器
static ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
@Test
public void test1(){
// 利用反射,得到spring自动配置的 ProductMapper 的
ProductMapper mapper = context.getBean(ProductMapper.class);
Product p = new Product();
p.setName("kobe");
p.setPrice(6666.66);
mapper.insert(p);
}
}
1)配置信息中引入事务的命名空间
xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
2)启用事务:用注解驱动的方式来管理事务
3)在 spring.xml 中配置事务管理器 : id=" transcationManager "
4) 在要使用事务的方法或类上,添加注解 @Transcational,spring会自动提交,回滚事务
/**
事务注解(@Transactional):
加在方法上,表示此方法受事务管理(自动提交,回滚事务)
加在类上,那么这个类中的所有方法都受事务管理
最后要注意的是,在业务方法中不要自己try-catch捕获异常,否则spring无法自动回滚事务
*/
@Transactional
public void business1(){
productMapper.delete(100000005);
productMapper.delete(100000006);
int i = 1/0;
productMapper.delete(100000007);
}
默认情况下,数据库处于自动提交模式,每一条语句处于一个单独的事务中,在这条语句执行完毕时,如果执行成功则隐式的提交事务,如果执行失败则隐式的回滚事务。
事务管理,是一组相关的操作处于一个事务之中,因此必须关闭数据库的自动提交模式。这点,Spring会在org/springframework/jdbc/datasource/DataSourceTransactionManager.java中将底层连接的自动提交特性设置为false
- @Transcational 默认情况下,只有方法出现的 是RuntimeException或Error以及它们的子类时(未检查异常),才会导致事务回滚 如果要改变默认情况
@Transactional(rollbackFor=异常类.class)
那么方法如果出现了该异常,或该异常的子类异常时,就会回滚 @Transactional(noRollbackFor=异常类.class)
当遇到这种异常时,不会回滚事务
***注意***:在业务方法中不要自己try-catch捕获异常,否则spring无法自动回滚事务- @Transactional(readOnly = true|false) :true表示只读(只有查询) false(会有增删改)
设置为true,性能会有所提升,但是底层数据库驱动支持(对mysql支持)- 事务超时设置:
@Transactional(timeout=30) //默认是30秒- 事务的传播行为: @Transcational (propagation=传播行为) 只有两个业务类的方法相互调用时,传播行为才会有效
ProductService 商品业务类
@Transactional(propagation=REQUIRED)
biz1() { // 事务1
biz2();
}
OrderService 订单业务类
@Transactional(propagation=REQUIRES_NEW)
biz2(); // 事务2
事务传播行为 说明 @Transactional(propagation=Propagation.REQUIRED) 如果有事务, 那么加入事务, 没有的话新建一个(默认情况) @Transactional(propagation=Propagation.NOT_SUPPORTED) 容器不为这个方法开启事务 @Transactional(propagation=Propagation.REQUIRES_NEW) 不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务 @Transactional(propagation=Propagation.MANDATORY) 必须在一个已有的事务中执行,否则抛出异常 @Transactional(propagation=Propagation.NEVER) 必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反) @Transactional(propagation=Propagation.SUPPORTS) 如果其他bean调用这个方法,在其他bean中声明事务,那就用事务。如果其他bean没有声明事务,那就不用事务 - 事务隔离级别: @Transcational( isolation = " 事务的隔离级别 ")
事务隔离级别
说明
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
读取未提交数据(会出现脏读, 不可重复读),基本不使用
@Transactional(isolation = Isolation.READ_COMMITTED)(SQLSERVER默认)
读取已提交数据(会出现不可重复读和幻读)
@Transactional(isolation = Isolation.REPEATABLE_READ)
可重复读(会出现幻读)
@Transactional(isolation = Isolation.SERIALIZABLE)
串行化
- 脏读 : 一个事务读取到另一事务未提交的更新数据
- 不可重复读 : 在同一事务中, 多次读取同一数据返回的结果有所不同, 换句话说, 后续读取可以读到另一事务已提交的更新数据。
- 可重复读:在同一事务中多次读取数据时,能够保证所读数据一样,也就是后续读取不能读到另一事务已提交的更新数据
- 幻读 : 一个事务读到另一个事务已提交的insert数据