浅谈Spring AOP的原理和应用
1.AOP简介
Aop(Aspect Oriented Programming)大家应该都知道这是面向切面编程思想,主要能够为我们在不影响原来的功能的前提,为软件横向拓展功能。简单来说,把对象中一些公用的行为抽取出来,减少代码冗余性,还可以将业务代码和系统功能代码分离开。以下,我对Spring AOP进行一系列探讨,(注意:Aop是一种思想,以下主要对Spring Aop技术的探讨的)
2.Aop使用的场景
1)权限验证
2)缓存
3)异常统一处理
4)日志系统
5)信息跟踪标记
6)信息校验
7)事务
以上只是简单自己的常见场景,AOP应用的方法千差万别,还有很多很多场景,具体还是看业务场景,主要判别的依据是业务代码,还是系统功能代码。我只是简单谈一下自己对Spring AOP。
3.AOP相关概念
方面(Aspect):一个关注点的模块化,这个关注点实现可能另外横切多个对象。事务管理是J2EE应用中一个很好的横切关注点例子。方面用Spring的 Advisor或拦截器实现。
连接点(Joinpoint): 程序执行过程中明确的点,如方法的调用或特定的异常被抛出。
通知(Advice): 在特定的连接点,AOP框架执行的动作。各种类型的通知包括“around”、“before”和“throws”通知。通知类型将在下面讨论。许多AOP框架包括Spring都是以拦截器做通知模型,维护一个“围绕”连接点的拦截器链。Spring中定义了四个advice: BeforeAdvice, AfterAdvice, ThrowAdvice和DynamicIntroductionAdvice
切入点(Pointcut): 指定一个通知将被引发的一系列连接点的集合。AOP框架必须允许开发者指定切入点:例如,使用正则表达式。 Spring定义了Pointcut接口,用来组合MethodMatcher和ClassFilter,可以通过名字很清楚的理解, MethodMatcher是用来检查目标类的方法是否可以被应用此通知,而ClassFilter是用来检查Pointcut是否应该应用到目标类上
引入(Introduction): 添加方法或字段到被通知的类。 Spring允许引入新的接口到任何被通知的对象。例如,你可以使用一个引入使任何对象实现 IsModified接口,来简化缓存。Spring中要使用Introduction, 可有通过DelegatingIntroductionInterceptor来实现通知,通过DefaultIntroductionAdvisor来配置Advice和代理类要实现的接口
目标对象(Target Object): 包含连接点的对象。也被称作被通知或被代理对象。POJO
AOP代理(AOP Proxy): AOP框架创建的对象,包含通知。 在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。
织入(Weaving): 组装方面来创建一个被通知对象。这可以在编译时完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。
该部分来自:
作者:蛙课网
链接:https://www.zhihu.com/question/23641679/answer/706649497
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处,侵删。
4.AOP实现基本原理
Spring提供的实现方式来生成代理对象的方式于两种:JDK proxy和Cglib。具体实现时候使用哪一种,可以配置的。默认的策略是如果目标类是接口,则使用JDK动态代理技术,否则使用Cglib来生成代理。https://www.zhihu.com/question/23641679
简单总结两者的区别:
相同点:
- 两者都是会为目标对象生成一个代理对象类。
不同点:
- 实现原理不同,JDK proxy主要是利用放射机制来实现对的,而Cglib利用ASM开源包,对代理对象class文件加载进来,对其的字节码生成子类进行处理。
- 性能上,根据查询多番资料发现Cglib更优化,详细没有自测过,但是随着jdk版本,jdk proxy性能也渐渐优化了。
- 各自局限,jdk动态代理只能代理实现了接口的类,cglib不能对final修饰的类进行代理。
所以还是要看情景来选择适合的实现方式,一般情况个人偏向选择cglib。
详细查看,总结来自:
5.AOP简单实现和应用
Spring Aop实现可以通过xml文件来实现或者利用注解方式,个人更偏向注解方式,编写方式更简单。所以以下列举一下注解实现的方式。
1.maven的依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.14.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.14.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.11</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
</dependencies>
2.创建一个简单的方法类
@Service
public class HelloWorld {
public String say(String content){
System.out.println("执行业务代码");
System.out.println("说话:"+content);
return content;
}
}
3.创建一个aop的实现类
@Aspect
@Component
public class LogAspect {
//定义切面的执行规规则
//execution(返回类型 方法名称 传入参数)
@Pointcut("execution(* com.rong.aop.aspect..*.*(..))")
public void pointCut() {
}
@Before("pointCut()")
public void logStart(JoinPoint joinPoint) {
System.out.println("------------------------------------------------");
System.out.println("方法切面开始");
//将JoinPoint相关参数输出
printlnJoinPoint(joinPoint);
System.out.println("------------------------------------------------");
}
//方法结束之后执行
@After("pointCut()")
public void logEnd() {
System.out.println("------------------------------------------------");
System.out.println("方法结束通知");
System.out.println("------------------------------------------------");
}
//抛出异常之后执行
@AfterThrowing("pointCut()")
public void logException() {
System.out.println("------------------------------------------------");
System.out.println("异常通知");
System.out.println("------------------------------------------------");
}
//方法返回之前执行,要比 @After和@AfterThrowing之后,其实 @AfterReturning实现是放在 //finally里面的
@AfterReturning(returning="obj",pointcut = "pointCut()")
public void logReturn(JoinPoint joinPoint,Object obj) {
System.out.println("------------------------------------------------");
System.out.println("返回通知:"+obj);
System.out.println("------------------------------------------------");
}
private void printlnJoinPoint(JoinPoint joinPoint){
System.out.println("目标方法名为:" + joinPoint.getSignature().getName());
System.out.println("目标方法所属类的简单类名:" + joinPoint.getSignature().getDeclaringType().getSimpleName());
System.out.println("目标方法所属类的类名:" + joinPoint.getSignature().getDeclaringTypeName());
System.out.println("目标方法声明类型:" + Modifier.toString(joinPoint.getSignature().getModifiers()));
//获取传入目标方法的参数
Object[] args = joinPoint.getArgs();
for (int i = 0; i < args.length; i++) {
System.out.println("第" + (i+1) + "个参数为:" + args[i]);
}
System.out.println("被代理的对象:" + joinPoint.getTarget());
System.out.println("代理对象自己:" + joinPoint.getThis());
}
}
@Pointcut的execution的匹配规则
基本公式:execution(返回类型 方法名称 传入参数)
常用的方式:
任意公共方法的执行:
execution(public * *(…))
任何一个以“set”开始的方法的执行:
execution(* set*(…))
HelloWorld接口的任意方法的执行:
execution(* com.rong.aop.aspect.HelloWorld.*(…))
定义在aspect包里的任意方法的执行:
execution(* com.rong.aop.aspect..(…))
定义在aspect包和所有子包里的任意类的任意方法的执行:
execution(* com.rong.aop.aspect….(…))
详细参考此资料:
4.添加一个配置类
package com.rong.aop.aspect;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
//开启扫描
@ComponentScan
//开启切面编程,开启cglib
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class MyConfigAspect {
}
5.添加一个启动类
public class AspectTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(MyConfigAspect.class);
HelloWorld helloWorld = annotationConfigApplicationContext.getBean(HelloWorld.class);
helloWorld.say("rong is coming");
}
}
6.运行结果
6.简单介绍@EnableAspectJAutoProxy
@EnableAspectJAutoProxy
该注解有两个方法:
proxyTargetClass默认为false,则是默认使用jdk动态代理,如果配置为ture,则使用cglib。
exposeProxy默认为false,则默认不暴露自己在aopContext,如果为ture,则可以将自己暴露出来,可访问。
详细可以查看:
(个人理解是自己调用自己方法,不会执行代理类之后的方法的。)
而这个注解的核心起作用是AspectJAutoProxyRegistrar
大概意思是注册一个目标类的代理类,然后去代替ioc容器中的现在的目标类,但是执行流程还是很复杂的。该类重写了注册BeanDefinition的方式。
参考来自,详细查看,很优秀的文章:
7.总结
本文只是简单介绍了Spring aop和对Spring aop简单的实现,而没有对实现的细节描述,主要是对基本的功能和理解,详细实现可以根据需求再一一查询。所以aop基本功能实现是可以的,但是觉得很遗憾是对spring的源码不了解,还需要很多的探究,因此只对EnableAspectJAutoProxy注解的浅层的了解,深入还需努力。发现错误,望各位多多指出。