AOP相关的概念
- Aspect :切面,切入系统的一个切面。比如事务管理是一个切面,权限管理也是一个切面;
- Join point :连接点,也就是可以进行横向切入的位置;
- Advice :通知,切面在某个连接点执行的操作(分为: Before advice , After returning advice , After throwing advice , After (finally) advice , Around advice );
- Pointcut :切点,符合切点表达式的连接点,也就是真正被切入的地方;
AOP分为静态AOP和动态AOP。
静态AOP是指AspectJ实现的AOP,他是将切面代码直接编译到Java类文件中。
动态AOP是指将切面代码进行动态织入实现的AOP。如Spring AOP,实现的技术为: JDK提供的动态代理技术 和 CGLIB(动态字节码增强技术)
Android平台AOP
在Android平台最常用的三剑客:APT、AspectJ、Javassist
APT
APT针对的是编译时注解
首先,APT(Annotation Processing Tool)是javac提供的一种工具,它在编译时扫描、解析、处理注解。它会对源代码文件进行检测,找出用户自定义的注解,根据注解、注解处理器和相应的apt工具自动生成代码。这段代码是根据用户编写的注解处理逻辑去生成的。最终将生成的新的源文件与原来的源文件共同编译(注意:APT并不能对源文件进行修改操作,只能生成新的文件,例如往原来的类中添加方法)。代表框架:DataBinding,Dagger2, ButterKnife, EventBus3
流程概要
1、创建自定义注解@interface;
2、创建并注册注解处理器AbstractProcessor,生成处理注解逻辑的.java文件;
3、封装一个供外部调用的API,用的是反射技术,具体来说就是调用第二步中生成的代码中的方法;
4、在项目中使用,比如Activity、Fragment、Adapter
实现步骤
1.创建自定义注解
创建一个Java Library,名称为annotation,作用是保存所有注解。
@Retention(RetentionPolicy.CLASS) //注解生命周期是编译期,存活于.class文件,当jvm加载class时就不在了
@Target(ElementType.FIELD) //目标对象是变量
public @interface BindView {
/**
* @return 控件变量的resourceId
*/
int value();
}
2.实现注解处理器AbstractProcessor
创建一个Java Library,名称为processor,作用是扫描、解析、处理注解。
processor的Gradle配置如下:
apply plugin: 'java-library'
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation project(':annotation')
//用于自动为 JAVA Processor 生成 META-INF 信息。
implementation 'com.google.auto.service:auto-service:1.0-rc3'
//快速生成.java文件的库
implementation 'com.squareup:javapoet:1.8.0'
}
创建自己的Processor来实现注解处理器
@AutoService(Processor.class)
public class ButterKnifeProcessor extends AbstractProcessor {
/**
* 生成文件的工具类
*/
private Filer filer;
/**
* 打印信息
*/
private Messager messager;
/**
* 元素相关
*/
private Elements elementUtils;
private Types typeUtils;
private Map<String, ProxyInfo> proxyInfoMap = new HashMap<>();
/**
* 一些初始化操作,获取一些有用的系统工具类
*
* @param processingEnv
*/
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
filer = processingEnv.getFiler();
messager = processingEnv.getMessager();
elementUtils = processingEnv.getElementUtils();
typeUtils = processingEnv.getTypeUtils();
}
/**
* 设置支持的版本
*
* @return 这里用最新的就好
*/
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
/**
* 设置支持的注解类型
*
* @return
*/
@Override
public Set<String> getSupportedAnnotationTypes() {
//添加支持的注解
HashSet<String> set = new HashSet<>();
set.add(BindView.class.getCanonicalName());
return set;
}
/**
* 注解内部逻辑的实现
* <p>
* Element代表程序的一个元素,可以是package, class, interface, method.只在编译期存在
* TypeElement:变量;TypeElement:类或者接口
*
* @param annotations
* @param roundEnv
* @return
*/
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
messager.printMessage(Diagnostic.Kind.NOTE, "annotations size--->" + annotations.size());
//1、获取要处理的注解的元素的集合
Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(BindView.class);
//process()方法会调用3次,只有第一次有效,第2,3次调用的话生成.java文件会发生异常
if (elements == null || elements.size() < 1) {
return true;
}
//2、按类来划分注解元素,因为每个使用注解的类都会生成相应的代理类
for (Element element : elements) {
checkAnnotationValid(element, BindView.class);
//获取被注解的成员变量
//这里被注解的类型只能是变量,所以可以直接强转
VariableElement variableElement = (VariableElement) element;
//获取该元素的父元素,这里是父类
TypeElement typeElement = (TypeElement) variableElement.getEnclosingElement();
//获取全类名
String className = typeElement.getQualifiedName().toString();
//获取被注解元素的包名
String packageName = elementUtils.getPackageOf(element).getQualifiedName().toString();
//获取注解的参数
int resourceId = element.getAnnotation(BindView.class).value();
//生成ProxyInfo对象
//一个类里面的注解都在一个ProxyInfo中处理
ProxyInfo proxyInfo = proxyInfoMap.get(className);
if (proxyInfo == null) {
proxyInfo = new ProxyInfo(typeElement, packageName);
proxyInfoMap.put(className, proxyInfo);
}
proxyInfo.viewVariableElement.put(resourceId, variableElement);
}
//3、生成注解逻辑处理类
for (String key : proxyInfoMap.keySet()) {
ProxyInfo proxyInfo = proxyInfoMap.get(key);
JavaFile javaFile = JavaFile.builder(proxyInfo.packageName, proxyInfo.generateProxyClass())
.addFileComment("auto generateProxyClass code,can not modify")
.build();
try {
javaFile.writeTo(filer);
} catch (IOException e) {
e.printStackTrace();
}
}
return true;
}
//通过javapoet生成注解逻辑代码的具体步骤如下:
public TypeSpec generateProxyClass() {
//代理类实现的接口
ClassName viewInjector = ClassName.get("com.zx.inject_api", "IViewInjector");
//原始的注解类
ClassName className = ClassName.get(typeElement);
// 泛型接口,implements IViewInjector<MainActivity>
ParameterizedTypeName parameterizedTypeName = ParameterizedTypeName.get(viewInjector, className);
//生成接口的实现方法inject()
MethodSpec.Builder bindBuilder = MethodSpec.methodBuilder("inject")
.addModifiers(Modifier.PUBLIC)
.addAnnotation(Override.class) //添加方法注解
.addParameter(className, "target")
.addParameter(Object.class, "source");
for (int id : viewVariableElement.keySet()) {
VariableElement element = viewVariableElement.get(id);
String fieldName = element.getSimpleName().toString();
bindBuilder.addStatement(" if (source instanceof android.app.Activity){target.$L = ((android.app.Activity) source).findViewById( $L);}" +
"else{target.$L = ((android.view.View)source).findViewById($L);}", fieldName, id, fieldName, id);
}
MethodSpec bindMethodSpec = bindBuilder.build();
//创建类
TypeSpec typeSpec = TypeSpec.classBuilder(proxyClassName)
.addModifiers(Modifier.PUBLIC)
.addSuperinterface(parameterizedTypeName) //实现接口
.addMethod(bindMethodSpec)
.build();
return typeSpec;
}
3.封装一个供外部调用的API
public interface IViewInjector<T> {
/**
* 通过source.findViewById()
*
* @param target 泛型参数,调用类 activity、fragment等
* @param source Activity、View
*/
void inject(T target, Object source);
}
/**
* 根据使用注解的类和约定的命名规则,通过反射找到动态生成的代理类(处理注解逻辑)
* @param object 调用类对象
*/
private static IViewInjector findProxyActivity(Object object) {
String proxyClassName = object.getClass().getName() + PROXY;
Log.e(TAG, "findProxyActivity: "+proxyClassName);
Class<?> proxyClass = null;
try {
proxyClass = Class.forName(proxyClassName);
// Constructor<?> constructor = proxyClass.getConstructor(object.getClass());
return (IViewInjector) proxyClass.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* Activity调用
*/
public static void bind(Activity activity) {
findProxyActivity(activity).inject(activity, activity);
}
/**
* Fragment、Adapter调用
*
* @param object
* @param view
*/
public static void bind(Object object, View view) {
findProxyActivity(object).inject(object, view);
}
4 在Activity、Fragment、Adapter等使用
implementation project(':annotation')
implementation project(':inject_api')
//gradle3.0以上apt的实现方式
annotationProcessor project(':processor')
Q1:注解处理器processor为什么要在META-INF注册?
在编译时,java编译器(javac)会去META-INF中查找实现了的AbstractProcessor的子类,并且调用该类的process函数,最终生成.java文件。其实就像activity需要注册一样,就是要到META-INF注册 ,javac才知道要给你调用哪个类来处理注解。
Q2:注解处理器processor是如何被系统调用的?
在完成注解处理类Processor之后,需要做2件事情:
1、在META-INF目录下注册Processor;
2、在项目中使用注解的地方添加apt工具annotationProcessor
Q3:注解申明和注解处理器为什么要分Module处理?
我们都知道processor的作用是:在编译器解析注解、生成新的.java文件。这个lib只在编译器用到,是不会被打包进apk的。对于调用者来说,你只是想使用这个注解,而不希望你已经编译好的项目中引进注解处理器相关的内容,所以为了不引入没必要的文件,我们一般选择将注解声明和注解处理分开处理。
AspectJ
AspectJ的核心是ajc编译器 (aspectjtools)和织入器 (aspectjweaver)。
ajc编译器:
基于Java编译器之上的,它是用来编译.aj文件,aspectj在Java编译器的基础上增加了一些它自己的关键字和方法。因此,ajc也可以编译Java代码。
weaver织入器:
它的工作原理是:通过Gradle Transform API,在class文件生成后至dex文件生成前,遍历并匹配所有符合AspectJ文件中声明的切点,然后将Aspect的代码织入到目标.class。织入代码后的新.class会加入多个JoinPoint,这个JoinPoint会建立目标.class与Aspect代码的连接,比如获得执行的对象、方法、参数等。
Java动态代理
动态代理是指在运行时动态生成代理类。即,代理类的字节码将在运行时生成并载入当前代理的 ClassLoader。与静态处理类相比,动态类有诸多好处。
主要使用到 InvocationHandler 接口和 Proxy.newProxyInstance() 方法。
①不需要写一个形式上完全一样的封装类;
②使用一些动态代理的生成方法甚至可以在运行时制定代理类的执行逻辑,从而大大提升系统的灵活性。
java.lang.reflect.Proxy:这是生成代理类的主类,通过 Proxy 类生成的代理类都继承了 Proxy 类。
Proxy提供了用户创建动态代理类和代理对象的静态方法,它是所有动态代理类的父类。
java.lang.reflect.InvocationHandler:这里称他为"调用处理器",它是一个接口。当调用动态代理类中的方法时,将会直接转接到执行自定义的InvocationHandler中的invoke()方法。即我们动态生成的代理类需要完成的具体内容需要自己定义一个类,而这个类必须实现 InvocationHandler 接口,通过重写invoke()方法来执行具体内容。
Proxy提供了如下两个方法来创建动态代理类和动态代理实例。
static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) 返回代理类的java.lang.Class对象。第一个参数是类加载器对象(即哪个类加载器来加载这个代理类到 JVM 的方法区),第二个参数是接口(表明你这个代理类需要实现哪些接口),第三个参数是调用处理器类实例(指定代理类中具体要干什么),该代理类将实现interfaces所指定的所有接口,执行代理对象的每个方法时都会被替换执行InvocationHandler对象的invoke方法。
**static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) **返回代理类实例。参数与上述方法一致。
public class ProxyDemo {
public static void main(String args[]){
RealSubject subject = new RealSubject();
Proxy p = new Proxy(subject);
p.request();
}
}
interface Subject{
void request();
}
class RealSubject implements Subject{
public void request(){
System.out.println("request");
}
}
class Proxy implements Subject{
private Subject subject;
public Proxy(Subject subject){
this.subject = subject;
}
public void request(){
System.out.println("PreProcess");
subject.request();
System.out.println("PostProcess");
}
}
对应上述两种方法创建动态代理对象的方式:
//创建一个InvocationHandler对象
InvocationHandler handler = new MyInvocationHandler(.args..);
//使用Proxy生成一个动态代理类
Class proxyClass = Proxy.getProxyClass(RealSubject.class.getClassLoader(),RealSubject.class.getInterfaces(), handler);
//获取proxyClass类中一个带InvocationHandler参数的构造器
Constructor constructor = proxyClass.getConstructor(InvocationHandler.class);
//调用constructor的newInstance方法来创建动态实例
RealSubject real = (RealSubject)constructor.newInstance(handler);
//创建一个InvocationHandler对象
InvocationHandler handler = new MyInvocationHandler(.args..);
//使用Proxy直接生成一个动态代理对象
RealSubject real =Proxy.newProxyInstance(RealSubject.class.getClassLoader(),RealSubject.class.getInterfaces(), handler);
newProxyInstance这个方法实际上做了两件事:第一,创建了一个新的类【代理类】,这个类实现了Class[] interfaces中的所有接口,并通过你指定的ClassLoader将生成的类的字节码加载到JVM中,创建Class对象;第二,以你传入的InvocationHandler作为参数创建一个代理类的实例并返回。
动态代理模式的简单实现
public class DynamicProxyDemo {
public static void main(String[] args) {
//1.创建目标对象
RealSubject realSubject = new RealSubject();
//2.创建调用处理器对象
ProxyHandler handler = new ProxyHandler(realSubject);
//3.动态生成代理对象
Subject proxySubject = (Subject)Proxy.newProxyInstance(RealSubject.class.getClassLoader(),
RealSubject.class.getInterfaces(), handler);
//4.通过代理对象调用方法
proxySubject.request();
}
}
/**
* 主题接口
*/
interface Subject{
void request();
}
/**
* 目标对象类
*/
class RealSubject implements Subject{
public void request(){
System.out.println("====RealSubject Request====");
}
}
/**
* 代理类的调用处理器
*/
class ProxyHandler implements InvocationHandler{
private Subject subject;
public ProxyHandler(Subject subject){
this.subject = subject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//定义预处理的工作,当然你也可以根据 method 的不同进行不同的预处理工作
System.out.println("====before====");
//调用RealSubject中的方法
Object result = method.invoke(subject, args);
System.out.println("====after====");
return result;
}
}
Retrofit 分析
动态代理,让Retrofit将我们的接口变为具体的类
public <T> T create(final Class<T> service) {
//做一些安全判断,判断是否为接口,不是接口java原生的动态代理会报错
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
//create其实就是通过Java的动态代理将接口中定义的方法转给了InvocationHandler的invoke方法
//在通过loadServiceMethod调用invoke,来发起网络请求(下篇文章会讲)
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service }
, new InvocationHandler() {
private final Platform platform = Platform.get();
private final Object[] emptyArgs = new Object[0];
@Override
public @Nullable Object invoke(Object proxy, Method method,
@Nullable Object[] args) throws Throwable {
// If the method is a method from Object then defer to normal invocation.
//如果该方法是Object自带的方法,那么我们直接反射出来这些方法就可以了
//比如说是.equest() 、 toString()
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
// .isDefaultMethod是Java8的特性
//它的意思是接口中定义的方法,因为java8支持了接口定义默认方法(default)这一特性
//也就是说,这里的if是判断上述特性,如果是就直接调用
//如果不是,就使用loadServiceMethod来获取方法
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
//返回了 封装了请求参数的一个接口,它知道怎么发起网络请求
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
newProxyInstance 看看它到底是怎么将接口进行代理,生成接口的代理类
//newProxyInstance传入了ClassLoader 还有接口,我们可以想象得到
//他是通过ClassLoader来生成代理类的。
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
...
/*
* Look up or generate the designated proxy class.
具体实现类代理的方法 getProxyClass0
*/
Class<?> cl = getProxyClass0(loader, intfs);
return cons.newInstance(new Object[]{h});
...
}
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
return proxyClassCache.get(loader, interfaces);
}
...
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>{
// prefix for all proxy class names
private static final String proxyClassNamePrefix = "$Proxy";
// next number to use for generation of unique proxy class names
private static final AtomicLong nextUniqueNumber = new AtomicLong();
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) {
/*
* Verify that the class loader resolves the name of this
* interface to the same Class object.
*/
Class<?> interfaceClass = null;
try {
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}
/*
* Verify that the Class object actually represents an
* interface.
*/
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
/*
* Verify that this interface is not a duplicate.
*/
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
String proxyPkg = null; // package to define proxy class in
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
/*
* Record the package of a non-public proxy interface so that the
* proxy class will be defined in the same package. Verify that
* all non-public proxy interfaces are in the same package.
*/
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL;
String name = intf.getName();
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}
if (proxyPkg == null) {
// if no non-public proxy interfaces, use the default package.
proxyPkg = "";
}
{
// Android-changed: Generate the proxy directly instead of calling
// through to ProxyGenerator.
List<Method> methods = getMethods(interfaces);
Collections.sort(methods, ORDER_BY_SIGNATURE_AND_SUBTYPE);
validateReturnTypes(methods);
List<Class<?>[]> exceptions = deduplicateAndGetExceptions(methods);
Method[] methodsArray = methods.toArray(new Method[methods.size()]);
Class<?>[][] exceptionsArray = exceptions.toArray(new Class<?>[exceptions.size()][]);
/*
* Choose a name for the proxy class to generate.
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
return generateProxy(proxyName, interfaces, loader, methodsArray,
exceptionsArray);
}
}
}
generateProxy(proxyName, interfaces, loader, methodsArray, exceptionsArray)是一个native方法