手写一个简单的Spring IOC 和 AOP

简单的IOC和AOP,只能独立运行。

简单的 IOC 的 实现:步骤

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

主要有以下几个类:

SimpleIOC // IOC 的实现类,实现了上面所说的4个步骤
SimpleIOCTest // IOC 的测试类
Car // IOC 测试使用的 bean
Wheel // 同上
ioc.xml // bean 配置文件
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");
                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);
    }
}
public class Wheel {
    private String brand;
    private String specification ;
......

public class Car {
    private String name;
    private String length;
    private String width;
    private String height;
    private Wheel wheel;
......
public class SimpleIOCTest {
    @Test
    public void getBean() throws Exception {
        String location = SimpleIOC.class.getClassLoader().getResource("ioc.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);
    }
}

测试结果:

D:\Dsoft\devEnvironment\Java\jdk1.8\bin\java.exe ...
Wheel{brand='Michelin', specification='265/60 R18'}
Car{name='Mercedes Benz G 500', length='4717mm', width='1855mm', height='1949mm', wheel=Wheel{brand='Michelin', specification='265/60 R18'}}

Process finished with exit code 0

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

简单 AOP 的实现

在介绍 AOP 的实现步骤之前,先引入 Spring AOP 中的一些概念,接下来我们会用到这些概念。

通知(Advice):通知定义了要织入目标对象的逻辑,以及执行时机。Spring 中对应了 5 种不同类型的通知:

  • 前置通知(Before):在目标方法执行前,执行通知
  • 后置通知(After):在目标方法执行后,执行通知,此时不关心目标方法返回的结果是什么
  • 返回通知(After-returning):在目标方法执行后,执行通知
  • 异常通知(After-throwing):在目标方法抛出异常后执行通知
  • 环绕通知(Around): 目标方法被通知包裹,通知在目标方法执行前和执行后都被会调用

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

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

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

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

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

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

public interface Advice extends InvocationHandler {}

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

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

public interface HelloService { void sayHelloWorld(); }

public class HelloServiceImpl implements HelloService {
    @Override
    public void sayHelloWorld() {
        System.out.println("hello world!");
    }
}
public class SimpleAOPTest {
    @Test
    public void getProxy() {
        // 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();
    }
}

参考:http://www.tianxiaobo.com/2018/01/18/自己动手实现的-Spring-IOC-和-AOP-上篇/

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值