文章目录
1 初识AOP
1.1 AOP概述
1.2 AOP使用场景
1.3 解决思路分析
静态代理图:
缺点:每一个service对应一个代理,代码多冗余
好处:为每一个service可以定制不同的功能
1.4 动态代理技术
1.5 案例介绍
1 基于接口的动态代理
/*
基于JDK的动态代理( Proxy )
作用: 用于创建AccountService 动态代理对象的 工厂类
*/
@Component
public class ProxyAccountServiceFactory {
//指定被代理对象(AccountServiceImpl对象)
@Autowired
@Qualifier("accountServiceImpl")
private AccountService accountService;
@Autowired
private TransactionManager transactionManager;
//用于创建AccountService 动态代理对象
@Bean("proxyAccountService")
public AccountService createAccountServiceProxy(){
//创建AccountService 的动态代理对象
/**
* 参数一: 被代理对象的类加载器
* 参数二: 被代理对象 所实现的所有接口
* 参数三: 指定原有方法 如何进行增强
*/
AccountService proxyAccountService = (AccountService) Proxy.newProxyInstance(
accountService.getClass().getClassLoader(),
accountService.getClass().getInterfaces(),
new InvocationHandler() {
/**
* invoke 方法, 就是对原有功能方法的增强
* @param proxy 就是创建出来的动态代理对象的引用
* @param method 被代理对象 所要执行的方法
* @param args 被代理对象 所要执行的方法 所接收的实际参数值
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("被代理对象 = " + accountService);
System.out.println("被增强的方法 = " + method.getName());
System.out.println("被增强的方法 所接收的实际参数值 = " + Arrays.toString(args));
Object rtValue = null;
try {
//开启事务(增强)
transactionManager.begin();
//调用 被代理对象 的原有方法
rtValue = method.invoke(accountService, args);
//提交事务(增强)
transactionManager.commit();
} catch (Exception e) {
e.printStackTrace();
//回滚事务(增强)
transactionManager.rollback();
} finally {
//释放连接(增强)
transactionManager.close();
}
return rtValue;
}
}
);
return proxyAccountService;
}
}
2 基于子类的动态代理
/*
基于CgLib的动态代理( 第三方jar包 )
作用: 用于创建AccountService 动态代理对象的 工厂类
*/
@Component
public class CglibAccountServiceFactory {
//指定被代理对象(AccountServiceImpl对象)
@Autowired
@Qualifier("accountServiceImpl")
private AccountService accountService;
@Autowired
private TransactionManager transactionManager;
//用于创建AccountService 动态代理对象
@Bean("cgLibAccountService")
public AccountService createAccountServiceProxy(){
//创建AccountService 的动态代理对象
AccountService cgLibAccountService = (AccountService) Enhancer.create(
accountService.getClass(),
new MethodInterceptor() {
/**
* intercept 方法, 就是对原有功能方法的增强
* @param proxy 就是创建出来的动态代理对象的引用
* @param method 被代理对象 所要执行的方法
* @param objects 被代理对象 所要执行的方法 所接收的实际参数值
* @param methodProxy 所要执行的方法的代理对象 method.invoke()
*/
@Override
public Object intercept(Object proxy, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("被代理对象 = " + accountService);
System.out.println("被增强的方法 = " + method.getName());
System.out.println("被增强的方法 所接收的实际参数值 = " + Arrays.toString(objects));
Object rtValue = null;
try {
//开启事务(增强)
transactionManager.begin();
//调用 被代理对象 的原有方法
rtValue = method.invoke(accountService, objects);
//提交事务(增强)
transactionManager.commit();
} catch (Exception e) {
e.printStackTrace();
//回滚事务(增强)
transactionManager.rollback();
} finally {
//释放连接(增强)
transactionManager.close();
}
return rtValue;
}
});
return cgLibAccountService;
}
}
2 AOP相关概念
2.1 AOP的基础知识
2.2 Spring中的AOP
3 Spring中应用AOP
3.1 基于XML配置的入门案例
1 案例介绍
在业务层方法执行之前,输出记录日志的语句
2 导入坐标
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
</dependencies>
3 编写基础代码
编写账户业务层接口
模拟账户保存的save()方法
编写对应实现类
实现save方法
(前置增强:日志打印)
编写日志切面类,实现提供实现类的增强方法
utils.logUtil
打印日志printLog()方法
4 配置Spring的IoC,配置Spring的AOP:ApplicationContext.xml
配置Service对象
配置LogUtil切面类(增强功能)对象
配置AOP,实现了对service中的方法进行增强LogUtil
<?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: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/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--配置service 对象-->
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"></bean>
<!--配置LogUtil 切面类(增强功能)对象-->
<bean id="logUtil" class="com.itheima.utils.LogUtil"></bean>
<!--配置AOP, 实现了对service 中的方法 进行增强(LogUtil)-->
<aop:config>
<!--配置切面-->
<aop:aspect id="logUtil" ref="logUtil">
<!--配置切入点表达式-->
<aop:before method="printLog" pointcut="execution(public void com.itheima.service.impl.AccountServiceImpl.save())"></aop:before>
</aop:aspect>
</aop:config>
</beans>
5 编写测试类测试
创建Spring容器对象
获取Service对象
调用方法
3.2 基于XML的AOP配置细节
3.3 五种通知类型
注意:后置通知和异常通知不会同时执行,后置通知表示成功执行后的,异常通知表示执行异常出现的。
3.3.6 五种通知类型的完整实例
1 导入坐标
2 编写基础代码:
接口
三个方法
接口实现类
三个方法的实现类:
save:保存账户
update(int i) 更新账户
int delete 删除账户
切面类:包含增强方法
切面类举例:
/*
日志的切面类(包含了 增强方法)
*/
public class LogUtil {
//配置 前置增强: 执行实现 在切入点方法执行前执行
public void beforePrintLog(JoinPoint joinPoint) throws Throwable {
//获取切入点方法的参数
Object[] args = joinPoint.getArgs();
System.out.println("前置增强beforePrintLog = " + Arrays.toString(args));
}
//配置 后置增强: 执行时机 在切入点方法执行完毕后, 执行; 若切入点方法执行过程中产生了异常, 后置增强不执行
public void afterReturningPrintLog(JoinPoint joinPoint, Object rtValue) throws Throwable {
System.out.println("后置增强afterReturningPrintLog = " + rtValue);
}
//配置 异常增强: 执行时机 在切入点方法执行过程中 抛出了异常,
public void afterThrowingPrintLog(JoinPoint joinPoint, Exception e) throws Throwable {
System.out.println("异常增强:afterThrowingPrintLog = " + e);
}
//配置 最终增强: 执行时机 在切入点方法执行完毕后, 执行(无论是否有异常, 最终增强都执行)
public void afterPrintLog(JoinPoint joinPoint) throws Throwable {
System.out.println("最终增强:afterPrintLog");
}
//配置 环绕通知: 包含了(前置增强, 后置增强, 异常增强, 最终增强)
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
Object rtValue = null;
try {
//配置 前置增强:
System.out.println("前置增强");
//获取方法执行时, 所需的实际参数
Object[] args = joinPoint.getArgs();
//原有的方法执行
rtValue = joinPoint.proceed(args);
//配置 后置增强:
System.out.println("后置增强");
} catch (Throwable throwable) {
throwable.printStackTrace();
//配置 异常增强:
System.out.println("异常增强");
} finally {
//配置 最终增强:
System.out.println("最终增强");
}
return rtValue;
}
}
3 Spring配置文件:ApplicationContext.xml
配置service
配置切面类
配置AOP:
配置通用的切入点表达式
配置切面(使用切面类中的方法增强):
配置前置增强:在切入点方法执行之前执行。配置后置,异常。。。。。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--配置AccountService-->
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"></bean>
<!--配置切面类(增强)-->
<bean id="logUtil" class="com.itheima.utils.LogUtil"></bean>
<!--配置AOP-->
<aop:config>
<!--配置 通用的 切入点表达式-->
<aop:pointcut id="pc" expression="execution(* com.itheima.service..*.*(..))"></aop:pointcut>
<!--配置切面(织入)-->
<aop:aspect id="logUtil" ref="logUtil">
<!--配置 前置增强: 执行时机 在切入点方法执行前执行-->
<aop:before method="beforePrintLog" pointcut-ref="pc"></aop:before>
<!--配置 后置增强: 执行时机 在切入点方法执行完毕后, 执行; 若切入点方法执行过程中产生了异常, 后置增强不执行-->
<aop:after-returning method="afterReturningPrintLog" pointcut-ref="pc" returning="rtValue"></aop:after-returning>
<!--配置 异常增强: 执行时机 在切入点方法执行过程中 抛出了异常, 执行-->
<aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="pc" throwing="e"></aop:after-throwing>
<!--配置 最终增强: 执行时机 在切入点方法执行完毕后, 执行(无论是否有异常, 最终增强都执行)-->
<aop:after method="afterPrintLog" pointcut-ref="pc"></aop:after>
<!--配置 环绕通知: 包含了(前置增强, 后置增强, 异常增强, 最终增强)-->
<aop:around method="around" pointcut-ref="pc"></aop:around>
</aop:aspect>
</aop:config>
</beans>
4 基于注解的AOP配置
4.1 半注解的形式
1 导入坐标
2 基础代码
Service层添加@Service注解
3 编写Spring配置文件
开启SpringIoC容器的注解扫描
开启SpringAOP的注解扫描
<?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:aop="http://www.springframework.org/schema/aop"
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/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--开启SpringIOC容器的注解扫描-->
<context:component-scan base-package="com.itheima"></context:component-scan>
<!--开启SpringAOP的注解扫描-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
4 切面类:
@Component:放到IoC容器中
设置切面类@Aspect :声明它是切面类,替换原xml中配置切面类的语句:
<bean id="logUtil" class="com.itheima.utils.LogUtil"></bean>
@Pointcut配置通用的切入点表达式
@Before引用@Pointcut修饰的方法
。。。
注意:增强的顺序是:前置、service业务、最终和后置(异常),其中最终居然在后置前面,如果是
事务的操作就会出错:先释放资源在提交事务错误
所以如果涉及到事务的操作要使用环绕增强@Around:
它的顺序:前置 service业务 后置 最终
/*
日志的切面类(包含了 增强方法)
*/
@Component
//设置切面类
@Aspect
public class LogUtil {
//配置通用的切入点表达式
@Pointcut("execution(* com.itheima.service..*.*(..))")
public void pointcut(){}
//配置 前置增强: 执行实现 在切入点方法执行前执行
//@Before("pointcut()")
public void beforePrintLog(JoinPoint joinPoint) throws Throwable {
//获取切入点方法的参数
Object[] args = joinPoint.getArgs();
System.out.println("前置增强beforePrintLog = " + Arrays.toString(args));
}
//配置 后置增强: 执行时机 在切入点方法执行完毕后, 执行; 若切入点方法执行过程中产生了异常, 后置增强不执行
//@AfterReturning("pointcut()")
public void afterReturningPrintLog(JoinPoint joinPoint) throws Throwable {
System.out.println("后置增强afterReturningPrintLog = ");
}
//配置 异常增强: 执行时机 在切入点方法执行过程中 抛出了异常
//@AfterThrowing("pointcut()")
public void afterThrowingPrintLog(JoinPoint joinPoint) throws Throwable {
System.out.println("异常增强:afterThrowingPrintLog = ");
}
//配置 最终增强: 执行时机 在切入点方法执行完毕后, 执行(无论是否有异常, 最终增强都执行)
//@After("pointcut()")
public void afterPrintLog(JoinPoint joinPoint) throws Throwable {
System.out.println("最终增强:afterPrintLog");
}
//配置 环绕通知: 包含了(前置增强, 后置增强, 异常增强, 最终增强)
@Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
Object rtValue = null;
try {
//配置 前置增强:
System.out.println("前置增强");
//获取方法执行时, 所需的实际参数
Object[] args = joinPoint.getArgs();
//原有的方法执行
rtValue = joinPoint.proceed(args);
//配置 后置增强:
System.out.println("后置增强");
} catch (Throwable throwable) {
throwable.printStackTrace();
//配置 异常增强:
System.out.println("异常增强");
} finally {
//配置 最终增强:
System.out.println("最终增强");
}
return rtValue;
}
}
4.2 纯注解的形式
创建Spring配置文件对应的java类:类上声明@Configuration
1 开启SpringIOC的注解扫描
@ComponentScan
2 开启SpringAOC的注解扫描
@EnableAspectJAutoProxy
/**
* Spring的核心配置文件
*/
@Configuration
//开启SpringIOC的注解扫描
@ComponentScan({"com.itheima"})
//开启SPringAOP的注解扫描
@EnableAspectJAutoProxy
public class SpringConfig {
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {SpringConfig.class})
public class AccountServiceTest {
@Autowired
private AccountService accountService;
@Test
public void save() {
accountService.save();
}
@Test
public void update() {
accountService.update(123);
}
@Test
public void delete() {
accountService.delete();
}
}