XML方式配置bean存在的问题:开发效率低下。Spring2.x提供了开发效率更高的注解式配置。注解开发替换XML配置的好处:简化编程,提高开发效率。
查找注解,配置查找注解的起始包名
applicationContext.xml
为类和属性添加注解
package com.bz.service.impl;import com.bz.entity.User;
import com.bz.mapper.UserMapper;
import com.bz.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;@Component("userService")
public class UserServiceImpl implements UserService {@Autowiredprivate UserMapper userMapper;public void setUserMapper(UserMapper userMapper) {this.userMapper = userMapper;}@Overridepublic boolean login(String name, String password) {User user = userMapper.selectUserByName(name);return user!= null && user.getPassword().equals(password);}}
@Component替换bean标签,创建对象。
@Controller 用在action层
@Service 用在service
@Repository 用在dao层
@Service
//@Component("userService")
public class UserServiceImpl implements UserService {@Autowiredprivate UserDao userDao;public void setUserDao(UserDao userDao) {this.userDao = userDao;}...
}
注意:
Controller Service Repository和Component作用一样,但更有识别性。
MyBatis框架下,Repository注解没有必需的使用场景
4个注解使用时,都可以不用设置id属性,id默认值:类名首字母小写
例如:UserServiceImpl ==> userServiceImpl
@Scope 决定对象是否单例
默认对象是单例的,可以通过@Scope(“prototype”)设置为多例
@Autowired 用来完成属性注入。
@Service
public class UserServiceImpl implements UserService {@Autowired(required = false)private UserDao userDao;public void setUserDao(UserDao userDao) {this.userDao = userDao;}...
}
注意:
@Qualifier(“bean的id”)
与@Autowired配合使用,当有多个满足属性类型要求的Bean时,可以用@Qualifier(“id”)来指定要注入的依赖。
class Address{private String city;private String address;...
}
class Person{private String personName;@Autowired@Qualifier("addr1")private Address addr;...
}
@Value用于为简单属性赋值,还可以读取properties文件中的参数
jdbc.properties
url=jdbc:mysql://localhost:3306/spring?useUnicde=true&characterEncoding=utf-8&useSSL=false&serverTimeZone=Asia/Shanghai
driverClassName=com.mysql.cj.jdbc.Driver
user=root
password=123456
JdbcProeprties.java
@Component
public class JdbcProperties{//@Value("root")@Value("${user}")private String username;@Value("${password}")private String password;@Value("${url}")private String url;@Value("${driverClassName}")private String driverClassName;...
}
行业标准:
使用Transactional注解描述类或者方法添加事务。
开发步骤:
创建原始对象
定义通知类(功能增强:事务控制)
Spring内置的DataSourceTransactionManager
配置通知类
使用Transactional注解描述类或方法
@Service
@Transactional
public class UserServiceImpl implements UserService {@Autowiredprivate UserDao userDao;@Transactional(readOnly=true)@Overridepublic boolean login(String name, String password) {User user = userDao.selectUserByName(name);return user!= null && user.getPassword().equals(password);}public void register(String username,String password){userDao.insertUser(new User(null,username,password));}
}
注意:
传统的Spring项目中,注解的事务控制使用较少,更多的还是使用xml配置的事务控制。但是在SpringBoot中大量的使用注解方式的事务配置。
直接使用JUnit测试Spring项目,需要手动编码获取context工厂,需要手动从工厂中获取对象,测试繁琐。
解决方案:使用Spring-test,简化了Spring项目的测试。
准备工作:导入JUnit、spring-test依赖
junit junit 4.12 test
org.springframework spring-test 5.2.8.RELEASE
测试:
//开启Spring对Junit的支持
@RunWith(SpringRunner.class)
//设置配置文件的路径
@ContextConfiguration("classpath:applicationContext.xml")
public class ApplicationContextTest {//自动从工厂中获取对象,为属性赋值@Autowiredprivate ApplicationContext context;@Autowiredprivate UserMapper userMapper;@Autowiredprivate UserService userService;...
}
小技巧:如果有多个测试类,多个测试类上方也有重复的配置,可以定义父类抽取共性
//开启Spring对Junit的支持
@RunWith(SpringRunner.class)
//设置配置文件的路径
@ContextConfiguration("classpath:applicationContext.xml")
public abstract class AbstractApplicationContextTest {
}public class ApplicationContextTest extends AbstractApplicationContextTest{@Autowiredprivate UserService userService;...
}
目前为止学习的标签:servlet filter listener context-param
加载顺序:
- context-param
- listener
- filter
- servlet
url-pattern配置形式种类:
精确匹配
/student/showPageStudents
路径匹配
/* /student/*
扩展名匹配
*.jsp *.do *.action
缺省匹配
/ 在前3种都无法匹配时做默认匹配
优先级:精确匹配>路径匹配>扩展名匹配>缺省匹配
注意:
都是路径匹配时,最长路径优先
http://localhost:8989/spring-day05/student/b
/student/* 比/* 优先匹配到。
路径匹配和扩展名匹配不能混用
不能出现如下配置:
/student/*.do
/ * / *.jsp
另外注意:
read-only="true"表示只读事务,提高查询(检索)效率。read-only的默认值是false。
注意:只能应用到查询操作,不能应用到增删操操作上。
timeout超时机制(单位秒):事务执行时间超过指定的时长后,自动失败回滚。
timeout默认值-1:表示跟数据库的默认超时设置有关。
//通过sleep方法,模拟业务方法执行超时
public void removeUser(Integer id) {try {Thread.sleep(5000);//当前线程睡眠5s} catch (InterruptedException e) {e.printStackTrace();}userDao.deleteUserById(id);
}
@Test
//测试超时
public void testTimeout(){ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");UserService userService = (UserService)ctx.getBean("userServiceImpl");userService.removeUser(1);
}
rollback-for:当发生rollback-for配置的异常类型的异常时,执行回滚。
no-rollback-for:当发生no-rollback-for配置的异常类型的异常时,不执行回滚。
Spring中默认:
RuntimeException及其子类(运行时异常)的异常执行回滚
Exception及其子类(编译时异常)的异常不会执行回滚
public void addUser(User u) {userDao.insertUser(u);// throw new RuntimeException("自定义未检查异常");//默认回滚throw new Exception("自定义已检查异常");//默认不回滚
}
rollback-for和no-roollback-for的生效规则:
企业开发中,会出现业务层方法调用业务层方法的情况。propagation定义了一个业务方法被另外一个业务方法调用时的事务传播方式。
常用的事务传播机制:
传播机制 | 内部方法对事务的要求 | 特点 |
---|---|---|
REQUIRED | 必须有 | 如果外部有事务,则加入;外部没有事务,则新建 |
REQUIRES_NEW | 必须有 | 无论外部是否有事务,内部都会新建 |
SUPPORTS | 可有可无 | 如果外部有事务则加入;如果没有则以无事务状态运行 |
NOT_SUPPORTED | 必须没有 | 如果外部有事务,则挂起外部事务 |
MANDATORY | 必须有 | 如果外部没有事务,直接抛出异常 |
NEVER | 必须没有 | 如果外部有事务,直接抛出异常 |
NESTED | 必须有 | 如果外部没有事务,开启事务;如果外部有事务,记录当前的保存点;一旦出错,不会全部回滚,只回滚到保存点 |
解决事务并发执行的问题,微观上,2个执行时间相近的事务相互影响的问题。
隔离性在数据库的并发访问时得以体现,随着数据库并发事务处理能力的大大增强,数据库资源的利用率也会大大提高,从而提高了数据库系统的事务吞吐量,可以支持更多的用户并发访问。但并发事务处理也会带来一些问题,如:脏读、不可重复读、幻读。这些问题是和隔离性相伴相生的。下面一一解释其含义。
脏读
B事务正在对一条记录做修改,在这个事务提交前,这条记录的数据就处于不一致状态;这时,另一个事务A也来读取同一条记录,如果不加控制,第二个事务读取了这些未提交的(“脏”)数据,并据此做进一步的处理,就会产生未提交的数据依赖关系。这种现象被形象的叫作"脏读"(Dirty Reads)。
不可重复读
一个事务中多次执行相同的查询SQL,不同时刻读取的数据不同。要么发现其读出的数据已经发生了改变、或某些记录已经被删除了!这种现象就叫作“ 不可重复读”(Non-Repeatable Reads)。
幻读
一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据,这种现象就称为“幻读”(Phantom Reads)。
隔离级别 | 特点 | 存在的问题 | |
---|---|---|---|
READ-UNCOMMITTED | 读未提交 | 脏读、不可重复读、幻读 | |
READ-COMMITTED | 读已提交 | 只能读到另外一个事务提交后的数据。 | 不可重复读、幻读 |
REPEATABLE-READ | 可重复读 | 同一个事务下,前后两次结果一致 | 不同数据库不同,有的数据库会有幻读 |
SERIALIZABLE | 序列化读 | 串行 | 性能太差 |
Oracle只支持READ-COMMITTED和SERIALIZABLE 2种隔离级别。
MySQL支持4种。
实战时,使用读已提交即可。
MySQL支持多种存储引擎:InnoDB(支持事务)、MyISAM、MEMORY
存储引擎:决定了数据库如何的保存管理数据。
show engines;//查询数据库支持的存储引擎
show variables like 'default_storage_engine';//查询默认引擎
查询当前使用的隔离级别:
修改当前使用的隔离级别:
MySQL中开启事务和结束事务
>start transaction;//开启事务
>commit;//提交
>rollback;//回滚
隔离级别
准备工作:
create table t_account(id int primary key,money double
);
start transaction;
insert into t_account values(1,100);
insert into t_account values(2,200);
commit;
读未提交隔离级别
脏读:B事务未提交,A事务可以读取到B事务插入的数据
读提交隔离级别
B事务不提交,A事务读取不到。B事务提交后A事务就可以读取到。
仍存在的问题:不可重复读和幻读
可重复读隔离级别
序列化读
文章对应源码地址:https://download.csdn.net/download/qq_36827283/87386038