您的位置:首页 > 编程语言 > Java开发

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]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: