什么是AOP?
AOP又称面向切面编程,简单来说就是在不影响主业务的情况下去增加其他的功能,举个例子:就是小明妈妈叫小明去超市买盐,这里的主要业务就是买盐,但小明在超市可以在买盐之前去买水果,然后去买盐,也可以买盐后,再去买其他的,只要不影响主业务,又想添加新功能,都可以使用AOP来解决。
AOP的相关术语
-
Target(目标对象):代理的目标对象
-
Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类
-
Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring,,这些点指的是方法,因为spring只支持方法类型的连接点
-
Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义
-
Advice(通知/增强):所谓通知是指拦截Joinpoint之后所要做的事情就是通知
-
Aspect(切面):是切入点和通知(引介)的结合
-
Weaving (织入):是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入
入门案例(注解方式)
1.导入坐标:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.8</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.8</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.6</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
2.创建service包,并在包下定义一个类
public interface ShoppingService {
public String buyGoods(Integer payMoney);
}
//并写一个实现类
@Component("shoppingService")
public class ShoppingServiceImpl implements ShoppingService{
@Override
public String buyGoods(Integer payMoney) {
System.out.println("小明支付的金额"+payMoney);
Integer productMoney = 10;
System.out.println("商品的金额:10元");
int remainMoney = payMoney-productMoney;
String result = "剩余金额"+remainMoney;
System.out.println(result);
return result;
}
}
3.创建需要切入的方法
@Aspect
@Component
@EnableAspectJAutoProxy
public class LogAnnotationAspectj {
private final static String EXPRESSION =
"execution(* org.example.service.*.*(..))";
//指定要执行的方法的根路径
//前置通知
@Before(EXPRESSION)
public void beforeAdvice(JoinPoint joinPoint){//joinPoint方法执行点
System.out.println("========== 【Aspectj前置通知】 ==========");
}
//后置通知:方法正常执行后,有返回值,执行该后置通知:
//如果该方法执行出现异常,则不执行该后置通知
@AfterReturning(value=EXPRESSION,returning = "returnVal")
public void afterReturningAdvice(JoinPoint joinPoint,Object returnVal){
System.out.println("========== 【Aspectj后置通知】 ==========");
}
//后置通知
@After(EXPRESSION)
public void afterAdvice(JoinPoint joinPoint){
System.out.println("========== 【Aspectj后置通知】 ==========");
}
//环绕通知
@Around(EXPRESSION)
public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("##########【环绕通知中的前置通知】##########");
Object returnVale = joinPoint.proceed();
System.out.println("##########【环绕通知中的后置通知】##########");
return returnVale;
}
// 异常通知:方法出现异常时,执行该通知
@AfterThrowing(value = EXPRESSION,throwing = "ex")
public void throwAdvice(JoinPoint joinPoint, Exception ex){
System.out.println("********** 【Aspectj异常通知】执行开始 **********");
System.out.println("出现异常:" + ex.getMessage());
System.out.println("********** 【Aspectj异常通知】执行结束 **********");
}
4.写配置类SpringConf这个类是用来代替applicationContext.xml的
@Configuration
@ComponentScan("org.example")
public class SpringConf {
}
//作用和applicationContext.xml一样,是将类交给容器管理,让容器来创建对象和管理对象
5.编写测试类
@Test
public void test3(){
ApplicationContext cxt = new AnnotationConfigApplicationContext(SpringConf.class);
ShoppingService shoppingService = (ShoppingService) cxt.getBean("shoppingService");
shoppingService.buyGoods(20);
}
6.展示运行结果![](https://i-blog.csdnimg.cn/blog_migrate/255a206c9e546aafb36256430b2b962b.png)
手动添加一个异常时:
名称 | 注解 | 说明 | |
---|---|---|---|
前置通知 | @Before | 用于配置前置通知。指定增强的方法在切入点方法之前执行 | |
后置通知 | @AfterReturning | 用于配置后置通知。指定增强的方法在切入点方法之后执行 | |
环绕通知 | @Around | 用于配置环绕通知。指定增强的方法在切入点方法之前和之后都执行 | |
异常抛出通知 | @AfterThrowing | 用于配置异常抛出通知。指定增强的方法在出现异常时执行 | |
最终通知 | @After | 用于配置最终通知。无论增强方式执行是否有异常都会执行 |
什么是事务?
事务是数据库操作的最小工作单元,是作为单个逻辑工作单元执行的一系列操作,这些操作作为一个整体一起向系统提交,要么都执行、要么都不执行;事务是一组不可再分割的操作集合。
事务的四大原则:
1.原子性(Atomicity):
一个事务已经是一个不可再分割的工作单位。事务中的全部操作要么都做;要么都不做
2.一致性(Consistency):
事务的执行使数据从一个状态转换为另一个状态,但是对于整个数据的完整性保持稳定。
3.持久性(Durability):
一个事务一旦提交,它对数据库中数据的改变会永久存储起来。其他操作不会对它产生影响
4.隔离性(Isolation):
事务允许多个用户对同一个数据进行并发访问,而不破坏数据的正确性和完整性。同时,并行事务的修改必须与其他并行事务的修改相互独立。
事务的隔离级别:
1.脏读 :在一个事务中读取到了另外一个事务修改的【未提交的数据】,而导致多次读取同一个数据返回的结果不一致 (必须要解决的)
2.不可重复读 : 在一个事务中读取到了另外一个事务修改的【已提交的数据】,而导致多次读取同一个数据返回的结果不一致
3.幻读(虚读): 一个事务读取了几行记录后,另一个事务插入一些记录,幻读就发生了。再后来的查询中,第一个事务就会发现有些原来没有的记录
事务的传播行为:
传播行为 | 说明 |
---|---|
REQUIRED【默认】 | 当前如果有事务,Spring就会使用该事务;否则会开始一个新事务(增、删、改) |
SUPPORTS | 当前如果有事务,Spring就会使用该事务;否则不会开始一个新事务(查询) |
MANDATORY | 当前如果有事务,Spring就会使用该事务;否则会抛出异常 |
REQUIRES_NEW | 当前如果有事务,把当前事务挂起,新建事务 |
NOT_SUPPORTED | 当前有事务,Spring也会在非事务环境下执行。如果当前有事务,则该事务挂起 |
NEVER | 当前有事务,Spring也会在非事务环境下执行。如果当前有事务,则抛出异常 |
NESTED | 当前有事务,则在嵌套事务中执行。如果没有,那么执行情况与REQUIRED一样 |
事务分为编程式事务管理和声明式事务管理:
案例;
编程式事务管理
//这是一个转账的业务,用来模拟编程式事务管理
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
public void transfer(String out, String in, Double money) {
try{
accountDao.outMoney(out, money);
int i=3/0;
accountDao.inMoney(in, money);
transactionManager.commit(status);//手动提交事务
}catch(Exception e){
transactionManager.rollback(status);//回滚事务
}
}
}
声明式事务管理:(注解方式)
1.创建spring 配置类
@Configuration
@ComponentScan("com.cqgcxy")
@Import({JdbcConfig.class})
//启动平台事务管理
@EnableTransactionManagement
public class SpringConfiguration {
}
2.业务层代码实现
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
@Transactional
public void transfer(String out, String in, Double money) {
accountDao.outMoney(out, money);
int i=3/0;
accountDao.inMoney(in, money);
}
}
这样就实现了在处理业务时发生错误,使其转账不会出现这个人已经转出去了,那个人还没收到的情况。