概念
面向切面编程(方面),将业务逻辑各个部分隔离,降低耦合度,提高开发效率
底层原理
1.AOP底层使用动态代理
两种情况的动态代理
1.有接口,使用JDK动态代理
2.没有接口,使用CGLIB动态代理
动态代理实现
理解: 在不修改接口和代理类的情况下增加功能,静态代理只能代理一个类,如果多个类同时需要相同的功能,不方便添加,动态代理可以根据不同的类添加功能
# 实现过程
Proxy.newProxyInstance方法解析接口中的method放到自己的变量中,然后调用handler中的invoke方法,将method变量放入invoke方法的参数中,进行调用,如果方法带参数,解析放到第三个参数中,第一个参数为代理对象
//增强方法 参数,需要增强的类
InvocationHandler handle = new ProxyHandler(被代理类);
/**
*参数
*1.类加载器
*2.被代理类接口
*3.增强方法
*/
Proxy.newProxyInstance(被代理类.getClass().getClassLoader(), 被代理类.getClass().getInterfaces(), handler);
//内部类
class ProxyHandler implements InvocationHandler{
//注入被代理对象,执行被代理对象中继承接口的方法
private Object object;
public ProxyHandler(Object object){
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before invoke " + method.getName());
//通过判断方法名,增加对应方法
method.invoke(object, args);
System.out.println("After invoke " + method.getName());
return null;
}
}
AOP术语
1.连接点 可以被增强的方法
2.切入点 已经被增强的方法
3.通知(增强) 增强的方法 前置通知,后置通知,环绕通知,异常通知,最终通知
4.切面 应用通知的过程
Spring框架基于AspectJ实现AOP操作
AspectJ不是spring的组成部分,独立于AOP框架,为了方便使用,经常把AspectJ和spring放在一起使用
切入点表达式
1.通过切入点表达式,直到对哪个类里面的哪个方法进行增强
2.语法结构
execution([权限修饰符][返回类型][类全路径][方法名称][参数列表])
# 对类路径中的所有方法进行增强
* com.atguigu.dao.BookDao.* (..)
理解执行过程
通过类全路径反射得到类加载器和类继承的接口,执行invoke方法,通过权限修饰符和返回类型和方法名称和参数列表和反射得到的对象,判断对哪个方法进行增强,使用注解的话,放在增强方法上,就会将增强方法执行在对应被增强方法上
注解进行操作
步骤
1.开启注解扫描
2.使用注解注入对象
3.在增强类上边添加注解@Aspect,声明此类为代理类
4.在spring配置文件中开启生成代理对象,开启之后才会将代理对象生成,替换原对象
com.zwj.aop.anno.User@4310d43 代理对象
com.zwj.aop.anno.User@2fd6b6c7 原对象
<aop:aspectj-autoproxy />有一个proxy-target-class属性,默认为false,表示使用jdk动态代理织入增强,当配为<aop:aspectj-autoproxy poxy-target-class=“true”/>时,表示使用CGLib动态代理技术织入增强。不过即使proxy-target-class设置为false,如果目标类没有声明接口,则spring将自动使用CGLib动态代理
package com.zwj.aop.anno;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* @author zwj
* @date 2022/2/15 - 16:52
*/
@Component
@Aspect
public class ProxyUser {
//前置通知
@Before("execution(* com.zwj.aop.anno.User.add())")
public void before(){
System.out.println("add前置通知");
}
//后置通知
@After("execution(* com.zwj.aop.anno.User.add())")
public void after(){
System.out.println("add后置通知");
}
//环绕通知
@Around("execution(* com.zwj.aop.anno.User.add())")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("Around 执行之前");
proceedingJoinPoint.proceed();
System.out.println("Around 执行之后");
}
//最终通知
@AfterReturning("execution(* com.zwj.aop.anno.User.add())")
public void afterReturning(){
System.out.println("AfterReturning");
}
//异常通知
@AfterThrowing("execution(* com.zwj.aop.anno.User.add())")
public void afterThrowing(){
System.out.println("afterThrowing");
}
}
<?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
http://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">
<!--开启注解扫描-->
<context:component-scan base-package="com.zwj"/>
<!--开启AspectJ生成代理对象,只有开启后使用getBean方法才会获得代理对象
或者使用注解
@EnableAspectJAutoProxy(proxyTargetClass = true)
-->
<aop:aspectj-autoproxy/>
</beans>
抽取公共切入点表达式
//切入点注解
@Pointcut("execution(* com.zwj.aop.anno.User.add())")
public void pointCut(){}
//前置通知
@Before("pointCut()")
public void before(){
System.out.println("add前置通知");
}
多个增强类同时增强
//在增强类上使用注解@Order来声明优先级
@Order(1) # 值越小,优先级越高,用在类上
配置文件进行操作
<?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
http://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 class="com.zwj.aop.xml.Book" id="book"></bean>
<bean class="com.zwj.aop.xml.BookProxy" id="bookProxy"></bean>
<aop:config>
<!--切入点-->
<aop:pointcut id="pointCut" expression="execution(* com.zwj.aop.xml.Book.buy())"/>
<!--通知绑定切入点-->
<aop:aspect ref="bookProxy">
<aop:before method="before" pointcut-ref="pointCut"></aop:before>
</aop:aspect>
</aop:config>
</beans>
jdbcTemplate
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.13</version>
</dependency>
<?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">
<context:property-placeholder location="classpath:info.properties"></context:property-placeholder>
<bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
<!--注入数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
事务
数据库操作的基本单元,事务中的一组操作,要么都成功,有一个失败都失败
事务四大特性
1.原子性: 操作要么都成功,要失败都失败
2.一致性:操作前后,数据总量不变,只update
3.隔离性: 每个事务之间互不影响
4.持久性:数据的持久性
注解方式
<?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:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:property-placeholder location="classpath:info.properties"></context:property-placeholder>
<bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
<!--注入数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--创建事务管理器 因为有针对多种orm事务的管理器-->
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--开启事务管理-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
</beans>
在类上添加注解,每个方法都会有事务
@Transactional
事务的传播行为(propagation)
多事务方法直接进行调用,这个过程事务是如何进行管理的
在一个事务方法中调用有事务方法
在一个事务方法中调用无事务方法
在一个没有事务方法中调用有事务方法
七种传播行为
1.PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,这是最常见的选择,也是Spring默认的事务传播行为。(required需要,没有新建,有加入)
2.PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。(supports支持,有则加入,没有就不管了,非事务运行)
3.PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。(mandatory强制性,有则加入,没有异常)
4.PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。(requires_new需要新的,不管有没有,直接创建新事务)
5.PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。(not supported不支持事务,存在就挂起)
6.PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。(never不支持事务,存在就异常)
7.PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按REQUIRED属性执行。(nested存在就在嵌套的执行,没有就找是否存在外面的事务,有则加入,没有则新建)
事务的隔离级别(isolation)
四种隔离级别
1.读未提交(READ UNCOMMITTED):在读未提交隔离级别下,事务A可以读取到事务B修改过但未提交的数据。可能发生脏读、不可重复读和幻读问题,一般很少使用此隔离级别。
2.读已提交(READ COMMITTED):在读已提交隔离级别下,事务B只能在事务A修改过并且已提交后才能读取到事务B修改的数据。读已提交隔离级别解决了脏读的问题,但可能发生不可重复读和幻读问题,一般很少使用此隔离级别。
3.可重复读(REPEATABLE READ):在可重复读隔离级别下,事务B只能在事务A修改过数据并提交后,自己也提交事务后,才能读取到事务B修改的数据。可重复读隔离级别解决了脏读和不可重复读的问题,但可能发生幻读问题。提问:为什么上了写锁(写操作),别的事务还可以读操作?因为InnoDB有MVCC机制(多版本并发控制),可以使用快照读,而不会被阻塞。
4.可串行化(SERIALIZABLE):各种问题(脏读、不可重复读、幻读)都不会发生,通过加锁实现(读锁和写锁)。但是需要排队,性能不好
xml方式
<!--创建事务管理器 因为有针对多种orm事务的管理器-->
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--开启注解事务管理-->
<!-- <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>-->
<!--使用aop增加事务-->
<aop:config>
<aop:pointcut id="pointCut" expression="execution(* com.zwj.dao.UserDBDaoImpl.getUser(..))"/>
<aop:advisor advice-ref="advice" pointcut-ref="pointCut"></aop:advisor>
</aop:config>
<!--配置事务通知-->
<tx:advice transaction-manager="transactionManager" id="advice">
<tx:attributes>
<!--指定在哪种规则(名称)的方法上面添加事务 可以添加属性-->
<tx:method name="getUser" propagation="REQUIRED"/>
<!-- <tx:method name="get*"/>-->
</tx:attributes>
</tx:advice>