簡(jiǎn)單實(shí)現(xiàn)并整合AOP和IOC

最近在看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)步驟:

  1. 加載xml文件,遍歷其標(biāo)簽
  2. 獲取標(biāo)簽中的id和class屬性意系,加載class屬性的類泥耀,并創(chuàng)建bean
  3. 遍歷標(biāo)簽中的屬性,獲取屬性值昔字,并將屬性值填充到bean中
  4. 將bean存儲(chǔ)到bean容器中
  5. 后處理爆袍,找出所有的實(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容器中。
  6. 通過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)吧柄延。

參考:自己動(dòng)手實(shí)現(xiàn)的 Spring IOC 和 AOP - 上篇

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市缀程,隨后出現(xiàn)的幾起案子搜吧,更是在濱河造成了極大的恐慌,老刑警劉巖杠输,帶你破解...
    沈念sama閱讀 222,681評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赎败,死亡現(xiàn)場(chǎng)離奇詭異秕衙,居然都是意外死亡蠢甲,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門据忘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鹦牛,“玉大人,你說我怎么就攤上這事勇吊÷罚” “怎么了?”我有些...
    開封第一講書人閱讀 169,421評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵汉规,是天一觀的道長(zhǎng)礼殊。 經(jīng)常有香客問我驹吮,道長(zhǎng),這世上最難降的妖魔是什么晶伦? 我笑而不...
    開封第一講書人閱讀 60,114評(píng)論 1 300
  • 正文 為了忘掉前任碟狞,我火速辦了婚禮,結(jié)果婚禮上婚陪,老公的妹妹穿的比我還像新娘族沃。我一直安慰自己,他們只是感情好泌参,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,116評(píng)論 6 398
  • 文/花漫 我一把揭開白布脆淹。 她就那樣靜靜地躺著,像睡著了一般沽一。 火紅的嫁衣襯著肌膚如雪盖溺。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,713評(píng)論 1 312
  • 那天铣缠,我揣著相機(jī)與錄音咐柜,去河邊找鬼。 笑死攘残,一個(gè)胖子當(dāng)著我的面吹牛拙友,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播歼郭,決...
    沈念sama閱讀 41,170評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼遗契,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了病曾?” 一聲冷哼從身側(cè)響起牍蜂,我...
    開封第一講書人閱讀 40,116評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎泰涂,沒想到半個(gè)月后鲫竞,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,651評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡逼蒙,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,714評(píng)論 3 342
  • 正文 我和宋清朗相戀三年从绘,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片是牢。...
    茶點(diǎn)故事閱讀 40,865評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡僵井,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出驳棱,到底是詐尸還是另有隱情批什,我是刑警寧澤,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布社搅,位于F島的核電站驻债,受9級(jí)特大地震影響乳规,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜合呐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,211評(píng)論 3 336
  • 文/蒙蒙 一驯妄、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧合砂,春花似錦青扔、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至缘屹,卻和暖如春凛剥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背轻姿。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工犁珠, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人互亮。 一個(gè)月前我還...
    沈念sama閱讀 49,299評(píng)論 3 379
  • 正文 我出身青樓犁享,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親豹休。 傳聞我的和親對(duì)象是個(gè)殘疾皇子炊昆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,870評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容

  • 本博中關(guān)于spring的文章:Spring IOC和AOP原理,Spring事務(wù)原理探究威根,Spring配置文件屬性...
    Maggie編程去閱讀 4,107評(píng)論 0 34
  • 前言 說是Java web洛搀,Spring已經(jīng)成為了事實(shí)標(biāo)準(zhǔn)敢茁,Spring原理的深入學(xué)習(xí),無論是在工作中留美,還是在面試...
    老呼閱讀 16,909評(píng)論 2 87
  • 一. Java基礎(chǔ)部分.................................................
    wy_sure閱讀 3,815評(píng)論 0 11
  • 概述 Spring是什么彰檬? Spring是一個(gè)開源框架,為了解決企業(yè)應(yīng)用開發(fā)的復(fù)雜性而創(chuàng)建的独榴,但是現(xiàn)在已經(jīng)不止于企...
    瑯筑閱讀 1,172評(píng)論 2 8
  • 關(guān)于《霸王別姬》隘道,關(guān)于程蝶衣的一輩子症歇;都是沉重的郎笆,以至于難以起筆。只是在看了影片之后再去聽那首《當(dāng)愛已成往事》忘晤,不...
    牙妹醬子閱讀 853評(píng)論 0 1