【Java】用註解實現注入
在Spring中,可以通過包掃描,找到帶有註解的類和方法,通過反射機制進行注入;
接下來會仿照這種模式,簡單模擬其原理,完成核心效果:
類標識的註解,只有帶有該標識,才進行之後方法的掃描,否則不進行:
1 import java.lang.annotation.ElementType; 2 import java.lang.annotation.Retention; 3 import java.lang.annotation.RetentionPolicy; 4 import java.lang.annotation.Target; 5 6 @Retention(RetentionPolicy.RUNTIME) 7 @Target(ElementType.TYPE) 8 public @interface Service { 9 }
方法的註解, 必須對註解中的action賦值,往後我們是要將action的值作為map中的鍵:
1 import java.lang.annotation.ElementType; 2 import java.lang.annotation.Retention; 3 import java.lang.annotation.RetentionPolicy; 4 import java.lang.annotation.Target; 5 6 @Retention(RetentionPolicy.RUNTIME) 7 @Target(ElementType.METHOD) 8 public @interface Actioner { 9String action(); 10 }
方法引數的註解,同樣要對其name賦值,為了以後能夠找到對應的引數,完成賦值:
1 import java.lang.annotation.ElementType; 2 import java.lang.annotation.Retention; 3 import java.lang.annotation.RetentionPolicy; 4 import java.lang.annotation.Target; 5 6 @Retention(RetentionPolicy.RUNTIME) 7 @Target(ElementType.PARAMETER) 8 public @interface AParameter { 9String name(); 10 }
我們需要將方法抽象成一個類,封裝起來:
1 import java.lang.reflect.Method; 2 import java.lang.reflect.Parameter; 3 import java.util.List; 4 5 public class ActionDefination { 6private Class<?> klass; // 該方法所對應的類 7private Object object;// 執行該方法的物件 8private Method method; // 該方法 9private List<Parameter> paramerterList;// 該方法的所有引數 10 11protected ActionDefination(Class<?> klass, Object object, Method method, List<Parameter> paramerterList) { 12this.klass = klass; 13this.object = object; 14this.method = method; 15this.paramerterList = paramerterList; 16} 17 18protected Class<?> getKlass() { 19return klass; 20} 21 22protected Object getObject() { 23return object; 24} 25 26protected Method getMethod() { 27return method; 28} 29 30protected List<Parameter> getParamerterList() { 31return paramerterList; 32} 33 34 }
所有準備工作都做好了,我們就需要通過包掃描的方式,找到帶有Service 註解的類,然後找到其中帶有Actioner 註解的方法,並且得到帶有註解的所有引數,若其某一引數沒有帶註解,則應該異常處理!
掃描包下符合要求的所有內容,最後形成一張map
1 import java.lang.reflect.Method; 2 import java.lang.reflect.Parameter; 3 import java.util.ArrayList; 4 import java.util.HashMap; 5 import java.util.List; 6 import java.util.Map; 7 8 public class ActionFactory { 9private static final Map<String, ActionDefination> actionDefinationMap = new HashMap<String, ActionDefination>(); 10 11// 單例模式 12private ActionFactory() { 13} 14public static ActionFactory newInstance() { 15return creatNewInstance.actionFactory; 16} 17 18private static class creatNewInstance { 19private static final ActionFactory actionFactory = new ActionFactory(); 20} 21 22// 通過類,掃描其所在包下的所有檔案 23public void scanAction(Class<?> klass) { 24scanAction(klass.getPackage().getName()); 25} 26 27//通過包名稱,掃描其下所有檔案 28public void scanAction(String packageName) { 29// 包掃描,在我的上一篇部落格有該方法的實現 30new PackageScanner() { 31 32@Override 33public void dealClass(Class<?> klass) { 34// 只處理帶有Service註解的類 35if (!klass.isAnnotationPresent(Service.class)) { 36return; 37} 38try { 39// 直接由反射機制產生一個物件,將其注入 40Object object = klass.newInstance(); 41// 掃描改類下的所有方法 42scanMethod(klass, object); 43} catch (Exception e) { 44e.printStackTrace(); 45} 46} 47}.scanPackage(packageName); 48} 49 50// 通過物件,掃描其所有方法 51public void scanAction(Object object) { 52try { 53scanMethod(object.getClass(), object); 54} catch (Exception e) { 55e.printStackTrace(); 56} 57} 58 59private void scanMethod(Class<?> klass, Object object) throws Exception { 60// 得到所有方法 61Method[] methods = klass.getDeclaredMethods(); 62 63// 遍歷所有方法,找到帶有Actioner註解的方法,並得到action的值 64for (Method method : methods) { 65if (!method.isAnnotationPresent(Actioner.class)) { 66continue; 67} 68Actioner actioner = method.getAnnotation(Actioner.class); 69String action = actioner.action(); 70 71// 判斷action是否已經定義 72if (actionDefinationMap.get(action) != null) { 73throw new ActionHasDefineException("方法" + action + "已定義!"); 74} 75 76// 得到所有引數,並判斷引數是否滿足要求 77Parameter[] parameters = method.getParameters(); 78List<Parameter> parameterList = new ArrayList<Parameter>(); 79for (int i = 0; i < parameters.length; i++) { 80Parameter parameter = parameters[i]; 81if (!parameters[i].isAnnotationPresent(AParameter.class)) { 82throw new MethodParameterNotDefineException("第" + (i+1) + "個引數未定義!"); 83} 84 85parameterList.add(parameter); 86} 87// 將得到的結果新增到map中 88addActionDefination(action, klass, object, method, parameterList); 89} 90} 91 92private void addActionDefination(String action, Class<?> klass, Object object, Method method, List<Parameter> parameterList) { 93ActionDefination actionDefination = new ActionDefination(klass, object, method, parameterList); 94actionDefinationMap.put(action, actionDefination); 95} 96 97protected ActionDefination getActionDefination(String action) { 98return actionDefinationMap.get(action); 99} 100 101 }
上述的ActionFactory可以幫助我們掃描到包下所有符合要求的方法,接下來就是通過傳遞引數執行這些方法。
要注意,這套工具的出發點是搭載在網路上,所以傳遞的引數就只能是字串或者位元組流的形式,所以,我們應該對傳遞的引數進行處理,將其生成能供我們識別的形式。
這裡我們將引數轉換為字串的形式,我會用到gson,方便我們將物件轉換為gson字串:
1 import java.util.HashMap; 2 import java.util.Map; 3 4 import com.google.gson.Gson; 5 import com.google.gson.GsonBuilder; 6 7 public class ArgumentMaker { 8// 註解AParameter中name的值 +引數物件轉換成的gson字串所形成的map 9private Map<String, String> argumentMap; 10private Gson gson; 11 12public ArgumentMaker() { 13gson = new GsonBuilder().create(); 14argumentMap = new HashMap<String, String>(); 15} 16 17// 其name就是註解AParameter中name的值,value就是引數的具體值 18public ArgumentMaker add(String name, Object value) { 19// 通過gson將引數物件轉換為gson字串 20argumentMap.put(name, gson.toJson(value)); 21return this; 22} 23 24// 將得到的name + 引數物件轉換成的gson字串map再次轉換成gson字串,以便於進行傳輸 25public String toOgnl() { 26if (argumentMap.isEmpty()) { 27return null; 28} 29 30return gson.toJson(argumentMap); 31} 32 33 }
接下來就是處理具體的action
1 import java.lang.reflect.Method; 2 import java.lang.reflect.Parameter; 3 import java.lang.reflect.Type; 4 import java.util.List; 5 import java.util.Map; 6 7 import com.google.gson.Gson; 8 import com.google.gson.GsonBuilder; 9 import com.google.gson.reflect.TypeToken; 10 11 public class Addition { 12private static final Gson gson; 13private static final Type type; 14 15static { 16gson = new GsonBuilder().create(); 17// 可以得到帶有泛型的map型別 18type = new TypeToken<Map<String, String>>(){}.getType(); 19} 20 21public String doRequest(String action, String parameter) throws Exception { 22ActionDefination ad = ActionFactory.newInstance().getActionDefination(action); 23 24if (ad == null) { 25throw new ActionNotDefineException("方法" + action + "未定義!"); 26} 27 28Object object = ad.getObject(); 29Method method = ad.getMethod(); 30 31Object[] parameters = getParameterArr(parameter, ad.getParamerterList()); 32Object result = method.invoke(object, parameters); 33 34return gson.toJson(result); 35} 36 37private Object[] getParameterArr(String parameterString, List<Parameter> parameterList) { 38Object[] results = new Object[parameterList.size()]; 39// 將字串形式的引數,轉換成map 40Map<String, String> parameterStringMap = gson.fromJson(parameterString, type); 41 42int index = 0; 43for (Parameter parameter : parameterList) { 44// 得到引數的註解AParameter中name的值 45String key = parameter.getAnnotation(AParameter.class).name(); 46 47// 以name的值為鍵,找到引數map中value,再通過gson將其從字串轉換成具體的物件 48Object value = gson.fromJson(parameterStringMap.get(key), 49// 得到引數的具體型別 50parameter.getParameterizedType()); 51 52results[index++] = value; 53} 54 55return results; 56}
演示如何使用
1 @Service 2 public class Demo { 3 4@Actioner(action="one") 5public void fun() { 6System.out.println("執行無參的fun方法"); 7} 8 9@Actioner(action="two") 10public void fun(@AParameter(name="1")int parameter) { 11System.out.println("執行單參的fun方法: parameter = " + parameter); 12} 13 14@Actioner(action="three") 15public void fun(@AParameter(name="1")int one, 16@AParameter(name="2")String two, 17@AParameter(name="3")boolean three) { 18System.out.println("執行三參的fun方法: one = " + one + " two = " + two + " three = " + three); 19} 20 21private static class Student { 22private String name; 23private int age; 24private boolean sex; 25 26private Student(String name, int age, boolean sex) { 27this.name = name; 28this.age = age; 29this.sex =sex; 30} 31 32@Override 33public String toString() { 34return "name = " + name + ", age = " + age + ", sex = " + (sex ? "男" : "女"); 35} 36} 37 38@Actioner(action="four") 39public void fun(@AParameter(name="1")Student student) { 40System.out.println("執行復雜型別引數的fun方法 :" + student); 41} 42 43public static void main(String[] args) throws Exception { 44// 掃描包,這裡直接掃描Demo所在的包 45ActionFactory.newInstance().scanAction(Demo.class); 46 47Addition addition = new Addition(); 48 49addition.doRequest("one", null); 50 51addition.doRequest("two", new ArgumentMaker() 52.add("1", 3) 53.toOgnl()); 54 55addition.doRequest("three",new ArgumentMaker() 56.add("3", true) 57.add("1", 3) 58.add("2", "這是第二個引數") 59.toOgnl()); 60 61Student student = new Student("小明", 15, true); 62addition.doRequest("four", new ArgumentMaker() 63.add("1", student) 64.toOgnl()); 65} 66 67 }
執行結果