zjs1224522500 / BlogIssue

This repo is created for collecting blogs with github issues written by Elvis.
0 stars 0 forks source link

Tiny-Spring #10

Open zjs1224522500 opened 5 years ago

zjs1224522500 commented 5 years ago

title: 'tinySpring学习笔记(一)-实现IOC容器' date: 2017-11-18 16:01:49 tags: Spring

tinySpring学习笔记(一)-实现IOC容器

为了更好的理解Spring的核心思想(IOC和AOP),开始阅读并总结体会 code4crafttinySpring模仿Spring的微型项目,并做一些笔记记录。

step1-最基本的容器

git checkout step-1-container-register-and-get

IOC最基本的组成:BeanFactory 和 Bean(容器和实体)

实现步骤:

step2-将Bean创建放入工厂

git checkout step-2-abstract-beanfactory-and-do-bean-initilizing-in-it

public abstract class AbstractBeanFactory implements BeanFactory { private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(); @Override public Object getBean(String name) { // 先获取对应的包装对象,再获取对应的实体 return beanDefinitionMap.get(name).getBean(); } @Override public void registerBeanDefinition(String name, BeanDefinition beanDefinition) { // 注入Bean之前先创建对应Bean Object bean = doCreateBean(beanDefinition); // 创建Bean之后并进行相应的包装 beanDefinition.setBean(bean); // 注入包装后的Bean对象 beanDefinitionMap.put(name, beanDefinition); } /**

}

public class AutowireCapableBeanFactory extends AbstractBeanFactory { @Override protected Object doCreateBean(BeanDefinition beanDefinition) { try { // 利用包装类对应的 class 对象,再利用 反射 创建对象 Object bean = beanDefinition.getBeanClass().newInstance(); return bean; } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } return null; } }


#### 实现步骤:(相比step1区别仅在于 ==初始化BeanFactory和注入Bean==)

- 1、初始化BeanFactory
```java
// 创建能够 使用容器创建Bean实例 的容器对象
BeanFactory beanFactory = new AutowireCapableBeanFactory();

step3-为Bean注入属性

git checkout step-3-inject-bean-with-property

实现步骤:

step4-读取xml配置来初始化bean

git checkout step-4-config-beanfactory-with-xml

/**
 * interface BeanDefinitionReader ->
 * abstract class AbstractBeanDefinitionReader ->
 * class XmlBeanDefinitionReader
 */
public interface BeanDefinitionReader {
    void loadBeanDefinitions(String location) throws Exception;
}

public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader {
    // 保存从配置文件中加载的所有的 beanDefinition 对象
    private Map<String,BeanDefinition> registry;
    /**
     * 依赖 ResourceLoader,该类又依赖 UrlResource 
     * UrlResource 继承自 Spring 自带的 Resource 内部资源定位接口
     * Resource 接口,标识一个外部资源。通过 getInputStream() 方法 获取资源的输入流 。
     * UrlResource 实现 Resource 接口的资源类,通过 URL 获取资源。
     * ResourceLoader 资源加载类。通过 getResource(String) 方法获取一个 Resource 对象,是获取 Resource 的主要途径.
     */
    private ResourceLoader resourceLoader;
    protected AbstractBeanDefinitionReader(ResourceLoader resourceLoader){
        this.registry = new HashMap<String, BeanDefinition>();
        this.resourceLoader = resourceLoader;
    }
    public Map<String, BeanDefinition> getRegistry() {
        return registry;
    }
    public ResourceLoader getResourceLoader() {
        return resourceLoader;
    }
}

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader{
    public XmlBeanDefinitionReader(ResourceLoader resourceLoader) {
        super(resourceLoader);
    }
    // 从配置文件中加载 Bean 的相关信息
    @Override
    public void loadBeanDefinitions(String location) throws Exception {
        // 利用文件路径创建相应的输入流
        InputStream inputStream = getResourceLoader().getResource(location).getInputStream();
        doLoadBeanDefinitions(inputStream);
    }
    protected void doLoadBeanDefinitions(InputStream inputStream) throws Exception {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder docBuilder = factory.newDocumentBuilder();
        // 输入流转换成对应的 Document 对象便于获取对应的元素
        Document doc = docBuilder.parse(inputStream);
        // 解析bean
        registerBeanDefinitions(doc);
        inputStream.close();
    }
    public void registerBeanDefinitions(Document doc) {
        // 获取文件中包含的元素 <beans></beans>
        Element root = doc.getDocumentElement();
        parseBeanDefinitions(root);
    }
    protected void parseBeanDefinitions(Element root) {
        // 获取元素包含的子节点链 <bean></bean> 链
        NodeList nl = root.getChildNodes();
        for (int i = 0; i < nl.getLength(); i++) {
            // 获取子节点链上对应的子节点 单个<bean></bean>
            Node node = nl.item(i);
            if (node instanceof Element) {
                Element ele = (Element) node;
                // 将节点强转为 Element 对象并进行解析
                processBeanDefinition(ele);
            }
        }
    }
    protected void processBeanDefinition(Element ele) {
        // 获取子节点对应的属性(name,class)来对应 Bean
        // <bean name="" class=""></bean>
        String name = ele.getAttribute("name");
        String className = ele.getAttribute("class");
        // 创建与之对应的 BeanDefinition ,并设置相应的属性
        BeanDefinition beanDefinition = new BeanDefinition();
        // 将节点包含的 Bean相关的属性信息注入创建的 BeanDefinition 中。
        processProperty(ele,beanDefinition);
        beanDefinition.setBeanClassName(className);
        // 统一管理(HashMap)通过配置文件加载的 beanDefinition 对象
        getRegistry().put(name, beanDefinition);
    }
    private void processProperty(Element ele,BeanDefinition beanDefinition) {
        // 获取元素对应的 Property 节点
        // <bean><property name="" value=""></property></bean>
        NodeList propertyNode = ele.getElementsByTagName("property");
        for (int i = 0; i < propertyNode.getLength(); i++) {
            // 遍历节点并取出节点对应的 key-value,添加到 BeanDefinition 对应的属性中
            Node node = propertyNode.item(i);
            if (node instanceof Element) {
                Element propertyEle = (Element) node;
                String name = propertyEle.getAttribute("name");
                String value = propertyEle.getAttribute("value");
                beanDefinition.getPropertyValues().addPropertyValue(new PropertyValue(name,value));
            }
        }
    }
}

实现步骤:

step5-为bean注入bean(处理Bean之间的依赖)

git checkout step-5-inject-bean-to-bean

protected void applyPropertyValues(Object bean, BeanDefinition mbd) throws Exception { for (PropertyValue propertyValue : mbd.getPropertyValues().getPropertyValues()) { // 获取对象中指定属性名( PropertyValue 的 key)对应的属性 Field declaredField = bean.getClass().getDeclaredField(propertyValue.getName()); declaredField.setAccessible(true); Object value = propertyValue.getValue(); // 若该属性的值对应的是一个Bean,是Bean对Bean的引用 if (value instanceof BeanReference) { // 原类型为 Object,需要强转为 BeanReference BeanReference beanReference = (BeanReference) value; // 再利用BeanReference存储的name属性获取到对应的bean(包装类 -> 实体类) value = getBean(beanReference.getName()); } declaredField.set(bean, value); } }

/**
 * 对于 getBean(),采用"lazy-init"的方式(延迟加载)。
 * 避免两个循环依赖的Bean在创建时陷入死锁。
 * 在注入Bean的时候,先尝试获取,获取不到再创建,故总是先创建后注入。
 */
public Object getBean(String name) throws Exception {
    BeanDefinition beanDefinition = beanDefinitionMap.get(name);
    if (beanDefinition == null) {
        throw new IllegalArgumentException("No bean named " + name + " is defined");
    }
    Object bean = beanDefinition.getBean();
    if (bean == null) {
        bean = doCreateBean(beanDefinition);
    }
    return bean;
}

#### 实现步骤:
- 1、读取配置
```java
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader());
xmlBeanDefinitionReader.loadBeanDefinitions("tinyioc.xml");

step6-ApplicationContext

git checkout step-6-invite-application-context

public abstract class AbstractApplicationContext implements ApplicationContext { // 依赖抽象容器 protected AbstractBeanFactory beanFactory; public AbstractApplicationContext(AbstractBeanFactory beanFactory) { this.beanFactory = beanFactory; } //声明方法 初始化所有的 Bean public void refresh() throws Exception{ } // 调用容器的获取Bean方法 @Override public Object getBean(String name) throws Exception { return beanFactory.getBean(name); } }

public class ClassPathXmlApplicationContext extends AbstractApplicationContext { private String configLocation; // 默认使用自动装配容器 public ClassPathXmlApplicationContext(String configLocation) throws Exception { this(configLocation, new AutowireCapableBeanFactory()); } // 根据 配置文件路径 容器类型 构造对应的 ApplicationContext public ClassPathXmlApplicationContext(String configLocation, AbstractBeanFactory beanFactory) throws Exception { super(beanFactory); this.configLocation = configLocation; refresh(); }

// 将读取配置文件以及将读取到的属性值注入Bean的方法进行封装
@Override
public void refresh() throws Exception {

    // 读取配置文件 封装原来的实现步骤一
    XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader());
    xmlBeanDefinitionReader.loadBeanDefinitions(configLocation);

    // 将获得的信息注入Bean 封装原来的实现步骤二
    for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : xmlBeanDefinitionReader.getRegistry().entrySet()) {
        beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue());
    }
}

}

#### 实现步骤:

- 1、初始化ApplicationContext(加载配置文件并初始化 Bean)
```java
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("tinyioc.xml");

title: 'tinySpring学习笔记(二)-实现AOP' date: 2018-04-10 16:01:49 tags: Spring

AOP及其实现

AOP分为配置(Pointcut,Advice),织入(Weave)两部分工作,当然还有一部分是将AOP整合到整个容器的生命周期中。

step1-使用JDK动态代理实现AOP织入

git checkout step-7-method-interceptor-by-jdk-dynamic-proxy

AOP 中两个重要角色:MethodInterceptor和MethodInvocation

public interface MethodInterceptor extends Interceptor {
    Object invoke(MethodInvocation invocation) throws Throwable;
}
public class JdkDynamicAopProxy implements AopProxy, InvocationHandler {

    /**
     * AdvisedSupport
     * Fields: TargetSource targetSource
     *         [Object target,Class targetClass];//target 代理对象,targetClass 继承的接口
     *         
     *         MethodInterceptor methodInterceptor;//Advice切面逻辑
     * Methods:对应属性的getter、setter
     */
    private AdvisedSupport advised;

    public JdkDynamicAopProxy(AdvisedSupport advised) {
        this.advised = advised;
    }

    //为要代理的对象的的类对应的创建动态代理
    @Override
    public Object getProxy() {

        /**
         * public static Object newProxyInstance(ClassLoader loader,
         *                                       Class<?>[] interfaces,
         *                                       InvocationHandler h)
         * @param loader the class loader to define the proxy class
         * @param interfaces the list of interfaces for the proxy class to implement
         * @param h the invocation handler to dispatch method invocations to
         */
        return Proxy.newProxyInstance(getClass().getClassLoader(), new Class[] { advised.getTargetSource()
                .getTargetClass() }, this);
    }

    /**
     * 重写 InvocationHandler 对应的 invoke() 方法
     * 调用拦截器对应的方法
     * (通过反射获取对应的切点,再根据切点指定的逻辑进行执行)
     * @Param Object proxy 代理
     * @Param Method method 对应的要调用方法
     * @Param Object[] args 方法需要的参数
     */
    @Override
    public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
        MethodInterceptor methodInterceptor = advised.getMethodInterceptor();

        /**
         * 其中 class MethodInterceptor extends Interceptor {
         *           Object invoke(MethodInvocation invocation) throws Throwable;
         *      }
         *      class ReflectiveMethodInvocation implements MethodInvocation
         * 故可通过继承 MethodInterceptor 重写相关 invoke() 方法实现 Advice 逻辑
         */
        return methodInterceptor.invoke(new ReflectiveMethodInvocation(advised.getTargetSource().getTarget(), method,
                args));
    }
}

实现步骤:

step2-使用AspectJ管理切面

git checkout step-8-invite-pointcut-and-aspectj

}

public interface ClassFilter {

boolean matches(Class targetClass);

}

public interface MethodMatcher {

boolean matches(Method method, Class targetClass);

}


- `AspectJ`是一个“对`Java`的`AOP`增强”。它最早是其实是一门语言,我们跟写`Java`代码一样写它,然后静态编译之后,就有了`AOP`的功能。下面是一段`AspectJ`代码:
```java
aspect PointObserving {
    private Vector Point.observers = new Vector();
    public static void addObserver(Point p, Screen s) {
        p.observers.add(s);
    }
    public static void removeObserver(Point p, Screen s) {
        p.observers.remove(s);
    }
    ...
}

实现测试:

// 对类做匹配 返回匹配结果
@Test
public void testClassFilter() throws Exception {
    String expression = "execution(* us.codecraft.tinyioc.*.*(..))";
    AspectJExpressionPointcut aspectJExpressionPointcut = new AspectJExpressionPointcut();
    aspectJExpressionPointcut.setExpression(expression);
    boolean matches = aspectJExpressionPointcut.getClassFilter().matches(HelloWorldService.class);
    Assert.assertTrue(matches);
}

// 对方法做匹配 返回匹配结果
@Test
public void testMethodInterceptor() throws Exception {
    String expression = "execution(* us.codecraft.tinyioc.*.*(..))";
    AspectJExpressionPointcut aspectJExpressionPointcut = new AspectJExpressionPointcut();
    aspectJExpressionPointcut.setExpression(expression);
    boolean matches = aspectJExpressionPointcut.getMethodMatcher().matches(HelloWorldServiceImpl.class.getDeclaredMethod("helloWorld"),HelloWorldServiceImpl.class);
    Assert.assertTrue(matches);
}

step3-将AOP融入Bean的创建过程中

git checkout step-9-auto-create-aop-proxy


- `AspectJAwareAdvisorAutoProxyCreator`就是`AspectJ`方式实现织入的核心。它其实是一个`BeanPostProcessor`。在这里它会扫描所有`Pointcut`,并对`bean`做织入。
- BeanPostProcessor:
```java
public interface BeanPostProcessor {

    Object postProcessBeforeInitialization(Object bean, String beanName) throws Exception;

    Object postProcessAfterInitialization(Object bean, String beanName) throws Exception;

}

- 此时的`JdkDynamicAopProxy`
```java
public class JdkDynamicAopProxy implements AopProxy, InvocationHandler {

    private AdvisedSupport advised;

    public JdkDynamicAopProxy(AdvisedSupport advised) {
        this.advised = advised;
    }

    @Override
    public Object getProxy() {
        return Proxy.newProxyInstance(getClass().getClassLoader(), advised.getTargetSource().getTargetClass(), this);
    }

    @Override
    public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
        MethodInterceptor methodInterceptor = advised.getMethodInterceptor();

        // invoke 时判断是否为要拦截的方法,是则执行 Advice 逻辑
        if (advised.getMethodMatcher() != null
                && advised.getMethodMatcher().matches(method, advised.getTargetSource().getTarget().getClass())) {
            return methodInterceptor.invoke(new ReflectiveMethodInvocation(advised.getTargetSource().getTarget(),
                    method, args));
        } else {
            return method.invoke(advised.getTargetSource().getTarget(), args);
        }
    }
}

##### 动态代理的步骤
- `AutoProxyCreator`(实现了 `BeanPostProcessor` 接口)在实例化所有的 `Bean` 前,最先被实例化。`postProcessBeforeInitialization`
- 其他普通 `Bean` 被实例化、初始化,在初始化的过程中,`AutoProxyCreator` 加载 `BeanFactory` 中所有的 `PointcutAdvisor`(这也保证了 `PointcutAdvisor` 的实例化顺序优于普通 `Bean`。),然后依次使用 `PointcutAdvisor` 内置的 `ClassFilter`,判断当前对象是不是要拦截的类。
- 如果是,则生成一个 `TargetSource`(要拦截的对象和其类型),并取出 `AutoProxyCreator` 的 `MethodMatcher`(对哪些方法进行拦截)、`Advice`(拦截的具体操作),再交给 `AopProxy` 去生成代理对象。
- `AopProxy` 生成一个 `InvocationHandler`,在它的 `invoke` 函数中,首先使用 `MethodMatcher` 判断是不是要拦截的方法,如果是则交给 `Advice` 来执行(`Advice` 由用户来编写,其中也要手动/自动调用原始对象的方法),如果不是,则直接交给 `TargetSource` 的原始对象来执行。

### step4-使用CGLib进行类的织入
> git checkout step-10-invite-cglib-and-aopproxy-factory

- 前面的`JDK`动态代理只能对接口进行代理,对于类则无能为力。这里我们需要一些字节码操作技术。这方面大概有几种选择:`ASM`,`CGLib`和`javassist`,后两者是对`ASM`的封装。`Spring`中使用了`CGLib`。
- 在这一步,我们还要定义一个工厂类`ProxyFactory`,用于根据`TargetSource`类型自动创建代理,这样就需要在调用者代码中去进行判断。
- TargetSource:
```java
public class TargetSource {

    private Class<?> targetClass;

    private Class<?>[] interfaces;

    private Object target;

    public TargetSource(Object target, Class<?> targetClass,Class<?>... interfaces) {
        this.target = target;
        this.targetClass = targetClass;
        this.interfaces = interfaces;
    }

    public Class<?> getTargetClass() {
        return targetClass;
    }

    public Object getTarget() {
        return target;
    }

    public Class<?>[] getInterfaces() {
        return interfaces;
    }
}

}


- 代理模式相关参见 [代理模式(Proxy Pattern)- 最易懂的设计模式解析](https://blog.csdn.net/carson_ho/article/details/54910472)