【Java】用注解实现注入

在Spring中,可以通过包扫描,找到带有注解的类和方法,通过反射机制进行注入;
接下来会仿照这种模式,简单模拟其原理,完成核心效果:

类标识的注解,只有带有该标识,才进行之后方法的扫描,否则不进行:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Service {
}

方法的注解, 必须对注解中的action赋值,往后我们是要将action的值作为map中的键:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Actioner {
	String action();
}

方法参数的注解,同样要对其name赋值,为了以后能够找到对应的参数,完成赋值:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface AParameter {
	String name();
}

我们需要将方法抽象成一个类,封装起来:

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.List;

public class ActionDefination {
	private Class<?> klass; 			// 该方法所对应的类
	private Object object; 				// 执行该方法的对象
	private Method method; 			    // 该方法
	private List<Parameter> paramerterList;		// 该方法的所有参数
	
	protected ActionDefination(Class<?> klass, Object object, Method method, List<Parameter> paramerterList) {
		this.klass = klass;
		this.object = object;
		this.method = method;
		this.paramerterList = paramerterList;
	}

	protected Class<?> getKlass() {
		return klass;
	}

	protected Object getObject() {
		return object;
	}

	protected Method getMethod() {
		return method;
	}

	protected List<Parameter> getParamerterList() {
		return paramerterList;
	}
	
}

所有准备工作都做好了,我们就需要通过包扫描的方式,找到带有Service 注解的类,然后找到其中带有Actioner 注解的方法,并且得到带有注解的所有参数,若其某一参数没有带注解,则应该异常处理!

扫描包下符合要求的所有内容,最后形成一张map

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ActionFactory {
	private static final Map<String, ActionDefination> actionDefinationMap = new HashMap<String, ActionDefination>();
	
	// 单例模式
	private ActionFactory() {
	}
	public static ActionFactory newInstance() {
		return creatNewInstance.actionFactory;
	} 
	
	private static class creatNewInstance {
		private static final ActionFactory actionFactory = new ActionFactory(); 
	}
	
	// 通过类,扫描其所在包下的所有文件
	public void scanAction(Class<?> klass) {
		scanAction(klass.getPackage().getName());
	}
	
	//  通过包名称,扫描其下所有文件
	public void scanAction(String packageName) {
		// 包扫描,在我的上一篇博客有该方法的实现
		new PackageScanner() {
			
			@Override
			public void dealClass(Class<?> klass) {
				// 只处理带有Service注解的类
				if (!klass.isAnnotationPresent(Service.class)) {
					return;
				}
				try {
					// 直接由反射机制产生一个对象,将其注入
					Object object = klass.newInstance();
					// 扫描改类下的所有方法
					scanMethod(klass, object);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}.scanPackage(packageName);
	}
	
	// 通过对象,扫描其所有方法
	public void scanAction(Object object) {
		try {
			scanMethod(object.getClass(), object);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void scanMethod(Class<?> klass, Object object) throws Exception {
		// 得到所有方法
		Method[] methods = klass.getDeclaredMethods();
		
		// 遍历所有方法,找到带有Actioner注解的方法,并得到action的值
		for (Method method : methods) {
			if (!method.isAnnotationPresent(Actioner.class)) {
				continue;
			}
			Actioner actioner = method.getAnnotation(Actioner.class);
			String action = actioner.action();
			
			// 判断action是否已经定义
			if (actionDefinationMap.get(action) != null) {
				throw new ActionHasDefineException("方法" + action + "已定义!");
			}
			
			// 得到所有参数,并判断参数是否满足要求
			Parameter[] parameters = method.getParameters();
			List<Parameter> parameterList = new ArrayList<Parameter>();
			for (int i = 0; i < parameters.length; i++) {
				Parameter parameter = parameters[i];
				if (!parameters[i].isAnnotationPresent(AParameter.class)) {
					throw new MethodParameterNotDefineException("第" + (i+1) + "个参数未定义!");
				}
				
				parameterList.add(parameter);
			}
			// 将得到的结果添加到map中
			addActionDefination(action, klass, object, method, parameterList);
		}
	}
	
		private void addActionDefination(String action, Class<?> klass, Object object, Method method, List<Parameter> parameterList) {
		ActionDefination actionDefination = new ActionDefination(klass, object, method, parameterList);
		actionDefinationMap.put(action, actionDefination);
	}
		
	protected ActionDefination getActionDefination(String action) {
		return actionDefinationMap.get(action);
	}
	
}

上述的ActionFactory可以帮助我们扫描到包下所有符合要求的方法,接下来就是通过传递参数执行这些方法。
要注意,这套工具的出发点是搭载在网络上,所以传递的参数就只能是字符串或者字节流的形式,所以,我们应该对传递的参数进行处理,将其生成能供我们识别的形式。
这里我们将参数转换为字符串的形式,我会用到gson,方便我们将对象转换为gson字符串:

import java.util.HashMap;
import java.util.Map;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

public class ArgumentMaker {
	// 注解AParameter中name的值 +  参数对象转换成的gson字符串所形成的map
	private Map<String, String> argumentMap;
	private Gson gson;
	
	public ArgumentMaker() {
		gson = new GsonBuilder().create();
		argumentMap = new HashMap<String, String>();
	}
	
	// 其name就是注解AParameter中name的值,value就是参数的具体值
	public ArgumentMaker add(String name, Object value) {
		// 通过gson将参数对象转换为gson字符串
		argumentMap.put(name, gson.toJson(value));
		return this;
	}
	
	// 将得到的name + 参数对象转换成的gson字符串map再次转换成gson字符串,以便于进行传输
	public String toOgnl() {
		if (argumentMap.isEmpty()) {
			return null;
		}
		
		return gson.toJson(argumentMap);
	}
	
}

接下来就是处理具体的action

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;

public class Addition {
	private static final Gson gson;
	private static final Type type;
	
	static {
		gson = new GsonBuilder().create();
		// 可以得到带有泛型的map类型 
		type = new TypeToken<Map<String, String>>(){}.getType();
	}
	
	public String doRequest(String action, String parameter) throws Exception {
		ActionDefination ad = ActionFactory.newInstance().getActionDefination(action);
		
		if (ad == null) {
			throw new ActionNotDefineException("方法" + action + "未定义!");
		}
		
		Object object = ad.getObject();
		Method method = ad.getMethod();
		
		Object[] parameters = getParameterArr(parameter, ad.getParamerterList());
		Object result = method.invoke(object, parameters);
		
		return gson.toJson(result);
	}
	
	private Object[] getParameterArr(String parameterString, List<Parameter> parameterList) {
		Object[] results = new Object[parameterList.size()];
		// 将字符串形式的参数,转换成map
		Map<String, String> parameterStringMap = gson.fromJson(parameterString, type);
		
		int index = 0;
		for (Parameter parameter : parameterList) {
			// 得到参数的注解AParameter中name的值
			String key = parameter.getAnnotation(AParameter.class).name();
			
			// 以name的值为键,找到参数map中value,再通过gson将其从字符串转换成具体的对象
			Object value = gson.fromJson(parameterStringMap.get(key),
					// 得到参数的具体类型 
					parameter.getParameterizedType());
			
			results[index++] = value;
		}
		
		return results;
	}

演示如何使用

@Service
public class Demo {

	@Actioner(action="one")
	public void fun() {
		System.out.println("执行无参的fun方法");
	}
	
	@Actioner(action="two")
	public void fun(@AParameter(name="1")int parameter) {
		System.out.println("执行单参的fun方法: parameter = " + parameter);
	}
	
	@Actioner(action="three")
	public void fun(@AParameter(name="1")int one, 
			@AParameter(name="2")String two, 
			@AParameter(name="3")boolean three) {
		System.out.println("执行三参的fun方法: one = " + one + " two = " + two + " three = " + three);
	}
	
	private static class Student {
		private String name;
		private int age;
		private boolean sex;
		
		private Student(String name, int age, boolean sex) {
			this.name = name;
			this.age = age;
			this.sex =sex;
		}
		
		@Override
		public String toString() {
			return "name = " + name + ", age = " + age + ", sex = " + (sex ? "男" : "女");
		}
	}
	
	@Actioner(action="four")
	public void fun(@AParameter(name="1")Student student) {
		System.out.println("执行复杂类型参数的fun方法 :" + student);
	}
	
	public static void main(String[] args) throws Exception {
		// 扫描包,这里直接扫描Demo所在的包
		ActionFactory.newInstance().scanAction(Demo.class);
		
		Addition addition = new Addition();
		
		addition.doRequest("one", null);
		
		addition.doRequest("two", new ArgumentMaker()
				.add("1", 3)
				.toOgnl());
		
		addition.doRequest("three",new ArgumentMaker()
				.add("3", true)
				.add("1", 3)
				.add("2", "这是第二个参数")
				.toOgnl());
	
		Student student = new Student("小明", 15, true);
		addition.doRequest("four", new ArgumentMaker()
				.add("1", student)
				.toOgnl());
	}

}

运行结果
在这里插入图片描述
这样做,我们的这套工具就可以完全独立出来,只需要用户向我们的框架中注入规定的方法,而不去关心其具体实现,达到了解耦!主要在于找到其对应关系,也可以通过XML文件的方式形成对应关系,但是用XML文件对于其参数的处理就会比较麻烦,比如在structs2中,提供了带有无参方法的接口,让用户去实现这个接口,将方法和其成员的对应关系写入XML文件,其所需要的参数是利用其成员来完成的,要给其成员赋值,没有这种方式灵活。
我们可以在服务器和客户端分别写出请求和响应的方法,通过网络间字符串的传递,在服务器和客户端分别执行我们注入的方法!

猜你喜欢

转载自blog.csdn.net/Z_ChenChen/article/details/82945975