1. 程式人生 > >Java反射—模擬Spring的Aop

Java反射—模擬Spring的Aop

1.    大概流程

  上篇文章已經結合Java反射解釋了SpringAop的原理,這裡我們簡單模擬以下Spring的Aop實現。大體流程如下:

  Ø  建立一個properties配置檔案模擬Spring配置檔案。

  Ø  建立一個增強介面與一個實現類模擬Spring的Advice。

  Ø  建立一個生成代理的工廠類,並在InvocationHandler類的invoke方法中織入增強方法(即aop)。

  Ø  建立一個生成Bean的工廠類(類似IOC工廠,只建立bean,沒有依賴注入的功能),生成Bean時判斷,如果是Spring管理的類,則返回目標物件的代理物件,如果不是spring管理的類則直接返回目標物件。

理論不再多說,直接看程式碼:

2.    例項程式碼

  2.1   配置檔案(config.properties):

#1、模擬沒有被spring管理的bean的class
xxx=java.util.ArrayList
#2、模擬被Spring管理的bean的class
#xxx=cn.itcast.day3.aopframework.ProxyFactoryBean

#3、xxx的增強類
xxx.advice=cn.itcast.day3.aopframework.MyAdvice
#4、xxx的目標類
xxx.target=java.util.ArrayList

  2.2  增強介面:

package cn.itcast.day3.aopframework;

import java.lang.reflect.Method;

/**
 * 增強介面
 * 
 * @author wangzhipeng
 * 
 */
public interface Advice {
	// 前置增強方法
	void beforeMethod(Method method);

	// 後置增強方法
	void afterMethod(Method method);
}

  2.3  增強實現:

package cn.itcast.day3.aopframework;

import java.lang.reflect.Method;

/**
 * 增強類
 * 
 * @author wangzhipeng
 * 
 */
public class MyAdvice implements Advice {
	long beginTime = 0;

	/**
	 * 前置增強方法
	 */
	public void beforeMethod(Method method) {
		System.out.println("到TGB來學習啦!");
		beginTime = System.currentTimeMillis();
	}

	/**
	 * 後置增強方法
	 */
	public void afterMethod(Method method) {
		System.out.println("從TGB畢業上班啦!");
		long endTime = System.currentTimeMillis();
		System.out.println(method.getName() + " running time of " + (endTime - beginTime));

	}

}

  2.4  生成代理的工廠類:

package cn.itcast.day3.aopframework;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 生成代理的工廠類
 * 
 * @author wangzhipeng
 * 
 */
public class ProxyFactoryBean {
	// 增強物件
	private Advice advice;
	// 目標物件
	private Object target;

	// 獲取目標物件的代理物件
	public Object getProxy() {
		Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
			// InvocationHandler介面的匿名內部類
			// 執行代理物件的任何方法時都將被替換為執行如下invoke方法
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

				advice.beforeMethod(method);// 執行【前置】增強方法
				Object retVal = method.invoke(target, args);// 執行目標方法
				advice.afterMethod(method);// 執行【後置】增強方法

				return retVal;// 返回目標方法執行結果,代理物件的方法返回值必須與目標物件的方法返回值相同
			}
		});
		return proxy;
	}

	public Advice getAdvice() {
		return advice;
	}

	public void setAdvice(Advice advice) {
		this.advice = advice;
	}

	public Object getTarget() {
		return target;
	}

	public void setTarget(Object target) {
		this.target = target;
	}

}

  2.5  生成Bean的工廠類(類似IOC工廠,這裡只能建立bean,沒有依賴注入)

package cn.itcast.day3.aopframework;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
 * 模擬Spring的IOC工廠(只提供建立bean功能,不能依賴注入)
 * 
 * @author wangzhipeng
 * 
 */
public class BeanFactory {
	Properties props = new Properties();

	/**
	 * 例項化Bean工廠時會掃描我們的配置檔案(傳入我們的配置檔案)
	 * 
	 * @param ips
	 *            配置檔案的stream流
	 */
	public BeanFactory(InputStream ips) {
		try {
			props.load(ips);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 生產bean
	 * 
	 * @param name
	 *            bean的名稱
	 */
	public Object getBean(String name) {
		String className = props.getProperty(name);
		Object bean = null;
		try {
			// 反射獲取類名為name的物件
			Class clazz = Class.forName(className);
			bean = clazz.newInstance();
		} catch (Exception e) {
			e.printStackTrace();
		}
		// 如果目標被Spring管理了(這裡以如果bean是ProxyFactoryBean型別的物件代替)則返回目標的代理物件,否則直接返回目標物件
		// 實際的Spring中,可以用spring的配置檔案或者註解來判斷是否被spring管理
		if (bean instanceof ProxyFactoryBean) {
			Object proxy = null;
			ProxyFactoryBean proxyFactoryBean = (ProxyFactoryBean) bean;
			try {
				// 反射獲得增強物件
				Advice advice = (Advice) Class.forName(props.getProperty(name + ".advice")).newInstance();
				// 反射獲得目標物件
				Object target = Class.forName(props.getProperty(name + ".target")).newInstance();
				// 生成目標物件的代理物件,並織入增強方法,實現了aop
				proxyFactoryBean.setAdvice(advice);
				proxyFactoryBean.setTarget(target);
				proxy = proxyFactoryBean.getProxy();
			} catch (Exception e) {
				e.printStackTrace();
			}
			return proxy;// 如果目標被spring管理,則返回目標的代理物件
		}
		return bean;// 如果沒有被spring管理,直接返回目標
	}
}

  2.6  測試類

package cn.itcast.day3.aopframework;

import java.io.InputStream;

/**
 * 測試類
 * 
 * @author wangzhipeng
 * 
 */
public class AopFrameworkTest {

	public static void main(String[] args) throws Exception {
		// 獲得配置檔案的stream流
		InputStream ips = AopFrameworkTest.class.getResourceAsStream("config.properties");
		// 利用bean工廠建立配置檔案中配置的類的物件
		Object bean = new BeanFactory(ips).getBean("xxx");
		// 測試BeanFactory生成的是目標還是代理,如果是代理會執行增強方法
		System.out.println(bean.getClass().getName());
		bean.hashCode();
	}
}

  2.7  測試結果1:

  如果配置檔案中配置的是“xxx=cn.itcast.day3.aopframework.ProxyFactoryBean “,則結果為

  

  說明返回的是代理物件,而且執行了增強方法。(因為我們的BeanFactory中有相關的判斷【if(bean instanceof ProxyFactoryBean)】)。

  2.8   測試結果2

  如果配置檔案中配置的是“xxx=cn.itcast.day3.aopframework.ProxyFactoryBean “,則結果為

  

  說明返回的是目標物件,而且沒有執行增強方法。