JavaWeb学习笔记(二十一)自己实现Spring IOC和AOP

这段时间一直在学习Spring框架,对IOC和AOP也只是原理上的了解,为了加深理解,参考田小波这篇博客实现了IOC和AOP的基本功能。

一. IOC的简单实现

1.实现流程

  1. 加载xml配置文件,遍历其中的标签
  2. 获取标签中的id 和class 属性,加载class 属性对应的类,并利用反射创建bean。
  3. 遍历标签中的元素,获取属性值,并将属性值填充到bean中。
  4. 将bean 注册到bean 容器中。

2.实现代码

  1. SimpleIOC类,ioc的主要实现类
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.FileInputStream;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

/**
 * @author Time
 * @created 2019/7/2
 */
public class SimpleIOC {
    // 用来存放bean的容器
    private Map<String , Object> beanMap = new HashMap<>();

    public SimpleIOC(String location) throws Exception{
        loadBeans(location);
    }
    // 获取bean
    public Object getBean(String name){
        System.out.println(beanMap);
        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));
                        }
                        registerBean(id , bean);
                    }
                }
            }
        }
    }
    private void registerBean(String id, Object bean){
        beanMap.put(id, bean);
    }
}

上述代码比较简单,通过dom解析的方式,获取到xml配置文件中的内容,然后生成实例,注入相关参数。注册到bean容器中。

  1. 注入类
    用来检测IOC容器是否成功的类

Wheell类

package test;

/**
 * @author Time
 * @created 2019/7/2
 */
public class Wheel {
    private String brand;
    private String specification;

    @Override
    public String toString() {
        return "Wheel{" +
                "brand='" + brand + '\'' +
                ", specification='" + specification + '\'' +
                '}';
    }
}

Car类

package test;

/**
 * @author Time
 * @created 2019/7/2
 */
public class Wheel {
    private String brand;
    private String specification;

    @Override
    public String toString() {
        return "Wheel{" +
                "brand='" + brand + '\'' +
                ", specification='" + specification + '\'' +
                '}';
    }
}

  1. xml配置文件
<beans>
    <bean id = "wheel" class="test.Wheel">
        <property  name="brand" value="Michelin"/>
        <property  name="specification" value="256/60 R18"/>
    </bean>
    <bean id="car" class="test.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>
</beans>
  1. 测试类

验证是否成功

/**
 * @author Time
 * @created 2019/7/2
 */
import test.*;
public class test {
    public static void main(String[] args) throws Exception {
        String location = SimpleIOC.class.getClassLoader().getResource("ioc.xml").getFile();
        System.out.println(location);
        SimpleIOC bf = new SimpleIOC(location);
        Wheel wheel = (Wheel) bf.getBean("wheel");
        System.out.println(wheel);
    }
}

输出结果:
在这里插入图片描述

二. AOP的简单实现

IOC的实现是基于代理模式的,Spring中的IOC有两种实现,当被代理类有接口时,Spring利用jdk的动态代理实现AOP,当被代理类没有继承接口时,Spring采用Cglib实现AOP。

1.SpringAOP中一些概念

  1. Advice

定义了织入对象的逻辑以及执行时机

  1. before :目标方法执行之前
  2. after: 目标方法执行之后,此时不管目标方法返回的结果是什么
  3. after-returning:目标方法执行后
  4. after-throwing: 目标方法抛出异常后执行
  5. around: 目标方法执行前后都会被调用
  1. PointCut

定义了执行的插入点

  1. Aspect

前面两个组和起来,就是Aspect。即在何时何处执行什么。

2.具体实现

  1. 定义切面接口,这个接口需要实现InvocationHandler,该接口中有invoke
    ()方法。后面生成的代理类执行相应的方法时,实质上是调用这个方法。
package MySpringAOP;

import java.lang.reflect.InvocationHandler;

/**
 * @author Time
 * @created 2019/7/2
 */
public interface Advice extends InvocationHandler {
}


// Advice 的实现类
package MySpringAOP;

import java.lang.reflect.Method;

/**
 * @author Time
 * @created 2019/7/2
 */
public class BeforeAdvice implements Advice {
    private  Object bean;
    private MethodInvocation methodInvocation;
    public BeforeAdvice(Object bean, MethodInvocation methodInvocation){
        this.bean = bean;
        this.methodInvocation = methodInvocation;
    }
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 在目标方法执行之前调用通知
        methodInvocation.invoke();
        return method.invoke(bean,args);
    }
}


  1. 定义一个需要被代理的类,这个类一定要实现相应的接口。因为jdk的底层就是通过这个接口来实现代理类的。
// 接口
package MySpringAOP;

/**
 * @author Time
 * @created 2019/7/2
 */
public interface HelloService {
    void sayHelloWorld();
}


// 接口实现类
package MySpringAOP;

/**
 * @author Time
 * @created 2019/7/2
 */
public class HelloServiceImpl implements HelloService {
    public void sayHelloWorld() {
        System.out.println("hello world!");
    }
}


  1. 添加一个接口,用来定义Advice
package MySpringAOP;

/**
 * @author Time
 * @created 2019/7/2
 */
public interface MethodInvocation {
    void invoke();
}

  1. 返回代理对象的类
package MySpringAOP;

import java.lang.reflect.Proxy;

/**
 * @author Time
 * @created 2019/7/2
 */
public class SimpleAOP {
    public static Object getProxy(Object bean,Advice advice){
        return Proxy.newProxyInstance(SimpleAOP.class.getClassLoader(), bean.getClass().getInterfaces(), advice);
    }
}

  1. 测试AOP功能,其实这里实现了两层代理,首先BeforeAdvice 代理了(包装) HelloServiceImpl类。最后又通过动态代理返回真正的代理类。
package MySpringAOP;

/**
 * @author Time
 * @created 2019/7/2
 */
public class SimpleAOPTest {
    public static void main(String[] args) {
        // 创建一个 MethodInvocation 实现类
        MethodInvocation logTask = new MethodInvocation() {
            public void invoke() {
                System.out.println("log task start");
            }
        };
        HelloServiceImpl helloServiceImpl = new HelloServiceImpl();

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

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

        helloServiceImplProxy.sayHelloWorld();
    }
}


执行结果:
在这里插入图片描述

发布了66 篇原创文章 · 获赞 26 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Time__Lc/article/details/94441981