模拟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就实现了,其核心就是动态代理。