模拟Spring的AOP

模拟Spring的AOP(demo也包含了Ioc):

1.新建接口Advice

/**
 * 标识性接口,关键在于分离before、after横切关注点
 *
 * @Version 1.0
 * @Author XiaoGang
 * @Created Jan 9, 2017 10:21:01 PM
 * @Description
 *              <p>
 * @Modification
 *               <p>
 *               Date Author Version Description
 *               <p>
 *               Jan 9, 2017 XiaoGang 1.0 create file
 */
public interface Advice {

}
两个子接口:

/**
 * after 横切关注点,即在原方法调用【之后】被调用
 */
interface AfterAdvice extends Advice {

	public void after();
}

/**
 * before 横切关注点,即在原方法调用【之前】被调用
 */
interface BeforeAdvice extends Advice {

	public void before();
}

两个实现

public class LogAfterAdvice implements AfterAdvice {

	@Override
	public void after() {
		System.out.println("\n原业务方法被调用【之后】再打印日志...\n");
	}
}
/**
 * 用户自定义具体实现的before横切关注点
 */
public class LogBeforeAdvice implements BeforeAdvice {

	@Override
	public void before() {
		System.out.println("\n原业务方法被调用【之前】先打印日志...\n");
	}
}

2.创建接口ReportCreator

/**
 * /**
 * 报表生成的公共接口
 *
 * @Version 1.0
 * @Author XiaoGang
 * @Created Jan 9, 2017 10:17:41 PM
 * @Description
 *              <p>
 * @Modification
 *               <p>
 *               Date Author Version Description
 *               <p>
 *               Jan 9, 2017 XiaoGang 1.0 create file
 */
public interface ReportCreator {

	public void getHtmlReport(String path);

	public void getPdfReport(String path);
}
及其实现

/**
 * 用于根据【本地】文件路径生成报表
 */
public class LocalReportCreator {

	public LocalReportCreator() {
		// TODO Auto-generated constructor stub
	}

	public void getHtmlReport(String path) {
		System.out.println("根据【本地】文件生成【HTML】格式的报表 ...");
	}

	public void getPdfReport(String path) {
		System.out.println("根据【本地】文件生成【PDF】格式的报表 ...");
	}
}
/**
 * 用于根据【远程】文件路径生成报表
 */
public class RemoteReportCreator implements ReportCreator {

	@Override
	public void getHtmlReport(String path) {
		System.out.println("根据【远程】文件生成【HTML】格式的报表 ...");
	}

	@Override
	public void getPdfReport(String path) {
		System.out.println("根据【远程】文件生成【PDF】格式的报表 ...");
	}
}

3.创建一个工具类

/**
 * 工具类:处理配置文件中K-V对,即创建或注入bean
 */
public class BeanUtil {

	/**
	 * 利用反射进行依赖注入
	 * 
	 * @param bean
	 *            需要注入外部依赖的主体类实例
	 * @param fieldName
	 *            需要注入的字段名
	 * @param fieldRef
	 *            被注入的组件实例
	 * @throws Exception
	 */
	public static void setProperty(Object bean, String fieldName, Object fieldRef) throws Exception {

		// 获取主体类的完整名称
		final String className = getClassName(bean);

		// 获取主体类的所有 Method
		final Class beanClass = Class.forName(className);
		final Method[] methods = beanClass.getMethods();

		// 准备对应 setter()方法的完整名称
		final String setterName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1, fieldName.length());

		// 遍历找到对应 setter 方法,并调用 invoke()方法进行注入
		for (final Method m : methods) {
			if (m.getName().equals(setterName)) {
				m.invoke(bean, fieldRef);
				System.out.println("已调用 " + m.getName() + "() 向 " + className + " 注入 " + getClassName(fieldRef));
				return;
			}
		}
		System.out.println(">>注入失败: " + className + "类中不存在" + fieldName + "字段对应的setter()方法 ...");
	}

	/**
	 * 根据 Object 实例获取类的完整名称
	 */
	private static String getClassName(Object o) {
		if (o == null) {
			System.out.println("传入的 Object 实例为 null ...");
			return null;
		}
		final String fullName = o.toString();
		final String className = fullName.substring(0, fullName.indexOf("@"));
		return className;
	}
}

4.创建代理

/**
 * 代理提供者:实现invoke方法,构造具有横切关注点的动态代理对象
 */
public class ProxyHandler implements InvocationHandler {

	private Object target; // 被代理的目标对象

	private Advice beforeAdvice;// before 横切关注点

	private Advice afterAdvice; // after 横切关注点

	public ProxyHandler() {
		// 空构造方法
	}

	/**
	 * 3个setter方式的用于依赖注入
	 */
	public void setTarget(Object target) {
		this.target = target;
	}

	public void setBeforeAdvice(Advice beforeAdvice) {
		this.beforeAdvice = beforeAdvice;
	}

	public void setAfterAdvice(Advice afterAdvice) {
		this.afterAdvice = afterAdvice;
	}

	/**
	 * 实现invoke()方法,添加before、after关注点以实现AOP
	 */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

		// 在被代理对象业务方法前后添加横切关注点方法
		aspect(beforeAdvice, "before");
		final Object result = method.invoke(target, args);
		aspect(afterAdvice, "after");

		return result;
	}

	/**
	 * 依据是否注入横切关注点来决定before、after的调用
	 */
	private void aspect(Advice advice, String aspectName) throws Exception {
		if (advice != null) {
			final Class c = advice.getClass();
			final Method[] methods = c.getMethods();
			for (final Method m : methods) {
				if (aspectName.equals(m.getName())) {
					// 以null参数调用已实现的before、after方法
					methods[0].invoke(advice, null);
				}
			}
		}
	}

	/**
	 * 静态工厂方法,用于获取已注入before、after的动态代理实例
	 */
	public Object getProxy(Object target) {
		final Class targetClass = target.getClass();
		/*
		 * loader: 目标类的类加载器 interfaces: 目标类已实现的接口 handler:
		 * 转发方法调用的调用处理类实例,这里是当前Handler
		 */
		final ClassLoader loader = targetClass.getClassLoader();
		final Class[] interfaces = targetClass.getInterfaces();

		// 创建并返回动态代理类实例
		return Proxy.newProxyInstance(loader, interfaces, this);
	}
}
5.创建代理工厂

/**
 * 工厂类:从外部读取配置信息创建并注入所需对象
 */
public class ProxyFactory {

	// 代理对象的提供者
	private ProxyHandler handler;

	// 被代理对象
	private Object target;

	// 所有组件类的集合
	private final Map<String, Object> beans;

	/**
	 * 读取外部配置文件初始化所有组件间的关系
	 */
	public ProxyFactory(String configFile) {
		beans = new HashMap<String, Object>();

		try {
			final Properties props = new Properties();
			props.load(new FileInputStream(configFile));

			// bean 声明及依赖注入的 key-value 对
			final Map<String, String> beanKV = new HashMap<String, String>();
			final Map<String, String> diKV = new HashMap<String, String>();

			for (final Map.Entry entry : props.entrySet()) {
				final String key = (String) entry.getKey();
				final String value = (String) entry.getValue();

				if (key.startsWith("Bean")) {
					beanKV.put(key, value);
				} else if (key.startsWith("DI")) {
					diKV.put(key, value);
				}
			}

			// 一定要先处理 bean 声明再进行依赖注入
			processKeyValue(beanKV);
			processKeyValue(diKV);

		} catch (final Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 处理 key-value
	 */
	private void processKeyValue(Map<String, String> map) throws Exception {
		for (final Map.Entry entry : map.entrySet()) {
			final String key = (String) entry.getKey();
			final String value = (String) entry.getValue();
			handleEntry(key, value);
		}
	}

	/**
	 * 利用工具类 BeanUtil 处理key-value对,即创建或注入bean
	 */
	private void handleEntry(String key, String value) throws Exception {
		final String[] keyParts = key.split("\\.");

		final String tag = keyParts[0];
		if ("Bean".equals(tag)) {
			// 组件定义:利用反射实例化该组件
			final Object bean = Class.forName(value).newInstance();
			System.out.println("利用反射创建对象:" + bean.getClass().getName());
			beans.put(keyParts[1], bean);

		} else if ("DI".equals(tag)) {
			// 依赖注入:获取需要bean的主体,以及被注入的实例
			final Object bean = beans.get(keyParts[1]);
			final Object fieldRef = beans.get(value);
			System.out.println("\n依赖注入目标:" + bean.getClass().getName() + ";被 注入对象:" + fieldRef.getClass().getName());
			BeanUtil.setProperty(bean, keyParts[2], fieldRef);
		}
	}

	/**
	 * 针对Factory已创建的Target获取代理对象
	 */
	public Object getProxy(String proxyName, String targetNanme) {
		final Object target = beans.get(targetNanme);
		if (target != null) {
			handler = (ProxyHandler) beans.get(proxyName);
			return handler.getProxy(target);
		}
		return null;
	}

}

6.创建一个配置文件config.properties

# define beans 
Bean.target=test.aop.report.RemoteReportCreator
Bean.targetProxy=test.aop.proxy.ProxyHandler
Bean.logBeforeAdvice=test.aop.advice.LogBeforeAdvice
Bean.logAfterAdvice=test.aop.advice.LogAfterAdvice
 
# define DI 
DI.targetProxy.target=target
DI.targetProxy.beforeAdvice=logBeforeAdvice
DI.targetProxy.afterAdvice=logAfterAdvice

7.创建测试类:

public class AOP_DI {

	public AOP_DI() {
		// TODO Auto-generated constructor stub
	}

	public static void main(String[] args) {
		// 初始化环境配置
		final ProxyFactory proxyFactory = new ProxyFactory("config.properties");
		// 获取被代理后的Target对象
		final ReportCreator reportCreator = (ReportCreator) proxyFactory.getProxy("targetProxy", "target");
		// 使用被代理后的target对象提供的服务
		reportCreator.getHtmlReport("http://code.google.com/file/...");
	}
}

测试结果:

利用反射创建对象:test.aop.proxy.ProxyHandler
利用反射创建对象:test.aop.advice.LogBeforeAdvice
利用反射创建对象:test.aop.advice.LogAfterAdvice
利用反射创建对象:test.aop.report.RemoteReportCreator

依赖注入目标:test.aop.proxy.ProxyHandler;被 注入对象:test.aop.advice.LogBeforeAdvice
已调用 setBeforeAdvice() 向 test.aop.proxy.ProxyHandler 注入 test.aop.advice.LogBeforeAdvice

依赖注入目标:test.aop.proxy.ProxyHandler;被 注入对象:test.aop.advice.LogAfterAdvice
已调用 setAfterAdvice() 向 test.aop.proxy.ProxyHandler 注入 test.aop.advice.LogAfterAdvice

依赖注入目标:test.aop.proxy.ProxyHandler;被 注入对象:test.aop.report.RemoteReportCreator
已调用 setTarget() 向 test.aop.proxy.ProxyHandler 注入 test.aop.report.RemoteReportCreator

原业务方法被调用【之前】先打印日志...

根据【远程】文件生成【HTML】格式的报表 ...

原业务方法被调用【之后】再打印日志...

一个简单的AOP就实现了,其核心就是动态代理。


转自 http://haolloyin.blog.51cto.com/1177454/476026/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值