最近在看Spring的ioc和aop相關(guān)的知識(shí)點(diǎn)容达,看了一點(diǎn)官方的源碼古涧,一知半解,只能說對(duì)這倆個(gè)東西有個(gè)宏觀的大致的了解花盐,具體實(shí)現(xiàn)細(xì)節(jié)還是不是很清晰羡滑。于是參考了網(wǎng)上的aop和ioc簡(jiǎn)單實(shí)現(xiàn)代碼,將二者簡(jiǎn)單整合了起來算芯。當(dāng)然柒昏,這個(gè)demo寫的還是問題很多,可能得慢慢思考如何進(jìn)行修改和優(yōu)化熙揍。由于對(duì)xml解析的水平不太高职祷,xml解析部分的代碼原作者沒有封裝成函數(shù),可以說是一個(gè)遺憾届囚,未來有時(shí)間希望可以修改一下有梆。
下面介紹一下大致的實(shí)現(xiàn)步驟:
- 加載xml文件,遍歷其標(biāo)簽
- 獲取標(biāo)簽中的id和class屬性意系,加載class屬性的類泥耀,并創(chuàng)建bean
- 遍歷標(biāo)簽中的屬性,獲取屬性值昔字,并將屬性值填充到bean中
- 將bean存儲(chǔ)到bean容器中
- 后處理爆袍,找出所有的實(shí)現(xiàn)了InvocationHandler接口的類(這些就是advice類)首繁。這個(gè)handler對(duì)應(yīng)的切面邏輯類已經(jīng)在bean的初始化的時(shí)候被注入了。從handler的屬性中獲取需要被代理的bean陨囊,創(chuàng)建jdk動(dòng)態(tài)代理弦疮,將代理類重新存儲(chǔ)到bean容器中。
- 通過getBean方法獲取代理類蜘醋,調(diào)用其方法進(jìn)行測(cè)試
以下是代碼清單:
ioc.xml //bean的配置文件
SimpleIOC //IOC和AOP的實(shí)現(xiàn)類
Car //IOC和AOP的測(cè)試接口
DazhongCar //IOC和AOP的測(cè)試類
Advice //繼承了InvocationHandler接口的接口
AfterMethodInvocation //后置通知的邏輯需實(shí)現(xiàn)接口(需要使用被代理類的方法返回值)
MethodInvocation //前置通知的邏輯需實(shí)現(xiàn)接口
AfterAdvice // 代理類胁塞,后置通知實(shí)現(xiàn)類,實(shí)現(xiàn)Advice
BeforeAdvice //代理類压语,前置通知方法實(shí)現(xiàn)類啸罢,實(shí)現(xiàn)Advice
AfterTask //后置通知方法實(shí)現(xiàn)邏輯
BeforeTask //前置通知方法實(shí)現(xiàn)邏輯
Test //測(cè)試類
以下是詳細(xì)代碼
ioc.xml
<beans>
<bean id="beforeTask" class="spring.aopAndIoc.BeforeTask"></bean>
<bean id="afterTask" class="spring.aopAndIoc.AfterTask"></bean>
<bean id="wheel" class="spring.ioc.simple.Wheel">
<property name="brand" value="Michelin" />
<property name="specification" value="265/60 R18" />
</bean>
<bean id="daZhongCar" class="spring.aopAndIoc.DazhongCar" >
<property name="name" value="Mercedes Benz G 500"/>
<property name="length" value="4717mm"/>
<property name="width" value="1855mm"/>
<property name="height" value="1949mm"/>
<property name="wheel" ref="wheel"/>
</bean>
<!-- <bean id="beforeAdvice" class="spring.aopAndIoc.BeforeAdvice">
<property name="methodInvocation" ref="beforeTask"/>
<property name="bean" ref="daZhongCar"/>
<property name="beanName" value="daZhongCar"/>
</bean> -->
<bean id="afterAdvice" class="spring.aopAndIoc.AfterAdvice">
<property name="afterInvocation" ref="afterTask"/>
<property name="bean" ref="daZhongCar"/>
<property name="beanName" value="daZhongCar"/>
</bean>
</beans>
SimpleIOC.java
import java.io.FileInputStream;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class SimpleIOC {
private Map<String, Object> beanMap = new HashMap<String, Object>();
public SimpleIOC(String location) throws Exception {
loadBeans(location);
}
public Object getBean(String name) {
Object bean = beanMap.get(name);
if (bean == null) {
throw new IllegalArgumentException("there is no bean with name " + name);
}
return bean;
}
private void loadBeans(String location) throws Exception {
// 加載 xml 配置文件
InputStream inputStream = new FileInputStream(location);
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = factory.newDocumentBuilder();
Document doc = docBuilder.parse(inputStream);
Element root = doc.getDocumentElement();
NodeList nodes = root.getChildNodes();
// 遍歷 <bean> 標(biāo)簽
for (int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
String id = ele.getAttribute("id");
String className = ele.getAttribute("class");
// 加載 beanClass
//反射
Class beanClass = null;
try {
beanClass = Class.forName(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
return;
}
// 創(chuàng)建 bean
Object bean = beanClass.newInstance();
// 遍歷 <property> 標(biāo)簽
NodeList propertyNodes = ele.getElementsByTagName("property");
if(propertyNodes==null||propertyNodes.getLength()==0) registerBean(id, bean);
for (int j = 0; j < propertyNodes.getLength(); j++) {
Node propertyNode = propertyNodes.item(j);
if (propertyNode instanceof Element) {
Element propertyElement = (Element) propertyNode;
String name = propertyElement.getAttribute("name");
String value = propertyElement.getAttribute("value");
// 利用反射將 bean 相關(guān)字段訪問權(quán)限設(shè)為可訪問
Field declaredField = bean.getClass().getDeclaredField(name);
declaredField.setAccessible(true);
if (value != null && value.length() > 0) {
// 將屬性值填充到相關(guān)字段中
declaredField.set(bean, value);//set值
} else {
String ref = propertyElement.getAttribute("ref");//如果是一個(gè)自定義的對(duì)象,那么需要去找這個(gè)對(duì)象的值
if (ref == null || ref.length() == 0) {
throw new IllegalArgumentException("ref config error");
}
// 將引用填充到相關(guān)字段中
declaredField.set(bean, getBean(ref));
}
// 將 bean 注冊(cè)到 bean 容器中
registerBean(id, bean);
}
}
}
}
//bean加載完成
postProcessAfterInitialization();
}
private void postProcessAfterInitialization() {
beanMap.forEach((beanName,bean) -> {
//是否需要增強(qiáng)
if(bean instanceof InvocationHandler){
createProxy(bean);
}
});
}
private void createProxy(Object advice) {
if(advice instanceof BeforeAdvice){
String beanName = ((BeforeAdvice) advice).getBeanName();
Object bean = beanMap.get(beanName);
if(bean==null){
new Exception("這個(gè)bean "+beanName+"還沒被初始化或者這個(gè)advice沒有綁定bean");
}
Object proxy = Proxy.newProxyInstance(SimpleIOC.class.getClassLoader(),
bean.getClass().getInterfaces(), (BeforeAdvice)advice);
registerBean(beanName, proxy);//重新注冊(cè)這個(gè)bean為它的代理類
}else if(advice instanceof AfterAdvice){ //后置增強(qiáng)
String beanName = ((AfterAdvice) advice).getBeanName();
Object bean = beanMap.get(beanName);
if(bean==null){
new Exception("這個(gè)bean "+beanName+"還沒被初始化或者這個(gè)advice沒有綁定bean");
}
Object proxy = Proxy.newProxyInstance(SimpleIOC.class.getClassLoader(),
bean.getClass().getInterfaces(), (AfterAdvice)advice);
registerBean(beanName, proxy);//重新注冊(cè)這個(gè)bean為它的代理類
}
}
private void registerBean(String id, Object bean) {
beanMap.put(id, bean);
}
}
Car.java
public interface Car {
public int driver();
}
DazhongCar.java
public class DazhongCar implements Car {
private String name;
private String length;
private String width;
private String height;
private Wheel wheel;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLength() {
return length;
}
public void setLength(String length) {
this.length = length;
}
public String getWidth() {
return width;
}
public void setWidth(String width) {
this.width = width;
}
public String getHeight() {
return height;
}
public void setHeight(String height) {
this.height = height;
}
public Wheel getWheel() {
return wheel;
}
public void setWheel(Wheel wheel) {
this.wheel = wheel;
}
/* (non-Javadoc)
* @see spring.aopAndIoc.Car#driver()
*/
@Override
public int driver() {
System.out.println("driver Dazhong car");
return 1;//運(yùn)行返回值胎食,測(cè)試后置增強(qiáng)
}
}
Advice.java
public interface Advice extends InvocationHandler{
}
AfterMethodInvocation.java
public interface AfterMethodInvocation {
void invoke(Object o);
}
AfterAdvice.java
import java.lang.reflect.Method;
public class AfterAdvice implements Advice{
private Object bean;
private AfterMethodInvocation afterInvocation;
private String beanName;
public Object getBean() {
return bean;
}
public void setBean(Object bean) {
this.bean = bean;
}
public AfterMethodInvocation getAfterInvocation() {
return afterInvocation;
}
public void setAfterInvocation(AfterMethodInvocation afterInvocation) {
this.afterInvocation = afterInvocation;
}
public String getBeanName() {
return beanName;
}
public void setBeanName(String beanName) {
this.beanName = beanName;
}
/* (non-Javadoc)
* @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object ret = method.invoke(bean, args);
// 在目標(biāo)方法執(zhí)行后調(diào)用通知扰才,并使用目標(biāo)方法的返回值
afterInvocation.invoke(ret);
return ret;
}
}
BeforeAdvice.java
import java.lang.reflect.Method;
public class BeforeAdvice implements Advice{
private Object bean;
private String beanName;
public String getBeanName() {
return beanName;
}
public void setBeanName(String beanName) {
this.beanName = beanName;
}
private MethodInvocation methodInvocation;
public Object getBean() {
return bean;
}
public void setBean(Object bean) {
this.bean = bean;
}
public MethodInvocation getMethodInvocation() {
return methodInvocation;
}
public void setMethodInvocation(MethodInvocation methodInvocation) {
this.methodInvocation = methodInvocation;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在目標(biāo)方法執(zhí)行前調(diào)用通知
methodInvocation.invoke();
return method.invoke(bean, args);//代理對(duì)象調(diào)用真實(shí)目標(biāo)對(duì)象的方法去處理用戶請(qǐng)求
}
}
AfterTask.java
public class AfterTask implements AfterMethodInvocation{
//private Object bean
@Override
public void invoke(Object o) {
System.out.println("bean 方法執(zhí)行的結(jié)果是"+o);
}
}
BeforeTask.java
public class BeforeTask implements MethodInvocation{
/* (non-Javadoc)
* @see spring.aopAndIoc.MethodInvocation#invoke()
*/
@Override
public void invoke() {
System.out.println("before the method");
}
}
Test.java
public class Test {
public static void main(String[] args) throws Exception {
String location = SimpleIOC.class.getClassLoader().getResource("ioc.xml").getFile();
SimpleIOC bf = new SimpleIOC(location);
Wheel wheel = (Wheel) bf.getBean("wheel");
// DazhongCar car = (DazhongCar) bf.getBean("car");
//原因:不能用接口的實(shí)現(xiàn)類(DazhongCar)來轉(zhuǎn)換Proxy的實(shí)現(xiàn)類,它們是同級(jí)厕怜,應(yīng)該用共同的接口來轉(zhuǎn)換衩匣。
Car car = (Car) bf.getBean("daZhongCar");
car.driver();
}
}
現(xiàn)階段存在的主要問題:
- 依賴類必須在配置文件中先被聲明,保證先被初始化粥航。需要修改讀取配置文件的部分琅捏,先讀取封裝好再來統(tǒng)一初始化。
- 不解決循環(huán)依賴递雀,這個(gè)可能還得啃一啃官方源碼才能實(shí)現(xiàn)吧柄延。