AOP实现原理
Spring AOP 基于动态代理实现原理:
○ 如果被代理的对象,已经实现某个接口,则 Spring AOP 会使用 JDK Proxy(反射),基于接口的方式,创建代理对象(JDK动态代理的核心是InvocationHandler接口和Proxy类);
○ 如果被代理的对象,没有实现某个接口,就无法使用 JDK Proxy 去进行代理了,这时候 Spring AOP 会使用 Cglib,基于继承的方式,生成一个被代理对象的子类来作为代理(Cglib动态代理的核心是MethodInterceptor接口和Enhancer类);
AOP有关知识点(通知+aspect+pointcut)
AOP通知类型:
AOP将抽取出来的共性功能称为通知;通知类型:以通知在上下文中的具体位置作为划分
前置通知(Before)
后置通知(After)
返回通知(After-returning)
异常通知(After-throwing)
环绕通知(Around)
AOP重要的知识点。
AOP切面:切点+通知
SpringAOP+AspectJ实现步骤:
1.坐标
2.配置<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.28</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
</dependencies>AspectJ 切面:可以理解为要注入方法的所有通知
切点:到哪一个类的哪一个方法
AOP实现流程
1,正常的制作程序
以上一篇的转账来说,事务管理注入到service的每一个方法当中。
2,将非共性功能开发到对应的目标对象类中,并制作成切入点方法,绿色的都可以当切入点
@Override
public void save(String name) {System.out.println("===>业务新增");
int a = 10/0;//模拟异常
}@Override
public int delete(int num) {
System.out.println("===>业务删除");
return 0;
}@Override
public void findAll() {
System.out.println("===>业务查询");
}
3,将共性功能独立开发出来,制作成“通知”
事务的开启到AOP前置通知的地方
事务的提交可以放到AOP的返回通知的地方。
事务失败可以放到AOP的异常通知的地方。
事务关闭可以放到AOP的后置通知的地方。finally必须要执行的那个模块。
package com.apesource.util;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import java.util.Date;
/**
* @author
* @version 1.0
* @since 2023/8/4
*/
public class LoggerUtil {
//前置通知
public void beforeMethod(){
System.out.println("前置通知=====>"+new Date());
}
//返回通知
public void afterRrturnMethod(){
System.out.println("返回通知=====>"+new Date());
}
//异常通知
public void throwMethod(){
System.out.println("异常通知=====>"+new Date());
}
//后置通知
public void afterMethod(){
System.out.println("后置通知=====>"+new Date());
}
//环绕通知
public Object arroundMethod(ProceedingJoinPoint pjp){
Object obj = null;
try {
//环绕通知---前置通知
System.out.println("环绕通知---前置通知");
Object[] objs = pjp.getArgs();
obj = pjp.proceed(objs);
//环绕通知---返回通知
System.out.println("环绕通知---返回通知");
} catch (Throwable throwable) {
//环绕通知---异常通知
System.out.println("环绕通知---异常通知");
throwable.printStackTrace();
} finally {
//环绕通知---后置通知
System.out.println("环绕通知---后置通知");
}
return obj;
}
}
4.在配置文件中,声明“切入点”,该步骤可以让Spring定位到实体类的方法
切点表达式配置语法:
execution(修饰符 返回值 包名称.类名称.方法名称(参数列表))
eg:
execution(public void com.apesource.service.ServiceImp.findAll())
1.修饰符可以省略代表任意
execution(返回值 包名称.类名称.方法名称(参数列表))
2.返回值可以使用“*”代表任意
execution(* 包名称.类名称.方法名称(参数列表))
3.包名可以使用“*”代表任意名称
execution(* *.*.*.类名称.方法名称(参数列表))
4.包名可以使用“..”代表任意个数
execution(* *...类名称.方法名称(参数列表))
5.类名与方法名可以使用“*”代表任意
execution(* *...*.*(参数列表))
6.参数列表可以使用".."代表任意个数任意类型
execution(* *...*.*(..))
如果有参数
int======>int
String===>java.lang.String
@Pointcut(value = "execution(* com.apesource.service.*.*(..))")
public void dian(){
}
修饰符为任意,返回值任意, com.apesource.service下的所有类的所有方法。
5.在配置文件中,声明"切入点"与"通知"间的关系(含通知类型),即"切面"
@Component
@Aspect
public class LoggerUtil {
@Pointcut(value = "execution(* com.apesource.service.*.*(..))")
public void dian(){
}
//前置通知
@Before("dian()")
public void beforeMethod(){
System.out.println("前置通知=====>"+new Date());
}
//返回通知
@AfterReturning("dian()")
public void afterRrturnMethod(){
System.out.println("返回通知=====>"+new Date());
}
//异常通知
@AfterThrowing("dian()")
public void throwMethod(){
System.out.println("异常通知=====>"+new Date());
}
//后置通知
@After("dian()")
public void afterMethod(){
System.out.println("后置通知=====>"+new Date());
}//环绕通知
@Around("dian()")
public Object arroundMethod(ProceedingJoinPoint pjp){Object obj = null;
try {
//环绕通知---前置通知
System.out.println("环绕通知---前置通知");
Object[] objs = pjp.getArgs();
obj = pjp.proceed(objs);//proceed等于invoke方法
//环绕通知---返回通知
System.out.println("环绕通知---返回通知");
} catch (Throwable throwable) {
//环绕通知---异常通知
System.out.println("环绕通知---异常通知");
throwable.printStackTrace();
} finally {
//环绕通知---后置通知
System.out.println("环绕通知---后置通知");
}return obj;
}
●运行阶段(AOP完成)=============spring帮我们做的
◆Spring容器加载配置文件,监控所有配置的“切入点”方法的执行
◆当监控到“切入点”方法被运行,使用“代理”机制,动态创建“目标对象”的“代理对象”,
根据“通知类别”,在“代理对象”的对应位置将“通知”对应的功能“织入”,完成完整的代码逻辑并运行
<context:component-scan base-package="com.apesource"></context:component-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<!--开启aop配置xml版本--> <!-- <aop:config>--> <!-- <aop:aspect id="mian" ref="loggerUtil">--> <!-- <aop:pointcut id="dian" expression="execution(* com.apesource.service.*.*(..))"/>--> <!-- <aop:before method="beforeMethod" pointcut-ref="dian"></aop:before> <!-- <aop:after-returning method="afterRrturnMethod" pointcut-ref="dian"></aop:after-returning> <!-- <aop:after-throwing method="throwMethod" pointcut-ref="dian"></aop:after-throwing>--> <!-- <aop:after method="afterMethod" pointcut-ref="dian"></aop:after>–>--> <!-- <aop:around method="arroundMethod" pointcut-ref="dian"></aop:around>--> <!-- </aop:aspect>--> <!-- </aop:config>--> <!--开启aop配置注解版--> <context:component-scan base-package="com.apesource"></context:component-scan> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
test测试类。
package com.apesource.test;
import com.apesource.service.IService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author
* @version 1.0
* @since 2023/8/4
*/
public class Test01 {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
IService service = (IService) applicationContext.getBean("serviceImp");
service.save("今天周五");
// service.delete(5);
// service.findAll();
}
}