Java Web 后端技术(五)–Spring
概述:全栈式(full-stack)轻量级开源框架。
两大核心
- IOC(Inverse Of Control:控制反转)
- AOP(Aspect Oriented Programming:面向切面编程)
优势: - 方便解耦合,简化开发。sping是一个容器,可以将所有对象创建和关系维护交给spring管理
- AOP编程的支持。方便实现程序进行权限拦截,运行监控等功能
- 声明式事务的支持。通过配置完成事务的管理,无需手动编程
- 方便测试。支持Junit4支持
- 方便集成各种优秀框架。内部提供各种优秀框架的直接支持
1.IOC
概述:一种设计思想,目的式指导设计出更加松耦合的程序。
获取方式:从spring容器中获取。
1.自定义IOC容器:
1. 创建java项目,导入自定义IOC相关坐标
2. 编写Dao接口和实现类
3. 编写Service接口和实现类
4. 编写beans.xml
5. 编写BeanFactory工具类
6. 编写测试代码
pom.xml相关IOC坐标
<dependencies>
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.1.6</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
IUserDao
public interface IUserDao {
public void save();
}
UserDaoImpl
public class UserDaoImpl implements IUserDao {
public void save() {
System.out.println("保存成功。。。");
}
}
IUserService
public interface IUserService {
public void save();
}
UserServiceImpl
public void save() {
//调用DAO层方法,传统方式
UserDaoImpl userDao = new UserDaoImpl();
userDao.save();
}
beans.xml
<beans>
<bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl"></bean>
</beans>
BeanFactory
public class BeanFactory {
private static Map<String,Object> iocmap = new HashMap<String, Object>();
//初始化对象实例
static {
//读取配置文件
InputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
//解析xml
SAXReader saxReader = new SAXReader();
try {
Document document = saxReader.read(resourceAsStream);
//编写xpath表达式
String xpath = "//bean";
//获取到所有的bean标签
List<Element> list = document.selectNodes(xpath);
//遍历list集合并使用反射创建对象实例,并存到map集合中
for (Element element : list) {
String id = element.attributeValue("id");
String className = element.attributeValue("class");
//使用反射生成实例对象
Object o = Class.forName(className).newInstance();
//存到map中
iocmap.put(id,o);
}
} catch (DocumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static Object getBean(String beanId){
Object o = iocmap.get(beanId);
return o;
}
}
测试代码
@Test
public void test1() {
IUserDao userDao = (IUserDao)BeanFactory.getBean("userDao");
userDao.save();
}
2.借助spring的IOC实现
1. 创建java项目,导入spring开发基本坐标
2. 编写Dao接口和实现类
3. 创建spring核心配置文件
4. 在spring配置文件中配置 UserDaoImpl
5. 使用spring相关API获得Bean实例
spring基本坐标
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl"></bean>
</beans>
测试
@Test
public void test2(){
//获取spring上下文对象,借助上下文对象获取ioc容器中的bean对象
ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext(
"applicationContext.xml");
//使用上下文对象从IOC容器中获取到bean对象
IUserDao userDao = (IUserDao) classPathXmlApplicationContext.getBean("userDao");
userDao.save();
}
ApplicationContext :代表应用上下文对象,获得spring中IOC容器的Bean对象
常用实现类:
1. ClassPathXmlApplicationContext 它是从**类的根路径下**加载配置文件 推荐使用这种。
2. FileSystemXmlApplicationContext 它是从**磁盘路径上**加载配置文件,配置文件可以在磁盘的任意位置。
3. AnnotationConfigApplicationContext 当使用**注解配置容器对象**时,需要使用此类来创建 spring 容器。它用来读取注解。
3.spring配置文件
<bean id="" class="" init-method="" destroy-method=""></bean>
用于配置对象交由Spring来创建。id为Bean实例在spring中的唯一标识;class为Bean的全类名。默认情况下它调用的是类中的无参构造函数,如果没有无参构造函数则不能创建成功。
scope 配置Bean标签的范围
取值范围 | 说明 |
singleton | 默认值,单例的 |
prototype | 多例的 |
request | WEB项目中,Spring创建一个Bean的对象,将对象存入到request域中 |
session | WEB项目中,Spring创建一个Bean的对象,将对象存入到session域中 |
globalsession | WEB项目中,应用在Portlet环境,如果没有Portlet环境那么globalSession 相当于 session |
init-method: 指定类中的初始化方法名称;
destroy-method: 指定类中销毁方法名称;在容器销毁以后对象也销毁了,销毁方法来不及销毁。如果想要查看销毁方法则需要手动销毁。
4.Bean实例化三种方式
4.1无参构造方法实例化
它会根据默认无参构造方法来创建类对象,如果bean中没有默认无参构造函数,将会创建失败。
<bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl"/>
4.2工厂静态方法实例化
应用场景 :依赖的jar包中有个A类,A类中有个静态方法m1,m1方法的返回值是一个B对象。如果我们频繁使用B对象,此时我们可以将B对象的创建权交给spring的IOC容器,以后我们在使用B对象时,无需调用A类中的m1方法,直接从IOC容器获得。
<bean id="userDao" class="com.lagou.factory.StaticFactoryBean" factory-method="createUserDao" />
4.3工厂普通方法实例化
应用场景 :依赖的jar包中有个A类,A类中有个普通方法m1,m1方法的返回值是一个B对象。如果我们频繁使用B对象,此时我们可以将B对象的创建权交给spring的IOC容器,以后我们在使用B对象时,无需调用A类中的m1方法,直接从IOC容器获得。
<bean id="dynamicFactoryBean" class="com.lagou.factory.DynamicFactoryBean"/>
<bean id="userDao" factory-bean="dynamicFactoryBean" factory-method="createUserDao"/>
5.Bean依赖注入
它是 Spring 框架核心 IOC 的具体实现。
通过框架把持久层对象传入业务层,而不用我们自己去获取。
依赖注入方法:
1.构造方法注入:
UserServiceImpl
public class UserServiceImpl implements IUserService {
private IUserDao userDao;
public UserServiceImpl(IUserDao userDao){
this.userDao = userDao;
}
public void save() {
userDao.save();
}
}
spring容器中调用有参构造注入
<bean id="userService" class="com.lagou.service.impl.UserServiceImpl">
<!-- <constructor-arg index="0" type="com.lagou.dao.IUserDao" ref="userDao"></constructor-arg> -->
<constructor-arg name="userDao" ref="userDao"></constructor-arg>
</bean>
2.set方法注入
UserServiceImpl
public void setUserDao(IUserDao userDao){
this.userDao = userDao;
}
spring容器中调用set方式注入
<property name="userDao" ref="userDao"></property>
数据类型注入
1.普通数据类型注入
UserDaoImpl
private String username;
private Integer age;
public void setUsername(String username) {
this.username = username;
}
public void setAge(Integer age) {
this.age = age;
}
<bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl" init-method="init" destroy-method="destory">
<property name="username" value="子慕"></property>
<property name="age" value="18"></property>
</bean>
2.集合数据类型注入
集合数据类型包括list集合,set集合,array数组,map集合;配置方法都大同小异,以list集合为例。
UserDaoImpl
private List<Object> list;
public void setList(List<Object> list) {
this.list = list;
}
applicationContext.xml配置
<!--user对象配置-->
<bean id="user" class="com.lagou.domain.User">
<property name="username" value="张三"></property>
<property name="age" value="18"></property>
</bean>
<!--进行list集合数据类型的注入-->
<property name="list">
<list>
<value>aaa</value>
<ref bean="user"></ref>
</list>
</property>
properties配置注入
private Properties properties;
public void setProperties(Properties properties) {
this.properties = properties;
}
<!--properties集合类型注入-->
<property name="properties">
<props>
<prop key="k1">v1</prop>
<prop key="k2">v2</prop>
</props>
</property>
6.配置文件模块化
实际开发中,Spring的配置内容非常多,这就导致Spring配置很繁杂且体积很大,所以,可以将部分
配置拆解到其他配置文件中,也就是所谓的配置文件模块化。
拆封方式:
- 按层级进行拆分:根据业务逻辑以及三层架构进行拆分
- 按业务逻辑进行拆分:按照不同模块原则进行拆分
加载方式:
- 并列的多个配置文件
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml","applicationContext-dao.xml",...);
- 主从配置文件
<import resource="applicationContext-xxx.xml"/>
7.springIOC注解开发
1.常用注解
注解 | 说明 |
@Component | 使用在类上用于实例化Bean |
@Controller | 使用在web层类上用于实例化Bean |
@Service | 使用在service层类上用于实例化Bean |
@Repository | 使用在dao层类上用于实例化Bean |
@Autowired | 使用在字段上用于根据类型依赖注入 |
@Qualifier | 结合@Autowired一起使用,根据名称进行依赖注入 |
@Resource | 相当于@Autowired+@Qualifier,按照名称进行注入 |
@Value | 注入普通属性 |
@Scope | 标注Bean的作用范围 |
@PostConstruct | 使用在方法上标注该方法是Bean的初始化方法 |
@PreDestroy | 使用在方法上标注该方法是Bean的销毁方法 |
注: JDK11以后完全移除了javax扩展导致不能使用@resource注解
使用注解进行开发时,需要在applicationContext.xml中配置组件扫描,作用是指定哪个包及其子包下的Bean需要进行扫描以便识别使用注解配置的类、字段和方法。
<context:component-scan base-package="com.lagou"></context:component-scan>
2.spring新注解
注解 | 说明 |
@Configuration | 用于指定当前类是一个Spring 配置类,当创建容器时会从该类上加载注解 |
@Bean | 使用在方法上,标注将该方法的返回值存储到 Spring 容器中 |
@PropertySource | 用于加载 properties 文件中的配置 |
@ComponentScan | 用于指定 Spring 在初始化容器时要扫描的包 |
@Import | 用于导入其他配置类 |
2.AOP
AOP 为 Aspect Oriented Programming 的缩写,意思为面向切面编程
AOP 是 OOP(面向对象编程) 的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
优点:
- 在程序运行期间,在不修改源码的情况下对方法进行功能增强
- 逻辑清晰,开发核心业务的时候,不必关注增强业务的代码
- 减少重复代码,提高开发效率,便于后期维护
1.AOP底层实现
实际上,AOP 的底层是通过 Spring 提供的的动态代理技术实现的。在运行期间,Spring通过动态代理技术动态的生成代理对象,代理对象方法执行时进行增强功能的介入,在去调用目标对象的方法,从而完成功能的增强。
2.AOP相关术语
Spring 的 AOP 实现底层就是对上面的动态代理的代码进行了封装,封装后我们只需要对需要关注的
部分进行代码编写,并通过配置的方式完成指定目标的方法增强。
术语 | 描述 |
Target | 代理的目标对象 |
Proxy | 一个类被 AOP 织入增强后,就产生一个结果代理类 |
Joinpoint | 在spring中,这些点指的是方法,因为 spring只支持方法类型的连接点 |
Pointcut | 指我们要对哪些 Joinpoint 进行拦截的定义;真正被增强的方法 |
Advice | 封装增强业务逻辑的方法;分类:前置通知、后置通知、异常通知、最终通知、环绕通知 |
Aspect | 切入点和通知(引介)的结合 |
Weaving | 指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织 入,而AspectJ采用编译期织入和类装载期织入 |
3.AOP开发明确事项
3.1 开发阶段
- 编写核心业务代码(目标类的目标方法) 切入点
- 把公用代码抽取出来,制作成通知(增强功能方法) 通知
- 在配置文件中,声明切入点与通知间的关系,即切面
3.2运行阶段(Spring框架控制)
Spring 框架监控切入点方法的执行。一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。
3.3底层代理实现
在 Spring 中,框架会根据目标类是否实现了接口来决定采用哪种动态代理的方式。
- 当bean实现接口时,会用JDK代理模式
- 当bean没有实现接口,用cglib实现( 可以强制使用cglib(在spring配置中加入<aop:aspectj-autoproxy proxyt-target-class=”true”/>)
4.基于XML的AOP开发
1. 创建java项目,导入AOP相关坐标
2. 创建目标接口和目标实现类(定义切入点)
3. 创建通知类及方法(定义通知)
4. 将目标类和通知类对象创建权交给spring
5. 在核心配置文件中配置织入关系,及切面
导入AOP相关坐标
<dependencies>
<!--导入spring的context坐标,context依赖aop-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<!-- aspectj的织入(切点表达式需要用到该jar包) -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
<!--spring整合junit-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
目标接口
public interface AccountService{
public void transfer();
}
目标接口实现类
public class AccountServiceImpl implements AccountService {
public void transfer() {
System.out.println("执行了...");
}
}
通知类及方法
public class MyAdvice {
public void before(){
System.out.println("前置通知执行....");
}
public void afterReturnning(){
System.out.println("后置通知执行....");
}
}
SpringContext.xml
<!--目标类交给IOC容器-->
<bean id="accountService" class="com.lagou.service.impl.AccountServiceImpl"></bean>
<!--通知类交给IOC容器-->
<bean id="myAdvice" class="com.lagou.advice.MyAdvice"></bean>
<!--导入AOP命名空间-->
<aop:config>
<aop:pointcut id="myPointcut" expression="execution(* com.lagou..service.impl.AccountServiceImpl.*(..))"/>
<aop:aspect ref="myAdvice">
<aop:before method="before" pointcut="execution(public void com.lagou.service.impl.AccountServiceImpl.transfer())"/>
<aop:after method="afterReturnning" pointcut-ref="myPointcut"/>
</aop:aspect>
</aop:config>
5.XML配置AOP详解
5.1切点表达式execution([修饰符] 返回值类型 包名.类名.方法名(参数))
- 访问修饰符可以省略
- 返回值类型、包名、类名、方法名可以使用星号 * 代替,代表任意
- 包名与类名之间一个点 . 代表当前包下的类,两个点 … 表示当前包及其子包下的类
- 参数列表可以使用两个点 … 表示任意个数,任意类型的参数列表
5.2通知类型<aop:通知类型 method=“通知类中方法名” pointcut=“切点表达式"></aop:通知类型>
标签 | 说明 |
aop:before | 用于配置前置通知。指定增强的方法在切入点方法之前执行 |
aop:afterReturning | 用于配置后置通知。指定增强的方法在切入点方法之后执行 |
aop:afterThrowing | 用于配置异常通知。指定增强的方法出现异常后执行 |
aop:after | 用于配置最终通知。无论切入点方法执行时是否有异常,都会执行 |
aop:around | 用于配置环绕通知。开发者可以手动控制增强代码在什么时候执行 |
注意:通常情况下,环绕通知都是独立使用的
6.基于注解的AOP开发
1. 创建java项目,导入AOP相关坐标
2. 创建目标接口和目标实现类(定义切入点)
3. 创建通知类(定义通知)
4. 将目标类和通知类对象创建权交给spring
5. 在通知类中使用注解配置织入关系,升级为切面类
6. 在配置文件中开启组件扫描和 AOP 的自动代理
将目标类和通知类对象创建权交给spring
@Service
public class AccountServiceImpl implements AccountService{}
@Component
public class MyAdvice{}
在通知类中使用注解配置织入关系,升级为切面类
@Component
@Aspect
public class MyAdvice {
@Before("execution(* com.lagou..*.*(..))")
public void before() {
System.out.println("前置通知...");
}
}
在配置文件中开启组件扫描和AOP的自动代理
<!--组件扫描-->
<context:component-scan base-package="com.lagou"/>
<!--aop的自动代理-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
7.基于注解的AOP开发的通知类型
通知的配置语法:@通知注解(“切点表达式")
标签 | 说明 |
@Before | 用于配置前置通知。指定增强的方法在切入点方法之前执行 |
@AfterReturning | 用于配置后置通知。指定增强的方法在切入点方法之后执行 |
@AfterThrowing | 用于配置异常通知。指定增强的方法出现异常后执行 |
@After | 用于配置最终通知。无论切入点方法执行时是否有异常,都会执行 |
@Around | 用于配置环绕通知。开发者可以手动控制增强代码在什么时候执行 |
3.JdbcTemplate
JdbcTemplate是spring框架中提供的一个模板对象,是对原始繁琐的Jdbc API对象的简单封装。
核心对象:JdbcTemplate jdbcTemplate = new JdbcTemplate(DataSource dataSource);
核心方法
1.int update();执行增删改语句
2.List<T> query();查询多个
3.T queryForObject();查询一个
4.new BeanPropertyRowMapper<>();实现ORM映射封装
3.1spring整合jdbcTemplate环境准备
导入依赖坐标
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.15</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
</dependencies>
创建Account类
public class Account {
private Integer id;
private String name;
private Double money;
//setter和getter
}
3.2 dao层准备
AccountDao接口
public interface AccountDao {
//查询所有账户
public List<Account> findAll();
//更具id查询账户
public Account findById(Integer id);
//添加账户
public void save(Account account);
//更新账户
public void update(Account account);
//根据id删除账户
public void delete(Integer id);
}
AccountDaoImpl类
@Repository
public class AccountDaoImpl implements AccountDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public List<Account> findAll() {
String sql = "select * from account";
List<Account> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Account>(Account.class));
return list;
}
public Account findById(Integer id) {
String sql = "select * from account where id = ?";
Account account = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Account>(Account.class), id);
return account;
}
public void save(Account account) {
String sql = "insert int account values(null,?,?)";
jdbcTemplate.update(sql, account.getMoney(), account.getName());
}
public void update(Account account) {
String sql = "update account set money = ? where name = ?";
jdbcTemplate.update(sql, account.getMoney(), account.getName());
}
public void delete(Integer id) {
String sql ="delete from account where id = ?";
jdbcTemplate.update(sql,id);
}
}
3.3service层准备
AccountService接口
public interface AccountService {
//查询所有账户
public List<Account> findAll();
//更具id查询账户
public Account findById(Integer id);
//添加账户
public void save(Account account);
//更新账户
public void update(Account account);
//根据id删除账户
public void delete(Integer id);
}
AccountServiceImpl类
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
public List<Account> findAll() {
List<Account> all = accountDao.findAll();
return all;
}
public Account findById(Integer id) {
Account account = accountDao.findById(id);
return account;
}
public void save(Account account) {
accountDao.save(account);
}
public void update(Account account) {
accountDao.update(account);
}
public void delete(Integer id) {
accountDao.delete(id);
}
}
ApplicaitonContext.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"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--IOC注解扫描-->
<context:component-scan base-package="com.lagou"/>
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--dataSource-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--jdbcTemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg name="dataSource" ref="dataSource"/>
</bean>
</beans>
4.事务
Spring的事务控制可以分为编程式事务控制和声明式事务控制。
- 编程式(了解): 开发者直接把事务的代码和业务代码耦合到一起,在实际开发中不用。
- 声明式(重点): 开发者采用配置的方式来实现的事务控制,业务代码与事务代码实现解耦合,使用的AOP思想。
4.1基于xml的声明式事务控制
在 Spring 配置文件中声明式的处理事务来代替代码式的处理事务。底层采用AOP思想来实现的。
声明式事务控制明确事项
- 核心业务代码(目标对象)
- 事务增强代码(Spring已提供事务管理器)
- 切面配置
步骤分析
- 引入tx命名空间
- 事务管理器通知配置
- 事务管理器AOP配置
<beans xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--通知增强-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--定义事务的属性-->
<tx:attributes>
<tx:method name="*"/> </tx:attributes>
</tx:advice>
<!--aop配置-->
<aop:config>
<!--切面配置-->
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.lagou.serivce..*.*(..))"> </aop:advisor>
</aop:config>
tx:method 事务参数的配置详解<tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" timeout="-1" read-only="false"/>
- name: 切点方法名称
- isolation:事务的隔离级别
- propogation:事务的传播行为
- timeout:超时时间
- read-only:是否只读
4.2基于注解的声明式事务控制
常用注解步骤分析
- 修改service层,增加事务注解
- 修改spring核心配置文件,开启事务注解支持
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.REPEATABLE_READ, timeout = -1, readOnly = false)
@Override
public void transfer(String outUser, String inUser, Double money) {
accountDao.out(outUser, money);
int i = 1 / 0;
accountDao.in(inUser, money);
}
}
<!--事务的注解支持-->
<tx:annotation-driven/>
5.web集成
5.1ApplicationContext应用上下文获取方式
应用上下文对象是通过 new ClasspathXmlApplicationContext(spring配置文件)
方式获取的,但是每次从容器中获得Bean时都要编写 new ClasspathXmlApplicationContext(spring配置文件)
,这样的弊端是配置文件加载多次,应用上下文对象创建多次。
解决思路分析:
在Web项目中,可以使用ServletContextListener监听Web应用的启动,我们可以在Web应用启动时,就加载Spring的配置文件,创建应用上下文对象ApplicationContext,在将其存储到最大的域servletContext域中,这样就可以在任意位置从域中获得应用上下文ApplicationContext对象了。
5.2 Spring提供获取应用上下文的工具
上面的分析不用手动实现,Spring提供了一个监听器ContextLoaderListener就是对上述功能的封装,该监听器内部加载Spring配置文件,创建应用上下文对象,并存储到ServletContext域中,提供了一个客户端工具WebApplicationContextUtils供使用者获得应用上下文对象。
操作
- 在web.xml中配置ContextLoaderListener监听器(导入spring-web坐标)
- 使用WebApplicationContextUtils获得应用上下文对象ApplicationContext
web坐标
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
</dependencies>
ContextLoaderListener监听器
<!--全局参数-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!--Spring的监听器-->
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>