概念阐述:
1.什么是AOP?
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
AOP:它是面向切面编程的语言,它可以让你的业务代码和非业务代码进行隔离。在不改变业务代码的前提下,可以增加新的非业务代码。
2.为什么使用AOP?
3.AOP应用场景
1:记录日志
2:权限校验
3:Spring事务管理
4.AOP的结构
AOP要做的三件事在哪里切入,也就是权限校验,等非业务操作在哪些业务 代码中执行;什么时候切入,是业务代码执行前还是执行后;切入后做什 么事,比如做权限校验、日志记录等。
正文:
如何使用AOP:
@Service
public class MathServiceImpl implements MathService{
@Override
public double add(double a, double b) {
double result = a+b;
System.out.println("The add method result ="+result);
return result;
}
@Override
public double subtract(double a, double b) {
double result = a-b;
System.out.println("The subtract method result ="+result);
return result;
}
@Override
public double ride(double a, double b) {
double result = a*b;
System.out.println("The ride method result ="+result);
return result;
}
@Override
public double divide(double a, double b) {
double result = a/b;
System.out.println("The divide method result ="+result);
return result;
}
}
我们每次操作,都记录日志,如果后期日志内容发生改变,需要在每个操作后都进行修改,不利于代码维护,这时候就可以使用AOP解决。
引入依赖
<!--引入spring核心依赖库-->
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.12.RELEASE</version>
</dependency>
<!--引入spring切面依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.12.RELEASE</version>
</dependency>
spring.xml文件
<!--包扫描-->
<context:component-scan base-package="com.pzy.aop"/>
<!--开启AOP切面注解驱动-->
<aop:aspectj-autoproxy/>
创建一个切面类
//标记该类为切面类
@Aspect
//该类对象的创建交于spring容器管理---和@service
@Component
public class MyAspect {
//定义切点 基础方法
//@Pointcut(value = "execution(public double com.pzy.aop.MathServiceImpl.add(double ,double ))")
//定义切点 使用通配符 ..表示任意参数 建议包不要用通配符
@Pointcut(value = "execution(* com.pzy.aop.*.*(..))")
private void mypointcut(){}
@After(value = "mypointcut()")
public void b(){
System.out.println("The add method result");
}
}
测试类
public class Test {
public static void main(String[] args) {
//加载spring配置文件
ApplicationContext app=new ClassPathXmlApplicationContext("classpath:spring.xml");
MathService mathServiceImpl = (MathService) app.getBean("mathServiceImpl");
System.out.println(mathServiceImpl.add(20, 10));
}
注解模式
自定义注解
定义注解类
@Target(value = {ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value() default "";
}
修改切面类
//定义为切点
@Pointcut(value = "@annotation(com.ykq.aop.MyAnnotation)")
private void mypointcut(){}
//使用MyAnntation注解方法后执行这个内容
@After(value = "mypointcut()")
public void b(){
System.out.println("AAA--->The add method result");
}
AOP切面通知的类型
定义切点
//定义切点
@Pointcut(value = "execution(* com.pzy.aop.*.*(..))")
private void mypointcut(){}
前置通知@Before,被代理的方法执行前-执行
//前置通知
@Before(value = "mypointcut()")
public void b(){
System.out.println("方法执行前,执行切面的内容--前置通知");
}
后置通知@After,被代理的方法执行结束后-执行
@After(value = "mypointcut1()")
public void a(){
System.out.println("The add method result");
}
后置返回通知@AfterReturning,被代理的方法碰到return-执行
//后置返回通知--方法return后 如果出现异常,这个通知不会被执行
//returning会把方法执行返回结果赋值给该变量
@AfterReturning(value = "mypointcut()",returning = "s")
public Object c(Object s){
System.out.println("方法执行后,执行切面的内容--后置通知"+s);
return s ;
}
后置异常通知@AfterThrowing,被代理的方法出现异常时-执行
//异常通知--当被切入的方法出现异常时,会执行
@AfterThrowing(value = "mypointcut()")
public void afterThrowing(){
System.out.println("异常通知");
}
环绕通知@Around,环绕被代理的方法通知
//环绕通知
@Around(value = "mypointcut()")
public Object around(ProceedingJoinPoint joinPoint){
System.out.println("执行前打印的代码");
try {
Object result = joinPoint.proceed();
return result;
} catch (Throwable e) {
e.printStackTrace();
System.out.println("方法出现异常时执行");
}finally {
System.out.println("方法无论如何都会执行");
}
return 1;
}
Spring操作事务
什么是事务?事务就是一系列的动作,它们被当作一个独立的单元,这些动作要么全部完成,要么全部不起作用,例如转账类型,一方减少一方增加,必须同时完成。
示例:JDBC的事务管理
public class Test {
public static void main(String[] args) {
Connection conn=null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
conn= DriverManager.getConnection("jdbc:mysql://localhost:3306/数据库名?serverTimezone=Asia/Shanghai","数据库名","密码");
conn.setAutoCommit(false);//设置事务手动提交。
PreparedStatement ps=conn.prepareStatement("update t_user set balance=balance-600 where id=7");
ps.executeUpdate();
//int i=10/0;
PreparedStatement ps2=conn.prepareStatement("update t_user set balance=balance+600 where id=6");
ps2.executeUpdate();
conn.commit();//事务提交
}catch (Exception e){
e.printStackTrace();
//事务回滚
try {
conn.rollback();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}finally {
}
}
}
使用spring框架提供的事务切面类。
前置通知---开启手动事务
后置返回通知---事务提交
异常通知---事务回滚
依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.12.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.12.RELEASE</version>
</dependency>
<!--spring事务依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.12.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.15.RELEASE</version>
</dependency>
<!--mybatis的依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.9</version>
</dependency>
<!--mybatis和spring整合的依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.7</version>
</dependency>
<!--druid的连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.28</version>
</dependency>
</dependencies>
spring配置文件
<?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:mvc="http://www.springframework.org/schema/mvc" 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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--springmvc的配置-->
<!--包扫描 扫描com.ykq以及该包下的子包-->
<context:component-scan base-package="com.ykq"/>
<!--spring整合mybatis的配置-->
<!--数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<!--mysql驱动为8.0以后必须使用时区-->
<property name="url" value="jdbc:mysql://localhost:3306/aaa?serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<!--spring封装了一个类SqlSessionFactoryBean类,可以把mybatis中的配置-->
<bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations" value="classpath:/mapper/*.xml"/>
</bean>
<!--为指定dao包下的接口生产代理实现类-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sessionFactory"/>
<!--它会为com.ykq.dao包下的所有接口生产代理实现类-->
<property name="basePackage" value="com.ykq.dao"/>
</bean>
<!----================以下内容是关于事务的配置===================------>
<!--事务切面管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--开启事务管理注解的驱动-->
<tx:annotation-driven/>
</beans>
dao类
public interface UserDao {
//1.修改账号余额
public void updateBalance(@Param("id") int id, @Param("money") double money);
}
xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace必须和dao接口的名称一模一样-->
<mapper namespace="com.ykq.dao.UserDao">
<update id="updateBalance">
update t_user set balance=balance+#{money} where id=#{id}
</update>
</mapper>
service接口
public interface UserService{
public void zhuanZhang(int id,int uid,double money);
}
serviceImpl
package com.ykq.service.impl;
import com.ykq.dao.UserDao;
import com.ykq.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* @program: qy168-transation
* @description:
* @author: 闫克起2
* @create: 2023-08-16 17:17
**/
@Service
public class UserServieImpl implements UserService {
@Autowired
private UserDao userDao;
//事务管理注解
@Transactional //该方法交于spring的事务来管理了---默认spring不识别该注解
public void zhuanzhang(int id, int uid, double money) {
//1.扣钱
userDao.updateBalance(id,-money);
//int c=10/0;
//2.收钱
userDao.updateBalance(uid,money);
}
//---事务管理: (1)自己写事务管理 --自己手动定义事务切面类
// (2)使用spring提供的事务切面类。
//
}
测试类
public class Test {
public static void main(String[] args) {
ApplicationContext app=new ClassPathXmlApplicationContext("classpath:spring.xml");
UserService userServieImpl = (UserService) app.getBean("userServieImpl");
userServieImpl.zhuanzhang(7,6,400);
}
}