java反射机制初步认识<四>注解+反射形成简易IOC
2015-05-05 14:38
489 查看
在经过前3篇对注解和反射的入门讲解之后,我们进行一次注解与反射的综合练习,制作一个自己的简易IOC,实现方法的自动回调,以及参数解析,,假设我们有一个账户(Account)对象,同时有一个账户系统(AccountSystem)对象类对账户进行管理,我们将这个管理类及类中的管理方法进行注解,已达到方法的自动调用。
1.首先,我们定义Account:
package annotation; public class Account { /** 账户id */ private int accId; /** 账户名称 */ private String name; /** 账户卡号 */ private long cardId; /** 账户余额 */ private long money; public Account() { } public Account(int accId, String name, long cardId) { this.accId = accId; this.name = name; this.cardId = cardId; } public int getAccId() { return accId; } public void setAccId(int accId) { this.accId = accId; } public String getName() { return name; } public void setName(String name) { this.name = name; } public long getCardId() { return cardId; } public void setCardId(long cardId) { this.cardId = cardId; } public long getMoney() { return money; } public void setMoney(long money) { this.money = money; } @Override public String toString() { return "[accId=" + accId + "],[name=" + name + "],[cardId=" + cardId + "],[money=" + money + "]"; } }
2.我们定义一个作用于”管理系统类”的注解,将每个管理系统的一个实例对象作为一个”Bean”放进IOC,本次演示中只定义了一个“管理系统类”-账户管理系统,实际应用中,我们会定义许多“管理系统类”,但是如何反射并获取一个class对象,形成一个实例放进IOC,其原理都是一样的。
package annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited public @interface Bean { // 对象管理者名称 public String beanName(); }
3.定义一个作用于“管理系统类”的方法的注解:
package annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface Listener {
// 方法编号
public int listenID();
}[/code]
4.定义一个作用于“管理系统类”的方法的形参的注解:
package annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ ElementType.FIELD, ElementType.PARAMETER }) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited public @interface Param { // 形参名称 public String pname() default ""; }<span style="font-family: Consolas;"> </span>
5.为了统一管理,我们先顶一个一个抽象的”系统管理”接口,要求每个“系统管理类”必须实现这个接口:
package annotation; public interface Isystem { // 系统初始化方法 public void init(); }6.定义我们的”账户管理系统”:
package annotation; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @Bean(beanName = "accountSystem") public class AccountSystem implements Isystem { private static AccountSystem accountSystem = new AccountSystem(); public static Map<Integer, Account> accountMap; public void init() { accountMap = new ConcurrentHashMap<>(); System.out.println("初始化账户系统over"); } public static AccountSystem getInstance() { return accountSystem; } @Listener(listenID = 10010) public Account createAccount(Account account) { Account newAcc = new Account(account.getAccId(), account.getName(), account.getCardId()); accountMap.put(account.getAccId(), newAcc); System.out.println("创建新账户:" + account.getName() + "成功:"); return newAcc; } @Listener(listenID = 10011) public Account addMoney(@Param(pname = "accId") int accId, Account account) { Account res = accountMap.get(accId); if (res == null) { return account; } System.out.println("账户:" + res.getName() + "余额:" + res.getMoney()); res.setMoney(res.getMoney() + 10000000000L); System.out.println("账户:" + res.getName() + "余额:" + res.getMoney()); return res; } }7.我们定义一个回调对象,用于存放所有”管理系统”的注解方法以及回调对象:
package annotation; import java.lang.reflect.Method; public class CallBack { /** 回调产生的结果 */ private Object object; /** 回调方法 */ private Method method; public CallBack() { } public CallBack(Object object, Method method) { this.object = object; this.method = method; } public Object getObject() { return object; } public void setObject(Object object) { this.object = object; } public Method getMethod() { return method; } public void setMethod(Method method) { this.method = method; } }8.这一步非常关键,我们定义自己的IOC,并实现自动扫描文件路径,加载带有Bean注解的”系统管理类”,加载带有Listener的方法并形成CallBack对象统一调用:
package annotation; import java.io.File; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class AutoAssembleIoc { private static final String TAIL = ".class"; private static final String DOT = "."; // 单例IOC private static AutoAssembleIoc instance = new AutoAssembleIoc(); private List<File> fileList; // 系统管理者的容器,key-系统管理者名称,value-系统管理者对象 public static Map<String, Object> sysMap; // 回调方法的容器 public static Map<Integer, CallBack> methodMap; public static AutoAssembleIoc getInstance() { return instance; } // 扫描指定路径下的所有的class文件 public void autoLoad(String filePath) throws ClassNotFoundException, InstantiationException, IllegalAccessException { fileList = new ArrayList<>(); sysMap = new HashMap<>(); methodMap = new HashMap<>(); File file = new File(filePath); scanFiles(file); int beginIndex = filePath.lastIndexOf('/'); for (File temp : fileList) { if (temp == null) { continue; } String absPath = temp.getAbsolutePath(); int index = absPath.indexOf(TAIL); if (index != -1) { String formatAbsPath = absPath.substring(0, index); String packageSource = formatAbsPath.substring(beginIndex + 1, formatAbsPath.length()).replaceAll("\\\\", DOT); Class<?> clazz = Class.forName(packageSource); // 反射初始化系统容器 Bean bean = clazz.getAnnotation(Bean.class); if (bean != null) { String beanName = bean.beanName(); Object object = clazz.newInstance(); sysMap.put(beanName, object); System.out.println("成功加载系统:" + beanName); if (Isystem.class.isInstance(object)) { ((Isystem) object).init(); } // 反射初始化方法容器 for (Method method : clazz.getDeclaredMethods()) { method.setAccessible(true); if (method.isAnnotationPresent(Listener.class)) { Listener listener = (Listener) method .getAnnotation(Listener.class); CallBack callBack = new CallBack(object, method); methodMap.put(listener.listenID(), callBack); System.out.println("成功加载方法:" + method.getName()); } } } } } } private void scanFiles(File f) { if (!f.isDirectory()) { fileList.add(f); return; } File[] fileArray = f.listFiles(); for (File file : fileArray) { scanFiles(file); } } }9.制作自己的适配器,用于解析并设置回调方法的参数值:
package annotation; import java.lang.annotation.Annotation; import java.lang.reflect.Method; public class Adaptor { public static Object[] adaptor(Method method, Account account) { // 按照声明顺序,解析该方法所有的形参类型,即使有相同的形参类型,也会解析一个class Class<?>[] clazz = method.getParameterTypes(); // 定义好要解析返回的参数数组长度 Object[] params = new Object[clazz.length]; // 该方法无形参 if (params.length == 0) { return params; } // 定义注解数组,用来装形参上的所有注解对象 Annotation[] needAdaptor = new Annotation[params.length]; // 获取该方法上的所有形参注解,1维代表形参位置,2维代表形参上的注解(一个形参上可能会有多个注解) Annotation[][] paramsAnn = method.getParameterAnnotations(); for (int i = 0; i < paramsAnn.length; i++) { Annotation[] ans = paramsAnn[i]; // 参数上无注解 if (ans.length == 0) { continue; } for (Annotation an : ans) { if (an instanceof Param) { needAdaptor[i] = (Param) an; } } } // 解析并形成参数组,这里我们约定在任何带有Listen注解的方法里面,最后一个形参必须是Account对象,其余形参必须要求有注解 for (int i = 0; i < needAdaptor.length; i++) { // 获取该字段上的注解 Annotation anns = needAdaptor[i]; // 获取该字段的calss,便于解析类型,如果Account是一个JSON对象,那么就可以用calz设置类型 // Class<?> calz = clazz[i]; if (anns instanceof Param) { String name = ((Param) anns).pname(); // 拓展:我们可以考虑将Account对象设计成一个JSON对象,这样,我们就可以直接用名字取值 if (name.equals("accId")) { params[i] = account.getAccId(); } else if (name.equals("name")) { params[i] = account.getName(); } else if (name.equals("cardId")) { params[i] = account.getCardId(); } else if (name.equals("money")) { params[i] = account.getMoney(); } } } params[needAdaptor.length - 1] = account; return params; } }10.写一个测试类进行测试:
package annotation; import java.lang.reflect.InvocationTargetException; public class Test { public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { AutoAssembleIoc.getInstance().autoLoad("D:/workspace/Tests/bin/"); Account account = new Account(3100, "张三", 6125455121248L); // 当然,这里的两种调用方法可以合并为一种 invokeNoAdaptorWork(10010, account); invokeAdaptorWork(10011, account); } public static Account invokeNoAdaptorWork(int number, Account account) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { CallBack callBack = AutoAssembleIoc.methodMap.get(number); if (callBack == null) { System.out.println("编号" + number + "无对应方法"); return account; } // 普通的调用 Account result = (Account) callBack.getMethod().invoke( callBack.getObject(), account); System.out.println(result.toString()); System.out.println(AccountSystem.accountMap.get(3100).toString()); return account; } public static Object[] invokeAdaptorWork(int number, Account account) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { CallBack callBack = AutoAssembleIoc.methodMap.get(number); if (callBack == null) { System.out.println("编号" + number + "无对应方法"); return null; } // 带形参解析的调用,反射解析形参 Object[] objects = Adaptor.adaptor(callBack.getMethod(), account); callBack.getMethod().invoke(callBack.getObject(), objects); System.out.println(AccountSystem.accountMap.get(3100).toString()); return objects; } }
测试结果:成功加载系统:accountSystem初始化账户系统over成功加载方法:addMoney成功加载方法:createAccount创建新账户:张三成功:[accId=3100],[name=张三],[cardId=6125455121248],[money=0][accId=3100],[name=张三],[cardId=6125455121248],[money=0]账户:张三余额:0账户:张三余额:10000000000[accId=3100],[name=张三],[cardId=6125455121248],[money=10000000000] 上述过程,我们完成了一个简易的Ioc,希望可以抛砖引玉。上述代码中,为了演示,我们未对所有抛出的异常进行处理,这是一种非常不好的习惯!!上述实例中抛出的异常种类较多,建议读者自行定义自己的异常处理机制,处理所有的异常。 Java的反射与注解讲解到此为止,下一篇开始,我们来认识一下Java的多线程模型以及一些并发相关的概念。 如有转载,请注明出处,请支持作者原创,谢谢!
[/code]
相关文章推荐
- java反射机制初步认识<三>注解(Annotation)的使用
- java反射机制初步认识<二>获取并执行方法
- java反射机制初步认识<一>获取成员变量
- 黑马程序员 高新技术<四>--->内省、注解与类加载器
- Android实战简易教程<四>(ScrollView和HorizontalScrollView动态添加控件并提供事件监听)
- <四>Ioc控制反转——为bean.xml瘦身第一步(附源码)
- Winsock程序设计初步之<四> Winsock函数用法说明
- Spring容器注解详解<四>
- 使用@Controller注解为什么要配置<mvc:annotation-driven />
- 精通Spring 之 注解【<context:property-placeholder/>】
- 四级核心词汇表<四>
- Winsock程序设计初步之<一> Winsock编程原理
- 如何实现具有层次结构的 TreeView <四> (WPF/TreeView/Style/Template)
- 黑马程序员 java基础<四>--IO流(2)
- quick-cocos2d-x的热更新机制实现<四>update包(lua)(下)
- 【javascript笔记】关于函数的构造函数和prototype<四>
- Android实战简易教程<十七>(LayoutAnimation布局动画)
- Android实战简易教程<五十八>(AlarmManager类用法研究小实例)
- Android实战简易教程<四十二>(github实用控件推荐BadgeView-图标左上角消息提示控件)