spring-framework

Introduction

Spring Framework是Spring生态圈中最基础的项目,是其他项目的根基

Architecture

Core Container

Core concepts

IoC

代码书写现状

解决方案

IoC(Inversion of Control)控制反转

对象的创建控制权由程序转移到外部,这种思想称为控制反转

Bean

Spring技术对IoC思想进行了实现:

DI

Dependency Injection, 依赖注入

在容器中建立bean与bean之间的依赖关系的整个过程,称为依赖注入

Aim

目标:充分解耦

最终效果

Cases

IoC case

Analysis of Ideas

  1. 管理什么?(Service与Dao) // Dao就是Mapper
  2. 如何将被管理的对象告知IoC容器?(配置)
  3. 被管理的对象交给IoC容器,如何获取到Ioc容器?(接口)
  4. Ioc容器得到后,如何从容器中获取bean?(接口方法)
  5. 使用Spring导入哪些坐标?(pom.xml)

Case in XML Way

  1. 导入Springs坐标
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-context</artifactId>
	<version>5.2.10.RELEASE</version>
</dependency>
  1. 定义Spring管理的类(接口)
public interface BookService{
	public void save();
}
public class BookServiceImpl implements BookService{
	private BookDao bookDao = new BookDaoImpl();
	public void save(){
		bookDao.save();
	}
}
  1. 创建Spring配置文件,配置对应类作为Spring管理的bean
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
	<bean id="bookService"class="com.test.service.impl.BookServiceImpl"></bean>
</beans>
  1. 初始化Ioc容器(Spring核心容器/Spring容器),通过容器获取bean
public class App{
	public static void main(String[]args){
	//加载配置文件得到上下文对像,也就是容器对象
		ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
	//获取资源
		BookService bookService =(BookService)ctx.getBean("bookService");
		bookService.save();
	}
}

DI case

Analysis of Ideas

  1. 基于IoC管理bean
  2. Service中使用new形式创建的Dao对象是否保留?(否)
  3. Service中需要的Dao对象如何进入到Service中?(提供方法)
  4. Service与Dao间的关系如何描述?(配置)

Case in XML Way

  1. 删除使用new的形式创建对象的代码
public class BookServiceImpl implements BookService{
	private BookDao bookDao = new BookDaoImpl();
	public void save(){
		bookDao.save();
	}
}
  1. 提供依赖对象对应的setter方法
public class BookServiceImpl implements BookService{

	private BookDao bookDao;
	
	public void save(){
		bookDao.save();
	}
	public void setBookDao(BookDao bookDao){
		this.bookDao = bookDao;
	}
}
  1. 配置service与dao之间的关系
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
	<bean id="bookService"class="com.test.di.service.impl.BookServiceImpl">
		<property name="bookDao" ref="bookDao"/>
	</bean>
	<bean id="bookDao"class="com.test.di.dao.impl.BookDaoImpl"/>
</beans>

Bean

Config

基础配置

参照上方案例

别名配置

范例

<bean id="bookDao"name="dao bookDaoImpl"class="com.test.dao.impl.BookDaoImpl"/>
<bean name="service,bookServiceImpl"class="com.test.service.impl.BookServiceImpl"/>

注意事项

获取bean无论是通过id还是name获取,如果无法获取到,将抛出异常NoSuchBeanDefinitionException

NoSuchBeanDefinitionException:No bean named 'bookServiceImpl'available

范围配置

定义bean的作用范围,可选范围如下

<bean id="bookDao"class="com.test.dao.impl.BookDaoImpl"scope="prototype"/>

说明:

bean默认为单例的原因:容器每使用一次bean都新建一个对象,容器会异常庞大,Spring不用来管理这一类bean

适合交给容器进行管理的bean

表现层对象

不适合交给容器进行管理的bean

Instantiation

bean实例化

实例化bean的三种方式 (Spring提供第四种)

构造方法(常用)

public class BookDaoImpl implements BookDao{
	public BookDaoImpl(){
		System.out.println("book constructor is running ...")
	}
	public void save(){
		System.out.println("book dao save ...")
	}
}

无参构造方法如果不存在,将抛出异常BeanCreationException

静态工厂

public class OrderDaoFactory{
	public static OrderDao getorderDao(){
		return new OrderDaoImpl();
	}
}	
<bean
	id="orderDao"
	factory-method="getorderDao"
	class="com.test.factory.OrderDaoFactory"
/>

实例工厂

public class UserDaoFactory{
	public UserDao getUserDao(){
		return new UserDaoImpl();
	}
}	
<bean id=
userDaoFactory
class="com.test.factory.UserDaoFactory"/>

<bean
	id="userDao"
	factory-method="getUserDao"
	factory-bean="userDaoFactory"
/>

FactoryBean

public class UserDaoFactoryBean implements FactoryBean<UserDao>{
	public UserDao getobject()throws Exception{
		return new UserDaoImpl();
	}	
	public Class<?> getobjectType(){
		return UserDao.class;
	}
}	
<bean
	id="userDao"
	class="com.test.factory.UserDaoFactoryBean"
/>

Life Circle

Control

方式1:提供生命周期控制方法

public class BookDaoImpl implements BookDao{
	public void save(){
		System.out.println("book dao save...");
	}	
	public void init(){
		System.out.println("book init ...")
	}	
}
	public void destory(){
		System.out.println("book destory ...")
	}
}	
<bean id="bookDao"
			class="com.test.dao.impl.BookDaoImpl"
			init-method="init"
			destroy-method="destory"
/>

方式2:实现InitializingBean,DisposableBean接口

public class BookServiceImpl implements BookService,InitializingBean,DisposableBean{
	public void save(){
		System.out.println("book service save ...")
	}	
	public void afterPropertiesset()throws Exception{
		System.out.println("afterPropertiesSet");
	}
	public void destroy()throws Exception{
		System.out.println("destroy");
	}
}

Process


bean销毁时机

关闭容器方式:

public class AppForLifeCycle{
	public static void main(String[] args){
		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
		ctx.close();
	}	
}

DI

DI methods

依赖注入方式

setter注入

在bean中定义引用类型属性并提供可访问的set方法

public class BookServiceImpl implements BookService{
	private BookDao bookDao;
	public void setBookDao(BookDao bookDao){
		this.bookDao = bookDao;
	}
}

配置中使用property标签ref属性注入引用类型对象

<bean id="bookService"class="com.test.service.impl.BookServiceImpl">
	<property name="bookDao"ref="bookDao"/>
</bean>
<bean id="bookDao"class="com.test.dao.impl.BookDaoImpl"/>

配置中使用property标签value属性注入简单类型数据

<bean id="bookDao"class="com.test.dao.impl.BookDaoImpl">
	<property name="connectionNumber"value="10"/>
</bean>

构造器注入

在bean中定义引用类型属性并提供可访问的构造方法

public class BookServiceImpl implements BookService{
	private BookDao bookDao;
	public BookServiceImpl(BookDao bookDao){
		this.bookDao = bookDao;
	}
}

配置中使用constructor-arg标签ref属性注入引用类型对象

<bean id="bookService"class="com.test.service.impl.BookServiceImpl">
	<constructor-arg name="bookDao"ref="bookDao"/>
</bean>
<bean id="bookDao"class="com.test.dao.impl.BookDaoImpl"/>

配置中使用constructor-arg标签value属性注入简单类型数据

<bean id="bookDao"class="com.test.dao.impl.BookDaoImpl">
	<constructor-arg name="connectionNumber"value="10"/>
</bean>

配置中使用constructor-arg标签type属性设置按形参类型注入

<bean id="bookDao"class="com.test.dao.impl.BookDaoImpl">
	<constructor-arg type="int"value="10"/>
	<constructor-arg type="java.lang.String"value="mysql"/>
</bean>

配置中使用constructor-arg标签index/属性设置按形参位置注入

<bean id="bookDao"class="com.test.dao.impl.BookDaoImpl">
	<constructor-arg index="0"value="10"/>
	<constructor-arg index="1"value="mysql"/>
</bean>

依赖注入方式选择

  1. 强制依赖使用构造器进行,使用setter注入有概率不进行注入导致null对象出现 (构造器未执行直接报错)
  2. 可选依赖使用setter注入进行,灵活性强
  3. Spring框架倡导使用构造器,第三方框架内部大多数采用构造器注入的形式进行数据初始化,相对严谨
  4. 如果有必要可以两者同时使用,使用构造器注入完成强制依赖的注入,使用sette注入完成可选依赖的注入
  5. 实际开发过程中还要根据实际情况分析,如果受控对象没有提供setter方法就必须使用构造器注入
  6. 自己开发的模块推荐使用setter注入

Autowire

依赖自动装配

配置中使用bean标签autowire属性设置自动装配的类型

<bean id="bookDao"class="com.test.dao.impl.BookDaoImpl"/>
<bean id="bookService"class="com.test.service.impl.BookServiceImpl"autowire="byType"/>

依赖自动装配特征

Collection Injection

<property name="array">
	<array>
		<value>100</value>
		<value>200</value>
		<value>300</value>
	</array>
</property>
<property name="list">
	<list>
		<value>it</value>
		<value>jjj</value>
		<value>bbb</value>
	</list>
</property>
<property name="set">
	<set>
		<value>it</value>
		<value>jjj</value>
		<value>bbb</value>
	</set>
</property>
<property name="map">
	<map>
		<entry key="country"value="china"/>
		<entry key="province"value="henan"/>
		<entry key="city"value="kaifeng"/>
	</map>
</property>
<property name="properties">
	<props>
		<prop key="country">china</prop>
		<prop key="province">henan</prop>
		<prop key="city">kaifeng</prop>
	</props>
</property>

Load properties file

<?xml version="1.0"encoding="UTF-8"?>
<beans xmlns="htatp://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">
</beans>
<context:property-placeholder location="jdbc.properties"/>
<property name="username"value="$fidbc.username}"/>

<context:property-placeholder location="jdbc.properties"system-properties-mode="NEVER"/>
<context:property-placeholder location="jdbc.properties,msg.properties"/>

加载所有properties文件

<context:property-placeholder location="*.properties"/>
加载properties文件标准格式
```xml
<context:property-placeholder location="classpath:*.properties"/>

从类路径或jar包中搜索并加载properties文件

<context:property-placeholder location="classpath*:*properties"/>

Container

Create

创建容器

ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
ApplicationContext ctx = new FileSystemXmlApplicationContext("D:\\applicationContext.xml");
ApplicationContext ctx = new ClassPathXmlApplicationContext("bean1.xml","bean2.xml");

获取bean

BookDao bookDao = (BookDao) ctx.getBean("bookDao");
BookDao bookDao = ctx.getBean("bookDao",BookDao.class);
BookDao bookDao = ctx.getBean(BookDao.class);

Hierarchy

BeanFactory初始化

Resource resources = new ClassPathResource("applicationContext.xml");
BeanFactory bf =  new XmlBeanFactory(resources);
BookDao bookDao bf.getBean("bookDao",BookDao.class);
bookDao.save();

ApplicationContext接口常用初始化类

Annotation

注解开发定义bean

@Component("bookDao")
public class BookDaoImpl implements BookDao{
}
@Component
public class BookServiceImpl implements BookService{
}
<context:component-scan base-package="com.package_name"/>

Spring提供@Component注解的三个衍生注解

@Repository("bookDao")
public class BookDaoImpl implements BookDao{
}
@Service
public class BookServiceImpl implements BookService{
}

纯注解开发

@Configuration
@ComponentScan("com.package_name")
public class SpringConfig{
}
@ComponentScan({"com.test.service","com.test.dao"})

读Spring核心配置文件初始化容器对象切换为读取Java配置类初始化容器对象

//加载配置文件初始化容器
ApplicationContext ctx  = new ClassPathXmlApplicationContext("applicationContext.xml");
//加载配置类初始化容器
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);

bean作用范围

@Repository
@Scope("singleton")
public class BookDaoImpl implements BookDao{
}

bean生命周期

@Repository
@Scope("singleton")
public class BookDaoImpl implements BookDao{
	public BookDaoImpl(){
		System.out.println("book dao constructor ...")
	}
	@PostConstruct
	public void init(){
		System.out.println("book init ...")
	}
	@PreDestroy
	public void destroy(){
		System.out.println("book destory ...")
	}
}

依赖注入

@Service
public class BookServiceImpl implements BookService{
	@Autowired
	private BookDao bookDao;
	
	//public void setBookDao(BookDao bookDao){
	//	this.bookDao bookDao;
	//}

	public void save(){
		System.out.println("book service save...");
		bookDao.save();
	}
}
@Service
public class BookServiceImpl implements BookService{
	@Autowired
	@Qualifier("bookDao")
	private BookDao bookDao;
}

注意:@Qualifier注解无法单独使用,必须配合@Autowired注解使用

@Repository("bookDao")
public class BookDaoImpl implements BookDao{
	@Va1ue("100")
	private String connectionNum;
}

加载properties文件

@Configuration
@ComponentScan("com.test")
@PropertySource("classpath:jdbc.properties")
public class SpringConfig

注意:路径仅支持单一文件配置,多文件使用数组格式配置,不允许使用通配符*

第三方Bean管理

@Configuration
public class SpringConfig{
	@Bean
	public DataSource dataSource(){
		DruidDataSource ds = new DruidDataSource()
		ds.setDriverclassName("com.mysql.jdbc.Drive
		ds.setUrl("jdbc:mysql://localhost:3306/spri
		ds.setusername("root");
		ds.setPassword("root");
		return ds;
	}	
}

将独立的配置类加入核心配置

public class JdbcConfig{
	@Bean
	public DataSource dataSource(){
		DruidDataSource ds new DruidDataSource();
		//相关配置
		return ds;
	}
} 
@Configuration
@Import(JdbcConfig.class)
	public class SpringConfig{
}
@Configuration
public class JdbcConfig{
	@Bean
	public DataSource dataSource(){
		DruidDataSource ds = new DruidDataSource();
		//相关配置
		return ds;
	}
}
@Configuration
@ComponentScan({"com.test.config","com.test.service","com.test.dao"})
public class SpringConfig{
}

第三方bean依赖注入

public class JdbcConfig
	@Value("com.mysq1.jdbc.Driver")
	private String driver;
	@Value("jdbc:mysq1://localhost:3306/spring_db")
	private string url;
	@Value("root")
	private String userName;
	@Value("root")
	private String password;
	@Bean
	public DataSource dataSource(){
		DruidDataSource ds new DruidDataSource();
		ds.setDriverclassName(driver);
		ds.setUrl(url);
		ds.setUsername(userName);
		ds.setPassword(userName);
		return ds;
	}
}
@Bean
public DataSource dataSource(BookService bookService){
	System.out.println(bookService);
	DruidDataSource ds new DruidDataSource();
	//属性设置
	return ds;
}

引用类型注入只需要为bean定义方法设置形参即可,容器会根据类型自动装配对象

Summary


Integration

Mybatis

Spring整合JUnit

@Bean
public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
	SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
	ssfb.setTypeAliasesPackage("com.test.domain");
	ssfb.setDataSource(dataSource);
	return ssfb;
}
@Bean
public MapperScannerConfigurer mapperScannerConfigurer(){
	MapperScannerConfigurer msc = new MapperScannerConfigurer();
	msc.setBasePackage("com.test.dao");
	return msc;
}

JUnit

Spring整合JUnit

@Runwith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class BookServiceTest{
	@Autowired
	private BookServicebookService;
	
	@Test
	public void testSave(){
		bookService.save();
	}
}

AOP

Introduction

AOP(Aspect Oriented Programming) 面向切面编程,一种编程范式,指导开发者如何组织程序结构

作用:在不惊动原始设计的基础上为其进行功能增强

Spring理念:无入侵式/无侵入式

Core concepts


Get Started

Analysis of Ideas

案例设定:测定接口执行效率
简化设定:在接口执行前输出当前系统时间
开发模式:XML or Annotation

思路分析:

  1. 导入坐标(pom.xml)
  2. 制作连接点方法(原始操作,Dao接口与实现类)
  3. 制作共性功能(通知类与通知)
  4. 定义切入点
  5. 绑定切入点与通知关系(切面)

AOP入门案例(Annotation type)

  1. 导入aop相关坐标
<dependency>
	<groupId>org.aspectj</groupId>
	<artifactId>aspectjweaver</artifactId>
	<version>1.9.4</version>
</dependency>

说明:spring-aop坐标依赖spring-context坐标

org.springframework:spring-context:5.2.10.RELEASE

org.aspectj:aspectjweaver:1.9.4

  1. 定义dao接口与实现类
public interface BookDao{
	public void save();
	public void update();
}
@Repository
public class BookDaoImpl implements BookDao{
	public void save(){
		System.out.println(System.currentTimeMillis());
		System.out.println("book dao save...");
	}

	public void update(){
		System.out.println("book dao update...");
	}	
}
  1. 定义通知类,制作通知
public class MyAdvice{
	public void before(){
		System.out.printIn(System.currentTimeMillis());
	}
}
  1. 定义切入点
public class MyAdvice{
	@Pointcut("execution(void com.test.dao.BookDao.update())")
	private void pt(){}

说明:切入点定义依托一个不具有实际意义的方法进行,即无参数,无返回值,方法体无实际逻辑

  1. 绑定切入点与通知关系,并指定通知添加到原始连接点的具体执行位置
public class MyAdvice{
	@Pointcut("execution(void com.test.dao.BookDao.update())")
	private void pt(){}
	
	@Before("pt()")
	public void before(){
		System.out.printIn(System.currentTimeMillis());
	}
}
  1. 定义通知类受Spring容器管理,并定义当前类为切面类
@Component
@Aspect
public class MyAdvice{
	@Pointcut("execution(void com.test.dao.BookDao.update())")
	private void pt(){}
	
	@Before("pt()")
	public void before(){
		System.out.println(System.currentTimeMillis());
	}
}
  1. 开启Spring对AOP注解驱动支持
@Configuration
@Componentscan("com.test")
@EnableAspectJAutoProxy
public class SpringConfig{
}

Process

AOP工作流程

  1. Spring容器启动
  2. 读取所有切面配置中的切入点
@Component
@Aspect
public class MyAdvice{
	@Pointcut("execution(void com.test.dao.BookDao.save())")
	private void ptx(){}
	
	@Pointcut("execution(void com.test.dao.BookDao.update())")
	private void pt(){}

	@Before("pt()")
	public void method(){
		System.out.println(System.currentTimeMillis());
	}
}
  1. 初始化bean,判定bean对应的类中的方法是否匹配到任意切入点
  1. 获取bean执行方法

Pointcut Expression

AOP切入点表达式

package com.test.dao;
public interface BookDao{
	public void update();
}
public class BookDaoImpl implements BookDao{
	public void update(){
		System.out.println("book dao update ...")
	}
}

描述方式一:

描述方式二:

Standard Format

切入点表达式标准格式:动作关键字(访问修饰符 返回值 包名.类/接口名.方法名(参数)异常名)

execution (public User com.test.service.UserService.findById (int))

描述切入点的行为动作,例如execution表示执行到指定切入点

Wildcard

可以使用通配符描述切入点,快速描述

execution (public com.test.*.UserService.find*(*)

匹配com.test包下的任意包中的UserService类或接口中所有find开头的带有一个参数的方法

execution (public User com..UserService.findById (..)

匹配com包下的任意包中的UserService类或接口中所有名称为findByld的方法

execution(**..*Service+.*(..))

Tips

书写技巧

Advice Types

AOP通知描述了抽取的共性功能,根据共性功能抽取的位置不同,最终运行代码时要将其加入到合理的位置
A0P通知共分为5种类型

名称:@Before
类型:方法注解
位置:通知方法定义上方
作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法前运行
范例

@Before("pt()")
public void before(){
	System.out.println("before advice ...")
}

相关属性:vlue(默认):切入点方法名,格式为类名.方法名()

名称:@After
类型:方法注解
位置:通知方法定义上方
作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法后运行
范例:

@After("pt()")
public void after(){
System.out.println("after advice...");
}

相关属性:value(默认):切入点方法名,格式为类名.方法名()

名称:@Around
类型:方法注解
位置:通知方法定义上方
作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法前后运行
范例:

@Around("pt()")
public Object around(ProceedingJoinPoint pjp)throws Throwable{
	System.out.println("around before advice...");
	Object ret = pjp.proceed();
	System.out.println("around after advice ...")
	return ret;

@Around注意事项

  1. 环绕通知必须依赖形参ProceedingJoinPoint才能实现对原始方法的调用,进而实现原始方法调用前后同时添加通知
  2. 通知中如果未使用ProceedingJoinPoint对原始方法进行调用将跳过原始方法的执行
  3. 对原始方法的调用可以不接收返回值,通知方法设置成void即可,如果接收返回值,必须设定为Objecta类型
  4. 原始方法的返回值如果是void类型,通知方法的返回值类型可以设置成void,也可以设置成Object
  5. 由于无法预知原始方法运行后是否会抛出异常,因此环绕通知方法必须抛出Throwable对象
@Around("pt()")
public Object around(ProceedingJoinPoint pjp)throws Throwable{
	System.out.println("around before advice ...")
	object ret = pjp.proceed();
	System.out.println("around after advice ...")
	return ret;
}

Case 1

案例: 测量业务层接口执行效率
需求: 任意业务层接口执行均可显示其执行效率(执行时长)
分析:

  1. 业务功能:业务层接口执行前后分别记录时间,求差值得到执行效率
  2. 通知类型选择前后均可以增强的类型一环绕通知

补充说明

当前测试的接口执行效率仅仅是一个理论值,并不是一次完整的执行过程

测量业务层接口执行效率(万次核心代码执行效率)

@Around("ProjectAdvice.servicept()")
public void runSpeed(ProceedingJoinPoint pjp)throws Throwable{
	long start = System.currentTimeMillis();
	for(inti=0;i<10000;i++){
		pjp.proceed();
	}
	long end = System.currentTimeMillis();
	System.out.printIn("业务层接口万次执行时间:"+(end-start)+"ms");
}
@Around("ProjectAdvice.servicept()")
public void runSpeed(ProceedingJoinPoint pjp)throws Throwable{
//获取执行签名信息
	Signature signature = pjp.getsignature();
//通过签名获取执行类型(接口名)
	String className signature.getDeclaringTypeName();
//通过签名获取执行操作名称(方法名)
	String methodName signature.getName();
	long start System.currentTimeMillis();
	for (int i=0;i<10000;i++){
		pjp.proceed();
	}
	long end = System.currentTimeMillis();
	System.out.println("万次执行:"+className+-"."+methodName+"--->"+(end-start)+"ms");
}

Get Data

获取切入点方法的参数

@Before("pt()")
public void before(JoinPoint jp){
	object[]args jp.getArgs();
	System.out.println(Arrays.tostring(args));
}
@Around("pt()")
public Object around(ProceedingJoinPoint pjp)throws Throwable{
	object[]args pjp.getArgs();
	System.out.println(Arrays.toString(args));
	Object ret pjp.proceed();
	return ret;
}

获取切入点方法返回值

@AfterReturning(value = "pt()",returning = "ret")
public void afterReturning(String ret){
	System.out.println("afterReturning advice..."+ret);
}
@Around("pt()")
public Object around(ProceedingJoinPoint pjp)throws Throwable{
	object ret pjp.proceed();
	return ret;
}

获取切入点方法运行异常信息


@AfterThrowing(value "pt()",throwing "t")
public void afterThrowing(Throwable t){
	System.out.println("afterThrowing advice ..." + t);
}
@Around("pt()")
public Object around(ProceedingJoinPoint pjp){
	Object ret=  null;
	try{
		ret =  pjp.proceed();
	}catch (Throwable t){
		t.printStackTrace();
	}
	return ret;
}

Case 2

案例 百度网盘分享链接输入密码数据错误兼容性处理

需求:对百度网盘分享链接输入密码时尾部多输入的空格做兼容处理

分析:

  1. 在业务方法执行之前对所有的输入参数进行格式处理---trim()
  2. 使用处理后的参数调用原始方法---环绕通知中存在对原始方法的调用
@Around("DataAdvice.servicePt()")
public object trimString(ProceedingJoinPoint pjp)throws Throwable{
	object[]args pjp.getArgs();
	//对原始参数的每一个参数进行操作
	for (int i 0;i<args.length;i++){
		//如果是字符串数据
		if(args[i].getclass().equals(String.class)){
			//取出数据,trim()操作后,更新数据
			args[i] = ar gs[i].toString().trim();
		}
	}
	return pjp.proceed(args);
}

Transaction

Introduction

Spring事务简介

在数据层保障一系列的数据库操作同成功同失败

在数据层或业务层保障一系列的数据库操作同成功同失败


public interface PlatformTransactionManager{
	void commit(TransactionStatus status)throws TransactionException;
	void rollback(TransactionStatus status)throws TransactionException;
}
public class DataSourceTransactionManager{
 ...
}

Case 1

案例 :模拟银行账户间转账业务

需求:实现任意两个账户间转账操作
需求微缩:A账户减钱,B账户加钱

分析:

  1. 数据层提供基础操作,指定账户减钱(outMoney),指定账户加钱(inMoney)
  2. 业务层提供转账操作(transfer),调用减钱与加钱的操作
  3. 提供2个账号和操作金额执行转账操作
  4. 基于Spring 整合MyBatis环境搭建上述操作

结果分析:

  1. 程序正常执行时,账户金额A减B加,没有问题
  2. 程序出现异常后,转账失败,但是异常之前操作成功,异常之后操作失败,整体业务失败

  1. 在业务层接口上添加Spring事务管理
public interface AccountService{
	@Transactional
	public void transfer(String out,String in Double money);
}

注意事项

Spring注解式事务通常添加在业务层接口中而不会添加到业务层实现类中,降低耦合
注解式事务可以添加到业务方法上表示当前方法开启事务,也可以添加到接口上表示当前接口所有方法开启事务

  1. 设置事务管理器
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource){
	DataSourceTransactionManager ptm = new DataSourceTransactionManager();
	ptm.setDataSource(dataSource);
	return ptm;
}

注意事项

事务管理器要根据实现技术进行选择
MyBatis框架使用JDBC事务

  1. 开启注解式事务驱动
@Configuration
@ComponentScan("com.test")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MybatisConfig.class})
@EnableTransactionManagement
public class SpringConfig{
}

Roles

Spring事务角色

Config


属性 作用 示例
readonly 设置是否为只读事务 readOnly=true只读事务
timeout 设置事务超时时间 timeout=-1(永不超时)
rollbackFor 设置事务回滚异常(class) rollbackFor =
rollbackForClassName 设置事务回滚异常(String) 同上格式为字符串
noRollbackFor 设置事务不回滚异常(class) noRollbackFor=
noRollbackForClassName 设置事务不回滚异常(String) 同上格式为字符串
propagation 设置事务传播行为 ...

Case 2

案例 转账业务追加日志
需求:实现任意两个账户间转账操作,并对每次转账操作在数据库进行留痕
需求微缩:A账户减钱,B账户加钱,数据库记录日志

分析:

  1. 基于转账操作案例添加日志模块,实现数据库中记录日志
  2. 业务层转账操作(transfer),调用减钱、加钱与记录日志功能

实现效果预期

存在的问题


事务传播行为:事务协调员对事务管理员所携带事务的处理态度

  1. 在业务层接口上添加Spring事务,设置事务传播行为REQUIRES_.NEW(需要新事务)
@Service
public class LogServiceImpl implements LogService{
	@Autowired
	private LogDao logDao;
	@Transactional(propagation =Propagation.REQUIRES_NEW)
	public void log(String out,String in,Double money ){
		logDao.log("转账操作由"+out+"到"+in+",金额:"+money);
	}
}

propagation



SpringMVC

Details see this page: spring-MVC