【小案例】
将src源文件中加了@Singleton注解的类都在程序启动时以【单例】的形式加载到内存。
首先我们来看一看本案例中创建的各个类(位置可以根据个人喜好随意):
首先 我们来创建一个用来 找出所有class文件,并将路径名转换为全限定名的工具类FileUtil
import java.io.File;
import java.util.List;
import java.util.ArrayList;
import java.util.stream.Collectors;
public class FileUtil {
public static List<String> getAllClassPath(File file){
List<String> classPaths = new ArrayList<>();
findAll(file.getAbsolutePath(),classPaths);
List<String> list = classPaths.stream().map(path -> {
return path.replace(file.getAbsolutePath()+"\\","")
.replaceAll("\\\\",".")
.replace(".class","");
}).collect(Collectors.toList());
return list;
}
private static void findAll(String path,List<String> list){
File file = new File(path);
File[] files = file.listFiles((f,n) -> new File(f,n).isDirectory() || n.contains(".class"));
//判断数组是否为空或长度是否为0
if(files == null || files.length == 0){
return;
}
//使用增强for循环迭代
for(File parent : files){
//判断是否为文件夹
if(parent.isDirectory()){
//是则递归
findAll(parent.getAbsolutePath(),list);
} else {
//不是则直接加入数组
list.add(parent.getAbsolutePath());
}
}
}
}
该类的findAllClassPath方法会返回一个泛型为String的List,存储了所有类的全限定名,接下来我们要做的就是通过这些全限定名,利用反射,得到加了@Singleton注解的类,并在程序启动时以单例的形式加载到内存。
我们先来添加一个Singleton.class
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Singleton {
}
再创建一个ApplicationContext类来存放最终我们要存放单例的Map映射,只能通过addSingleton和getSingleton来添加或获取单例.
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class ApplicationContext {
private static final Map<Class<?>,Object> context = new ConcurrentHashMap<>();
public static void addSingleton(Class<?> clazz, Object entity){
context.put(clazz,entity);
}
public static <T> T getSingleton(Class<T> clazz){
return (T)context.get(clazz);
}
}
最后我们写一个处理器SingletonHandler 用来找出加了@Singleton注解的类,并将其和其对应的单例放到我们的context中
import java.util.List;
public class SingletonHandler {
public static void handler(List<String> classPaths){
for(String classPath : classPaths){
Class<?> clazz = null;
try {
clazz = Class.forName(classPath);
} catch (ClassNotFoundException e){
e.printStackTrace();
}
Singleton annotation = null;
if(clazz != null) {
annotation = clazz.getAnnotation(Singleton.class);
}
if(annotation != null){
Object instance = null;
try{
instance = clazz.newInstance();
} catch (IllegalAccessException | InstantiationException e){
e.printStackTrace();
}
ApplicationContext.addSingleton(clazz,instance);
}
}
}
}
温馨提示:别忘了导包哦~
最后创建一个Bootstrap类,将上面那些工具类的调用放入静态代码块中
import java.net.URL;
import java.io.File;
import java.util.List;
import java.util.ArrayList;
public class Bootstrap {
static{
//尝试获取类路径
//获取当前线程,再获取当前的类加载器,若为空字符串则获取根路径
final URL resource = Thread.currentThread().getContextClassLoader().getResource("");
if(resource != null) {
//findAll(resource.getFile());
File file = new File(resource.getFile());
List<String> list = FileUtil.getAllClassPath(file);
SingletonHandler.handler(list);
}
}
public static void main(String[] args){
}
}
写到这里,那么恭喜你,小案例已经完成啦!
接下来我们来实验一下我们的成果:
创建一个Dog类,并为它加上@Singleton注解
@Singleton
public class Dog {
private String name;
public Dog() {
}
private Dog(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
'}';
}
}
写个main方法尝试运行一下:
public static void main(String[] args){
Dog dog = ApplicationContext.getSingleton(Dog.class);
System.out.println(dog);
}
我们来看看运行结果
成功啦!!