今天在寫基於maven plugin的一個小程序,它的功能是在maven執行install階段將已經打好包,從這個包中抽取分佈式服務中所有標識@Dic註解的字典枚舉類,之後會將這些字典枚舉類打成一個jar包。也就是執行了mvn install之後會在工程的target中生成兩個jar包,一個是服務器端部署包,一個是字典依賴包,同時會把該依賴包depoly到私服,把jar包座標信息,字典信息上傳給相應的服務展示出來供他人使用。
package com.annotation.processor;
import com.alibaba.fastjson.JSONObject;
import com.annotition.Dic;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.project.MavenProject;
import java.io.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.*;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
/**
* @author lmfeng
* @goal dic
* @requiresDependencyResolution runtime
* @phase deploy
*/
public class DicGenerateMojo extends AbstractMojo {
/**
* @parameter default-value="${project}"
*/
private MavenProject project;
/**
* @parameter property="scanDir" default-value="${basedir}/src/main/java/"
*/
private String scanDir;
/**
* @parameter property="scanPackage" default-value="com.xxx"
*/
private String scanPackage;
/**
* @parameter property="groupId" default-value="com.xxx"
*/
private String groupId;
/**
* @parameter property="version" default-value="1.0"
*/
private String version;
/**
* @parameter property="packaging" default-value="jar"
*/
private String packaging;
/**
* @parameter property="url" default-value="https://repo.xxx-inc.com/repository/snapshots"
*/
private String url;
/**
* @parameter property="reportUrl"
*/
private String reportUrl;
/**
* @parameter property="repositoryId" default-value="nexus-snapshots"
*/
private String repositoryId;
/**
* @parameter property="classPath" default-value="${basedir}/target/classes/"
*/
private String classPath;
/**
* 字典類全路徑集合
*/
private Set<String> dicClassesRealPath = new LinkedHashSet<String>();
private Set<Object> dicInformation = new HashSet<Object>();
// class類的集合
private Set<Class<?>> classes = new LinkedHashSet<Class<?>>();
public static void main(String[] args) throws IOException, MojoFailureException, MojoExecutionException {
DicGenerateMojo dicGenerateMojo = new DicGenerateMojo();
dicGenerateMojo.execute();
}
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
// package
String dicJarPath = packageing();
// deploy
boolean deployDicJar = deployDicJar(dicJarPath);
getLog().info(deployDicJar ? "deploy dic jar successed":"deploy dic jar failed");
if (deployDicJar){
// delete jar
deleteJar(dicJarPath);
// 上報信息 url
jsonPost(reportUrl,dicInformation);
}
}
/**
* 刪除 jar
*
* @param filePath
* @return
*/
private boolean deleteJar(String filePath){
return new File(filePath).delete();
}
// mvn deploy:deploy-file -DgroupId=com.test -DartifactId=test -Dversion=1.0 -Dpackaging=jar
private boolean deployDicJar(String dicJarPath){
try {
if (dicJarPath == null){
getLog().warn("dicJarPath is null+");
return false;
}
StringBuffer cmd = new StringBuffer("mvn deploy:deploy-file");
String artifactId = app+"-dic";
cmd.append(" -DgroupId="+groupId).append(" -DartifactId="+artifactId).append(" -Dversion="+version).append(" -Dpackaging="+packaging)
.append(" -Dfile="+dicJarPath).append(" -Durl="+url).append(" -DrepositoryId="+repositoryId);
System.err.println(cmd.toString());
// Linux or Mac下
Process process = Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", cmd.toString()});
process.waitFor();
process.destroy();
System.err.println("dic 座標是:=======>");
StringBuffer sb = new StringBuffer("<dependency>");
sb.append("\n").append("<groupId>").append(groupId).append("<groupId>").append("\n")
.append("<artifactId>").append(artifactId).append("<artifactId>").append("\n")
.append("<version>").append(version).append("<version>").append("\n").append("<dependency>");
System.err.println(sb.toString());
return true;
} catch (Exception e) {
getLog().error("deploy is failed !",e);
}
return false;
}
// 通過 jar cvf appName-dic.jar 將 class 生成 jar 包
private String packageing(){
try {
Set<String> dicClassesRealPath = getDicClassesRealPath(scanPackage);
if (dicClassesRealPath.size() == 0){
getLog().warn("scan "+scanPackage+", no class is used for @Dic annotation");
return null;
}
// 打成 jar
StringBuffer cmd = new StringBuffer("jar cvf ");
if (app == null){
app = "flm_test";
}
String jarName = app+"-dic"+'-'+version+'.'+packaging;
cmd.append(jarName);
for (String str : dicClassesRealPath) {
cmd.append(" ").append(str);
}
System.err.println(cmd.toString());
// Linux or Mac下
Process process = Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", cmd.toString()});
process.waitFor();
process.destroy();
File dicJarFile = new File(jarName);
if (dicJarFile.exists()){
String dicJarPath = dicJarFile.getAbsolutePath();
System.out.println("dicJarPath is => "+dicJarPath);
return dicJarPath;
}
} catch (Exception e) {
getLog().error("package failed",e);
}
return null;
}
// TODO 判斷當前系統是 Linux or windows
/**
* 獲取字典類全路徑
*
* @param scanPackage
* @return
*/
public Set getDicClassesRealPath(String scanPackage){
// 包下面的類
if (scanPackage == null){
scanPackage = "com.xxx";
System.err.println("scanPackage is default => "+scanPackage);
}
System.out.println("scanPackage is =>"+scanPackage);
getClasses(scanPackage);
return dicClassesRealPath;
}
/**
* 獲取方法上的註解
*
* @param clazz
*/
private void getMethodsAnnotation(Class<?> clazz) {
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
Annotation[] annotations = method.getDeclaredAnnotations();
for (Annotation annotation : annotations) {
System.out.println(clazz.getSimpleName().concat(".").concat(method.getName()).concat(".")
.concat(annotation.annotationType().getSimpleName()));
}
}
}
/**
* 從包package中獲取所有的Class
*
* @param pack
* @return
*/
public void getClasses(String pack) {
// 是否循環迭代
boolean recursive = true;
// 獲取包的名字 並進行替換
String packageName = pack;
String packageDirName = packageName.replace('.', '/');
// 定義一個枚舉的集合 並進行循環來處理這個目錄下的things
Enumeration<URL> dirs;
try {
URL[] urls = new URL[1];
urls[0] = new URL("file:"+classPath);
URLClassLoader urlClassLoader = new URLClassLoader(urls,Thread.currentThread().getContextClassLoader());
dirs = urlClassLoader.getResources(packageDirName);
// 使用上下文類加載器有問題。。。
// dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
// 循環迭代下去
System.out.println("dirs.hasMoreElements()"+dirs.hasMoreElements());
while (dirs.hasMoreElements()) {
// 獲取下一個元素
URL url = dirs.nextElement();
System.out.println("URL is => "+url);
// 得到協議的名稱
String protocol = url.getProtocol();
// 如果是以文件的形式保存在服務器上
if ("file".equals(protocol)) {
System.err.println("file類型的掃描");
// 獲取包的物理路徑
String packagePath = URLDecoder.decode(url.getFile(), "UTF-8");
System.out.println("packagePath is => "+packagePath);
// 以文件的方式掃描整個包下的文件 並添加到集合中
findAndAddClassesInPackageByFile(packageName, packagePath, recursive, classes,urlClassLoader);
}
// else if ("jar".equals(protocol)) {
// packageName = scanJarFile(classes, recursive, packageName, packageDirName, url);
// }
}
} catch (IOException e) {
getLog().error("getClasses failed ", e);
}
}
/**
* load jar
*/
private String scanJarFile(Set<Class<?>> classes, boolean recursive, String packageName, String packageDirName, URL url) {
// 如果是jar包文件 定義一個JarFile System.err.println("jar類型的掃描");
JarFile jar;
try {
// 獲取jar
jar = ((JarURLConnection) url.openConnection()).getJarFile();
// 從此jar包 得到一個枚舉類
Enumeration<JarEntry> entries = jar.entries();
// 同樣的進行循環迭代
while (entries.hasMoreElements()) {
// 獲取jar裏的一個實體 可以是目錄 和一些jar包裏的其他文件 如META-INF等文件
JarEntry entry = entries.nextElement();
String name = entry.getName();
// 如果是以/開頭的
if (name.charAt(0) == '/') {
// 獲取後面的字符串
name = name.substring(1);
}
// 如果前半部分和定義的包名相同
if (name.startsWith(packageDirName)) {
int idx = name.lastIndexOf('/');
// 如果以"/"結尾 是一個包
if (idx != -1) {
// 獲取包名 把"/"替換成"."
packageName = name.substring(0, idx).replace('/', '.');
}
// 如果可以迭代下去 並且是一個包
if ((idx != -1) || recursive) {
// 如果是一個.class文件 而且不是目錄
if (name.endsWith(".class") && !entry.isDirectory()) {
// 去掉後面的".class" 獲取真正的類名
String className = name.substring(packageName.length() + 1, name.length() - 6);
try {
// 添加到classes
classes.add(Class.forName(packageName + '.' + className));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
}
} catch (IOException e) {
// log.error("在掃描用戶定義視圖時從jar包獲取文件出錯");
e.printStackTrace();
}
return packageName;
}
/**
* 以文件的形式來獲取包下的所有Class
*
* @param packageName
* @param packagePath
* @param recursive
* @param classes
*/
public void findAndAddClassesInPackageByFile(String packageName, String packagePath, final boolean recursive,
Set<Class<?>> classes,ClassLoader classLoader) {
// 獲取此包的目錄 建立一個File
File dir = new File(packagePath);
// 如果不存在或者 也不是目錄就直接返回
if (!dir.exists() || !dir.isDirectory()) {
getLog().warn("用戶定義包名 " + packagePath + " 下沒有任何文件");
return;
}
// 如果存在 就獲取包下的所有文件 包括目錄
File[] dirfiles = dir.listFiles(new FileFilter() {
// 自定義過濾規則 如果可以循環(包含子目錄) 或則是以.class結尾的文件(編譯好的java類文件)
public boolean accept(File file) {
return (recursive && file.isDirectory()) || (file.getName().endsWith(".class"));
}
});
// 循環所有文件
for (File file : dirfiles) {
// 如果是目錄 則繼續掃描
if (file.isDirectory()) {
findAndAddClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive,
classes,classLoader);
} else {
// 如果是java類文件 去掉後面的.class 只留下類名
String className = file.getName().substring(0, file.getName().length() - 6);
try {
// 添加到集合中去 這裏用forName有一些不好,會觸發static方法,沒有使用classLoader的load乾淨
// classes.add(Class.forName(packageName + '.' + className));
System.out.println("packageName+className => "+packageName + '.' + className);
// Class<?> clazz = Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className);
Class<?> clazz = classLoader.loadClass(packageName + '.' + className);
System.out.println("clazz is => "+clazz);
if (clazz.isEnum() && isDicClass(clazz)){
dicClassesRealPath.add(file.getAbsolutePath());
// 獲得枚舉類詳細信息
dicInformation.add(enumInformation(clazz));
}
} catch (Exception e) {
getLog().error("load class failed" ,e);
}
}
}
}
/**
* 是否使用@Dic 註解
*
* @param clazz
* @return
*/
private boolean iDicClass(Class<?> clazz){
Annotation[] annos = clazz.getAnnotations();
for (Annotation anno : annos) {
if(Dic.class.equals(anno.annotationType())){
return true;
}
}
return false;
}
/**
* 枚舉類詳細信息
*
* @param clazz
*/
private Map<String,List<Map<String,Object>>> enumInformation(Class<?> clazz){
Map<String,List<Map<String,Object>>> enumTypeMap = new HashMap<String,List<Map<String,Object>>>();
Class<Enum> enumClass = (Class<Enum>) clazz;
List<Map<String,Object>> list = new ArrayList<Map<String,Object>>();
// TODO 把信息添加到DicJar裏
String clazzName = enumClass.getName();
Enum[] enumConstants = enumClass.getEnumConstants();
Map<String, Method> methods = getMethods(enumClass, enumConstants);
for (Enum enumType : enumConstants) {
Map<String,Object> map = new HashMap<String,Object>();
for (String key : methods.keySet()) {
try {
Method method = methods.get(key);
Object invoke = method.invoke(enumType);
map.put(key,invoke);
} catch (Exception e) {
e.printStackTrace();
}
}
String name = enumType.name();
int ordinal = enumType.ordinal();
map.put("name",name);
map.put("ordinal",ordinal);
list.add(map);
}
enumTypeMap.put(clazzName,list);
return enumTypeMap;
}
private Map<String,Method> getMethods(Class<Enum> enumClass,Enum[] enumConstants){
List<String> enumNames = new ArrayList<String>();
Map<String,Method> methods = new HashMap<String, Method>();
for (Enum enumType : enumConstants) {
enumNames.add(enumType.name());
}
Field[] declaredFields = enumClass.getDeclaredFields();
for (Field field:declaredFields) {
String fieldName = field.getName();
if (!enumNames.contains(fieldName) && !fieldName.equals("$VALUES")){
try {
Method method = enumClass.getMethod("get" + (fieldName.charAt(0) + "").toUpperCase() + fieldName.substring(1, fieldName.length()));
methods.put(fieldName,method);
} catch (NoSuchMethodException e) {
getLog().error(e.getMessage(),e);
}
}
}
return methods;
}
/**
* 父類、接口
*
* @param clazz
* @param classesAll
* @return
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public Set<Class<?>> getByInterface(Class clazz, Set<Class<?>> classesAll) {
Set<Class<?>> classes = new LinkedHashSet<Class<?>>();
// 獲取指定接口的實現類
if (!clazz.isInterface()) {
try {
/**
* 循環判斷路徑下的所有類是否繼承了指定類 並且排除父類自己
*/
Iterator<Class<?>> iterator = classesAll.iterator();
while (iterator.hasNext()) {
Class<?> cls = iterator.next();
/**
* isAssignableFrom該方法的解析,請參考博客:
* http://blog.csdn.net/u010156024/article/details/44875195
*/
if (clazz.isAssignableFrom(cls)) {
if (!clazz.equals(cls)) {// 自身並不加進去
classes.add(cls);
} else {
}
}
}
} catch (Exception e) {
System.out.println("出現異常");
}
}
return classes;
}
/**
* 發送HttpPost請求
*
* @param strURL 服務地址
* @param params
*
* @return 成功:返回json字符串<br/>
*/
public String jsonPost(String strURL, Object params) {
try {
URL url = new URL(strURL);// 創建連接
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setUseCaches(false);
connection.setInstanceFollowRedirects(true);
connection.setRequestMethod("POST"); // 設置請求方式
connection.setRequestProperty("Accept", "application/json"); // 設置接收數據的格式
connection.setRequestProperty("Content-Type", "application/json"); // 設置發送數據的格式
connection.connect();
OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream(), "UTF-8"); // utf-8編碼
out.append(JSONObject.toJSONString(params));
out.flush();
out.close();
int code = connection.getResponseCode();
InputStream is = null;
if (code == 200) {
is = connection.getInputStream();
} else {
is = connection.getErrorStream();
}
// 讀取響應
int length = (int) connection.getContentLength();// 獲取長度
if (length != -1) {
byte[] data = new byte[length];
byte[] temp = new byte[512];
int readLen = 0;
int destPos = 0;
while ((readLen = is.read(temp)) > 0) {
System.arraycopy(temp, 0, data, destPos, readLen);
destPos += readLen;
}
String result = new String(data, "UTF-8"); // utf-8編碼
return result;
}
} catch (IOException e) {
getLog().error("Exception occur when send http post request!", e);
}
return "error"; // 自定義錯誤信息
}
}
-
@Dic字典註解
package com.annotation.dic; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @author lmfeng */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Dic { public String value() default ""; /** * 字典描述信息 * * @return string */ public String dicDecription() default ""; }
-
maven依賴
<dependencies>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-descriptor</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-project</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-model</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-artifact</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.29</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.5</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>1.3</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
</dependencies>