前言:之前一直想熟练自定义注解,但当时没有实际的应用需求,也就是自己写了个实例,就搁置下来了。 这回,是在做一个工程的时候,需要根据变化,注入新的实例到工厂。 为了方便,也是代码整洁,就用到了自定义注解。
首先,有几个点需要说明:
1,我需要监控指定路径的变化,比如:文件的删除、文件的增加、修改等。 当文件删除时,我需要销毁工厂中的实例,并发出预警,做好备份。 当文件新增时(可能是class文件,也可能是jar包等)我需要逐层扫描注解,将新的实例注入到工厂备用 等等
2,目前做的实现,只写了class文件的递归扫描, 而且文件的监控,还没有写具体的响应方法
3,基本步骤:首先:定义注解;其次:解析注解
一、定义注解
import java.lang.annotation.*;
/**
* 自定义excel模板注入注解
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
@Documented
public @interface ExcelTemplate {
//类名—必须录入
String name();
//提供当前模板解析的人
String author() default "Angelina";
//当前模板被提供的日期
String supportedDate();
}
/**
* 自定义excel模板注入注解
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
@Documented
public @interface ExcelTemplate {
//类名—必须录入
String name();
//提供当前模板解析的人
String author() default "Angelina";
//当前模板被提供的日期
String supportedDate();
}
如果是必须录入的属性,就不要提供默认值,其余的一些属性,就自己查查吧 PS:最开始还以为这个玩意儿和之前写的一个权限注解一样的,原来当时用的Jeddict里面,提供了一些框架整合的东西,尴尬了。
二、注解解析
/**
* 解析注解
*
* @param packageName 包名
* @throws Exception
*/
public void parsingAnnotation(String packageName) throws Exception {
List<Class<?>> classes = getAllClass(packageName);
if (!classes.isEmpty()) {
AbstractExcelFactory excelFactory = AbstractExcelFactory.getInstance();
for (Class _class : classes) {
if (_class.isAnnotationPresent(ExcelTemplate.class)) {
ExcelTemplate excelTemplate = (ExcelTemplate) _class.getAnnotation(ExcelTemplate.class);
//获取注解中的类名称
String name = excelTemplate.name();
String strBeanName=_class.getName();
// 注册
excelFactory.registryExcelReaderBean(name, strBeanName);
}
}
}
}
* 解析注解
*
* @param packageName 包名
* @throws Exception
*/
public void parsingAnnotation(String packageName) throws Exception {
List<Class<?>> classes = getAllClass(packageName);
if (!classes.isEmpty()) {
AbstractExcelFactory excelFactory = AbstractExcelFactory.getInstance();
for (Class _class : classes) {
if (_class.isAnnotationPresent(ExcelTemplate.class)) {
ExcelTemplate excelTemplate = (ExcelTemplate) _class.getAnnotation(ExcelTemplate.class);
//获取注解中的类名称
String name = excelTemplate.name();
String strBeanName=_class.getName();
// 注册
excelFactory.registryExcelReaderBean(name, strBeanName);
}
}
}
}
解析注解,其实就很简单,就跟实现一个接口一样:确定当扫描到此注解时,需要执行的工作。 比如说,此处:当我扫描到ExcelTemplate注解时,将其类实例注入到工厂。
三、辅助查找所有包下的class文件方法
/**
* 获取包里所有的class文件
*
* @return
* @throws IOException
*/
public List<Class<?>> getAllClass(String packageName) throws IOException {
Enumeration<URL> enumeration = Thread.currentThread().getContextClassLoader().getResources(packageName);
List<Class<?>> classes = new ArrayList<Class<?>>();
while (enumeration.hasMoreElements()) {
URL url = enumeration.nextElement();
String protocol = url.getProtocol();
if ("file".equals(protocol)) {
String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
getAllFile(packageName, filePath, classes);
}
}
return classes;
}
/**
* 获取目录下的所有文件
*
* @param packageName 包名
* @param packagePath 包的路径
* @param classes 包下的所有类文件
*/
public void getAllFile(String packageName, String packagePath, List<Class<?>> classes) {
File fileDirector = new File(packagePath);
File[] files = fileDirector.listFiles(new FileFilter() {
//自定义过滤规则
@Override
public boolean accept(File file) {
return (true && file.isDirectory()) || (file.getName().endsWith(".class"));
}
});
for (File file : files) {
if (file.isDirectory()) {
String newPackageName = packageName + "." + file.getName();
String newPackagePath = file.getAbsolutePath();
getAllFile(newPackageName, newPackagePath, classes);
} else {
String className = file.getName().substring(0, file.getName().length() - 6);
try {
classes.add(Class.forName(packageName + '.' + className));
} catch (ClassNotFoundException e) {
throw new ExcelException(ExceptionEnum.FAILDFINDTEMPLATE.getCode(),
ExceptionEnum.FAILDFINDTEMPLATE.getName());
}
}
}
}
* 获取包里所有的class文件
*
* @return
* @throws IOException
*/
public List<Class<?>> getAllClass(String packageName) throws IOException {
Enumeration<URL> enumeration = Thread.currentThread().getContextClassLoader().getResources(packageName);
List<Class<?>> classes = new ArrayList<Class<?>>();
while (enumeration.hasMoreElements()) {
URL url = enumeration.nextElement();
String protocol = url.getProtocol();
if ("file".equals(protocol)) {
String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
getAllFile(packageName, filePath, classes);
}
}
return classes;
}
/**
* 获取目录下的所有文件
*
* @param packageName 包名
* @param packagePath 包的路径
* @param classes 包下的所有类文件
*/
public void getAllFile(String packageName, String packagePath, List<Class<?>> classes) {
File fileDirector = new File(packagePath);
File[] files = fileDirector.listFiles(new FileFilter() {
//自定义过滤规则
@Override
public boolean accept(File file) {
return (true && file.isDirectory()) || (file.getName().endsWith(".class"));
}
});
for (File file : files) {
if (file.isDirectory()) {
String newPackageName = packageName + "." + file.getName();
String newPackagePath = file.getAbsolutePath();
getAllFile(newPackageName, newPackagePath, classes);
} else {
String className = file.getName().substring(0, file.getName().length() - 6);
try {
classes.add(Class.forName(packageName + '.' + className));
} catch (ClassNotFoundException e) {
throw new ExcelException(ExceptionEnum.FAILDFINDTEMPLATE.getCode(),
ExceptionEnum.FAILDFINDTEMPLATE.getName());
}
}
}
}
这里只是处理了.class文件,如果要解析jar包,则需要做响应的判断和读取,如 if("file".equals(protocol)) 类似的判断,随后做相应的逻辑处理
四、总结
书到用时方恨少,但什么时候学习都不晚。 注解,是通过统一集中处理,从而给开发带来方便,也使得代码更为整洁。 但怎么说呢,凡事看需求吧,因为有时候注解是很方便,但在一些逻辑识别的时候,也有一定的阻碍。
有时候我就在想,我们平时用到的一系列注解,到底都做了什么?它是怎么做到的?它的这种做法,是不是更好的?
在做这个注解的过程中,一个很大的体会: 既然要利用我提供的便利,那就得遵守我定下的规则! 一切都是取舍平衡
附录:文件监控
1,定义文件变动时事件
import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;
import java.io.File;
/**
* 文件监听类—新增、修改、删除excel模板
*/
public class FileListener extends FileAlterationListenerAdaptor {
@Override
public void onFileCreate(File file) {
System.out.println("[新建]:" + file.getAbsolutePath());
System.out.println("此时,调用注入方法,注入新添加的excel模板解析类");
}
@Override
public void onFileChange(File file) {
System.out.println("[修改]:" + file.getAbsolutePath());
System.out.println("日志记录,警告");
System.out.println("此时,销毁旧的excel模板解析类,重新加载新的,但命名一定要能区分出更改的文件");
}
@Override
public void onFileDelete(File file) {
System.out.println("[删除]:" + file.getAbsolutePath());
System.out.println("此时,发布异常");
}
}
import java.io.File;
/**
* 文件监听类—新增、修改、删除excel模板
*/
public class FileListener extends FileAlterationListenerAdaptor {
@Override
public void onFileCreate(File file) {
System.out.println("[新建]:" + file.getAbsolutePath());
System.out.println("此时,调用注入方法,注入新添加的excel模板解析类");
}
@Override
public void onFileChange(File file) {
System.out.println("[修改]:" + file.getAbsolutePath());
System.out.println("日志记录,警告");
System.out.println("此时,销毁旧的excel模板解析类,重新加载新的,但命名一定要能区分出更改的文件");
}
@Override
public void onFileDelete(File file) {
System.out.println("[删除]:" + file.getAbsolutePath());
System.out.println("此时,发布异常");
}
}
2,实施监控
import org.apache.commons.io.filefilter.FileFilterUtils;
import org.apache.commons.io.monitor.FileAlterationMonitor;
import org.apache.commons.io.monitor.FileAlterationObserver;
import java.util.concurrent.TimeUnit;
/**
* 容器监听—根据文件监听的变化而变化
*/
public class ContainerListener {
public static void main(String[] args) throws Exception{
// 监控目录
String rootDir = "example";
// 轮询间隔 7 天—视情况而定
//long interval = TimeUnit.DAYS.toDays(7);
long interval=TimeUnit.SECONDS.toMillis(10);
FileAlterationObserver observer = new FileAlterationObserver(
rootDir,
//设置监控过滤
FileFilterUtils.and(
FileFilterUtils.fileFileFilter(),
FileFilterUtils.suffixFileFilter(".java")),
null);
observer.addListener(new FileListener());
FileAlterationMonitor monitor = new FileAlterationMonitor(interval, observer);
// 开始监控
monitor.start();
}
}
import org.apache.commons.io.monitor.FileAlterationMonitor;
import org.apache.commons.io.monitor.FileAlterationObserver;
import java.util.concurrent.TimeUnit;
/**
* 容器监听—根据文件监听的变化而变化
*/
public class ContainerListener {
public static void main(String[] args) throws Exception{
// 监控目录
String rootDir = "example";
// 轮询间隔 7 天—视情况而定
//long interval = TimeUnit.DAYS.toDays(7);
long interval=TimeUnit.SECONDS.toMillis(10);
FileAlterationObserver observer = new FileAlterationObserver(
rootDir,
//设置监控过滤
FileFilterUtils.and(
FileFilterUtils.fileFileFilter(),
FileFilterUtils.suffixFileFilter(".java")),
null);
observer.addListener(new FileListener());
FileAlterationMonitor monitor = new FileAlterationMonitor(interval, observer);
// 开始监控
monitor.start();
}
}