吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 4073|回复: 2
收起左侧

[Java 转载] Spring源码分析-IOC(二)入门

[复制链接]
秀秀秀秀钰 发表于 2017-4-26 14:44
本帖最后由 秀秀秀秀钰 于 2017-4-26 14:49 编辑

原文地址:www.zzcode.cn/springioc/thread-32.html
--------------------------------------------------------------------------------------------------------------------------------------------------------------

总目录 SpringIOC源码分析系列
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ClassPathXmlApplicationContext介绍
ApplicationContext是spring中较高级的容器。和BeanFactory类似,它可以加载配置文件中定义的bean,将所有的bean集中在一起,当有请求的时候分配bean。 另外,它增加了企业所需要的功能,比如,从属性文件从解析文本信息和将事件传递给所指定的监听器。这个容器在org.springframework.context.ApplicationContext接口中定义。ApplicationContext包含BeanFactory所有的功能,一般情况下,相对于BeanFactory,ApplicationContext会被推荐使用。但BeanFactory仍然可以在轻量级应用中使用,比如移动设备或者基于applet的应用程序。
ApplicationContext接口关系
1.支持不同的信息源。扩展了MessageSource接口,这个接口为ApplicationContext提供了很多信息源的扩展功能,比如:国际化的实现为多语言版本的应用提供服务。
2.访问资源。这一特性主要体现在ResourcePatternResolver接口上,对Resource和ResourceLoader的支持,这样我们可以从不同地方得到Bean定义资源。
   这种抽象使用户程序可以灵活地定义Bean定义信息,尤其是从不同的IO途径得到Bean定义信息。这在接口上看不出来,不过一般来说,具体ApplicationContext都是继承了DefaultResourceLoader的子类。因为DefaultResourceLoader是AbstractApplicationContext的基类,关于Resource后面会有更详细的介绍。
3.支持应用事件。继承了接口ApplicationEventPublisher,为应用环境引入了事件机制,这些事件和Bean的生命周期的结合为Bean的管理提供了便利。
4.附件服务。EnvironmentCapable里的服务让基本的Ioc功能更加丰富。
5.ListableBeanFactory和HierarchicalBeanFactory是继承的主要容器。  最常被使用的ApplicationContext接口实现类:
   1,FileSystemXmlApplicationContext:该容器从XML文件中加载已被定义的bean。在这里,你需要提供给构造器XML文件的完整路径。
   2,ClassPathXmlApplicationContext:该容器从XML文件中加载已被定义的bean。在这里,你不需要提供XML文件的完整路径,只需正确配置CLASSPATH
   环境变量即可,因为,容器会从CLASSPATH中搜索bean配置文件。
   3,WebXmlApplicationContext:该容器会在一个 web 应用程序的范围内加载在XML文件中

准备工作
需要一个message类
[Java] 纯文本查看 复制代码
public class Message {
    public void sayMessage(){
        System.out.println("IOC Message!!!");
    }
}
还需要一个写一个spring的配置文件spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
      <bean id="message" class="ioc.Message"></bean>
</beans>
还需要一个测试类
[Java] 纯文本查看 复制代码
public class Message_Test { 
    @Test
    public void test1(){
        ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
        Message bean = context.getBean("message", Message.class);
        bean.sayMessage();
    }
}
好了准备工作完成好了,我们先看一下这个ClassPathXmlApplicationContext里面是做了什么。
[Java] 纯文本查看 复制代码
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
        this(new String[] {configLocation}, true, null);
    }
//这个构造方法实际上是调用了下面
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
            throws BeansException {
 
        super(parent);
        setConfigLocations(configLocations);
        if (refresh) {
            refresh();
        }
    }
先看一下这个super() 里面做了什么(注意,这里传递过来的parent为null),追踪发现在AbstractApplicationContext类中调用了setParent(parent);
[Java] 纯文本查看 复制代码
private ApplicationContext parent;
//其实就是把ApplicationContext作为父容器
@Override
    public void setParent(ApplicationContext parent) {
        this.parent = parent;
        if (parent != null) {
            Environment parentEnvironment = parent.getEnvironment();
            if (parentEnvironment instanceof ConfigurableEnvironment) {
                getEnvironment().merge((ConfigurableEnvironment) parentEnvironment);
            }
        }
    }
接着回到ClassPathXmlApplicationContext的构造方法看一下 下一句代码setConfigLocations(),追踪代码可以找到在PropertyPlaceholderHelper类中的parseStringValue()方法进行了字符串替换  ,path 路径中含有 ${user.dir} ,则将替换为: System.getProperty(user.dir);
[Java] 纯文本查看 复制代码
protected String parseStringValue(
            String strVal, PlaceholderResolver placeholderResolver, Set<String> visitedPlaceholders) {
 
        StringBuilder result = new StringBuilder(strVal);
 
        int startIndex = strVal.indexOf(this.placeholderPrefix);
        while (startIndex != -1) {
            int endIndex = findPlaceholderEndIndex(result, startIndex);
            if (endIndex != -1) {
                String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex);
                String originalPlaceholder = placeholder;
                if (!visitedPlaceholders.add(originalPlaceholder)) {
                    throw new IllegalArgumentException(
                            "Circular placeholder reference '" + originalPlaceholder + "' in property definitions");
                }
                // Recursive invocation, parsing placeholders contained in the placeholder key.
                //这里递归查找
                placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
                // Now obtain the value for the fully resolved key...
                String propVal = placeholderResolver.resolvePlaceholder(placeholder);
                if (propVal == null && this.valueSeparator != null) {
                    int separatorIndex = placeholder.indexOf(this.valueSeparator);
                    if (separatorIndex != -1) {
                        String actualPlaceholder = placeholder.substring(0, separatorIndex);
                        String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length());
                        //这里是调用了实现PropertyPlaceholderHelper内部接口PlaceholderResolver方法resolvePlaceholder
                        //:占位符 key -> value
                        propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder);
                        if (propVal == null) {
                            propVal = defaultValue;
                        }
                    }
                }
                if (propVal != null) {
                    // Recursive invocation, parsing placeholders contained in the
                    // previously resolved placeholder value.
                    propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);
                    //// 替换占位符具体值
                    result.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal);
                    if (logger.isTraceEnabled()) {
                        logger.trace("Resolved placeholder '" + placeholder + "'");
                    }
                    startIndex = result.indexOf(this.placeholderPrefix, startIndex + propVal.length());
                }
                else if (this.ignoreUnresolvablePlaceholders) {
                    // Proceed with unprocessed value.
                    startIndex = result.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length());
                }
                else {
                    throw new IllegalArgumentException("Could not resolve placeholder '" +
                            placeholder + "'" + " in string value \"" + strVal + "\"");
                }
                visitedPlaceholders.remove(originalPlaceholder);
            }
            else {
                startIndex = -1;
            }
        }
 
        return result.toString();
    }

再回到ClassPathXmlApplicationContext中,因为refresh为true,所以要执行refresh方法,这个refresh会牵扯一系列复杂操作,对于不同容器,实现都是类似的,所以封装在一个基类中完成,而在ClassPathXmlApplicationContext看到的只是一些简单的调用。

免费评分

参与人数 1吾爱币 +1 热心值 +1 收起 理由
咚咚锵啦啦啦 + 1 + 1 我很赞同!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

xdhs 发表于 2017-8-25 09:07
嗯,回暖
hp140715 发表于 2018-5-20 09:14
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-3-29 18:56

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表