一个简单的编译期注解

注解可分为编译期注解和运行时注解

依赖注入

XUtils IOC (运行时注解,影响效率)

ButterKnife IOC(编译期注解)

本质:在编译时访问Java类,然后生成辅助类

JRE APT
Mirror 访问Java类


1.如何通过Activity的辅助类给Activity的控件属性赋值的?
->JasonKnife.inject中通过外部类Activity获取辅助类(Activity的内部类MainActivity$InjectAdapter)
->调用辅助类(MainActivity$InjectAdapter)的injects方法,完成注入

2.如何生成辅助类的?(重点)
->继承AbstractProcessor(在编译时期,可以处理被编译的Java类)
->带有我们指定注解的Java类,会经过我们的处理
->收集我们有用的信息,去生成辅助类

AbstractProcessor为处理注解,提供一个父类

生成辅助类
->获取到所有相关的注解信息
->生成辅助类

环境配置
1.jre
2.指定APT插件所需要的Processor,编译期运行
resources\META-INF\services\javax.annotation.processing.Processor
3.打成jar包
4.Java Compiler->Annotation Processor

反射:在运行时期,访问类的成员
Mirror:在编译期访问Java类文件的各个成员


环境配置中的1到3步是框架项目需要做的

步骤4是在引用项目中要做的

详细步骤如下:

1.jre步骤



2.指定APT插件所需要的Processor,编译期运行

项目中新建目录resources\META-INF\services,其中resources和src目录同级

在目录中新建文件javax.annotation.processing.Processor,在该文件里指定相关Processor,并且可以指定多个processor,以换行符分隔

com.jason.jasonknife.apt.ViewInjectorProcessor

3.打成jar包


把生成的jar包引入到需要使用的项目里并做如下配置

4.Java Compiler->Annotation Processor

点击advanced按钮,如果发现以下图片,则说明配置成功,分别点击应用和确定来结束配置





===============================================================

编译时注解

APT Annotation Processing Tools 注解处理工具

JDK1.5 之后自带的工具

APT是一个命令行工具(Eclipse自带APT插件)

配置:告诉Eclipse在编译我们的代码时,使用APT工具,对工程中的注解进行处理

怎么处理的?
通过Mirror API(com.sun.mirror.*)描述程序语义结构
通过Mirror API可以获取被注解的Java类型元素信息,交给了Processor的子类

通过Mirror API在编译期访问Java类,它把Java类分为这些部分:
PackageElement 包元素
TypeElement 类型元素
ExecutableElement 可执行元素
VariableElement 变量元素
TypeParameterElement 类型参数元素

通过ElementVisitor访问Java类的这些元素

JDK关于Mirror API的底层实现,并没有开源


代码如下:

----------------------------------------------------------------------------------

类库项目:


javax.annotation.processing.Processor

com.jason.jasonknife.apt.ViewInjectorProcessor

com.jason.jasonknife包:

package com.jason.jasonknife;

import java.util.HashMap;
import java.util.Map;

import com.jason.jasonknife.adapter.InjectAdapter;
import com.jason.jasonknife.adapter.NullAdapter;

import android.app.Activity;

public class JasonKnife {
	
	//缓存,宿主类与辅助类对象的关系
	static Map<Class<?>, InjectAdapter<?>> mInjectCache = new HashMap<Class<?>, InjectAdapter<?>>();

	/**
	 * 辅助类类名
	 */
	public static String SUFFIX = "$InjectAdapter";
	
	public static void inject(Activity target){
		//获取Activity中的内部类(辅助类)
		InjectAdapter<Activity> adapter = getViewAdapter(target.getClass());
		adapter.injects(target);
	}

	/**
	 * 获取一个指定类的InjectAdapter内部类
	 * @param clazz
	 * @return
	 */
	private static <T> InjectAdapter<T> getViewAdapter(Class<?> clazz) {
		//先从缓存获取
		InjectAdapter<T> adapter = (InjectAdapter<T>)mInjectCache.get(clazz);
		if(adapter != null){
			return adapter;
		}
		//使用反射,通过内部类的Class实例化出该内部类的对象
		//com.jason.jasonknife.demo.MainActivity$InjectAdapter
		String adapterClassName = clazz.getName() + SUFFIX;
		try {
			//1.通过该内部类的完整类名,加载出一个class
			Class<?> adapterClass = Class.forName(adapterClassName);
			//2.通过Class实例化对象
			adapter = (InjectAdapter<T>)adapterClass.newInstance();
			//放入缓存
			mInjectCache.put(clazz, adapter);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
		//Null Object Pattern 空对象设计模式
		return adapter == null ? new NullAdapter() : adapter;
	}
	
}

com.jason.jasonknife.adapter包


package com.jason.jasonknife.adapter;

/**
 * 辅助类的父接口
 * @date 2016年3月23日
 * @version 1.0
 */
public interface InjectAdapter<T> {

	void injects(T target);
	
}

package com.jason.jasonknife.adapter;

public class NullAdapter implements InjectAdapter {

	@Override
	public void injects(Object target) {
		
	}

}


com.jason.jasonknife.anno包:


package com.jason.jasonknife.anno;

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 ViewInjector {
	int value();
}
com.jason.jasonknife.anno.handler包:

package com.jason.jasonknife.anno.handler;

import java.util.List;
import java.util.Map;

import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.VariableElement;

/**
 * 注解处理器
 * @date 2016年3月21日
 * @version 1.0
 */
public interface AnnotationHandler {

	/**
	 * 关联“处理环境”
	 * @param env
	 */
	void attachProcessingEnvironment(ProcessingEnvironment env);
	
	/**
	 * 处理注解
	 * @param env “周边环境”
	 * @return Map<key 注解的宿主类名, value 宿主类中使用了我们注解的属性集合>
	 */
	Map<String, List<VariableElement>> handleAnnotation(RoundEnvironment env);
	
}

package com.jason.jasonknife.anno.handler;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;

import com.jason.jasonknife.anno.ViewInjector;
import com.jason.jasonknife.utils.AnnotationUtils;
import com.jason.jasonknife.utils.FileUtils;

/**
 * ViewInjector注解的处理实现
 * @date 2016年3月21日
 * @version 1.0
 */
public class ViewInjectHandler implements AnnotationHandler {

	private ProcessingEnvironment processingEnv;

	@Override
	public void attachProcessingEnvironment(ProcessingEnvironment env) {
		this.processingEnv = env;
	}

	@Override
	public Map<String, List<VariableElement>> handleAnnotation(RoundEnvironment env) {
		Map<String, List<VariableElement>> annotationMap = new HashMap<String, List<VariableElement>>();
		
		//获取所有使用了ViewInjector注解的元素(属性)
	 	Set<? extends Element> elementSet = env.getElementsAnnotatedWith(ViewInjector.class);
		//搞清楚哪个属性属于哪一个类
	 	for (Element element : elementSet) {
	 		//VariableElement 代表属性、枚举等等
			VariableElement varElement = (VariableElement)element;
			//获取属性宿主类的完成类名
			String className = getParentClassName(varElement);
			
			//key->value
			List<VariableElement> cacheElements = annotationMap.get(className);
			if(cacheElements == null){
				cacheElements = new ArrayList<VariableElement>();
				annotationMap.put(className, cacheElements);
			}
			cacheElements.add(varElement);
		}
	 	//-----------------for test
	 	//FileUtils.output(annotationMap.toString());
	 	
		return annotationMap;
	}

	/**
	 * 获取属性宿主类的完成类名
	 * @param varElement
	 * @return
	 */
	private String getParentClassName(VariableElement varElement) {
		TypeElement typeElement = (TypeElement)varElement.getEnclosingElement();
		String packageName =  AnnotationUtils.getPackageName(processingEnv, typeElement);
		//完整类名
		return packageName + "." + typeElement.getSimpleName().toString();
	}

}

com.jason.jasonknife.apt包:


package com.jason.jasonknife.apt;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
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.TypeElement;
import javax.lang.model.element.VariableElement;

import com.jason.jasonknife.anno.handler.AnnotationHandler;
import com.jason.jasonknife.anno.handler.ViewInjectHandler;
import com.jason.jasonknife.writer.AbstractWriter;
import com.jason.jasonknife.writer.DefaultJavaFileWriter;

//使用了ViewInjector的注解的Java类,需要处理
@SupportedAnnotationTypes("com.jason.jasonknife.anno.ViewInjector")
//支持的Java版本
@SupportedSourceVersion(SourceVersion.RELEASE_6)
public class ViewInjectorProcessor extends AbstractProcessor{
	
	List<AnnotationHandler> mHandlers = new ArrayList<AnnotationHandler>();
	AbstractWriter mWriter;
	Map<String, List<VariableElement>> map = new HashMap<String, List<VariableElement>>();
	
	//初始化工作
	@Override
	public synchronized void init(ProcessingEnvironment processingEnv) {
		super.init(processingEnv);
		//初始化注解处理器
		registerHandler(new ViewInjectHandler());
		
		//初始化辅助类生成器
		mWriter = new DefaultJavaFileWriter(processingEnv);
	}
	
	/**
	 * 注册处理器
	 * @param handler
	 */
	protected void registerHandler(AnnotationHandler handler){
		mHandlers.add(handler);
	}
	

	/**
	 * 在编译时期,会把指定的Java类传入进行处理(process)
	 */
	@Override
	public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
		//怎么处理?
		
		//1.拿到注解信息(暂时只有处理注入视图的注解,将来还有可能要处理注入布局、事件等等)
		for (AnnotationHandler handler : mHandlers) {
			//关联环境
			handler.attachProcessingEnvironment(processingEnv);
			//处理注解
			map.putAll(handler.handleAnnotation(roundEnv));
		}
		
		//2.生成Java文件
		mWriter.generate(map);
		
		return true;
	}
	
	

}

com.jason.jasonknife.utils包:

package com.jason.jasonknife.utils;

import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Element;

public class AnnotationUtils {

	/**
	 * 获取包名
	 * @param env
	 * @param element
	 * @return
	 */
	public static String getPackageName(ProcessingEnvironment env,Element element){
		return env.getElementUtils().getPackageOf(element).getQualifiedName().toString();
	}
	
}

package com.jason.jasonknife.utils;

import java.io.BufferedWriter;
import java.io.FileWriter;

public class FileUtils {

	public static void output(String info){
		if(info == null){
			return;
		}
		try {
			BufferedWriter writer = new BufferedWriter(new FileWriter("d://jason.txt",true));
			writer.newLine();
			writer.append(info);
			writer.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
}

package com.jason.jasonknife.utils;

import java.io.Closeable;
import java.io.IOException;

public class IOUtil {

	public static void closeQuietly(Closeable closeable){
		try {
			if(closeable != null){
				closeable.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
}

package com.jason.jasonknife.utils;

import android.app.Activity;
import android.view.View;

public class ViewFinder {

	
	public static <T extends View> T findViewById(Activity activity, int id){
		return (T)activity.findViewById(id);
	}
	
}

com.jason.jasonknife.writer包:

package com.jason.jasonknife.writer;

import java.io.IOException;
import java.io.Writer;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.annotation.processing.Filer;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.tools.JavaFileObject;

import com.jason.jasonknife.JasonKnife;
import com.jason.jasonknife.utils.AnnotationUtils;
import com.jason.jasonknife.utils.FileUtils;
import com.jason.jasonknife.utils.IOUtil;

/**
 * 辅助类生成器的模板方法
 * @date 2016年3月21日
 * @version 1.0
 */
public abstract class AbstractWriter implements AdapterWriter {
	
	protected ProcessingEnvironment mProcessingEnv;
	//用于创建源文件
	protected Filer mFiler;
	
	public AbstractWriter(ProcessingEnvironment mProcessingEnv) {
		super();
		this.mProcessingEnv = mProcessingEnv;
		this.mFiler = mProcessingEnv.getFiler();
		FileUtils.output("AbstractWriter 初始化");
	}

	/**
	 * 模板方法
	 */
	@Override
	public void generate(Map<String, List<VariableElement>> map) {
		Iterator<Entry<String, List<VariableElement>>> iterator = map.entrySet().iterator();
		FileUtils.output("generate:"+map);
		while(iterator.hasNext()){
			Entry<String, List<VariableElement>> entry = iterator.next();
			//属性集合
			List<VariableElement> cacheElements = entry.getValue();
			if(cacheElements == null || cacheElements.size() == 0){
				continue;
			}
			//创建Java源文件
			//拿集合中的第0个属性元素出来,构建一个InjectInfo
			InjectInfo info = createInjectInfo(cacheElements.get(0));
			Writer writer = null;
			try {
				JavaFileObject javaFileObject = mFiler.createSourceFile(info.getClassFullName());
				//通过Writer编写Java代码
				writer = javaFileObject.openWriter();
				//头部
				FileUtils.output("generateImport");
				generateImport(writer, info);
				//属性赋值部分
				for (VariableElement variableElement : cacheElements) {
					writeField(writer, variableElement, info);
				}
				FileUtils.output("writeField");
				//尾部
				writeEnd(writer);
				FileUtils.output("writeEnd");
			} catch (IOException e) {
				e.printStackTrace();
			} finally{
				IOUtil.closeQuietly(writer);
			}
		}
	}
	
	private InjectInfo createInjectInfo(VariableElement element){
		//获取变量的宿主(类-类型元素),转为TypeElement
		TypeElement typeElement = (TypeElement)element.getEnclosingElement();
		String packageName = AnnotationUtils.getPackageName(mProcessingEnv, element);
		String className = typeElement.getSimpleName().toString();
		return new InjectInfo(packageName, className);
	}
	
	/**
	 * 产生头部
	 * @param writer
	 * @param info
	 */
	protected abstract void generateImport(Writer writer, InjectInfo info)throws IOException;
	
	/**
	 * 产生为属性赋值的代码
	 * @param writer
	 * @param element
	 * @param info
	 */
	protected abstract void writeField(Writer writer, VariableElement element, InjectInfo info)throws IOException;
	
	/**
	 * 产生结尾部分
	 * @param writer
	 */
	protected abstract void writeEnd(Writer writer)throws IOException;
	
	/**
	 * 注入信息
	 * @author Jason
	 * QQ: 1476949583
	 * @date 2016年3月23日
	 * @version 1.0
	 */
	class InjectInfo{
		//宿主类的包名
		public String packageName;
		//宿主类的类名
		public String className;
		//要创建的辅助类的类名
		public String newClassName;
		
		public InjectInfo(String packageName, String className) {
			super();
			this.packageName = packageName;
			this.className = className;
			this.newClassName = className + JasonKnife.SUFFIX;
		}
		
		/**
		 * 获取辅助类的完整类名
		 * @return
		 */
		public String getClassFullName(){
			return this.packageName + "." + this.newClassName;
		}
		
	}

}
ackage com.jason.jasonknife.writer;

import java.util.List;
import java.util.Map;

import javax.lang.model.element.VariableElement;

/*
 * 辅助类生成器
 */
public interface AdapterWriter {

	/**
	 * 生成辅助类
	 * @param map
	 */
	void generate(Map<String, List<VariableElement>> map);
	
}

package com.jason.jasonknife.writer;

import java.io.IOException;
import java.io.Writer;

import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.VariableElement;

import com.jason.jasonknife.anno.ViewInjector;

/**
 * 默认的Java文件生成器
 * @date 2016年3月21日
 * @version 1.0
 */
public class DefaultJavaFileWriter extends AbstractWriter {

	public DefaultJavaFileWriter(ProcessingEnvironment mProcessingEnv) {
		super(mProcessingEnv);
	}

	@Override
	protected void generateImport(Writer writer, InjectInfo info) throws IOException{
		writer.write("package "+info.packageName +";");
		writer.write("\n\n");
		writer.write("import com.jason.jasonknife.adapter.InjectAdapter;");
		writer.write("\n");
		writer.write("import com.jason.jasonknife.utils.ViewFinder;");
		writer.write("\n\n\n");
		writer.write("/*This class is generated by JasonKnife, do not modify!*/");
		writer.write("\n");
		writer.write("public class "+info.newClassName+" implements InjectAdapter<"+info.className+"> { ");
		writer.write("\n");
		writer.write("\n");
		writer.write("public void injects("+info.className+" target)  {");
		writer.write("\n");
	}

	@Override
	protected void writeField(Writer writer, VariableElement element, InjectInfo info)throws IOException {
		//获取ViewInjector注解的值
		ViewInjector viewInjector = element.getAnnotation(ViewInjector.class);
		String fieldName = element.getSimpleName().toString();
		int id = viewInjector.value();
		writer.write("target."+fieldName+" =  ViewFinder.findViewById(target, "+id+");");
		writer.write("\n");
	}

	@Override
	protected void writeEnd(Writer writer)throws IOException {
		writer.write("  }");
		writer.write("\n\n");
		writer.write(" }");
	}

}


使用案例项目:

activity_main.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="文字游戏" />
    
    <Button
        android:id="@+id/btn"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="按钮" />

</LinearLayout>

MainActivity:

package com.jason.jasonknife.demo;

import com.jason.jasonknife.JasonKnife;
import com.jason.jasonknife.anno.ViewInjector;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity {
	
	@ViewInjector(R.id.btn)
	protected Button btn;
	
	@ViewInjector(R.id.text)
	protected TextView text;
	

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		JasonKnife.inject(this);
		
		Log.d("jason", btn.getText().toString());
		Log.d("jason", text.getText().toString());
	}
	
}

运行结果如下:



整理自教程


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值