Java反射—模擬Spring的Aop
阿新 • • 發佈:2019-01-23
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 “,則結果為
說明返回的是目標物件,而且沒有執行增強方法。