简单实现并整合AOP和IOC

最近在看Spring的ioc和aop相关的知识点,看了一点官方的源码,一知半解,只能说对这俩个东西有个宏观的大致的了解,具体实现细节还是不是很清晰。于是参考了网上的aop和ioc简单实现代码,将二者简单整合了起来。当然,这个demo写的还是问题很多,可能得慢慢思考如何进行修改和优化。由于对xml解析的水平不太高,xml解析部分的代码原作者没有封装成函数,可以说是一个遗憾,未来有时间希望可以修改一下。

下面介绍一下大致的实现步骤:

  1. 加载xml文件,遍历其标签
  2. 获取标签中的id和class属性,加载class属性的类,并创建bean
  3. 遍历标签中的属性,获取属性值,并将属性值填充到bean中
  4. 将bean存储到bean容器中
  5. 后处理,找出所有的实现了InvocationHandler接口的类(这些就是advice类)。这个handler对应的切面逻辑类已经在bean的初始化的时候被注入了。从handler的属性中获取需要被代理的bean,创建jdk动态代理,将代理类重新存储到bean容器中。
  6. 通过getBean方法获取代理类,调用其方法进行测试

以下是代码清单:

ioc.xml   //bean的配置文件
SimpleIOC  //IOC和AOP的实现类
Car          //IOC和AOP的测试接口
DazhongCar   //IOC和AOP的测试类
Advice      //继承了InvocationHandler接口的接口
AfterMethodInvocation   //后置通知的逻辑需实现接口(需要使用被代理类的方法返回值)
MethodInvocation   //前置通知的逻辑需实现接口
AfterAdvice   // 代理类,后置通知实现类,实现Advice      
BeforeAdvice  //代理类,前置通知方法实现类,实现Advice 
AfterTask    //后置通知方法实现逻辑
BeforeTask  //前置通知方法实现逻辑
Test         //测试类

以下是详细代码
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> 标签
        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;
                }
                   // 创建 bean
                Object bean = beanClass.newInstance();

                // 遍历 <property> 标签
                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 相关字段访问权限设为可访问
                        Field declaredField = bean.getClass().getDeclaredField(name);
                        declaredField.setAccessible(true);

                        if (value != null && value.length() > 0) {
                            // 将属性值填充到相关字段中
                            declaredField.set(bean, value);//set值
                        } else {
                            String ref = propertyElement.getAttribute("ref");//如果是一个自定义的对象,那么需要去找这个对象的值
                            if (ref == null || ref.length() == 0) {
                                throw new IllegalArgumentException("ref config error");
                            }
                            // 将引用填充到相关字段中
                            declaredField.set(bean, getBean(ref));
                        }
                        // 将 bean 注册到 bean 容器中
                        registerBean(id, bean);
                    }
                }
            }
        }
        //bean加载完成
        postProcessAfterInitialization();
        
    }


    private void postProcessAfterInitialization() {
        beanMap.forEach((beanName,bean) -> {
            //是否需要增强
            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("这个bean "+beanName+"还没被初始化或者这个advice没有绑定bean");
            }
            Object proxy =  Proxy.newProxyInstance(SimpleIOC.class.getClassLoader(), 
                    bean.getClass().getInterfaces(), (BeforeAdvice)advice);
            
            registerBean(beanName, proxy);//重新注册这个bean为它的代理类
        }else if(advice instanceof AfterAdvice){  //后置增强
            String beanName = ((AfterAdvice) advice).getBeanName();
            Object bean = beanMap.get(beanName);
            if(bean==null){
                new Exception("这个bean "+beanName+"还没被初始化或者这个advice没有绑定bean");
            }
            Object proxy =  Proxy.newProxyInstance(SimpleIOC.class.getClassLoader(), 
                    bean.getClass().getInterfaces(), (AfterAdvice)advice);
            registerBean(beanName, proxy);//重新注册这个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;//运行返回值,测试后置增强
    }
}

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);
        // 在目标方法执行后调用通知,并使用目标方法的返回值
        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 {
            // 在目标方法执行前调用通知
            methodInvocation.invoke();
            return method.invoke(bean, args);//代理对象调用真实目标对象的方法去处理用户请求
        }
}

AfterTask.java

public class AfterTask implements AfterMethodInvocation{
    //private Object bean

    @Override
    public void invoke(Object o) {
        System.out.println("bean 方法执行的结果是"+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");
            //原因:不能用接口的实现类(DazhongCar)来转换Proxy的实现类,它们是同级,应该用共同的接口来转换。
            Car car = (Car) bf.getBean("daZhongCar");
            car.driver();
        }
}

现阶段存在的主要问题:

  • 依赖类必须在配置文件中先被声明,保证先被初始化。需要修改读取配置文件的部分,先读取封装好再来统一初始化。
  • 不解决循环依赖,这个可能还得啃一啃官方源码才能实现吧。

参考:自己动手实现的 Spring IOC 和 AOP - 上篇

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,142评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,298评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,068评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,081评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,099评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,071评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,990评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,832评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,274评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,488评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,649评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,378评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,979评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,625评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,643评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,545评论 2 352

推荐阅读更多精彩内容