java反射机制初步认识注解+反射形成简易IOC

	在经过前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();
}

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 "";
}	

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 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 fileList;

	// 系统管理者的容器,key-系统管理者名称,value-系统管理者对象
	public static Map sysMap;
	// 回调方法的容器
	public static Map 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的多线程模型以及一些并发相关的概念。

如有转载,请注明出处,请支持作者原创,谢谢

你可能感兴趣的:(java基础)