1、AOP概述
AOP为Aspect Orientend Programming 的缩写,意为 :面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。
AOP是OOP的延续,是软件开发中的一个热点,也是Spring 框架中的一个重要内容。利用AOP可以对业务逻辑的各个部分进行隔离,从而使地业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
AOP OOP在字面上虽然非常类似,但却是不同领域的两种设计思想。
OOP(面向对象编程),针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更高清晰高效的逻辑单元划分。
AOP针对业务处理过程中的切面进行提取,他所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。这两种设计思想在目标上有着本质的差异。
面向切面编程好处:
减少重复,专注业务
注意:面向切面编程只是面向对象编程的一种补充
核心原理:
使用动态代理的方式在执行方法前后或者出现异常的时候加入相关的逻辑。
eg:
事务处理:开启事务,关闭事务,出现异常后回滚事务
权限判断:在执行方法前,判断是否具有权限
日志:在执行前进行日志处理
2、AOP的基本概念
连接点(Joinpoint) :类中可以被增强的方法,这个方法就称为连接点
切入点(pointcut):类中有很多方法可以被增强,但实际中只有add和update被增加了,那么add和update 方法就被称为切入点(实际实现的连接点)
通知(Advice):通知是指一个切面在特定的连接点要做的事情(增强的功能)。通知分为方法执行前通知,方法执行后通知,环绕通知。
切面(Aspect):把通知添加到切入点的过程叫切面
目标(Target):代理的目标对象(要增强的类)
代理(Proxy):向目标对象应用通知之后创建的代理对象
3、springAOP实现
对于AOP这种编程思想,很多框架都进行了实现。Spring就是其中之一,可以完成切面编程。
AspectJ也实现了AOP功能,AspectJ是一个基于Java语言的AOP框架,他提供了强大的AOP功能且实现方式更为简洁,方便,而且还支持注解式开发。所以,Spring 又将AspectJ的对于AOP的实现也引入到了自己的框架中。
在Spring 中使用AOP开发时,一般使用AspectJ的实现方式。
AspectJ是一个优秀面向切面的框架,他扩展了Java语言,提供了强大的切面实现。
AspectJ中常用的通知有五种类型:
前置通知 ,后置通知 ,环绕通知 , 异常通知 , 最终通知
1、下载相关jar
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
2、基于aspectJ的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"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="aopdemo" class="com.ffyc.spring.aop.AopDemo"></bean>
<aop:config>
<!--配置切入点-->
<aop:pointcut id="savedao" expression="execution(* com.ffyc.spring.dao.UserDao.savedao(..))"/>
<!--配置通知和切入点-->
<aop:aspect ref="aopdemo">
<!-- <aop:before method="saveLog" pointcut-ref="savedao"></aop:before>
<aop:after method="saveLog" pointcut-ref="savedao"></aop:after>
<aop:after-throwing method="exceptionAdice" pointcut-ref="savedao" throwing="e"></aop:after-throwing>-->
<aop:around method="aroundAdice" pointcut-ref="savedao"></aop:around>
</aop:aspect>
</aop:config>
</beans>
在spring.xml中添加配置,启动AspectJ支持:
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
3、实现通知
环绕通知
@Around("execution(* com.ffyc.spring.dao.UserDao.savedao())")
public void aroundAdice(ProceedingJoinPoint proceedingJoinPoint){
System.out.println("前置方法");
try {
proceedingJoinPoint.proceed();
}catch (Throwable e){
System.out.println(e.getMessage());
}
System.out.println("后置方法");
}
前置通知
@Before("execution(* com.ffyc.spring.dao.UserDao.savedao())")
后置通知
@After("execution(* com.ffyc.spring.dao.UserDao.savedao())")
异常通知
@AfterThrowing(value = "execution(* com.ffyc.spring.dao.UserDao.savedao())",throwing = "e")
public void exceptionAdice(Throwable e){
System.out.println(e.getMessage());
}
最终通知
@AfterReturning("execution(* com.ffyc.spring.dao.UserDao.savedao())")
UserDao
@Repository(value = "userDao")
public class UserDao{
@Autowired
JdbcTemplate jdbcTemplate;
public void savedao(){
System.out.println("保存dao方法");
}
public void deletedao(){
System.out.println("删除");
}
}
4、Spring事务管理
事务可以看做是由数据库若干操作组成的一个单元。
事务的作用:保证用户的每一个操作都是可靠的,事务的每一步操作都必须成功执行,只要有发生异常就回退到事务开始未进行操作的状态,这些操作要么都完成,要么都取消,保证数据满足一致性的要求。
Spring中的事务管理分为两种形式:编程式事务;声明式事务
**编程式事务:**在项目中很少使用,这种方式需要注入一个事务管理对象TransactionTemplate,然后在我们代码中需要提交事务或回滚事务时自己写代码实现。
**声明式事务:**管理建立在AOP基础上,本质是对方法前后进行拦截,所以声明式事务是方法级别的。
Spring声明式事务管理方式有两种:
基于xml配置
基于注解实现
Spring 事务管理API
PlatformTransactionManager 事物管理器接口
Spring 针对不同的 dao 框架,提供了不同的实现类,Jdbc,mybatis 事物管理实
现类是 DataSourceTransactionManager.
1、配置事务管理器
<!--数据库事务:是对数据库若干操作组成的一个单元(整体)
在我们的一次save时,里面包含了两个或者更多个对数据库的操作,
那么我们认为着若干操作,应该是一个单元(整体),
这就是事务的原子性(不可分割性),多个操作要么执行成功,要么都不执行成功。
-->
<!-- 配置 spring 事务管理类, 并注入数据源 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="druidDataSource"></property>
</bean>
2、XML配置方式
<tx:advice id="txadvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="savedao" expression="execution(* com.ffyc.spring.dao.UserDao.savedao(..))"/>
<aop:advisor advice-ref="txadvice" pointcut-ref="savedao"/>
</aop:config>
3、注解方式
<!– 开启注解事务管理 –>
<tx:annotation-driven transaction-manager="transactionManager"/>
4、例子
eg:
@Repository(value = "userDao")
public class UserDao{
@Autowired
JdbcTemplate jdbcTemplate;
@Transactional
public void savedao(){
jdbcTemplate.update("insert into admin(account,password,sex)values (?,?,?)","122","222","男");
int s=10/0;
jdbcTemplate.update("insert into admin(account,password,sex)values (?,?,?)","122","222","女");
}
public void deletedao(){
System.out.println("删除");
}
如果去掉 int s=10/0; 不会出现异常,则运行成功,且数据插入数据库中
5、Spring事务传播行为
什么叫事务传播行为?
传播,至少有两个东西,才能发生传播。单体不存在传播行为。事务传播行为(propagation behavior) 指的就是当一个事务方法被另一个事务方法调用时,这个事务应该如何进行。事务传播行为是Spring 框架独有的事务增强特性,他不属于事务实际提供数据库行为。
Spring定义了七种传播行为:
1、 PROPAGATION_REQUIRED
Propagation.REQUIRED A 和 B 都为REQUIRED ,A调用B方法,那么B事务加入到A事务中执行 A 无事务,调用B方法,传播行为为REQUIRED,B会自己开启一个事务
eg:事务A
@Service(value = "deptService")
public class DeptService {
@Autowired
DeptDao deptDao;
@Autowired
CommonService commonService;
@Transactional(propagation = Propagation.REQUIRED)
public void save(){
deptDao.save();
commonService.saveLog();
//int s=10/0;
}
}
事务B
@Service(value = "commonService")
public class CommonService {
@Autowired
CommonDao commonDao;
public void saveLog(){
commonDao.saveLog();
}
}
2、 PROPAGATION_SUPPORTS
/*
Propagation.SUPPORTS A 传播行为为REQUIRED ,B传播行为为SUPPORTS ,那么B加入到A中执行
A无事务,B也就无事务
*/
@Transactional(propagation = Propagation.SUPPORTS)
public void saveLog(){
commonDao.saveLog();
int s=10/0;
}
3、 PROPAGATION_REQUIRES_NEW
/*
Propagation.REQUIRES_NEW B传播事务为Propagation.REQUIRES_NEW,B方法会开启一个独立的新事务,B不会受到A的影响
A 没有事务,B传播事务为_new ,B方法会开启一个独立的新事务
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveLog(){
commonDao.saveLog();
}
4、 PROPAGATION_MANDATORY
使用当前的事务,如果当前没有事务,就抛出异常
5、 PROPAGATION_NOT_SUPPORTED
以非事务方式执行操作,如果当前存在事务,就把当前事务
挂起
6、PROPAGATION_NEVER
以非事务方式执行,如果当前存在事务,则抛出异常
7、PROPAGATION_NESTED
如果当前存在事务,则在嵌套事务内执行。如果当前没有事
务,则执行与 PROPAGATION_REQUIRED 类似的操作
声明式事务不生效的场景
@Transactional应用在非public修饰的方法上
@Transactional注解属性propagation设置错误
同一个类中方法调用,导致@Transactional失效
异常被catch捕获导致@Transactional失效
数据库引擎不支持事务
6、Spring集成Mybatis
Spring集成Mybatis其核心是将SqlSessionFactory 交由Spring管理,并由Spring管理对dao接口的代理实现。
1、导入mybatis jar包
<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.2</version>
</dependency>
2、导入 Spring - mybatis jar包
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
3、配置 sqlSessionFactory
<!--配置 sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="druidDataSource"></property>
<property name="configLocation" value="classpath:mybatis_config.xml"></property>
<property name="mapperLocations" value="classpath:mapper/*Mapper.xml"></property>
</bean>
4、指定生成接口代理
<!--接口代理-->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.ffyc.ssm.dao"></property>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
5、在service 中注入Dao代理接口,此接口有Sping代理实现
@Autowired
AdminDao adminDao;
eg:
CommonResult
package com.ffyc.ssm.common;
/**
* 结果封装类
*/
public class CommonResult<T> {
private Integer code;
private String msg;
private T data;
public CommonResult(Integer code, String msg, T data) {
this.code = code;
this.msg = msg;
this.data = data;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
Controller
@Controller
@RequestMapping(path = "/admin")
public class AdminController{
@Autowired
AdminService adminService;
@PostMapping(path = "/getAdmin")
public void getAdmin(Admin admin, Integer flag){
System.out.println("admin:"+admin.toString());
}
}
dao
public interface AdminDao {
void saveAdmin();
}
modle
package com.ffyc.ssm.modle;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
public class Admin {
private Integer id;
private String account;
private String password;
private String sex;
@DateTimeFormat(pattern = "YYYY-MM-dd")
private Date birthday;
private String oldFileName;
private String newFileName;
public String getOldFileName() {
return oldFileName;
}
public void setOldFileName(String oldFileName) {
this.oldFileName = oldFileName;
}
public String getNewFileName() {
return newFileName;
}
public void setNewFileName(String newFileName) {
this.newFileName = newFileName;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
service
@Service
public class AdminService {
@Autowired
AdminDao adminDao;
@Transactional
public void saveAdmin(){
adminDao.saveAdmin();
}
}
mapper
<mapper namespace="com.ffyc.ssm.dao.AdminDao">
<insert id="saveAdmin">
insert into admin(account,password)values('123','123')
</insert>
</mapper>
需要配置这些xml
html
<a href="admin/getAdmin?id=1&name='zhangsan'">admin</a>
<form method="post" action="admin/getAdmin">
<input type="text" name="account">
<input type="text" name="password">
<input type="text" name="sex">
<input type="text" name="birthday">
<input type="submit">
</form>
如果所有格式都正确会响应回来:
不正确格式,则会响应: