上章AOP章节讲到了APT功能,这里我们来详解下APT功能
APT(Annotation Processing Tool)即注解处理器,注解处理器 Java5 中叫APT(Annotation Processing Tool),在Java6开始,规范化为 Pluggable Annotation Processing。Apt应该是这其中我们最常见到的了,难度也最低。定义编译期的注解,再通过继承Proccesor实现代码生成逻辑,实现了编译期生成代码的逻辑。
上个项目里之所以做了IOC注入Bean对象这个功能,是为了解决,在安卓开发中,关于activity、业务、ui之间的架构问题。目前架构有MVC 、MVP 、MVVM种种,都很不错,但是这种框架,无疑会让工程变得无比臃肿,接口满天飞,特别不理解,ui层也套接口,还一个控件,一个接口,界面一旦调整,改起来那是想死的心都有了。所以其实没必要为了接口而接口,为了框架而框架,有的人说当项目大了,它的优势也就来了,一个小项目代码都那么臃肿了,大项目的业务何其复杂,更加不适合这种更加会让项目变得臃肿的结构设计,倒是谷歌官方提供了一种databinding MVVM框架,我觉得这种更为适合处理业务与ui之间的结构问题。
所以我还是采用了MVC方案,activity负责生命周期调度,component 负责业务处理,ui层负责所有的ui操作。层与层之间通过EventBus之类的消息组件框架进行交互与通讯。由AS插件负责生成三层java、xml相关的代码,插件我们下回在分解。再由本文中所讲的apt ioc功能注入各自的对象。
结构如下:
言归正传,我们继续详说APT。
创建Java Module Library
build.gradle如下
apply plugin: 'java-library'
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
compile 'com.squareup:javapoet:1.4.0'
implementation project(':apiannot')//自定义注解库
}
sourceCompatibility = "1.7"
targetCompatibility = "1.7"
1、apt-annotation(自定义注解)
@Bean : 用于标注需要注入的字段
package cn.jesse.apilib.annotation.apt;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface Bean {
}
@IBean : 用于标注在实体对象上,默认实体对象可不标注,当实体对象需要IOC构成成单例模式时,可标注此注解,并把
scope = IBean.Scope.Singleton 即可。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface IBean {
public enum Scope {
Default, Singleton,
}
Scope scope() default Scope.Default;//是否单例模式,默认非单例模式
}
APT 这里采用javapoet生产代码, 代码如下:
import com.app.apt.helper.EBeanModel;
import com.app.apt.processor.ApiFactoryProcessor;
import com.app.apt.processor.InstanceProcessor;
import com.app.apt.processor.OkBusAnnotacionProcessor;
import com.app.apt.util.Utils;
import com.google.auto.service.AutoService;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.FilerException;
import javax.annotation.processing.Messager;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import cn.jesse.apilib.annotation.apt.Bean;
import cn.jesse.apilib.annotation.apt.IBean;
import static com.squareup.javapoet.TypeSpec.classBuilder;
import static javax.lang.model.element.Modifier.FINAL;
import static javax.lang.model.element.Modifier.PUBLIC;
import static javax.lang.model.element.Modifier.STATIC;
@AutoService(Processor.class)//自动生成 javax.annotation.processing.IProcessor 文件
@SupportedSourceVersion(SourceVersion.RELEASE_8)//java版本支持
@SupportedAnnotationTypes({//标注注解处理器支持的注解类型
"cn.jesse.apilib.annotation.apt.Bean",
})
public class IOCProcessor extends AbstractProcessor {
public Filer mFiler; //文件相关的辅助类
public Elements mElements; //元素相关的辅助类
public Messager mMessager; //日志相关的辅助类
private Map<TypeElement, List<Element>> mIOCClassMap = new LinkedHashMap<>();
private Map<String,Element> SingletonMap = new LinkedHashMap<>();
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
mFiler = roundEnv.getFiler();
mElements = roundEnv.getElementUtils();
mMessager = roundEnv.getMessager();
String CLASS_NAME = "IocFactory";
TypeSpec.Builder tb = classBuilder(CLASS_NAME).addModifiers(PUBLIC, FINAL).addJavadoc("@ 此类由apt自动生成");//类注释
MethodSpec.Builder methodBuilder1 = MethodSpec.methodBuilder("bind") // MethodSpec.Builder 创建一个函数,可设置函数名称、注释、修饰符、入参、返回值等。
.addJavadoc("@此方法由apt自动生成")
.addModifiers(PUBLIC, STATIC)
.addParameter(Object.class, "object");
List<ClassName> mList = new ArrayList<>();
CodeBlock.Builder blockBuilderIoc = CodeBlock.builder();//创建一段代码块
blockBuilderIoc.beginControlFlow(" switch (object.getClass().getName())");//括号开始
List<EBeanModel> meBeanModels = new ArrayList<>();
try {
/*处理单例模式*/
processSingleton(roundEnv, tb);
/**
* 获得 Bean 注解集合
*/
queryBeanAnnot(roundEnv, meBeanModels);
//处理所有需要IOC注入的类
for (TypeElement classElementAnnotationIn : mIOCClassMap.keySet()) {
// 全限定名称
ClassName currentType = ClassName.get(classElementAnnotationIn);
//类名
String clazzName = currentType.simpleName().toLowerCase();
final List<Element> list = mIOCClassMap.get(classElementAnnotationIn);
blockBuilderIoc.add("case $S: \n", currentType);//
blockBuilderIoc.add("{"+currentType +" "+clazzName+"=("+currentType+")object ;");
for (Element methodElement : list) {
String singName=getSingName(methodElement.asType().toString());
if(SingletonMap.get(singName)!=null){//单例模式
blockBuilderIoc.add(clazzName+"." + methodElement.getSimpleName() + "= " +singName+ ";");
}else{//构造新对象
blockBuilderIoc.add(clazzName+"." + methodElement.getSimpleName() + "=new " + methodElement.asType().toString() + "();");
}
}
blockBuilderIoc.addStatement("}break");
}
blockBuilderIoc.addStatement("default: break");
blockBuilderIoc.endControlFlow();
methodBuilder1.addCode(blockBuilderIoc.build());
tb.addMethod(methodBuilder1.build());//添加函数
JavaFile javaFile = JavaFile.builder(Utils.PackageName, tb.build()).build();// 生成源代码
javaFile.writeTo(mFiler);// 在 app module/build/generated/source/包名/ 生成一份源代码
} catch (FilerException e) {
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
/**
* 获得对象中所有标注了 Bean 注解 字段
*
* @param roundEnv
* @param meBeanModels
*/
private void queryBeanAnnot(RoundEnvironment roundEnv, List<EBeanModel> meBeanModels) {
for (Element element : roundEnv.getElementsAnnotatedWith(Bean.class)) {
// 找到注解字段所在类的类名,比如user 字段写在MainActivity中,那么这里返回的TypeElement 就描述了关于 MainActivity的详细信息
TypeElement classElementAnnotationIn = (TypeElement) element.getEnclosingElement();
List<Element> methodList = mIOCClassMap.get(classElementAnnotationIn);
if (methodList == null) {
methodList = new ArrayList<>();
mIOCClassMap.put(classElementAnnotationIn, methodList);
}
//存储element
methodList.add(element);
EBeanModel eBeanModel = new EBeanModel();
eBeanModel.setElement(classElementAnnotationIn);
//返回该元素直接包含的子元素, 包含构造函数、函数、字段等。这里取标注有Bean 注解的元素。并记录保存
for (Element childElement : element.getEnclosedElements()) {
Bean bean = childElement.getAnnotation(Bean.class);
if (bean != null) {
eBeanModel.getBeanElements().add(childElement);
}
}
meBeanModels.add(eBeanModel);
}
}
/* 处理单例模式
如果该对象为单例模式时, 则生成静态字段, 并在static代码块里构造此对象。
*
* */
private void processSingleton(RoundEnvironment roundEnv, TypeSpec.Builder tb) {
StringBuffer staticField=new StringBuffer();
for (Element element : roundEnv.getElementsAnnotatedWith(IBean.class)) {
IBean iBean= element.getAnnotation(IBean.class);
TypeName typeName = ClassName.get(element.asType());
if(iBean.scope()==IBean.Scope.Singleton){
String fieldName=getSingName(element.asType().toString());
SingletonMap.put(fieldName,element);
//创建静态字段
FieldSpec extraField = FieldSpec.builder(ParameterizedTypeName.get(element.asType()), fieldName) .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .build();
tb.addField(extraField);
//并在静态代码块中,构造此对象
staticField.append(fieldName).append("=new ").append(typeName+"();\n");
}
}
staticField.append("\r\n");
tb.addStaticBlock(CodeBlock.builder().add(staticField.toString()).build());
}
private String getSingName(String typeName) {
return typeName.replace(".","_");
}
}
其中EBeanModel对象
import java.util.ArrayList;
import java.util.List;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
public class EBeanModel {
TypeElement element;//当前注入对象元素
List<Element> beanElements = new ArrayList<>();//要注入的Element列表
public List<Element> getBeanElements() {
return beanElements;
}
public TypeElement getElement() {
return element;
}
public void setElement(TypeElement mElement) {
this.element = mElement;
}
}
打完收工,测试代码如下:
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
@Bean
public User user;
@Bean
public Book book;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main3);
IocFactory.bind(this);
}
}
apt IocFactory代码如下:
import com.app.model.Book;
import java.lang.Object;
/**
* @ 此类由apt自动生成 */
public final class IocFactory {
public static Book com_app_model_Book;
static {
com_app_model_Book=new com.app.model.Book();
}
/**
* @此方法由apt自动生成 */
public static void bind(Object object) {
switch (object.getClass().getName()) {
case "com.app.ui.MainActivity":
{com.app.ui.MainActivity mainactivity=(com.app.ui.MainActivity)object ;mainactivity.user=new com.app.model.User();mainactivity.book= com_app_model_Book;}break;
default: break;
}
}
}
关于apt的优缺点,有一篇博文中总结得很好,那我就做一次搬运工吧。
难点:
就apt本身来说没有任何难点可言,难点一在于设计模式和解耦思想的灵活应用,二在与代码生成的繁琐,你可以手动字符串拼接,当然有更高级的玩法用squareup的javapoet,用建造者的模式构建出任何你想要的源代码。
想详细了解可以看官网或这篇博客:
Android 利用 APT 技术在编译期生成代码
优点:
它的强大之处无需多言,看代表框架的源码,你可以学到很多新姿势。总的一句话:它可以做任何你不想做的繁杂的工作,它可以帮你写任何你不想重复代码。懒人福利,老司机必备神技,可以提高车速,让你以任何姿势漂移。它可以生成任何源代码供你在任何地方使用,就像剑客的剑,快疾如风,无所不及。
关于apt 详细api使用可以查看 https://www.jianshu.com/p/899063e8452e