帝斯曼亮相2015印度塑料展 展示最新汽車與移動通信应用創新成果

作為全球生命科學與材料科學領先企業,荷蘭皇家帝斯曼集團將攜多項全新應用亮相2015年2月5日至10日期間舉辦的2015印度塑膠展(PlastIndia 2015)。帝斯曼的工程塑料賦予了這些應用更出色的性能、更輕的重量和更佳的可持續性,涉及汽車、移動電子及電氣設備等領域。屆時,帝斯曼新近推出的部分新型材料也將在展會上亮相。帝斯曼的展位號為14R-D4

在2015印度塑料展上,帝斯曼将重点展示其在生产中所应用的“绿色”技术。

在汽車領域中,其中的一項主要應用為帝斯曼新一代的Diablo耐高溫聚醯胺。該新型材料是帝斯曼Stanyl® 聚醯胺46和Akulon®聚醯胺6系列產品的一員,主要應用於發動機艙內的中冷集成進氣歧管等。帝斯曼最新推出的Stanyl Diablo聚醯胺46能夠承受230°C的連續使用溫度,而新型Akulon Diablo則可持續在220°C的高溫環境中工作。值得關注的是,這兩個新類型產品均提高了對短期高溫峰值的抵抗能力。

帝斯曼最新推出的Stanyl Diablo聚酰胺46能够承受230°C的连续使用温度,而新型Akulon Diablo则可持续在220°C的高温环境中工作。

此外,帝斯曼還將展示汽車領域的其他創新應用,例如以甲烷或氫氣為燃料的兩輪或四輪汽車的複合儲氣罐,以及一款獲得多項大獎的曲軸端蓋。該燃料儲氣罐是一款壓力容器,其高阻隔性內膽由帝斯曼的Akulon® 燃油阻隔6吹塑成型,外殼為連續纖維增強塑膠。其重量約為鋼罐的三分之一,因此燃料利用率得到了明顯的改善。其外殼甚至可以捨棄传统的热固性树脂,轉而採用热塑性塑料——比如由可再生原料制成的帝斯曼高性能EcoPaXX® 聚酰胺410。

此前, EcoPaXX®材料剛剛憑藉其在汽車領域的輕型多功能曲軸端蓋應用斬獲獎項。該獎項為美國塑膠工程師協會(SPE)汽車事業部創新獎項競賽中動力總成類產品的桂冠。EcoPaXX曲轴端盖由帝斯曼的汽车零部件专业合作伙伴KACO在德国制造,专供大众集团研发的最新一代柴油发动机使用。其重量比採用鋁材製成的類似幾何形狀的曲軸端蓋減輕了約40%。

帝斯曼在電子電氣領域的廣泛應用也值得關注,尤其是移動通信方面。例如,為了满足不斷增加的微型化、薄壁化、无卤阻燃及热能管理方面的要求,公司开发了一系列新型解决方案。在帝斯曼的展位上,觀眾将近距離與行業专家探討连接器、线圈骨架、接线盒、电线电缆、天线及分离器等元器件的最新材料选择。

在電子電氣領域的其他方面,帝斯曼走在無鹵阻燃高性能聚醯胺研發的前沿,為塑殼斷路器(MCCBs)中的強化玻璃熱固性複合材料研製出了一種更高效、更經濟、更環保的替代材料。帝斯曼在LED照明領域也推出了諸多創新解決方案,其導熱複合材料可以替代鋁材,使散熱片更易製造、更輕便並具備更強的功能性。領先的照明設備生產商歐司朗(Osram)近期已開始使用以聚醯胺46為基材的帝斯曼Stanyl TC复合材料为其全新的LED筒灯系列制造散热片。帝斯曼还有专门针对电线电缆的无卤材料,比如用于电气及通信电缆的Arnitel热塑性弹性体(TPE)。

帝斯曼走在无卤阻燃高性能聚酰胺研发的前沿,为塑壳断路器(MCCBs)中的强化玻璃热固性复合材料研制出了一种更高效、更经济、更环保的替代材料。

除此之外,用於醫療領域的Arnitel也是帝斯曼的突破性技術之一。Arnitel VT能夠極大程度地隔離傳染性病毒,並且因為具有透氣性,穿著舒適,適合作為一次性醫用防護服及窗簾的織物層壓材料。

帝斯曼浦那工廠利用太陽能及風能大幅降低能源消耗

在2015印度塑膠展上,帝斯曼也將重點展示其在生產中所應用的“綠色”技術。今年9月,帝斯曼的太陽能技術演示中心在其位於印度浦那的聚酯和聚醯胺複合物生產工廠落成。這一先進技術中心旨在展示帝斯曼太陽能技術的創新成果,並將通過太陽能滿足工廠25%的電力需求,同時減少工廠的二氧化碳足跡。

帝斯曼致力於通過新型技術和材料的研發與商業化,提高太陽能模組的效率與產量,從而推動太陽能發電的發展。為此,帝斯曼先進表面業務部開發了一款名為KhepriCoat®的防反射塗層材料,可顯著提高太陽能設備的透光率。帝斯曼先進表面業務部立志成為世界領先的太陽能光伏材料解決方案供應商。

帝斯曼工程塑料工廠現可以通過可再生能源滿足自身總電力需求的50%,公司計畫進一步提高這個比例。在該太陽能技術展示中心揭幕之前,其中25%的能源已經通過風力發電實現。

浦那工廠在水資源高效管理方面也保持著領先優勢。作為帝斯曼首批完全消除生產過程廢水的工廠之一,浦那工廠所使用的全部水源均經過處理、檢測並重新用於園藝等用途。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

新北清潔公司,居家、辦公、裝潢細清專業服務

Spring IoC bean 的加載

前言

本系列全部基於 Spring 5.2.2.BUILD-SNAPSHOT 版本。因為 Spring 整個體系太過於龐大,所以只會進行關鍵部分的源碼解析。

本篇文章主要介紹 Spring IoC 容器是怎麼加載 bean 的。

正文

我們先看一下Spring IoC BeanDefinition 的加載和註冊一文中獲取 bean 的實例代碼:

public class BeanDefinitionDemo {

    public static void main(String[] args) {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
        reader.loadBeanDefinitions("META-INF/bean-definition.xml");
        User user = beanFactory.getBean("user", User.class);
        System.err.println(user);
    }

}

通過 beanFactory.getBean() 這個方法就獲取了在 XML 中定義的 bean,下面我們就重點分析這個方法背後做了什麼操作。

在正式開始之前,我們先了解一下 FactoryBean 及其用法。

FactoryBean 介紹

FactoryBean 接口對於 Spring 框架來說佔有重要的地位,Spring 自身就提供了70多個 FactoryBean 的實現。它們隱藏了一下複雜 bean 的細節,給上層應用帶來了便利。下面是該接口的定義:

public interface FactoryBean<T> {

    // 返回由FactoryBean創建的bean實例,如果isSingleton()返回true,
    // 則該實例會放到Spring容器中單例緩存池中
    @Nullable
    T getObject() throws Exception;
	
    // 返回FactoryBean創建的bean類型
    @Nullable
    Class<?> getObjectType();

    // 返回由FactoryBean創建的bean實例的作用域是singleton還是prototype
    default boolean isSingleton() {
        return true;
    }

}

當配置文件中 <bean>class 屬性配置的實現類時 FactoryBean 時,通過 getBean() 返回的不是 FactoryBean 本身,而是 FactoryBean#getObject() 所返回的對象,相當於 FactoryBean#getObject() 代理了 getBean()。下面用簡單的代碼演示一下:

首先定義一個 Car 實體類:

public class Car {
	
    private Integer maxSpeed;
    private String brand;
    private Double price;

    public Integer getMaxSpeed() {
        return maxSpeed;
    }

    public void setMaxSpeed(Integer maxSpeed) {
        this.maxSpeed = maxSpeed;
    }

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public Double getPrice() {
        return price;
    }

    public void setPrice(Double price) {
        this.price = price;
    }
}

上面的實體類,如果用傳統方式配置,每一個屬性都會對應一個 <property> 元素標籤。如果用 FactoryBean 的方式實現就會靈活一點,下面通過逗號分隔的方式一次性的為 Car 的所有屬性配置值。

public class CarFactoryBean implements FactoryBean<Car> {
	
    private String carInfo;
	
    @Override
    public Car getObject() throws Exception {
        Car car = new Car();
        String[] infos = carInfo.split(",");
        car.setBrand(infos[0]);
        car.setMaxSpeed(Integer.valueOf(infos[1]));
        car.setPrice(Double.valueOf(infos[2]));
        return car;
    }

    @Override
    public Class<?> getObjectType() {
        return Car.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

    public String getCarInfo() {
        return carInfo;
    }

    public void setCarInfo(String carInfo) {
        this.carInfo = carInfo;
    }
}

接下來,我們在 XML 中配置。

<bean id="car" class="com.leisurexi.ioc.domain.CarFactoryBean">
    <property name="carInfo" value="超級跑車,400,2000000"/>
</bean>

最後看下測試代碼和運行結果:

@Test
public void factoryBeanTest() {
    DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
    XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
    reader.loadBeanDefinitions("META-INF/factory-bean.xml");
    Car car = beanFactory.getBean("car", Car.class);
    System.out.println(car);
    CarFactoryBean carFactoryBean = beanFactory.getBean("&car", CarFactoryBean.class);
    System.out.println(carFactoryBean);
}

可以看到如果 beanName 前面加上 & 獲取的是 FactoryBean 本身,不加獲取的 getObject() 返回的對象。

FactoryBean 的特殊之處在於它可以向容器中註冊兩個 bean,一個是它本身,一個是 FactoryBean.getObject() 方法返回值所代表的 bean

bean 的加載

AbstractBeanFactory#getBean

public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
    // 調用doGetBean方法(方法以do開頭實際做操作的方法)
    return doGetBean(name, requiredType, null, false);
}

/**
* @param name          bean的名稱
* @param requiredType  bean的類型
* @param args          显示傳入的構造參數
* @param typeCheckOnly 是否僅僅做類型檢查
*/
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
    // 獲取bean的實際名稱,見下文詳解
    final String beanName = transformedBeanName(name);
    Object bean;

    // 直接嘗試從緩存獲取或 singletonFactories 中的 ObjectFactory 中獲取,見下文詳解
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null && args == null) {
        // 檢查bean是否是FactoryBean的實現。不是直接返回bean,
        // 是的話首先檢查beanName是否以&開頭,如果是返回FactoryBean本身,
        // 不是調用FactoryBean#getObject()返回對象,見下文詳解
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }

    else {
        // 只有在單例情況下才會去嘗試解決循環依賴,原型模式下,如果存在A中有
        // B屬性,B中有A屬性,那麼當依賴注入時,就會產生當A還未創建完的時候
        // 對於B的創建而在此返回創建A,造成循環依賴
        if (isPrototypeCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }

        // 檢查當前bean的BeanDefinition是否在當前的bean工廠中,
        // 不在遞歸調用父工廠的getBean()去獲取bean
        BeanFactory parentBeanFactory = getParentBeanFactory();
        if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
            String nameToLookup = originalBeanName(name);
            if (parentBeanFactory instanceof AbstractBeanFactory) {
                return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
                    nameToLookup, requiredType, args, typeCheckOnly);
            }
            else if (args != null) {
                return (T) parentBeanFactory.getBean(nameToLookup, args);
            }
            else if (requiredType != null) {
                return parentBeanFactory.getBean(nameToLookup, requiredType);
            }
            else {
                return (T) parentBeanFactory.getBean(nameToLookup);
            }
        }
        // 如果不是僅僅做類型檢查,則是創建bean,這裏要進行記錄
        if (!typeCheckOnly) {
            // 記錄bean已經創建過
            markBeanAsCreated(beanName);
        }

        try {
            // 合併BeanDefinition,見下文詳解
            final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
            checkMergedBeanDefinition(mbd, beanName, args);

            // 實例化bean前先實例化依賴bean,也就是depends-on屬性中配置的beanName
            String[] dependsOn = mbd.getDependsOn();
            if (dependsOn != null) {
                for (String dep : dependsOn) {
                    // 檢查是否循環依賴,即當前bean依賴dep,dep依賴當前bean,見下文詳解
                    if (isDependent(beanName, dep)) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
                    }
                    // 將dep和beanName的依賴關係放入到緩存中,見下文詳解
                    registerDependentBean(dep, beanName);
                    try {
                        // 獲取依賴dep對應的bean實例,如果還未創建實例,則先去創建
                        getBean(dep);
                    }
                    catch (NoSuchBeanDefinitionException ex) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName,"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
                    }
                }
            }

            // 如果 bean 的作用域是單例
            if (mbd.isSingleton()) {
                // 創建和註冊單例 bean,見下文詳解
                sharedInstance = getSingleton(beanName, () -> {
                    try {
                        // 創建 bean 實例
                        return createBean(beanName, mbd, args);
                    }
                    catch (BeansException ex) {
                        destroySingleton(beanName);
                        throw ex;
                    }
                });
                // 獲取bean實例
                bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
            }
            // bean 的作用域是原型
            else if (mbd.isPrototype()) {
                Object prototypeInstance = null;
                try {
                    // 原型 bean 創建前回調,
                    // 默認實現是將 beanName 保存到 prototypesCurrentlyInCreation 緩存中
                    beforePrototypeCreation(beanName);
                    // 創建 bean 實例
                    prototypeInstance = createBean(beanName, mbd, args);
                }
                finally {
                    // 原型 bean 創建后回調,
                    // 默認實現是將 beanName 從prototypesCurrentlyInCreation 緩存中移除
                    afterPrototypeCreation(beanName);
                }
                // 獲取bean實例
                bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
            }
            // 自定義作用域
            else {
                // 獲取自定義作用域名稱
                String scopeName = mbd.getScope();
                // 獲取作用域對象
                final Scope scope = this.scopes.get(scopeName);
                // 如果為空表示作用域未註冊,拋出異常
                if (scope == null) {
                    throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
                }
                try {
                    // 其他 Scope 的 bean 創建
                    // 新建了一個 ObjectFactory,並且重寫了 getObject 方法
                    Object scopedInstance = scope.get(beanName, () -> {
                        // 調用原型 bean 創建前回調
                        beforePrototypeCreation(beanName);
                        try {
                            // 創建 bean 實例,下篇文章詳解
                            return createBean(beanName, mbd, args);
                        }
                        finally {
                            // 調用原型 bean 創建后回調
                            afterPrototypeCreation(beanName);
                        }
                    });
                    // 獲取bean實例
                    bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
                }
                catch (IllegalStateException ex) {
                    throw new BeanCreationException(beanName, "Scope '" + scopeName + "' is not active for the current thread; consider " + "defining a scoped proxy for this bean if you intend to refer to it from a singleton",ex);
                }
            }
        }
        catch (BeansException ex) {
            cleanupAfterBeanCreationFailure(beanName);
            throw ex;
        }
    }

    // 檢查所需的類型是否與實際 bean 實例的類型匹配
    if (requiredType != null && !requiredType.isInstance(bean)) {
        try {
            // 如果類型不等,進行轉換,轉換失敗拋出異常;轉換成功直接返回
            T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
            if (convertedBean == null) {
                throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
            }
            return convertedBean;
        }
        catch (TypeMismatchException ex) {
            throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
        }
    }
    // 返回 bean 實例
    return (T) bean;
}

上面方法就是獲取 bean 的整個流程,下面我們對其調用的其它主要方法來一一分析。

轉換對應的 beanName

AbstractBeanFactory#transformedBeanName

protected String transformedBeanName(String name) {
    return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}

// BeanFactoryUtils.java
public static String transformedBeanName(String name) {
    Assert.notNull(name, "'name' must not be null");
    // 如果name不是&開頭,直接返回
    if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
        return name;
    }
    // 去除name的&前綴
    return transformedBeanNameCache.computeIfAbsent(name, beanName -> {
        do {
            beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
        }
        while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
        return beanName;
    });
}

// SimpleAliasRegistry.java
public String canonicalName(String name) {
    String canonicalName = name;
    String resolvedName;
    // 如果name是別名,則會循環去查找bean的實際名稱
    do {
        resolvedName = this.aliasMap.get(canonicalName);
        if (resolvedName != null) {
            canonicalName = resolvedName;
        }
    }
    while (resolvedName != null);
    return canonicalName;
}

嘗試從單例緩存獲取 bean

AbstractBeanFactory#getSingleton

public Object getSingleton(String beanName) {
    // allowEarlyReference設置為true表示允許早期依賴
    return getSingleton(beanName, true);
}

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 先從一級緩存中,檢查單例緩存是否存在
    Object singletonObject = this.singletonObjects.get(beanName);
    // 如果為空,並且當前bean正在創建中,鎖定全局變量進行處理
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            // 從二級緩存中獲取
            singletonObject = this.earlySingletonObjects.get(beanName);
            // 二級緩存為空 && bean允許提前曝光
            if (singletonObject == null && allowEarlyReference) {
                // 從三級緩存中獲取bean對應的ObjectFactory
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    // 調用預先設定的getObject(),獲取bean實例
                    singletonObject = singletonFactory.getObject();
                    // 放入到二級緩存中,並從三級緩存中刪除
                    // 這時bean已經實例化完但還未初始化完
                    // 在該bean未初始化完時如果有別的bean引用該bean,可以直接從二級緩存中取出返回
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

上面方法主要就是嘗試從緩存中獲取 bean,緩存有三級,這也是 Spring 解決循環依賴的關鍵所在;後續會在 循環依賴 中重點講述。

獲取 bean 實例對象

AbstractBeanFactory#getObjectForBeanInstance

protected Object getObjectForBeanInstance(Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {

    // name 是否以 & 開頭
    if (BeanFactoryUtils.isFactoryDereference(name)) {
        // 如果是 null 直接返回
        if (beanInstance instanceof NullBean) {
            return beanInstance;
        }
        // beanName 以 & 開頭,但又不是 FactoryBean 類型,拋出異常
        if (!(beanInstance instanceof FactoryBean)) {
            throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
        }
        // 設置 isFactoryBean 為 true
        if (mbd != null) {
            mbd.isFactoryBean = true;
        }
        // 返回 bean 實例
        return beanInstance;
    }

    // name 不是 & 開頭,並且不是 FactoryBean 類型,直接返回
    if (!(beanInstance instanceof FactoryBean)) {
        return beanInstance;
    }

    // 到這裏就代表name不是&開頭,且是FactoryBean類型
    // 即獲取FactoryBean.getObject()方法返回值所代表的bean
    Object object = null;
    if (mbd != null) {	
        mbd.isFactoryBean = true;
    }
    else {
        // 從緩存中獲取實例
        object = getCachedObjectForFactoryBean(beanName);
    }
    if (object == null) {
        // 將 beanInstance 強轉成 FactoryBean
        FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
        // 合併 BeanDefinition
        if (mbd == null && containsBeanDefinition(beanName)) {
            mbd = getMergedLocalBeanDefinition(beanName);
        }
        boolean synthetic = (mbd != null && mbd.isSynthetic());
        // 獲取實例
        object = getObjectFromFactoryBean(factory, beanName, !synthetic);
    }
    return object;
}

// FactoryBeanRegistrySupport.java
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
    // 如果是單例 bean,並且已經存在緩存中
    if (factory.isSingleton() && containsSingleton(beanName)) {
        // 加鎖
        synchronized (getSingletonMutex()) {
            // 從緩存中獲取
            Object object = this.factoryBeanObjectCache.get(beanName);
            if (object == null) {
                // 調用 FactoryBean 的 getObject() 獲取實例
                object = doGetObjectFromFactoryBean(factory, beanName);
                Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
                // 如果該 beanName 已經在緩存中存在,則將 object 替換成緩存中的
                if (alreadyThere != null) {
                    object = alreadyThere;
                }
                else {
                    if (shouldPostProcess) {
                        // 如果當前 bean 還在創建中,直接返回
                        if (isSingletonCurrentlyInCreation(beanName)) {
                            return object;
                        }
                        // 單例 bean 創建前回調
                        beforeSingletonCreation(beanName);
                        try {
                            // 對從 FactoryBean 獲得給定對象的後置處理,默認按原樣返回
                            object = postProcessObjectFromFactoryBean(object, beanName);
                        }
                        catch (Throwable ex) {
                            throw new BeanCreationException(beanName,
                                                            "Post-processing of FactoryBean's singleton object failed", ex);
                        }
                        finally {
                            // 單例 bean 創建后回調
                            afterSingletonCreation(beanName);
                        }
                    }
                    if (containsSingleton(beanName)) {
                        // 將 beanName 和 object 放到 factoryBeanObjectCache 緩存中
                        this.factoryBeanObjectCache.put(beanName, object);
                    }
                }
            }
            // 返回實例
            return object;
        }
    }
    else {
        // 調用 FactoryBean 的 getObject() 獲取實例
        Object object = doGetObjectFromFactoryBean(factory, beanName);
        if (shouldPostProcess) {
            try {
                // 對從 FactoryBean 獲得給定對象的後置處理,默認按原樣返回
                object = postProcessObjectFromFactoryBean(object, beanName);
            }
            catch (Throwable ex) {
                throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
            }
        }
        // 返回實例
        return object;
    }
}

// FactoryBeanRegistrySupport.java
private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName) throws BeanCreationException {

    Object object;
    try {
        // 調用 getObject() 獲取實例
        object = factory.getObject();
    }
    // 省略異常處理...

    // 如果 object 為 null,並且當前 singleton bean 正在創建中,拋出異常
    if (object == null) {
        if (isSingletonCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName, "FactoryBean which is currently in creation returned null from getObject");
        }
        object = new NullBean();
    }
    // 返回 object 實例
    return object;
}

上面代碼總結起來就是:如果 beanName& 開頭,直接返回 FactoryBean 實例;否則調用 getObject() 方法獲取實例,然後執行 postProcessObjectFromFactoryBean() 回調,可以在回調方法中修改實例,默認按原樣返回。

合併 bean 定義元信息

AbstractBeanFactory#getMergedLocalBeanDefinition

下文將合併后的 BeanDefinition 簡稱為 MergedBeanDefinition

protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
    // 從緩存獲取MergedBeanDefinition
    RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
    // 如果存在MergedBeanDefinition,並且不是過期的,直接返回
    if (mbd != null && !mbd.stale) {
        return mbd;
    }
    // 獲取已經註冊的BeanDefinition然後去合併
    return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
}

protected RootBeanDefinition getMergedBeanDefinition(String beanName, BeanDefinition bd)
		throws BeanDefinitionStoreException {
    // 頂級bean獲取合併后的BeanDefinition
    return getMergedBeanDefinition(beanName, bd, null);
}

/**
 * @param containingBd 如果是嵌套bean該值為頂級bean,如果是頂級bean該值為null
 */
protected RootBeanDefinition getMergedBeanDefinition(
		String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd)
		throws BeanDefinitionStoreException {
	// 加鎖
    synchronized (this.mergedBeanDefinitions) {
        // 本次的RootBeanDefinition
        RootBeanDefinition mbd = null;
        // 以前的RootBeanDefinition
        RootBeanDefinition previous = null;

        // 如果bean是頂級bean,直接獲取MergedBeanDefinition
        if (containingBd == null) {
            mbd = this.mergedBeanDefinitions.get(beanName);
        }
		// 沒有MergedBeanDefinition || BeanDefinition過期了
        if (mbd == null || mbd.stale) {
            previous = mbd;
            // 如果bean沒有parent
            if (bd.getParentName() == null) {
                // 如果bd本身就是RootBeanDefinition直接複製一份,否則創建一個
                if (bd instanceof RootBeanDefinition) {
                    mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();
                }
                else {
                    mbd = new RootBeanDefinition(bd);
                }
            }
            else {	
                // bean有parent
                BeanDefinition pbd;
                try {
                    // 獲取parent bean的實際名稱
                    String parentBeanName = transformedBeanName(bd.getParentName());
                    if (!beanName.equals(parentBeanName)) {
                        // 當前beanName不等於它的parentBeanName
                      	// 獲取parent的MergedBeanDefinition
                        pbd = getMergedBeanDefinition(parentBeanName);
                    }
                    else {
                        // 如果parentBeanName與bd的beanName相同,則拿到父BeanFactory
                        // 只有在存在父BeanFactory的情況下,才允許parentBeanName與自己相同
                        BeanFactory parent = getParentBeanFactory();
                        if (parent instanceof ConfigurableBeanFactory) {
                            // 如果父BeanFactory是ConfigurableBeanFactory類型
                            // 則通過父BeanFactory獲取parent的MergedBeanDefinition
                            pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName);
                        }
                        else {
                            // 如果父BeanFactory不是ConfigurableBeanFactory,拋出異常
                            throw new NoSuchBeanDefinitionException(parentBeanName,
"Parent name '" + parentBeanName + "' is equal to bean name '" + beanName + "': cannot be resolved without an AbstractBeanFactory parent");
                        }
                    }
                }
                catch (NoSuchBeanDefinitionException ex) {
                    throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanName, "Could not resolve parent bean definition '" + bd.getParentName() + "'", ex);
                }
                // 使用父MergedBeanDefinition構建一個新的RootBeanDefinition對象(深拷貝)
                mbd = new RootBeanDefinition(pbd);
                // 覆蓋與parent相同的屬性
                mbd.overrideFrom(bd);
            }
            
            // 如果bean沒有設置scope屬性,默認是singleton
            if (!StringUtils.hasLength(mbd.getScope())) {
                mbd.setScope(RootBeanDefinition.SCOPE_SINGLETON);
            }

            // 當前bean是嵌套bean && 頂級bean的作用域不是單例 && 當前bean的作用域是單例
            // 這裏總結起來就是,如果頂層bean不是單例的,那麼嵌套bean也不能是單例的
            if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) {
                // 設置當前bean的作用域和頂級bean一樣
                mbd.setScope(containingBd.getScope());
            }

            // 當前bean是頂級bean && 緩存bean的元數據(該值默認為true)
            if (containingBd == null && isCacheBeanMetadata()) {
                // 將當前bean的MergedBeanDefinition緩存起來
                this.mergedBeanDefinitions.put(beanName, mbd);
            }
        }
        // 以前的RootBeanDefinition不為空,拷貝相關的BeanDefinition緩存
        if (previous != null) {
            copyRelevantMergedBeanDefinitionCaches(previous, mbd);
        }
        return mbd;
    }
}	

上面代碼主要是獲取 MergedBeanDefinition ,主要步驟如下:

  1. 首先從緩存中獲取 beanMergedBeanDefinition,如果存在並且未過期直接返回。

  2. 不存在或者已過期的 MergedBeanDefinition ,獲取已經註冊的 BeanDefinition 去作為頂級 bean 合併。

  3. bean 沒有 parent (就是 XML 中的 parent 屬性),直接封裝成 RootBeanDefinition

  4. beanparent ,先去獲取父 MergedBeanDefinition ,然後覆蓋和合併與 parent 相同的屬性。

    注意:這裏只有 abstractscopelazyInitautowireModedependencyCheckdependsOnfactoryBeanNamefactoryMethodNameinitMethodNamedestroyMethodName 會覆蓋,而 constructorArgumentValuespropertyValuesmethodOverrides 會合併。

  5. 如果沒有設置作用域,默認作用域為 singleton

  6. 緩存 MergedBeanDefinition

上文中提到如果 beanparent,會合併一些屬性,這裏我們稍微展示一下合併后的 propertyValues:

首先定義一個 SuperUser 繼承上面定義的 User,如下:

public class SuperUser extends User {

    private String address;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "SuperUser{" +
            "address='" + address + '\'' +
            '}';
    }

}

然後我們在 XML 文件中配置一下,如下:

<bean id="superUser" class="com.leisurexi.ioc.domain.SuperUser" parent="user">
    <property name="address" value="北京"/>
</bean>

然後下圖是我 Debug 的截圖,可以看到 superUserpropertyValues 合併了 useridname 屬性。

上文還提到了嵌套 bean ,下面我們簡單看一下什麼是嵌套 bean

在 Spring 中,如果某個 bean 所依賴的 bean 不想被 Spring 容器直接訪問,可以使用嵌套 bean。和普通的 bean 一樣,使用 bean 元素來定義嵌套的 bean,嵌套 bean 只對它的外部 bean 有效,Spring 無法直接訪問嵌套 bean ,因此定義嵌套 bean 也無需指定 id 屬性。如下配置片段是一個嵌套 bean 示例:

採用上面的配置形式可以保證嵌套 bean 不能被容器訪問,因此不用擔心其他程序修改嵌套 bean。外部 bean 的用法和使用結果和以前沒有區別。

嵌套 bean 提高了 bean 的內聚性,但是降低了程序的靈活性。只有在確定無需通過 Spring 容器訪問某個 bean 實例時,才考慮使用嵌套 bean 來定義。

尋找依賴

DefaultSingletonBeanRegistry#isDependent

protected boolean isDependent(String beanName, String dependentBeanName) {
    // 加鎖
    synchronized (this.dependentBeanMap) {
        // 檢測beanName和dependentBeanName是否有循環依賴
        return isDependent(beanName, dependentBeanName, null);
    }
}

private boolean isDependent(String beanName, String dependentBeanName, @Nullable Set<String> alreadySeen) {
    // 如果當前bean已經檢測過,直接返回false
    if (alreadySeen != null && alreadySeen.contains(beanName)) {
        return false;
    }
    // 解析別名,獲取實際的beanName
    String canonicalName = canonicalName(beanName);
    // 獲取canonicalName所依賴beanName集合
    Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName);
    // 如果為空,兩者還未確定依賴關係,返回false
    if (dependentBeans == null) {
        return false;
    }
    // 如果dependentBeanName已經存在於緩存中,兩者已經確定依賴關係,返回true
    if (dependentBeans.contains(dependentBeanName)) {
        return true;
    }
    // 循環檢查,即檢查依賴canonicalName的所有beanName是否被dependentBeanName依賴(即隔層依賴)
    for (String transitiveDependency : dependentBeans) {
        if (alreadySeen == null) {
            alreadySeen = new HashSet<>();
        }
        // 將已經檢查過的記錄下來,下次直接跳過
        alreadySeen.add(beanName);
        if (isDependent(transitiveDependency, dependentBeanName, alreadySeen)) {
            return true;
        }
    }
    return false;
}

DefaultSingletonBeanRegistry#registerDependentBean

public void registerDependentBean(String beanName, String dependentBeanName) {
    // 解析別名,獲取實際的beanName
    String canonicalName = canonicalName(beanName);
    // 加鎖
    synchronized (this.dependentBeanMap) {
        // 獲取canonicalName依賴beanName集合,如果為空默認創建一個LinkedHashSet當做默認值
        Set<String> dependentBeans =
            this.dependentBeanMap.computeIfAbsent(canonicalName, k -> new LinkedHashSet<>(8));
        // 如果dependentBeanName已經記錄過了,直接返回
        if (!dependentBeans.add(dependentBeanName)) {
            return;
        }
    }
    // 加鎖
    synchronized (this.dependenciesForBeanMap) {
        // 這裡是和上面的dependentBeanMap倒過來,key為dependentBeanName
        Set<String> dependenciesForBean =
            this.dependenciesForBeanMap.computeIfAbsent(dependentBeanName, k -> new LinkedHashSet<>(8));
        dependenciesForBean.add(canonicalName);
    }
}

下面我們舉個A、B的 depends-on 屬性都是對方的例子:

首先獲取A,調用 isDependent() 方法,因為第一次獲取A,所以 dependentBeanMap 中沒有記錄依賴關係,直接返回 false;接着調用registerDependentBean(),這裡會向 dependentBeanMap 中反過來存儲依賴關係,也就是以B為 key value 是一個包含A的 Set集合。

接着會調用 getBean() 方法獲取B,首先調用 isDependent() 方法,因為在獲取A時已經存儲了B的依賴關係,所以獲取到的dependentBeans 的集合中包含A,所以直接返回true,拋出循環引用異常。

這個方法又引入了一個跟 dependentBeanMap 類似的緩存 dependenciesForBeanMap。這兩個緩存很容易搞混,這裏再舉一個簡單的例子:A 依賴 B,那麼 dependentBeanMap 存放的是 key 為 B,value 為含有 A 的 Set;而 dependenciesForBeanMap 存放的是key 為 A,value 為含有 B 的 Set

創建和註冊單例 bean

DefaultSingletonBeanRegistry#getSingleton

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(beanName, "Bean name must not be null");
    // 加鎖
    synchronized (this.singletonObjects) {
        Object singletonObject = this.singletonObjects.get(beanName);
        // 緩存中不存在當前 bean,也就是當前 bean 第一次創建
        if (singletonObject == null) {
            // 如果當前正在銷毀 singletons,拋出異常
            if (this.singletonsCurrentlyInDestruction) {
                throw new BeanCreationNotAllowedException(beanName, "Singleton bean creation not allowed while singletons of this factory are in destruction " + "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
            }
            // 創建單例 bean 之前的回調
            beforeSingletonCreation(beanName);
            boolean newSingleton = false;
            boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
            if (recordSuppressedExceptions) {
                this.suppressedExceptions = new LinkedHashSet<>();
            }
            try {
                // 獲取 bean 實例,在此處才會去真正調用創建 bean 的方法
                singletonObject = singletonFactory.getObject();
                newSingleton = true;
            }
            // 省略異常處理...
            finally {
                if (recordSuppressedExceptions) {
                    this.suppressedExceptions = null;
                }
                // 創建單例 bean 之後的回調
                afterSingletonCreation(beanName);
            }
            if (newSingleton) {
                // 將 singletonObject 放入緩存
                addSingleton(beanName, singletonObject);
            }
        }
        // 返回 bean 實例
        return singletonObject;
    }
}

// 單例 bean 創建前的回調方法,默認實現是將 beanName 加入到當前正在創建 bean 的緩存中,
// 這樣便可以對循環依賴進行檢測
protected void beforeSingletonCreation(String beanName) {
    if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
        throw new BeanCurrentlyInCreationException(beanName);
    }
}

// 單例 bean 創建后的回調方法,默認實現是將 beanName 從當前正在創建 bean 的緩存中移除
protected void afterSingletonCreation(String beanName) {
    if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
        throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
    }
}

protected void addSingleton(String beanName, Object singletonObject) {
    synchronized (this.singletonObjects) {
        // 這邊bean已經初始化完成了,放入一級緩存
        this.singletonObjects.put(beanName, singletonObject);
        // 移除三級緩存
        this.singletonFactories.remove(beanName);
        // 移除二級緩存
        this.earlySingletonObjects.remove(beanName);
        // 將 beanName 添加到已註冊 bean 緩存中
        this.registeredSingletons.add(beanName);
    }
}

自定義作用域示例

我們實現一個 ThreadLocal 級別的作用域,也就是同一個線程內 bean 是同一個實例,不同線程的 bean 是不同實例。首先我們繼承 Scope 接口實現,其中方法。如下:

public class ThreadLocalScope implements Scope {

    /** scope 名稱,在 XML 中的 scope 屬性就配置此名稱 */
    public static final String SCOPE_NAME = "thread-local";

    private final NamedThreadLocal<Map<String, Object>> threadLocal = new NamedThreadLocal<>("thread-local-scope");

    /**
    * 返回實例對象,該方法被 Spring 調用
    */
    @Override
    public Object get(String name, ObjectFactory<?> objectFactory) {
        Map<String, Object> context = getContext();
        Object object = context.get(name);
        if (object == null) {
            object = objectFactory.getObject();
            context.put(name, object);
        }
        return object;
    }

    /**
    * 獲取上下文 map
    */
    @NonNull
    private Map<String, Object> getContext() {
        Map<String, Object> map = threadLocal.get();
        if (map == null) {
            map = new HashMap<>();
            threadLocal.set(map);
        }
        return map;
    }

    @Override
    public Object remove(String name) {	
        return getContext().remove(name);
    }

    @Override
    public void registerDestructionCallback(String name, Runnable callback) {
        // TODO
    }
	
    @Override
    public Object resolveContextualObject(String key) {
        Map<String, Object> context = getContext();
        return context.get(key);
    }

    @Override
    public String getConversationId() {
        return String.valueOf(Thread.currentThread().getId());
    }

}

上面的 ThreadLocalScope 重點關注下 get() 即可,該方法是被 Spring 調用的。

然後在 XML 中配置 beanscopethread-local。如下:

<bean id="user" name="user" class="com.leisurexi.ioc.domain.User" scope="thread-local">
    <property name="id" value="1"/>
    <property name="name" value="leisurexi"/>
</bean>

接着我們測試一下。測試類:

@Test
public void test() throws InterruptedException {
    DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
    // 註冊自定義作用域
    beanFactory.registerScope(ThreadLocalScope.SCOPE_NAME, new ThreadLocalScope());
    XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
    reader.loadBeanDefinitions("META-INF/custom-bean-scope.xml");
    for (int i = 0; i < 3; i++) {
        Thread thread = new Thread(() -> {
            User user = beanFactory.getBean("user", User.class);
            System.err.printf("[Thread id :%d] user = %s%n", Thread.currentThread().getId(), user.getClass().getName() + "@" + Integer.toHexString(user.hashCode()));
            User user1 = beanFactory.getBean("user", User.class);
            System.err.printf("[Thread id :%d] user1 = %s%n", Thread.currentThread().getId(), user1.getClass().getName() + "@" + Integer.toHexString(user1.hashCode()));
        });
        thread.start();
        thread.join();
    }
}

說一下我們這裏的主要思路,新建了三個線程,查詢線程內 user bean 是否相等,不同線程是否不等。

結果如下圖:

總結

本文主要介紹了 getBean() 方法流程,我們可以重新梳理一下思路:

  1. 獲取 bean 實際名稱,如果緩存中存在直接取出實際 bean 返回。
  2. 緩存中不存在,判斷當前工廠是否有 BeanDefinition ,沒有遞歸去父工廠創建 bean
  3. 合併 BeanDefinition ,如果 depends-on 不為空,先去初始化依賴的 bean
  4. 如果 bean 的作用域是單例,調用 createBean() 方法創建實例,這個方法會執行 bean 的其它生命周期回調,以及屬性賦值等操作;接着執行單例 bean 創建前後的生命周期回調方法,並放入 singletonObjects 緩存起來。
  5. 如果 bean 的作用域是原型,調用 createBean() 方法創建實例,並執行原型 bean 前後調用生命周期回調方法。
  6. 如果 bean 的作用域是自定義的,獲取對應的 Scope 對象,調用重寫的 get() 方法獲取實例,並執行原型 bean 前後調用生命周期回調方法。
  7. 最後檢查所需的類型是否與實際 bean 實例的類型匹配,如果不等進行轉換,最後返回實例。

關於 createBean() 方法的細節,會在後續文章中進行分析。

最後,我模仿 Spring 寫了一個精簡版,代碼會持續更新。地址:https://github.com/leisurexi/tiny-spring。

參考

  • 《Spring 源碼深度解析》—— 郝佳
  • https://github.com/geektime-geekbang/geekbang-lessons

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

新北清潔公司,居家、辦公、裝潢細清專業服務

澳洲歷來最大宗集體訴訟 環境受污染4萬人告政府

摘錄自2019年10月29日中央社報導

澳洲一家律師事務所今天(29日)表示,數萬名澳洲人因化學物質污染土地,將對政府提出訴訟。好萊塢電影「永不妥協」本尊、美國環保鬥士柏克維奇也出面力挺受害者。

澳洲廣播公司(ABC)指出,本案將成為澳洲歷來最大規模的集體訴訟。

法新社報導,涉及本案的化學品名為全氟及多氟烷基物質(PFAS)。這類物質存在於消防泡沫內,疑似與某些癌症有關。澳洲多處陸軍基地和消防局曾於1970至2000年代使用。

澳洲律師事務所Shine Lawyers表示,將代表8處國防部門重要基地的附近居民提出集體訴訟。

不過PFAS對健康的衝擊仍有爭議。美國環境保護署(Environmental Protection Agency)形容PFAS「可能致癌」,澳洲衛生部門則指無「確鑿證據」支持這類物質與癌症有關。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

新北清潔公司,居家、辦公、裝潢細清專業服務

特斯拉上季呈現虧損;擬針對中國客戶調整電動車功能

美國電動車廠特斯拉(Tesla)公布季度虧損,同時宣布將針對全球最大汽車市場、也就是中國大陸的客戶,調整電動車功能。   特斯拉第 4 季扣除特定項目,每股虧損 13 美分。此外,特斯拉執行長 Elon Musk 先前說過,他預期中國銷售量最快在 2015 年就可能匹敵美國銷售量。但消息人士透露,今年 1 月特斯拉在中國賣出大約 120 輛汽車,遠低於公司目標。   特斯拉表示,目前正在針對中國客戶進行調整,希望能於交車前先在買主家中安裝充電系統,讓更多大陸客戶對購買 Model S 更有信心。      

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

新北清潔公司,居家、辦公、裝潢細清專業服務

.NET高級調試系列-Windbg調試入門篇

Windbg是.NET高級調試領域中不可或缺的一個工具和利器,也是日常我們分析解決問題的必備。準備近期寫2篇精華文章,集中給大家分享一下如果通過Windbg進行.NET高級調試。

今天我們來一篇入門的文章。首先,Windbg是什麼?

Windows Debugger,簡稱WinDbg,.NET 最強分析調試利器。它可以用來:

  • 調試內核模式和用戶模式代碼
  • 分析Crash dump
  • 分析代碼執行時 CPU 寄存器信息

我們可以通過WinDbg調試以下具體問題:

  • 線程阻塞
  • 內存泄露
  • 分析查詢運行時線程堆棧和變量
  • 分析進程Crash原因
  • 分析消耗CPU原因
  • 查看並調試CLR異常

那麼,首先我們先進行Windbg下載安裝、配置。

一、下載安裝WinDbg,配置調試環境

1. 推薦下載鏈接

https://raw.githubusercontent.com/EasyDarwin/Tools/master/Windbg_x86_x64/dbg_amd64.msi

或者從Windows Store下載 WingDbg Preview版本

下載后一步一步安裝即可

 2. 配置調試符號

大家會問一個問題:為什麼要配置調試符號?

若要使用 WinDbg 提供的所有高級功能,必須加載適當的符號:比如說我們可以調試、查看.NET CLR程序堆棧,此時要加載對應的調試符號。

 微軟提供了統一的調試服務服務器地址:

http://msdl.microsoft.com/download/symbols,將這個地址提供的調試符號,下載緩存到本地,Windbg調試的時候可以用上。
srv*c:\symcache*http://msdl.microsoft.com/download/symbols;c:\symcache

  

3. 下載並使用WinDbg調試器擴展

 Windbg調試器擴展是Windbg調試的精華和核心,可以這麼說,掌握各類Windbg調試器擴展,你就掌握了各類調試技能。

 默認情況下,WinDbg的調試指令是有限的,通過一些WinDbg調試器擴展,可以方便我們進行.NET 程序調試

 SOS調試擴展 : 隨着.NET Framework安裝,可以直接加載:  .load sos clr

 SOS這個dll在哪裡呢(分32位和64位)?

 4.0, 32-bit –> C:\Windows\Microsoft.NET\Framework\v4.0.30319
 4.0, 64-bit –> C:\Windows\Microsoft.NET\Framework64\v4.0.30319

  MEX調試擴展:This extension is widely used by Microsoft Support Engineers in troubleshooting process applications

 下載地址:https://www.microsoft.com/en-us/download/details.aspx?id=53304

 下載完成后,將32/64位的Mex.dll 拷貝到windbg安裝目錄中

 例如:C:\Program Files\Debugging Tools for Windows (x64)\Mex.dll

 詳細使用說明:https://github.com/REhints/WinDbg/tree/master/MEX

 下載、安裝、配置完成Windbg之後,接下來我們了解一下一些基本的調試命令。

二、基本的WinDbg調試指令

1. WinDbg自帶的調試指令

 

   

    更多指令,可以查看一下鏈接:

    https://docs.microsoft.com/zh-cn/windows-hardware/drivers/debugger/getting-started-with-windbg

 2. SOS調試擴展常用的調試指令

   

    

    

 

  

  

  

  

 

  

   3. Mex調試擴展常用的調試指令

   

     

    

    

    

    

     更多Mex調試指令,可以查看鏈接:https://github.com/REhints/WinDbg/tree/master/MEX

 

  以上是整個Windbg調試入門篇的介紹,希望大家能夠掌握,下一篇我們將通過一些具體的案例,示意各個指令的使用場景。

 

周國慶

2020/6/27

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

新北清潔公司,居家、辦公、裝潢細清專業服務

研究:強風暴可能會動搖海床 引起「風暴地震」

編譯:嚴融怡(胡適國小創思組科任教師)

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

新北清潔公司,居家、辦公、裝潢細清專業服務

新能源汽車再迎搶灘者 陽光電源1億揮軍進入

陽光電源3月10日晚間公告稱,計畫在安徽省合肥市投資設立陽光電動力科技有限公司(暫定名),註冊資本為1億元人民幣,公司占註冊資本的100%,將以其自有資金出資。   此新公司主營業務為:以全資、控股或參股的方式,重點開發新能源汽車電動力系統、輔助電氣系統、電池管理系統、太陽能充電樁業務等。項目建成後,將具備年產10萬台套新能源汽車電驅動及配套系統、年產1000套新能源汽車太陽能充電站的相關研發生產和服務運營能力。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

新北清潔公司,居家、辦公、裝潢細清專業服務

電動車不只轎跑車市場,電動垃圾車可成利基

電動車熱潮可說成也特斯拉(Tesla)、敗也特斯拉,先是因特斯拉成功引起媒體追逐熱潮而一時暴紅,也因為特斯拉表現不如預期遭市場唱空而暫時沉寂,不過,電動車的商機並不只有特斯拉所主打的時尚轎、跑車,其實若論節能減碳與減少空污,每天固定行駛里程數更高,煞停又重新起步更頻繁,而且重量與油耗都遠大於轎車的垃圾車,更是利基市場。  
    電動車固然需要充電,而電力也可能來自於火力發電,但是火力發電廠的能源效率可達 60%,汽油引擎內燃機卻只有 30%,這是電動車節能的理論基礎之一;此外,電動車可應用煞車回電,甚至如輪胎大廠固特異正研發「燒胎回電」,在煞車時可將車體的動能部分回收為電能,內燃機引擎車煞車時所有動能都化為摩擦熱喪失,這是電動車較內燃機引擎車節能的理論基礎之二,因此,車體越重,煞停時消耗的動能越大、行駛時煞車次數越多,電動車較內燃機節省能源的幅度就更高。   以此觀之,沉重的垃圾車,每到定點就要停車,等裝完垃圾再重新啟動,顯然比開在高速公路的輕量跑車來得更有節能空間。   於是,原本也是特斯拉共同創辦人之一的伊恩萊特(Ian Wright),選擇鎖定垃圾車,成立新創事業「萊特速度」(Wrightspeed),伊恩萊特表示,一般標準垃圾車每天都要煞停、再啟動 1,000 次,導致每 3 個月就會磨光煞車皮,每年燒掉 1.4 加侖汽油。這是很有節能潛力的目標。   萊特速度改裝現有垃圾車,將原本內燃機動力總成改裝為電動動力總成,並且加裝煞車回電系統,除了可回收電力以外,也能減輕煞車皮的負擔,不再每三個月就磨光煞車皮,而為了延長行駛距離,動力總成上還加裝了一組汽油發電機,作為備用能源,這與油電混合動力車不同,汽油發電機並不是直接驅動車輛,而是供電給電池,透過電動動力總成來驅動車輛,維持全電動的動力總成架構。    
省成本又解決空污問題   有了汽油發電機,只要有加油站,就不擔心會停擺,也因此,萊特速度的電池容量也不大,標準垃圾車電池容量僅 78 度電,而中型垃圾車電池容量更只有 26 度電,相較之下,特斯拉 Model S 最大擁有 85 度電容量的電池。   但萊特速度在煞車回電上的能力則遠大於特斯拉轎車,煞車回電的發電容量最高達 730 千瓦,以提供強大的煞車力來煞停沉重的垃圾車,並將盡可能多的動能回收為電能。   而煞車回電不僅回收電池電量,減少煞車皮損耗,也減少了煞車時對車軸與整個車身造成的損耗,伊恩萊特表示,一般垃圾車行駛 20 萬英里就要報廢,大概只能開上 5 年,並且需要大量維修工作,轉換為萊特速度電動系統以後,可大為延長使用期限,每年除了減少 3.5 萬美元的燃料費用以外,還能再減少 2 萬美元的維修費用,這讓萊特速度的客戶可在 4 年內回本。   除了省錢以外,改裝為電動車也解決了老舊垃圾車的空污問題,以加州為例,新的空污標準讓老舊垃圾車瀕臨淘汰,使用老舊垃圾車的單位面臨必須重買新車以符合空污排放標準的難題,但如果以萊特速度的技術改裝為電動車,就解決了空污問題,其成本也比重買新車來得低。   而這不僅是空氣污染問題,垃圾車穿越社區時,引擎聲往往擾人清夢,伊恩萊特認為,改為電動車之後,安靜將是一大競爭力。由於美國有 11 萬輛垃圾車,除了垃圾車以外,送貨用車如快遞業者的送貨廂型車等也是可能的客戶,萊特速度的改裝生意大有可為。   伊恩萊特更自信滿滿地表示,雖然特斯拉做得很好,但在保護環境方面,萊特速度能減少的碳排放、空氣污染與噪音一定能超過特斯拉。萊特速度到 2015 年 3 月,募資總額達 3,200 萬美元,目前正在進行最新一輪募資,預期要雇用至 300 名員工,提升最大年產能至 5,000 輛規模。     本文全文授權轉載自《科技新報》─〈〉

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

新北清潔公司,居家、辦公、裝潢細清專業服務

冰島冰川融多快?冰島學生實地測量

摘錄自2019年11月10日中央通訊社冰島報導

冰島7年級學生莉亞(Lilja Einarsdottir)今天和同學來了趟不尋常的田野之旅:他們一同測量索爾黑馬冰川過去這一年間縮減了多少,親身見證氣候的變遷。莉亞說:「很漂亮,但看到冰河消融了這麼多,也覺得很難過。」

2010年起的每年10月,現已退休的教師史提凡松(Jon Stefansson)都會帶60公里外村落霍爾斯沃德呂爾(Hvolsvollur)的13歲學生,前來測量索爾黑馬冰川(Solheimajokull)的演變。他們的測量結果讓人不寒而慄:過去10年間,夾在兩大山坡間的索爾黑馬冰川,每年平均縮減40公尺。

莉亞說:「(第一批學生)開始在這裡測量時,看不到任何的水,所以(冰川)一開始非常的大。」

冰島境內約1成1面積是冰川,但過去25年間,冰川消失約250立方公里,相當於總體積的7%。而學生們測量的索爾黑馬冰川因距離冰島首都雷克雅維克(Reykjavik)最近,僅150公里。在學生們測量的這將近10年間,索爾黑馬冰川縮減了380公尺。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

新北清潔公司,居家、辦公、裝潢細清專業服務

簽訂「首爾宣言」 新北市力推電動車

新北市副市長陳伸賢昨出席「國際地方政府環境行動理事會(ICLEI)2015 世界大會」,並與各國代表簽訂 ICLEI「首爾宣言」,宣誓將持續減碳,新北也將推動環保電動車各項措施。   陳伸賢昨與首爾市長朴元淳交流時,對首爾市推動省下一座核電廠政策非常感佩,他表示,面對全球環境汙染、能源消耗及氣候變遷等問題,新北市也努力降低汙染,包括推動黃金里資收站、路燈與交通號誌燈全面 LED 化、綠能屋頂等各項措施。目前新北市綠色能源發電比例已達 5%。   針對車輛產生的汙染,未來新北市將推廣與建設大眾運輸、鼓勵使用綠色交通工具如電動機車、電動車、公共自行車等方式,並將「電動車」列為重點發展產業,持續推動電動車普及化。   (照片:韓國首爾市長朴元淳、新北市副市長陳伸賢。照片來源:新北市政府)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

新北清潔公司,居家、辦公、裝潢細清專業服務