一、需求設想
現在我有一個設定檔,裡面配置了Bean的相關資訊,如bean的類名(包括包名)、代理工廠(主要負責產生代理類)、目標類(被代理的類)、業務織入介面(Advice)。然後通過BeanFactory來產生Bean的執行個體,如果設定檔中配置的Bean是ProxyFactoryBean的執行個體,我們則產生這個Bean一個代理類的執行個體,還可以通過此設定檔進行切換,是使用代理類還是使用目標類來完成相應的業務功能,該設定檔的格式如下:
圖1-1
如圖1-1中所示,beanName就是我們要通過Bean工廠動態產生的執行個體或代理類執行個體,如果設定檔中的beanName指定的是ProxyFactoryBean,則獲得的執行個體則是根據beanName.target對應的類名,由ProxyFactoryBean產生一個代理類的執行個體,並由beanName.advice對應的Advice織入相應的業務功能到該目標類中。
二、功能實現
1、步聚: BeanFactory即然是專門用來產生Bean的,那就必須得知道產生這些個Bean的設定檔在哪裡?這個設定檔事是先由程式員配置好的。所以在初始化BeanFactory的時候,就必要要得到產生這些Bean的設定檔。 1)、建立BeanFactory的一個唯一的構造方法,接收一個InputStream參數,這個參數用於獲得Bean的設定檔。初始化的時候,通過Properties對象載入這個設定檔。 2)、建立一個getBean方法,接收一個參數(類名),用於根據beanName動態建立該類的一個執行個體。該beanName就是設定檔中的beanName。 3)、使用Class.forName方法根據beanName產生一個Class位元組碼對象,併產生一個執行個體對象 4)、如果該執行個體對象是ProxyFactoryBean的執行個體,則返回由ProxyFactoryBean產生的一個代理類執行個體,並注入設定檔中beanName.advice對應的Advice,否則直接返回該執行個體。
2、代碼執行個體1)、BeanFactory類
package proxy.aopframework;import java.io.IOException;import java.io.InputStream;import java.util.Properties;import proxy.Advice;/** * Bean工廠,負責產生代理類或目標類的執行個體 */public class BeanFactory<T> {private Properties props = new Properties();/** * 初始化bean工廠,載入bean的設定檔,該設定檔是一個標準的Properties檔案,該檔案由Key=Value的格式組成 * @param inStream bean的設定檔 */public BeanFactory(InputStream inStream) {try {props.load(inStream);} catch (IOException e) {e.printStackTrace();}}/** * 擷取一個Bean的執行個體 * @param beanName bean javabean的名稱 * @return 根據設定檔,返回目標類或代理類的執行個體 * @throws ClassNotFoundException */public T getBean(String beanName) throws ClassNotFoundException {String className = props.getProperty(beanName);Class clazz = Class.forName(className);T bean = null;try {bean = (T)clazz.newInstance();if (bean instanceof ProxyFactoryBean) {ProxyFactoryBean proxyFactoryBean = (ProxyFactoryBean) bean;Advice advice = (Advice) Class.forName(props.getProperty(beanName + ".advice")).newInstance();Object target = Class.forName(props.getProperty(beanName + ".target")).newInstance();proxyFactoryBean.setAdvice(advice);proxyFactoryBean.setTarget(target);bean = (T)proxyFactoryBean.getProxy();}} catch (Exception e) {e.printStackTrace();} return bean;}}2)、ProxyFactoryBean類
package proxy.aopframework;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import proxy.Advice;/** * 代理工廠,負責產生目標對象的代理類 */public class ProxyFactoryBean {/** * 被代理的目標對象 */private Object target;/** * 目標對象要插入的商務邏輯 */private Advice advice;public Object getTarget() {return target;}public void setTarget(Object target) {this.target = target;}public Advice getAdvice() {return advice;}public void setAdvice(Advice advice) {this.advice = advice;}/** * 獲得一個代理類對象 * @return */public Object getProxy() {return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method,Object[] args) throws Throwable {Object retVal = null;try {advice.doBefore(target, method, args);retVal = method.invoke(target, args);advice.doAfter(target, method, args, retVal);} catch (Exception e) {advice.doThrow(target, method, args, e);} finally {advice.doFinally(target, method, args);}return retVal;}});}}3)、Advice介面和LogAdvice實作類別
package proxy;import java.lang.reflect.Method;/** * aop介面,提供方法運行前、方法運行後、方法運行中產生Exception、方法最終運行代碼 * */public interface Advice {/** * 方法運行前 * @param target 被代理的目標對象 * @param method 被調用的方法 * @param args 方法的參數 */public void doBefore(Object target, Method method, Object[] args);/** * 方法運行後 * @param target 被代理的目標對象 * @param method 被調用的方法對象 * @param args 方法的參數 * @param retVal 方法的傳回值 */public void doAfter(Object target, Method method, Object[] args, Object retVal);/** * 方法運行時產生的異常 * @param target 被代理的目標對象 * @param method 被調用的方法 * @param args 方法參數 * @param e 運行時的異常對象 */public void doThrow(Object target, Method method, Object[] args, Exception e);/** * 最終要執行的功能(如釋放資料庫連接的資源、關閉IO流等) * @param target 被代理的目標對象 * @param method 被調用的方法 * @param args 方法參數 */public void doFinally(Object target, Method method, Object[] args);}package proxy;import java.lang.reflect.Method;import java.util.Arrays;/** * 日誌功能切入類 * @author 楊信 * */public class LogAdvice implements Advice {long beginTime = System.currentTimeMillis();@Overridepublic void doBefore(Object target, Method method, Object[] args) {System.out.println(target.getClass().getSimpleName() +"." + method.getName() + "方法被調用,參數值:" + Arrays.toString(args));}@Overridepublic void doAfter(Object target, Method method, Object[] args, Object retVal) {long endTime = System.currentTimeMillis();System.out.println(target.getClass().getSimpleName() +"." + method.getName() + "方法運行結束,傳回值:" + retVal + ",耗時" + (endTime - beginTime) + "毫秒。");}@Overridepublic void doThrow(Object target, Method method, Object[] args,Exception e) {System.out.println("調用" + target.getClass().getSimpleName() +"." + method.getName() + "方法發生異常,異常訊息:");e.printStackTrace();}@Overridepublic void doFinally(Object target, Method method, Object[] args) {System.out.println("doFinally...");}}
4)、config.properties檔案
#beanName=java.util.ArrayListbeanName=proxy.aopframework.ProxyFactoryBeanbeanName.advice=proxy.LogAdvicebeanName.target=java.util.ArrayList#hashMap=java.util.HashMaphashMap=proxy.aopframework.ProxyFactoryBeanhashMap.advice=proxy.LogAdvicehashMap.target=java.util.HashMap
5)、測試類別AopFrameworkTest
package proxy.aopframework;import java.io.InputStream;import java.util.HashMap;import java.util.Map;public class AopFrameworkTest {public static void main(String[] args) throws ClassNotFoundException {InputStream inStream = AopFrameworkTest.class.getResourceAsStream("config.properties");/*BeanFactory<ArrayList> beanFactory = new BeanFactory<ArrayList>(inStream);List list = beanFactory.getBean("ArrayList");System.out.println(list.getClass().getName());list.add("zhangsan");*/BeanFactory<HashMap> beanFactory = new BeanFactory<HashMap>(inStream);Map map = beanFactory.getBean("hashMap");System.out.println(map.getClass().getName());map.put("name", "zhangsan");System.out.println(map.size());}}
6)、測試結果