我的理解AOP:就是代码执行的时候,他切入一个点,让代码进入你写好的代码去执行,然后在执行切点代码后面或者里面的代码。
OOP是面向对象开发,有逻辑对象抽象,功能高度独立,都是已一个类或者方法的形式实现逻辑,但是耦合性太高,修改一个地方,几乎所有使用的地方都需要修改。
AOP:是面向切面编程,所谓的切面就是一个程序插入的入口,正如上面所言,在这个切面我做的操作都在一个地方实现,当所有的地方执行到这个切面时候,统一的逻辑到一个地方去执行,那么就解决了,以后修改代码的成本。其实也是spring DI和AOP2个最重要的内容。
开始在Android上运用了。
AOP 可以做很多东西,比如日志管理
现在就做一个日志的例子。
1.首先在app.gradle 里面 在整个文件的最外层 添加如下代码
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;
}
}
}
}
2.在build.gradle里面添加一个aspectj的插件
dependencies {
classpath'org.aspectj:aspectjtools:1.8.9'
}
需求是这个的我们通过注解的方式在调用一个方法时候就注解下,我们调用了这个方法,当然你也可以在实现这个注解的方法里面干你想干的事情。
比如我们在随便的一个页面上调用这个initListeners方法时候注解下。
@BehaviorTrace(value ="dddd")
@Override
protected void initListeners() {
}
我们开始写代码
我们先写一个注解类
@Target(ElementType.METHOD)//注解应用在哪里
@Retention(RetentionPolicy.RUNTIME)//运行在哪里
// public @interface 注解名称{}
public @interface BehaviorTrace {
String value();
}
这个@@interface 是自定义注解的意思
value是我们自定义的属性。
最终我们注解的样子是这样使用的
@BehaviorTrace(value ="xxxx")
写完注解类后然后就是切面去切入这个注解类了
我们写一个BeHaviorAspect 类,注意必须在类上面@Aspect 这个意思是标注这个类是一个切面使用的类,在Android编译时候,他会去匹配那些地方使用了切面,然后动态的生成代码放入切入的代码地方。
@Aspect
public class BeHaviorAspect {
}
然后我们在这个类里面写入代码前,我们讲究几个概念
JPoint:代码可注入的点,比如一个方法的调用处或者方法内部、“读、写”变量等。
Pointcut:用来描述 JPoint 注入点的一段表达式,比如:调用 BehaviorTrace 类 fly 方法的地方,call(* BehaviorTrace.fly(..))。
Advice:常见的有 Before、After、Around 等,表示代码执行前、执行后、替换目标代码,也就是在 Pointcut 何处注入代码。
Aspect:Pointcut 和 Advice 合在一起称作 Aspect。
我们写一个切入的方法
//切点 标签注释 这个类的 所有方法(所有参数)
/**executeion 执行在方法的内部 call执行在方法的外部 这个是注解的方式实现
* call 的调用方法的外面执行
* execution是在调用的方法内部执行
* 这个是直接在调用一个类的方法时候切入 其实和注解方式的本质是一样的
**/
@Pointcut("execution(@com.yunsoft.shaoshupai.aop.BehaviorTrace * *(..))")
public void AnnoBehavor(){
}
这个AnnoBehavor方法,相当我们声明了一个切入点,切入点是BehaviorTrace 的任何的方法调用时候 ,切入这个切点。
然后下面是处理这个切入的点的切入方法
//处理的是那个切点
// Before 是在这个点的前面
//Around 是返回的前面 或者🍯
//第二个参数是前面的切点方法名称()
@Around("AnnoBehavor()")
public Object Deal(ProceedingJoinPoint point)throws Throwable{
//取得注解类型
MethodSignature methodSignature = (MethodSignature) point.getSignature();
//取得注解
BehaviorTrace behaviorTrace = methodSignature.getMethod().getAnnotation(BehaviorTrace.class);
//取得注解内容
String contentType = behaviorTrace.value();
//在执行方法前
long beagin=System.currentTimeMillis();
//方法执行
Object object =null;
try {
object =point.proceed();
}catch (Exception e){
}
//在执行方法后
long end=System.currentTimeMillis();
Log.e(TAG,contentType+"使用时间: "+simpleDateFormat.format(end-beagin));
return object;
}
然后是前面
@Before("AnnoBehavor()")
public void excutMethod(JoinPoint joinPoint){
Log.e(TAG,"执行方法前"+joinPoint.getTarget().toString()+"#"+joinPoint.getSignature().getName());
}
如果是后面
@after("AnnoBehavor()")
public void excutMethod(JoinPoint joinPoint){
Log.e(TAG,"执行方法前"+joinPoint.getTarget().toString()+"#"+joinPoint.getSignature().getName());
}
在定义切点时候你也可以这样定义
/**
* 在LoginSelectActivity的initViews调用的切点 可以是任意的方法
* call 的调用方法的外面执行
* execution是在调用的方法内部执行
* 这个是直接在调用一个类的方法时候切入 其实和注解方式的本质是一样的
*/
@Pointcut("call(* xxxx.LoginSelectActivity.initViews(..))")
public void initViews(){
}
网友评论