1、定义
Aspect Oriented Programming(AOP),面向切面编程,是一个比较热门的话题。AOP主要实现的目的是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。比如我们最常见的就是日志记录了,举个例子,我们现在提供一个查询学生信息的服务,但是我们希望记录有谁进行了这个查询。如果按照传统的OOP的实现的话,那我们实现了一个查询学生信息的服务接口(StudentInfoService)和其实现 类 (StudentInfoServiceImpl.java),同时为了要进行记录的话,那我们在实现类(StudentInfoServiceImpl.java)中要添加其实现记录的过程。这样的话,假如我们要实现的服务有多个呢?那就要在每个实现的类都添加这些记录过程。这样做的话就会有点繁琐,而且每个实现类都与记录服务日志的行为紧耦合,违反了面向对象的规则。那么怎样才能把记录服务的行为与业务处理过程中分离出来呢?看起来好像就是查询学生的服务自己在进行,但却是背后日志记录对这些行为进行记录,并且查询学生的服务不知道存在这些记录过程,这就是我们要讨论AOP的目的所在。AOP的编程,好像就是把我们在某个方面的功能提出来与一批对象进行隔离,这样与一批对象之间降低了耦合性,可以就某个功能进行编程。
--以上概念摘抄自百度百科,方便自己学习--
-----------------------------------------------------------------------------------
那么AOP大概都是解决哪些的问题的呢?我认为就是在很多已知完整的功能逻辑代码(方法)前或者后需要添加额外的相同逻辑的代码,这个时候用AOP比较合适,比如说我需要统计一个APP里大概二十多个功能,哪些功能用户点击率比较高。再或者我需要统计APP里所有涉及A、B类型操作的操作时间,那么我是不是需要在A或者B功能前开始计时,功能代码结束后去统计时长。再或者我们写安卓,有些功能需要用户登录才能进入,如果没有登录,那么就需要跳转到登录界面。
再说一遍,其实就是
在很多已知完整的功能逻辑代码(方法)前或者后需要添加额外的相同逻辑的代码
配置
build.gradle
//这是根目录需要添加的
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'org.aspectj:aspectjtools:1.8.9'
classpath 'org.aspectj:aspectjweaver:1.8.9'
}
}
app build.gradle
import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main
final def log = project.logger
final def variants = project.android.applicationVariants
variants.all { variant ->
if (!variant.buildType.isDebuggable()) {
log.debug("Skipping non-debuggable build type '${variant.buildType.name}'.")
return;
}
JavaCompile javaCompile = variant.javaCompile
javaCompile.doLast {
String[] args = ["-showWeaveInfo",
"-1.8",
"-inpath", javaCompile.destinationDir.toString(),
"-aspectpath", javaCompile.classpath.asPath,
"-d", javaCompile.destinationDir.toString(),
"-classpath", javaCompile.classpath.asPath,
"-bootclasspath", project.android.bootClasspath.join(File.pathSeparator)]
log.debug "ajc args: " + Arrays.toString(args)
MessageHandler handler = new MessageHandler(true);
new Main().run(args, handler);
for (IMessage message : handler.getMessages(null, true)) {
switch (message.getKind()) {
case IMessage.ABORT:
case IMessage.ERROR:
case IMessage.FAIL:
log.error message.message, message.thrown
break;
case IMessage.WARNING:
log.warn message.message, message.thrown
break;
case IMessage.INFO:
log.info message.message, message.thrown
break;
case IMessage.DEBUG:
log.debug message.message, message.thrown
break;
}
}
}
}
dependencies {
...
implementation 'org.aspectj:aspectjrt:1.8.9'
...
}
自己再举个例子
比如说我要统计一个APP,用户在哪些界面操作比较多,在APP的首页上有A功能、B功能、C功能。是不是我点击其中任何一个功能要记录一下,一般记录的代码就放在方法开头,类似于
//A功能
public void A(){
Log.e("log","记录用户操作,点击了首页");
Log.e("log","A功能逻辑");
}
//B功能
public void B(){
Log.e("log","记录用户操作,点击了首页");
Log.e("log","B功能逻辑");
}
//C功能
public void C(){
Log.e("log","记录用户操作,点击了首页");
Log.e("log","C功能逻辑");
}
也就是在每个需要统计的地方都写一遍记录用户操作的代码,如果整个工程都这样写是不是有点头疼呢,那么我们下面用AOP的思想来解决这个问题。
首先我们新建文件,进行自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface IAspect {
String value() default "";
}
对这段代码疑惑的同学建议看看这位大神
关于注解的讲解
那我们怎么去理解这个 IAspect 的作用呢,想必童鞋你肯定看过火影,四代火影波风水门有一个瞬间移动的忍术,就是他会在一个地方做一个标记,然后无论在哪里可以瞬间移动到这个地方,人称“黄色闪光”,那么这里的IAspect ,你就可以看成是水门的查克拉,可以做标记。
水门在战斗的时候,是不是在接触敌人的时候,会把自己的查克拉标记出去
这个时候敌人就是功能 A、B、C,那么标记后就成了
//A功能
@IAspect //(切面)
public void A(){
Log.e("log","记录用户操作,点击了首页");
Log.e("log","A功能逻辑");
}
//B功能
@IAspect //(切面)
public void B(){
Log.e("log","记录用户操作,点击了首页");
Log.e("log","B功能逻辑");
}
//C功能
@IAspect //(切面)
public void C(){
Log.e("log","记录用户操作,点击了首页");
Log.e("log","C功能逻辑");
}
水门在战斗的时候是不是会先在一个地方把螺旋丸准备好,然后瞬移到目标地点进行攻击,就好比我们先把记录的代码写好,然后统一传送到功能代码那里去,下面代码就相当于螺旋丸吧
@Aspect
public class DemoAspect {
//com.potevio.aopapplication.IAspect : IAspect 的路径
//* * 表示匹配任意方法
//(..) 表示匹配任意参数
@Pointcut("execution(@com.potevio.aopapplication.IAspect * *(..))")
public void point(){
}
@Around("point()")
public void arount(ProceedingJoinPoint joinPoint) throws Throwable {
Log.e("log","记录用户操作,点击了首页");
joinPoint.proceed();
}
}
point(),你把这个方法看作一个点,带有@IAspect的方法看作一个点,比如A()方法,@Around("point()")看字面意思表示point()方法执行前后,其实就是A()执行前后
也就是用户代码执行到A(),发现了我自定义的@IAspect,然后到@Pointcut("execution(@com.potevio.aopapplication.IAspect * *(..))"),得知切点是point()方法,然后发现了 @Around("point()"),开始执行,上面代码首先执行的是Log.e("log","记录用户操作,点击了首页");这个是一个公共的操作,joinPoint.proceed();就是A()方法的执行,大概就是这个样子。
其实不止有@Around,还有
@Before("point()")
public void before(JoinPoint point) {
}
@Around("point()")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
}
@After("point()")
public void after(JoinPoint point) {
System.out.println("@After");
}
@AfterReturning("point()")
public void afterReturning(JoinPoint point, Object returnValue) {
}
@AfterThrowing(value = "point()", throwing = "ex")
public void afterThrowing(Throwable ex) {
}
我改一下需求,我要统计首页具体每个功能的使用频率,同时记录每个功能操作需要的时间,我直接给代码
// *********** IAspect 文件 ***********
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface IAspect {
String value() default "";
int type() default 0;
}
// *********** MainActivity 文件 ***********
@IAspect(value = "A功能",type = 1)
public void A(){
Log.e("log","A功能逻辑");
}
//B功能
@IAspect(value = "B功能",type = 1)
public void B(){
Log.e("log","B功能逻辑");
}
//C功能
@IAspect(value = "C功能",type = 1)
public void C(){
Log.e("log","C功能逻辑");
}
// *********** DemoAspect 文件 ***********
@Aspect
public class DemoAspect {
@Pointcut("execution(@com.potevio.aopapplication.IAspect * *(..))")
public void point(){
}
@Around("point()")
public void arount(ProceedingJoinPoint joinPoint) throws Throwable {
long beginTime = SystemClock.currentThreadTimeMillis();
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
IAspect iAspect = method.getAnnotation(IAspect.class);
String value = iAspect.value();
int type = iAspect.type();
joinPoint.proceed();//执行功能逻辑 代码
long endTime = SystemClock.currentThreadTimeMillis();
long dx = endTime - beginTime;
if(type == 1){
Log.e("qwer","首页" + value + "记录一次,操作了时间 : " + dx);
}
}
@Before("point()")
public void before(JoinPoint point) {
}
@After("point()")
public void after(JoinPoint point) {
}
@AfterReturning("point()")
public void afterReturning(JoinPoint point, Object returnValue) {
}
@AfterThrowing(value = "point()", throwing = "ex")
public void afterThrowing(Throwable ex) {
}
}
版权声明:个人原创,若转载,请注明出处
网友评论