【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 {
ActionFactory.newInstance().scanAction("com.zc.server_client.actioner.demo");
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中,提供了帶有無參方法的介面,讓使用者去實現這個介面,而其所需要的引數是利用其成員來完成的,要給其成員賦值! 我們可以在伺服器和客戶端分別寫出請求和響應的方法,通過網路間字串的傳遞,在伺服器和客戶端分別執行我們注入的方法!