仿Spring 实现简单的 IoC 和 AOP

仿 Spring 实现简单的 IoC 和 AOP 仿照 tiny-spring 项目编写的的一个玩具级 Spring 项目,实现了简单的 IoC 和 AOP 功能

本文包含相关资料包-----> 点击直达获取<-------

仿 Spring 实现简单的 IoC 和 AOP

仿照 tiny-spring 项目编写的的一个玩具级 Spring 项目,实现了简单的 IoC 和 AOP 功能。

本项目包含了两个不同版本的 IoC 和 AOP 实现,相关源码包结构如下:

  • simple,包含了简单的 IoC 和 AOP 实现代码
  • IoC,包含了较为复杂的 IoC 实现代码
  • aop,包含了较为复杂的 AOP 实现代码

1. 背景

我在大四实习的时候开始接触 J2EE 方面的开发工作,也是在同时期接触并学习 Spring 框架,到现在也有快有两年的时间了。不过之前没有仿写过 Spring IoC 和 AOP,只是宏观上对 Spring IoC 和 AOP 原理有一定的认识。所以为了更进一步理解 Spring IoC 和 AOP 原理。在工作之余,参考了一些资料和代码,动手实现了一个简单的 IoC 和 AOP,并实现了如下功能:

  • 根据 XML 配置文件加载相关 bean
  • 对 BeanPostProcessor 类型的 bean 提供支持
  • 对 BeanFactoryAware 类型的 bean 提供支持
  • 实现了基于 JDK 动态代理的 AOP
  • 整合了 IoC 和 AOP,使得二者可很好的协同工作

在实现自己的 IoC 和 AOP 前,我的想法比较简单,就是实现一个非常简单的 IoC 和 AOP,哪怕是几十行代码实现的都行。后来实现后,感觉还很有意思的。不过那个实现太过于简单,和 Spring IoC,AOP 相去甚远。后来想了一下,不能仅满足那个简单的实现,于是就有了这个仿写项目。相对来说仿写的代码要复杂了一些,功能也多了一点,看起来也有点样子的。尽管仿写出的项目仍然是玩具级,不过写仿写的过程中,还是学到了一些东西。总体上来说,收获还是很大的。在接下来文章中,我也将从易到难,实现不同版本的 IoC 和 AOP。好了,不多说了,开始干活。

2. 简单的 IoC 和 AOP 实现

2.1 简单的 IoC

先从简单的 IoC 容器实现开始,最简单的 IoC 容器只需 4 步即可实现,如下:

  • 加载 XML 配置文件,遍历其中的 标签
  • 获取 标签中的 id 和 class 属性,加载 class 属性对应的类,并创建 bean
  • 遍历 标签中的 标签,获取属性值,并将属性值填充到 bean 中
  • 将 bean 注册到 bean 容器中

如上所示,仅需 4 步即可,是不是觉得很简单。好了,Talk is cheap, Show me the code. 接下来要上代码了。不过客官别急,上代码前,容我对代码结构做一下简单介绍:

SimpleIOC // IOC 的实现类,实现了上面所说的4个步骤 SimpleIOCTest // IOC 的测试类 Car // IOC 测试使用的 bean Wheel // 同上 ioc.xml // bean 配置文件

容器实现类 SimpleIOC 的代码:

``` public class SimpleIOC {

private Map<String, Object> beanMap = new HashMap<>();

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");
            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);
                    } 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);
                }
            }
        }
    }
}

private void registerBean(String id, Object bean) {
    beanMap.put(id, bean);
}

} ```

容器测试使用的 bean 代码:

``` public class Car { private String name; private String length; private String width; private String height; private Wheel wheel;

// 省略其他不重要代码

}

public class Wheel { private String brand; private String specification ;

// 省略其他不重要代码

} ```

bean 配置文件 ioc.xml 内容:

```

<bean id="car" class="v2a_49f0_f069a11 com.titizz.simulation.toyspring.Car">
    <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>

```

IoC 测试类 SimpleIOCTest:

public class SimpleIOCTest { @Test public void getBean() throws Exception { String location = SimpleIOC.class.getClassLoader().getResource("spring-test.xml").getFile(); SimpleIOC bf = new SimpleIOC(location); Wheel wheel = (Wheel) bf.getBean("wheel"); System.out.println(wheel); Car car = (Car) bf.getBean("car"); System.out.println(car); } }

测试结果:

以上是简单 IoC 实现的全部内容,难度不大,代码也不难看懂,这里不再多说了。下面说说简单 AOP 的实现。

2.2 简单的 AOP 实现

AOP 的实现是基于代理模式的,这一点相信大家应该都知道。代理模式是 AOP 实现的基础,代理模式不难理解,这里就不花篇幅介绍了。在介绍 AOP 的实现步骤之前,先引入 Spring AOP 中的一些概念,接下来我们会用到这些概念。

通知(Advice)

通知定义了要织入目标对象的逻辑,以及执行时机。 Spring 中对应了 5 种不同类型的通知: · 前置通知(Before):在目标方法执行前,执行通知 · 后置通知(After):在目标方法执行后,执行通知,此时不关系目标方法返回的结果是什么 · 返回通知(After-returning):在目标方法执行后,执行通知 · 异常通知(After-throwing):在目标方法抛出异常后执行通知 · 环绕通知(Around): 目标方法被通知包裹,通知在目标方法执行前和执行后都被会调用

切点(Pointcut)

mipsasm 如果说通知定义了在何时执行通知,那么切点就定义了在何处执行通知。所以切点的作用就是 通过匹配规则查找合适的连接点(Joinpoint),AOP 会在这些连接点上织入通知。

切面(Aspect)

undefined 切面包含了通知和切点,通知和切点共同定义了切面是什么,在何时,何处执行切面逻辑。

说完概念,接下来我们来说说简单 AOP 实现的步骤。这里 AOP 是基于 JDK 动态代理实现的,只需 3 步即可完成:

  • 定义一个包含切面逻辑的对象,这里假设叫 logMethodInvocation
  • 定义一个 Advice 对象(实现了 InvocationHandler 接口),并将上面的 logMethodInvocation 和 目标对象传入
  • 将上面的 Adivce 对象和目标对象传给 JDK 动态代理方法,为目标对象生成代理

上面步骤比较简单,不过在实现过程中,还是有一些难度的,这里要引入一些辅助接口才能实现。接下来就来介绍一下简单 AOP 的代码结构:

java MethodInvocation 接口 // 实现类包含了切面逻辑,如上面的 logMethodInvocation Advice 接口 // 继承了 InvocationHandler 接口 BeforeAdvice 类 // 实现了 Advice 接口,是一个前置通知 SimpleAOP 类 // 生成代理类 SimpleAOPTest // SimpleAOP 从测试类 HelloService 接口 // 目标对象接口 HelloServiceImpl // 目标对象

MethodInvocation 接口代码:

java public interface MethodInvocation { void invoke(); }

Advice 接口代码:

java public interface Advice extends InvocationHandler {}

BeforeAdvice 实现代码:

```java public class BeforeAdvice implements Advice { private Object bean; private MethodInvocation methodInvocation;

public BeforeAdvice(Object bean, MethodInvocation methodInvocation) {
    this.bean = bean;
    this.methodInvocation = methodInvocation;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    // 在目标方法执行前调用通知
    methodInvocation.invoke();
    return method.invoke(bean, args);
}

} ```

SimpleAOP 实现代码:

java public class SimpleAOP { public static Object getProxy(Object bean, Advice advice) { return Proxy.newProxyInstance(SimpleAOP.class.getClassLoader(), bean.getClass().getInterfaces(), advice); } }

HelloService 接口,及其实现类代码:

```java public interface HelloService { void sayHelloWorld(); }

public class HelloServiceImpl implements HelloService { @Override public void sayHelloWorld() { System.out.println("hello world!"); } } ```

SimpleAOPTest 代码:

```java public class SimpleAOPTest { @Test public void getProxy() throws Exception { // 1. 创建一个 MethodInvocation 实现类 MethodInvocation logTask = () -> System.out.println("log task start"); HelloServiceImpl helloServiceImpl = new HelloServiceImpl();

    // 2. 创建一个 Advice
    Advice beforeAdvice = new BeforeAdvice(helloServiceImpl, logTask);

    // 3. 为目标对象生成代理
    HelloService helloServiceImplProxy = (HelloService) SimpleAOP.getProxy(helloServiceImpl,beforeAdvice);

    helloServiceImplProxy.sayHelloWorld();
}

} ```

输出结果:

以上实现了简单的 IoC 和 AOP,不过实现的 IoC 和 AOP 还很简单,且只能独立运行。

参考文献

  • 基于Struts2+Spring3+Hibernate3+Ajax的DRP系统(长安大学·申海杰)
  • Spring框架研究与应用(长沙理工大学·肖露)
  • 基于Spring框架的楼盘推介管理系统的研究与设计(武汉理工大学·翟向梅)
  • 基于Struts2+Spring3+Hibernate3+Ajax的DRP系统(长安大学·申海杰)
  • 基于轻量级J2EE的互联网订票系统的研究与应用(西南交通大学·董莉)
  • 基于IoC和ORM框架的进销存系统设计(山东大学·张幼鹏)
  • 基于构件的办公自动化系统的研究及其安全子系统的实现(江西师范大学·廖春华)
  • .NET框架下基于AOP和Web服务的轻量级应用访问控制系统(江苏大学·陈立军)
  • 基于J2EE平台的Spring框架分析研究与应用(武汉科技大学·刘行亮)
  • J2EE轻量级容器的研究与应用(武汉理工大学·吴青)
  • 使用AOP技术在.NET平台下实现组件运行时服务(吉林大学·费大威)
  • 基于SHT和工作流引擎的安全培训系统实现(湖南师范大学·梁小利)
  • 基于构件的办公自动化系统的研究及其安全子系统的实现(江西师范大学·廖春华)
  • J2EE轻量级容器的研究与应用(武汉理工大学·吴青)
  • 基于AOP的安全信息收集与统计系统的设计与实现(西安电子科技大学·王加加)

本文内容包括但不限于文字、数据、图表及超链接等)均来源于该信息及资料的相关主题。发布者:代码驿站 ,原文地址:https://m.bishedaima.com/yuanma/35888.html

相关推荐

发表回复

登录后才能评论