该案例将自定义注解、反射相关知识结合起来实现Spring的自动注入机制,希望能够对学习该方面知识的人员有所帮助!
由于这是本人第一次写技术类的文章,对待其相关步骤也是一头雾水,排版什么的毫无美感可言,所以还请见谅。但是代码会全部贴出。如果读者在运行期间有任何问题的话,也欢迎留言来一起讨论。联系QQ:1402343592
另外读者运行案例需要注意以下问题:
1.该案例只会扫描ClassLoaderContext所在包及子包中的class文件
1.自定义注解(用于标记要注入的属性)
package com.model.command1.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value=ElementType.FIELD)
public @interface AbstractInjection {
Class<?> value();
}
2.提供工厂接口类,以便实现类以不同的方式处理对象
package com.inject.ryu.chapter1.context;
public interface Context {
public abstract void initContext() throws ClassNotFoundException;
}
3.Spring工厂类,用于实现具体的操作
package com.inject.ryu.chapter1.context;
import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.inject.ryu.chapter1.util.ClassUtil;
import com.inject.ryu.chapter1.util.FileUtil;
/**
* 模拟Spring的对象工厂
*
* @author raoyu
*
*/
public class ClassLoaderContext implements Context {
//要扫描的注解类型
private Class injectType;
//装载所有的class文件
private static List<File> fileList ;
//对象工厂
private static Map<String, Object> objectFactory = new HashMap<>();
@Override
public void initContext() throws ClassNotFoundException {
//可配置在文件中
injectType = Class.forName("com.model.command1.annotation.AbstractInjection");
String filePath = this.getClass().getResource("").getFile();
// System.out.println(filePath);
fileList = FileUtil.getCurrentPathAllFile(filePath);
for (File file : fileList) {
String className = FileUtil.getClassNameByFile(file);
Class<?> clazz = ClassUtil.loadClassByName(className);
if(!clazz.isInterface()) {
autoWrired(clazz);
}
}
}
public void autoWrired(Class<?> clazz) {
try {
//当前类的对象
Object obj = null;
if(lookupDependency(clazz) != null) {
obj = objectFactory.get(clazz.getName());
}else {
obj = clazz.newInstance();
}
//拿到所有属性
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
//设置私有属性的可访问性
if (!field.isAccessible()) {
field.setAccessible(true);
}
//获取被指定类型所注解的属性
Annotation annotation = field.getAnnotation(injectType);
if (annotation != null) {
//获取注解中所有的方法
Method[] methods = annotation.annotationType().getDeclaredMethods();
for (Method method : methods) {
//获取当前属性的声明类型
Class<?> keyClass = field.getType();
//获取注解中的值的类型(要注入的类型)
Class<?> instanceClass = (Class<?>) method.invoke(annotation);
System.out.println("被注入的类型:"+instanceClass.getName());
Object injectObj = null;
//查找工厂是否存在同类型的对象
if(lookupDependency(keyClass) == null) {
injectObj = instanceClass.newInstance();
objectFactory.put(keyClass.getName(), injectObj);
}else {
injectObj = lookupDependency(keyClass);
}
//为属性赋值
field.set(obj, injectObj);
}
}
}
} catch (SecurityException e) {
System.err.println("不可访问底层类");
e.printStackTrace();
} catch (IllegalAccessException e) {
System.err.println("访问权限未设置");
e.printStackTrace();
} catch (IllegalArgumentException e) {
System.err.println("调用的目标对象所需的参数不匹配");
e.printStackTrace();
} catch (InvocationTargetException e) {
System.err.println("反射调用的目标对象不存在");
e.printStackTrace();
} catch (InstantiationException e) {
System.err.println("反射实例化对象异常");
e.printStackTrace();
}
}
/**
* 用于依赖查找
* @param keyClass
* @return
* @throws InstantiationException
* @throws IllegalAccessException
*/
private Object lookupDependency(Class<?> keyClass) throws InstantiationException, IllegalAccessException{
return objectFactory.get(keyClass.getName());
}
}
4.测试相关的实体类
package com.inject.ryu.chapter1.api;
/**
* 命令模式,类似于状态模式,均可用于自由组合状态链
* 命令接口,执行命令
* @author raoyu
*
*/
public abstract class Command {
public abstract void operation();
}
package com.inject.ryu.chapter1.impl;
import com.inject.ryu.chapter1.api.Command;
public class SoupCommand extends Command{
@Override
public void operation() {
// TODO Auto-generated method stub
System.out.println("上个汤-----");
}
}
package com.inject.ryu.chapter1.api;
/**
* 用于执行命令
* @author raoyu
* @since 2018/01/17
*/
public interface Executor<T> {
/**
* 执行一个操作
* @param t
*/
public abstract void execute(T t);
}
package com.inject.ryu.chapter1.impl;
import com.inject.ryu.chapter1.api.Command;
import com.inject.ryu.chapter1.api.Executor;
/**
* 厨师
* @author raoyu
*
*/
public class Kitchener implements Executor<Command>{
public Kitchener() {
// TODO Auto-generated constructor stub
}
@Override
public void execute(Command command) {
System.out.println("===========执行命令===========");
command.operation();
}
}
5.要注入的类,对于需要的依赖属性也会一并注入
package com.inject.ryu.chapter1.context;
import com.inject.ryu.chapter1.api.Command;
import com.inject.ryu.chapter1.api.Executor;
import com.inject.ryu.chapter1.impl.Kitchener;
import com.model.command1.annotation.AbstractInjection;
public class TestAnnotation {
@AbstractInjection(value = Kitchener.class)
private Executor<Command> executor;
public Executor<Command> getExecutor() {
return executor;
}
}
6.相关的工具类
package com.inject.ryu.chapter1.util;
public class ClassUtil {
public static Class<?> loadClassByName(String className) throws ClassNotFoundException{
Class<?> clazz = Thread.currentThread().getContextClassLoader().loadClass(className);
System.out.println("====被装载的类=======" + clazz.getName());
return clazz;
}
}
package com.inject.ryu.chapter1.util;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class FileUtil {
private static List<File> fileList = new ArrayList<>();
public static boolean isClassFile(File file) {
return isClassFile(file.getName());
}
public static boolean isClassFile(String fileName) {
return fileName.endsWith(".class");
}
public static String getClassNameByFile(File file){
String filePath = file.getAbsolutePath().replace("\\", "/");
String className = filePath.split("bin/")[1].replaceAll("/", ".").replaceAll(".class", "");
return className;
}
/**
* 递归查找当前类所在包下的所有文件
* @param rootPath
* @return
*/
public static List<File> getCurrentPathAllFile(String rootPath) {
File[] files = new File(rootPath).listFiles();
for (File file : files) {
if (file.isDirectory()) {
getCurrentPathAllFile(file.getAbsolutePath());
} else if(isClassFile(file)){
fileList.add(file);
}
}
return fileList;
}
public static void main(String[] args) {
System.out.println(isClassFile("jj.class.1"));
}
}
7.测试类
package com.inject.ryu.chapter1.context;
import com.inject.ryu.chapter1.api.Command;
import com.inject.ryu.chapter1.impl.SoupCommand;
import com.model.command1.annotation.AbstractInjection;
public class Client {
@AbstractInjection(value = TestAnnotation.class)
private static TestAnnotation testAnnotation;
@AbstractInjection(value = SoupCommand.class)
private static Command command ;
public static void main(String[] args) throws ClassNotFoundException {
Context context = new ClassLoaderContext();
context.initContext();
testAnnotation.getExecutor().execute(command);
}
}