AOP相關(guān)的概念
- Aspect :切面宣谈,切入系統(tǒng)的一個切面尔破。比如事務(wù)管理是一個切面,權(quán)限管理也是一個切面莺匠;
- Join point :連接點金吗,也就是可以進行橫向切入的位置;
- Advice :通知趣竣,切面在某個連接點執(zhí)行的操作(分為: Before advice , After returning advice , After throwing advice , After (finally) advice , Around advice )摇庙;
- Pointcut :切點,符合切點表達式的連接點遥缕,也就是真正被切入的地方卫袒;
AOP分為靜態(tài)AOP和動態(tài)AOP。
靜態(tài)AOP是指AspectJ實現(xiàn)的AOP单匣,他是將切面代碼直接編譯到Java類文件中夕凝。
動態(tài)AOP是指將切面代碼進行動態(tài)織入實現(xiàn)的AOP。如Spring AOP户秤,實現(xiàn)的技術(shù)為: JDK提供的動態(tài)代理技術(shù) 和 CGLIB(動態(tài)字節(jié)碼增強技術(shù))
Android平臺AOP
在Android平臺最常用的三劍客:APT码秉、AspectJ、Javassist
APT
APT針對的是編譯時注解
首先鸡号,APT(Annotation Processing Tool)是javac提供的一種工具转砖,它在編譯時掃描、解析膜蠢、處理注解堪藐。它會對源代碼文件進行檢測,找出用戶自定義的注解挑围,根據(jù)注解礁竞、注解處理器和相應的apt工具自動生成代碼。這段代碼是根據(jù)用戶編寫的注解處理邏輯去生成的杉辙。最終將生成的新的源文件與原來的源文件共同編譯(注意:APT并不能對源文件進行修改操作模捂,只能生成新的文件,例如往原來的類中添加方法)。代表框架:DataBinding,Dagger2, ButterKnife, EventBus3
流程概要
1狂男、創(chuàng)建自定義注解@interface综看;
2、創(chuàng)建并注冊注解處理器AbstractProcessor岖食,生成處理注解邏輯的.java文件红碑;
3、封裝一個供外部調(diào)用的API泡垃,用的是反射技術(shù)析珊,具體來說就是調(diào)用第二步中生成的代碼中的方法;
4蔑穴、在項目中使用忠寻,比如Activity、Fragment存和、Adapter
實現(xiàn)步驟
1.創(chuàng)建自定義注解
創(chuàng)建一個Java Library奕剃,名稱為annotation,作用是保存所有注解捐腿。
@Retention(RetentionPolicy.CLASS) //注解生命周期是編譯期纵朋,存活于.class文件,當jvm加載class時就不在了
@Target(ElementType.FIELD) //目標對象是變量
public @interface BindView {
/**
* @return 控件變量的resourceId
*/
int value();
}
2.實現(xiàn)注解處理器AbstractProcessor
創(chuàng)建一個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'
}
創(chuàng)建自己的Processor來實現(xiàn)注解處理器
@AutoService(Processor.class)
public class ButterKnifeProcessor extends AbstractProcessor {
/**
* 生成文件的工具類
*/
private Filer filer;
/**
* 打印信息
*/
private Messager messager;
/**
* 元素相關(guān)
*/
private Elements elementUtils;
private Types typeUtils;
private Map<String, ProxyInfo> proxyInfoMap = new HashMap<>();
/**
* 一些初始化操作品山,獲取一些有用的系統(tǒng)工具類
*
* @param processingEnv
*/
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
filer = processingEnv.getFiler();
messager = processingEnv.getMessager();
elementUtils = processingEnv.getElementUtils();
typeUtils = processingEnv.getTypeUtils();
}
/**
* 設(shè)置支持的版本
*
* @return 這里用最新的就好
*/
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
/**
* 設(shè)置支持的注解類型
*
* @return
*/
@Override
public Set<String> getSupportedAnnotationTypes() {
//添加支持的注解
HashSet<String> set = new HashSet<>();
set.add(BindView.class.getCanonicalName());
return set;
}
/**
* 注解內(nèi)部邏輯的實現(xiàn)
* <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()方法會調(diào)用3次扮休,只有第一次有效奴璃,第2姻成,3次調(diào)用的話生成.java文件會發(fā)生異常
if (elements == null || elements.size() < 1) {
return true;
}
//2检诗、按類來劃分注解元素暮胧,因為每個使用注解的類都會生成相應的代理類
for (Element element : elements) {
checkAnnotationValid(element, BindView.class);
//獲取被注解的成員變量
//這里被注解的類型只能是變量享完,所以可以直接強轉(zhuǎn)
VariableElement variableElement = (VariableElement) element;
//獲取該元素的父元素政供,這里是父類
TypeElement typeElement = (TypeElement) variableElement.getEnclosingElement();
//獲取全類名
String className = typeElement.getQualifiedName().toString();
//獲取被注解元素的包名
String packageName = elementUtils.getPackageOf(element).getQualifiedName().toString();
//獲取注解的參數(shù)
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() {
//代理類實現(xiàn)的接口
ClassName viewInjector = ClassName.get("com.zx.inject_api", "IViewInjector");
//原始的注解類
ClassName className = ClassName.get(typeElement);
// 泛型接口雄家,implements IViewInjector<MainActivity>
ParameterizedTypeName parameterizedTypeName = ParameterizedTypeName.get(viewInjector, className);
//生成接口的實現(xiàn)方法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();
//創(chuàng)建類
TypeSpec typeSpec = TypeSpec.classBuilder(proxyClassName)
.addModifiers(Modifier.PUBLIC)
.addSuperinterface(parameterizedTypeName) //實現(xiàn)接口
.addMethod(bindMethodSpec)
.build();
return typeSpec;
}
3.封裝一個供外部調(diào)用的API
public interface IViewInjector<T> {
/**
* 通過source.findViewById()
*
* @param target 泛型參數(shù)效诅,調(diào)用類 activity、fragment等
* @param source Activity、View
*/
void inject(T target, Object source);
}
/**
* 根據(jù)使用注解的類和約定的命名規(guī)則乱投,通過反射找到動態(tài)生成的代理類(處理注解邏輯)
* @param object 調(diào)用類對象
*/
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調(diào)用
*/
public static void bind(Activity activity) {
findProxyActivity(activity).inject(activity, activity);
}
/**
* Fragment咽笼、Adapter調(diào)用
*
* @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的實現(xiàn)方式
annotationProcessor project(':processor')
Q1:注解處理器processor為什么要在META-INF注冊剑刑?
在編譯時,java編譯器(javac)會去META-INF中查找實現(xiàn)了的AbstractProcessor的子類双肤,并且調(diào)用該類的process函數(shù)施掏,最終生成.java文件。其實就像activity需要注冊一樣杨伙,就是要到META-INF注冊 其监,javac才知道要給你調(diào)用哪個類來處理注解。
Q2:注解處理器processor是如何被系統(tǒng)調(diào)用的限匣?
在完成注解處理類Processor之后抖苦,需要做2件事情:
1、在META-INF目錄下注冊Processor米死;
2锌历、在項目中使用注解的地方添加apt工具annotationProcessor
Q3:注解申明和注解處理器為什么要分Module處理?
我們都知道processor的作用是:在編譯器解析注解峦筒、生成新的.java文件究西。這個lib只在編譯器用到,是不會被打包進apk的物喷。對于調(diào)用者來說卤材,你只是想使用這個注解,而不希望你已經(jīng)編譯好的項目中引進注解處理器相關(guān)的內(nèi)容峦失,所以為了不引入沒必要的文件扇丛,我們一般選擇將注解聲明和注解處理分開處理。
AspectJ
AspectJ的核心是ajc編譯器 (aspectjtools)和織入器 (aspectjweaver)尉辑。
ajc編譯器:
基于Java編譯器之上的帆精,它是用來編譯.aj文件,aspectj在Java編譯器的基礎(chǔ)上增加了一些它自己的關(guān)鍵字和方法隧魄。因此卓练,ajc也可以編譯Java代碼。
weaver織入器:
它的工作原理是:通過Gradle Transform API购啄,在class文件生成后至dex文件生成前襟企,遍歷并匹配所有符合AspectJ文件中聲明的切點,然后將Aspect的代碼織入到目標.class狮含≌海織入代碼后的新.class會加入多個JoinPoint,這個JoinPoint會建立目標.class與Aspect代碼的連接拱撵,比如獲得執(zhí)行的對象、方法表蝙、參數(shù)等拴测。
Java動態(tài)代理
動態(tài)代理是指在運行時動態(tài)生成代理類。即府蛇,代理類的字節(jié)碼將在運行時生成并載入當前代理的 ClassLoader集索。與靜態(tài)處理類相比,動態(tài)類有諸多好處汇跨。
主要使用到 InvocationHandler 接口和 Proxy.newProxyInstance() 方法务荆。
①不需要寫一個形式上完全一樣的封裝類;
②使用一些動態(tài)代理的生成方法甚至可以在運行時制定代理類的執(zhí)行邏輯穷遂,從而大大提升系統(tǒng)的靈活性函匕。
java.lang.reflect.Proxy:這是生成代理類的主類,通過 Proxy 類生成的代理類都繼承了 Proxy 類蚪黑。
Proxy提供了用戶創(chuàng)建動態(tài)代理類和代理對象的靜態(tài)方法盅惜,它是所有動態(tài)代理類的父類。
java.lang.reflect.InvocationHandler:這里稱他為"調(diào)用處理器"忌穿,它是一個接口抒寂。當調(diào)用動態(tài)代理類中的方法時,將會直接轉(zhuǎn)接到執(zhí)行自定義的InvocationHandler中的invoke()方法掠剑。即我們動態(tài)生成的代理類需要完成的具體內(nèi)容需要自己定義一個類屈芜,而這個類必須實現(xiàn) InvocationHandler 接口,通過重寫invoke()方法來執(zhí)行具體內(nèi)容朴译。
Proxy提供了如下兩個方法來創(chuàng)建動態(tài)代理類和動態(tài)代理實例井佑。
static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) 返回代理類的java.lang.Class對象。第一個參數(shù)是類加載器對象(即哪個類加載器來加載這個代理類到 JVM 的方法區(qū))眠寿,第二個參數(shù)是接口(表明你這個代理類需要實現(xiàn)哪些接口)毅糟,第三個參數(shù)是調(diào)用處理器類實例(指定代理類中具體要干什么),該代理類將實現(xiàn)interfaces所指定的所有接口澜公,執(zhí)行代理對象的每個方法時都會被替換執(zhí)行InvocationHandler對象的invoke方法。
**static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) **返回代理類實例喇肋。參數(shù)與上述方法一致坟乾。
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");
}
}
對應上述兩種方法創(chuàng)建動態(tài)代理對象的方式:
//創(chuàng)建一個InvocationHandler對象
InvocationHandler handler = new MyInvocationHandler(.args..);
//使用Proxy生成一個動態(tài)代理類
Class proxyClass = Proxy.getProxyClass(RealSubject.class.getClassLoader(),RealSubject.class.getInterfaces(), handler);
//獲取proxyClass類中一個帶InvocationHandler參數(shù)的構(gòu)造器
Constructor constructor = proxyClass.getConstructor(InvocationHandler.class);
//調(diào)用constructor的newInstance方法來創(chuàng)建動態(tài)實例
RealSubject real = (RealSubject)constructor.newInstance(handler);
//創(chuàng)建一個InvocationHandler對象
InvocationHandler handler = new MyInvocationHandler(.args..);
//使用Proxy直接生成一個動態(tài)代理對象
RealSubject real =Proxy.newProxyInstance(RealSubject.class.getClassLoader(),RealSubject.class.getInterfaces(), handler);
newProxyInstance這個方法實際上做了兩件事:第一,創(chuàng)建了一個新的類【代理類】蝶防,這個類實現(xiàn)了Class[] interfaces中的所有接口甚侣,并通過你指定的ClassLoader將生成的類的字節(jié)碼加載到JVM中,創(chuàng)建Class對象间学;第二殷费,以你傳入的InvocationHandler作為參數(shù)創(chuàng)建一個代理類的實例并返回印荔。
動態(tài)代理模式的簡單實現(xiàn)
public class DynamicProxyDemo {
public static void main(String[] args) {
//1.創(chuàng)建目標對象
RealSubject realSubject = new RealSubject();
//2.創(chuàng)建調(diào)用處理器對象
ProxyHandler handler = new ProxyHandler(realSubject);
//3.動態(tài)生成代理對象
Subject proxySubject = (Subject)Proxy.newProxyInstance(RealSubject.class.getClassLoader(),
RealSubject.class.getInterfaces(), handler);
//4.通過代理對象調(diào)用方法
proxySubject.request();
}
}
/**
* 主題接口
*/
interface Subject{
void request();
}
/**
* 目標對象類
*/
class RealSubject implements Subject{
public void request(){
System.out.println("====RealSubject Request====");
}
}
/**
* 代理類的調(diào)用處理器
*/
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 {
//定義預處理的工作,當然你也可以根據(jù) method 的不同進行不同的預處理工作
System.out.println("====before====");
//調(diào)用RealSubject中的方法
Object result = method.invoke(subject, args);
System.out.println("====after====");
return result;
}
}
Retrofit 分析
動態(tài)代理详羡,讓Retrofit將我們的接口變?yōu)榫唧w的類
public <T> T create(final Class<T> service) {
//做一些安全判斷仍律,判斷是否為接口,不是接口java原生的動態(tài)代理會報錯
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
//create其實就是通過Java的動態(tài)代理將接口中定義的方法轉(zhuǎn)給了InvocationHandler的invoke方法
//在通過loadServiceMethod調(diào)用invoke实柠,來發(fā)起網(wǎng)絡(luò)請求(下篇文章會講)
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是判斷上述特性,如果是就直接調(diào)用
//如果不是蟹漓,就使用loadServiceMethod來獲取方法
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
//返回了 封裝了請求參數(shù)的一個接口炕横,它知道怎么發(fā)起網(wǎng)絡(luò)請求
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.
具體實現(xiàn)類代理的方法 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方法