Java模块 -- Java注解及应用

之前一段时间在折腾ServiceCenter服务发布中心,这玩意是个框架,主要作用是集成WebService与Http服务。

同时发布两种服务,互不影响,业务逻辑,通过应用(SDK模块)的方式集成进去。

通过将业务模块化,来实现业务模块的可插拔。


这个框架与应用之间有一些参数肯定是需要进行关联映射的,使用的是比较普通的配置文件进行关联的。

当时也没有觉得不妥,后来在框架上线部署....

之前有段时间没事看看Spring,看到了Spring的注解...

然后就想,这个ServiceCenter框架与模块之间能不能不写配置文件,通过注解的方式,传递必要的一些参数...


然后就有了大致的一个思路:

1.首先自定义注解

2.在模块的主类的中使用

3.模块在加入框架之后,框架要能扫描到这个注解所在的主类。


好了,下面直接贴笔记、代码


Java注解


参考资料:

《深入理解Java:注解(Annotation)自定义注解入门》


元注解

元注解的作用是负责注解其他的注解(自定义注解);

Java5中定义了四个标准的meta-annotation类型,它们被用来提供对其他annotation类型说明。

          1. @Target

          2. @Retention

          3. @Documented

          4. @Inherited

上面这些类型和他们所支持的类,可以在java.lang.annotation包中找到。


下面我们分别来看看。


@Target

该注解说明了Annoataion所修饰的对象范围,在Annotation类型的生命中使用了target便可以更加明确其修饰的目标。

取值:

CONSTRUCTOR

用于描述构造器

FIELD

用于描述域

LOCAL_VARIABLE

用于描述局部变量

METHOD

用于描述方法

PACKAGE

用于描述包

PARAMETER

用于描述参数

TYPE

用于描述类、接口(包括注解类型) 或enum声明



@Retention

该注解定义了Annotation被保留的时间长短;用于描述该注解的生命周期

取值:

SOURCE

在源文件中有效(即源文件保留)

CLASS

Class文件中有效(class保留)

RUNTIME

在运行时有效(即运行时保留)



@Documented

该注解用于描述其他类型的annotation应该被作为被标注程序成员的公共API,因此可以被例如javadoc此类的工具文档化。

Documented是一个标记注解,没有成员。



@Inherited

该注解是一个标记注解,Inherited注解阐述了某个被标注的类型是被继承的。

例如一个使用了@Inherited修改的annotation类型被用于一个class,则这个annotation将被用于该class的子类。



自定义注解


使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节。

在定义注解时,不能继承其他注解或接口。

@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。

方法的名称就是参数的名称,返回值类型就是参数的类型。可以通过default来声明参数的默认值。


定义注解格式:

public @interface 注解名 {定义体}


注解参数可支持数据类型:

1. 所有的基本类型

2. String类型

3. Class类型

4. enum类型

5. Annotation类型

6. 以上所有类型的数组


下面来一个Demo

/**
* 水果名称注解
* 
* @author CYX
* @time 2017年9月13日上午9:18:24
*/
// 注解所修饰的对象范围(类、接口、枚举、成员变量、方法参数等..),这里是用于描述域
@Target(ElementType.FIELD)

// 定义该注释被保留的时间长短(生命周期);这里是在运行时有效
@Retention(RetentionPolicy.RUNTIME)

// 标记注解,javadoc文档化
@Documented
public @interface FruitName {

	String value() default "";

}


/**
* 水果颜色注解
* 
* @author CYX
* @time 2017年9月13日上午9:24:24
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitColor {

	/**
	 * 颜色枚举
	 * 
	 * @author CYX
	 * @time 2017年9月13日上午9:25:34
	 */
	public enum Color {
			BULE, RED, GREEN
	};

	/**
	 * 颜色属性
	 * 
	 * @return
	 */
	Color fruitColor() default Color.GREEN;

}


public class Apple {

	@FruitName(value = "Apple")
	private String appleName;

	@FruitColor(fruitColor = Color.GREEN)
	private String appleColor;

	public String getAppleName() {
			return appleName;
	}

	public void setAppleName(String appleName) {
			this.appleName = appleName;
	}

	public String getAppleColor() {
			return appleColor;
	}

	public void setAppleColor(String appleColor) {
			this.appleColor = appleColor;
	}

	public void displayName() {
			System.out.println("水果名字是:苹果");
	}

}

public class TestMain {

	public static void main(String[] args) throws Exception {

	Class<?> clazz = Class.forName("com.demo.annotation.annotation_1.Apple");

	Field[] fields = clazz.getDeclaredFields();

	for (Field field : fields) {

	Annotation annotation = field.getAnnotation(FruitName.class);

	if (annotation instanceof FruitName) {

	FruitName fruitName = (FruitName) annotation;
	System.out.println(fruitName.value());

	}

	}

	}

}


下面贴上获取注解的其他方式,仅供参考

1。



2.



3.



4.




使用注解之后,怎么找到这个注解所在的类呢?

这里就需要一个扫描所有类的工具,直接上代码:

/**
* 处理Class类相关的工具类
* 
* @author Simba
* @time 2017年9月12日下午7:23:37
*/
public class ClassAnalysis {

/**
* 传入指定包路径,获取包下所有全类名。<br>
* 
* <p>
* <tt>注意:eclipse直接导出的jar包,可能无法读取,因为jar包 <i>/META-INF/MANIFEST.MF</i>文件中,缺少补充信息。</tt>
* </p>
* 
* <p>
* <tt>推荐使用Ant打包,或者使用Eclipse中的build.xml进行打包</tt>
* </p>
* 
* @param packageName
*            包名(例如:org、org.apache)
* 
* @return 指定包名下,所有全类名
* 
* @throws Exception
*/
public static Set<String> getClassNamesByPackage(String packageName) throws Exception {

// 第一个class类的集合,使用linkedHashSet为了保证顺序。
Set<String> classes = new LinkedHashSet<String>();

// 是否循环迭代
boolean recursive = true;

// 获取包的名字 并进行替换
String packageDirName = packageName.replace('.', '/');

// 定义一个枚举的集合 并进行循环来处理这个目录下的things
Enumeration<URL> dirs;

try {

dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);

} catch (IOException e) {
	throw new Exception(e);
}

// 循环迭代下去
while (dirs.hasMoreElements()) { // 获取下一个元素

try {

URL url = dirs.nextElement();
System.err.println(url);

// 得到协议的名称
String protocol = url.getProtocol();

// 如果是以文件的形式
if ("file".equals(protocol)) {

// 获取包的物理路径
String filePath = URLDecoder.decode(url.getFile(), "UTF-8");

// 以文件的方式扫描整个包下的文件 并添加到集合中
findAndAddClassesInPackageByFile(packageName, filePath, recursive, classes);

} else if ("jar".equals(protocol)) {

// 获取jar
JarFile jar = ((JarURLConnection) url.openConnection()).getJarFile();

// 从此jar包 得到一个枚举类
Enumeration<JarEntry> entries = jar.entries();

// 同样的进行循环迭代
while (entries.hasMoreElements()) {

findAndAddClassesInPackageByJar(packageName, packageDirName, entries, recursive, classes);

}
}

} catch (Exception e) {
	throw new Exception(e);
}

}

return classes;
}

/**
* jar包的形式来获取包路径下的所有Class
* 
* @param packageName
*            包名(用点划分'com.apache')
* @param packageDirName
*            包名(用斜线划分'com/apache')
* @param entries
*            返回jar包中所有的文件条目
* @param recursive
*            是否循环迭代
* @param classes
*            存放class集合
* @throws Exception
*/
private static void findAndAddClassesInPackageByJar(String packageName, String packageDirName, Enumeration<JarEntry> entries, final boolean recursive, Set<String> classes) throws Exception {

// 获取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);
					// 添加到classes
					classes.add(packageName + '.' + className);
			}
	}
}

}

/**
* 文件的形式来获取包路径下的所有Class
* 
* @param packageName
*            包名(用点划分'com.apache')
* @param packagePath
*            包路径(系统全路径)
* @param recursive
*            是否循环迭代
* @param classes
*            存放class集合
*/
private static void findAndAddClassesInPackageByFile(String packageName, String packagePath, final boolean recursive, Set<String> classes) throws Exception {

// 获取此包的目录 建立一个File
File dir = new File(packagePath);

// 如果不存在或者 也不是目录就直接返回
if (!dir.exists() || !dir.isDirectory()) {
	return;
}

// 如果存在 就获取包下的所有文件 包括目录
File[] dirfiles = dir.listFiles(new FileFilter() {
	// 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
	public boolean accept(File file) {
			return (recursive && file.isDirectory()) || (file.getName().endsWith(".class"));
	}
});

List<String> clazzs = new ArrayList<String>();

// 循环所有文件
for (File file : dirfiles) {
	// 如果是目录 则继续扫描
	if (file.isDirectory()) {
			findAndAddClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive, classes);
	} else {
			// 如果是java类文件 去掉后面的.class 只留下类名
			String className = file.getName().substring(0, file.getName().length() - 6);
			// 添加到集合中去
			clazzs.add(packageName + '.' + className);
			classes.add(packageName + '.' + className);
	}
}
}
}

带有一定特殊要求的工具类

/**
* 处理Class类相关的工具类
* 
* @author Simba
* @time 2017年9月12日下午7:23:37
*/
public class ClassAnalysis {

/**
* 传入指定包路径,获取包下所有使用<i>@ModuleContextAnn</i>类注解的全类名。<br>
* 
* <p>
* <tt>注意:eclipse直接导出的jar包,可能无法读取,因为jar包 <i>/META-INF/MANIFEST.MF</i>文件中,缺少补充信息。</tt>
* </p>
* 
* <p>
* <tt>推荐使用Ant打包,或者使用Eclipse中的build.xml进行打包</tt>
* </p>
* 
* @param <A>
* 
* @param <A>
* 
* @param packageName
*            包名(例如:org、org.apache)
* 
* @return 返回使用指定注解的全类名
* 
* @throws Exception
*/
public static Set<Class<?>> getAnnotationClassByPackage(String packageName) throws Exception {

Set<String> allTempClass = getClassNamesByPackage(packageName);

Set<Class<?>> resultClazzs = new LinkedHashSet<Class<?>>();

for (String allClass : allTempClass) {

Class<?> clazz = Class.forName(allClass);

// 判断类中是否使用ModuleContextAnn注解
if (clazz.isAnnotationPresent(ModuleContextAnn.class)) {

resultClazzs.add(clazz);

}

}

return resultClazzs;

}

/**
* 传入指定包路径,获取包下所有全类名。<br>
* 
* <p>
* <tt>注意:eclipse直接导出的jar包,可能无法读取,因为jar包 <i>/META-INF/MANIFEST.MF</i>文件中,缺少补充信息。</tt>
* </p>
* 
* <p>
* <tt>推荐使用Ant打包,或者使用Eclipse中的build.xml进行打包</tt>
* </p>
* 
* @param packageName
*            包名(例如:org、org.apache)
* 
* @return 指定包名下,所有全类名
* 
* @throws Exception
*/
public static Set<String> getClassNamesByPackage(String packageName) throws Exception {

// 第一个class类的集合,使用linkedHashSet为了保证顺序。
Set<String> classes = new LinkedHashSet<String>();

// 是否循环迭代
boolean recursive = true;

// 获取包的名字 并进行替换
String packageDirName = packageName.replace('.', '/');

// 定义一个枚举的集合 并进行循环来处理这个目录下的things
Enumeration<URL> dirs;

try {

dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);

} catch (IOException e) {
	throw new Exception(e);
}

// 循环迭代下去
while (dirs.hasMoreElements()) { // 获取下一个元素

try {

URL url = dirs.nextElement();
System.err.println(url);

// 得到协议的名称
String protocol = url.getProtocol();

// 如果是以文件的形式
if ("file".equals(protocol)) {

// 获取包的物理路径
String filePath = URLDecoder.decode(url.getFile(), "UTF-8");

// 以文件的方式扫描整个包下的文件 并添加到集合中
findAndAddClassesInPackageByFile(packageName, filePath, recursive, classes);

} else if ("jar".equals(protocol)) {

// 获取jar
JarFile jar = ((JarURLConnection) url.openConnection()).getJarFile();

// 从此jar包 得到一个枚举类
Enumeration<JarEntry> entries = jar.entries();

// 同样的进行循环迭代
while (entries.hasMoreElements()) {

findAndAddClassesInPackageByJar(packageName, packageDirName, entries, recursive, classes);

}
}

} catch (Exception e) {
	throw new Exception(e);
}

}

return classes;
}

/**
* jar包的形式来获取包路径下的所有Class
* 
* @param packageName
*            包名(用点划分'com.apache')
* @param packageDirName
*            包名(用斜线划分'com/apache')
* @param entries
*            返回jar包中所有的文件条目
* @param recursive
*            是否循环迭代
* @param classes
*            存放class集合
* @throws Exception
*/
private static void findAndAddClassesInPackageByJar(String packageName, String packageDirName, Enumeration<JarEntry> entries, final boolean recursive, Set<String> classes) throws Exception {

// 获取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);
					// 添加到classes
					classes.add(packageName + '.' + className);
			}
	}
}

}

/**
* 文件的形式来获取包路径下的所有Class
* 
* @param packageName
*            包名(用点划分'com.apache')
* @param packagePath
*            包路径(系统全路径)
* @param recursive
*            是否循环迭代
* @param classes
*            存放class集合
*/
private static void findAndAddClassesInPackageByFile(String packageName, String packagePath, final boolean recursive, Set<String> classes) throws Exception {

// 获取此包的目录 建立一个File
File dir = new File(packagePath);

// 如果不存在或者 也不是目录就直接返回
if (!dir.exists() || !dir.isDirectory()) {
	return;
}

// 如果存在 就获取包下的所有文件 包括目录
File[] dirfiles = dir.listFiles(new FileFilter() {
	// 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
	public boolean accept(File file) {
			return (recursive && file.isDirectory()) || (file.getName().endsWith(".class"));
	}
});

List<String> clazzs = new ArrayList<String>();

// 循环所有文件
for (File file : dirfiles) {
	// 如果是目录 则继续扫描
	if (file.isDirectory()) {
			findAndAddClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive, classes);
	} else {
			// 如果是java类文件 去掉后面的.class 只留下类名
			String className = file.getName().substring(0, file.getName().length() - 6);
			// 添加到集合中去
			clazzs.add(packageName + '.' + className);
			classes.add(packageName + '.' + className);
	}
}
}
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值