Spring如何解決循環依賴?

介紹

先說一下什麼是循環依賴,Spring在初始化A的時候需要注入B,而初始化B的時候需要注入A,在Spring啟動后這2個Bean都要被初始化完成

Spring的循環依賴有兩種場景

  1. 構造器的循環依賴
  2. 屬性的循環依賴

構造器的循環依賴,可以在構造函數中使用@Lazy註解延遲加載。在注入依賴時,先注入代理對象,當首次使用時再創建對象完成注入

屬性的循環依賴主要是通過3個map來解決的

構造器的循環依賴

@Component
public class ConstructorA {

	private ConstructorB constructorB;

	@Autowired
	public ConstructorA(ConstructorB constructorB) {
		this.constructorB = constructorB;
	}
}
@Component
public class ConstructorB {

	private ConstructorA constructorA;

	@Autowired
	public ConstructorB(ConstructorA constructorA) {
		this.constructorA = constructorA;
	}
}
@Configuration
@ComponentScan("com.javashitang.dependency.constructor")
public class ConstructorConfig {
}
public class ConstructorMain {

	public static void main(String[] args) {
		AnnotationConfigApplicationContext context =
				new AnnotationConfigApplicationContext(ConstructorConfig.class);
		System.out.println(context.getBean(ConstructorA.class));
		System.out.println(context.getBean(ConstructorB.class));
	}
}

運行ConstructorMain的main方法的時候會在第一行就報異常,說明Spring沒辦法初始化所有的Bean,即上面這種形式的循環依賴Spring無法解決。

我們可以在ConstructorA或者ConstructorB構造函數的參數上加上@Lazy註解就可以解決

@Autowired
public ConstructorB(@Lazy ConstructorA constructorA) {
	this.constructorA = constructorA;
}

因為我們主要關注屬性的循環依賴,構造器的循環依賴就不做過多分析了

屬性的循環依賴

先演示一下什麼是屬性的循環依賴

@Component
public class FieldA {

	@Autowired
	private FieldB fieldB;
}
@Component
public class FieldB {

	@Autowired
	private FieldA fieldA;
}
@Configuration
@ComponentScan("com.javashitang.dependency.field")
public class FieldConfig {
}
public class FieldMain {

	public static void main(String[] args) {
		AnnotationConfigApplicationContext context =
				new AnnotationConfigApplicationContext(FieldConfig.class);
		// com.javashitang.dependency.field.FieldA@3aa9e816
		System.out.println(context.getBean(FieldA.class));
		// com.javashitang.dependency.field.FieldB@17d99928
		System.out.println(context.getBean(FieldB.class));
	}
}

Spring容器正常啟動,能獲取到FieldA和FieldB這2個Bean

屬性的循環依賴在面試中還是經常被問到的。總體來說也不複雜,但是涉及到Spring Bean的初始化過程,所以感覺比較複雜,我寫個demo演示一下整個過程

Spring的Bean的初始化過程其實比較複雜,為了方便理解Demo,我就把Spring Bean的初始化過程分為2部分

  1. bean的實例化過程,即調用構造函數將對象創建出來
  2. bean的初始化過程,即填充bean的各種屬性

bean初始化過程完畢,則bean就能被正常創建出來了

下面開始寫Demo,ObjectFactory接口用來生產Bean,和Spring中定義的接口一樣

public interface ObjectFactory<T> {
	T getObject();
}
public class DependencyDemo {

	// 初始化完畢的Bean
	private final Map<String, Object> singletonObjects =
			new ConcurrentHashMap<>(256);

	// 正在初始化的Bean對應的工廠,此時對象已經被實例化
	private final Map<String, ObjectFactory<?>> singletonFactories =
			new HashMap<>(16);

	// 存放正在初始化的Bean,對象還沒有被實例化之前就放進來了
	private final Set<String> singletonsCurrentlyInCreation =
			Collections.newSetFromMap(new ConcurrentHashMap<>(16));

	public  <T> T getBean(Class<T> beanClass) throws Exception {
		// 類名為Bean的名字
		String beanName = beanClass.getSimpleName();
		// 已經初始化好了,或者正在初始化
		Object initObj = getSingleton(beanName, true);
		if (initObj != null) {
			return (T) initObj;
		}
		// bean正在被初始化
		singletonsCurrentlyInCreation.add(beanName);
		// 實例化bean
		Object object = beanClass.getDeclaredConstructor().newInstance();
		singletonFactories.put(beanName, () -> {
			return object;
		});
		// 開始初始化bean,即填充屬性
		Field[] fields = object.getClass().getDeclaredFields();
		for (Field field : fields) {
			field.setAccessible(true);
			// 獲取需要注入字段的class
			Class<?> fieldClass = field.getType();
			field.set(object, getBean(fieldClass));
		}
		// 初始化完畢
		singletonObjects.put(beanName, object);
		singletonsCurrentlyInCreation.remove(beanName);
		return (T) object;
	}

	/**
	 * allowEarlyReference參數的含義是Spring是否允許循環依賴,默認為true
	 * 所以當allowEarlyReference設置為false的時候,當項目存在循環依賴,會啟動失敗
	 */
	public Object getSingleton(String beanName, boolean allowEarlyReference) {
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null 
				&& isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
				if (singletonObject == null && allowEarlyReference) {
					ObjectFactory<?> singletonFactory =
							this.singletonFactories.get(beanName);
					if (singletonFactory != null) {
						singletonObject = singletonFactory.getObject();
					}
				}
			}
		}
		return singletonObject;
	}

	/**
	 * 判斷bean是否正在被初始化
	 */
	public boolean isSingletonCurrentlyInCreation(String beanName) {
		return this.singletonsCurrentlyInCreation.contains(beanName);
	}

}

測試一波

public static void main(String[] args) throws Exception {
	DependencyDemo dependencyDemo = new DependencyDemo();
	// 假裝掃描出來的對象
	Class[] classes = {A.class, B.class};
	// 假裝項目初始化所有bean
	for (Class aClass : classes) {
		dependencyDemo.getBean(aClass);
	}
	// true
	System.out.println(
			dependencyDemo.getBean(B.class).getA() == dependencyDemo.getBean(A.class));
	// true
	System.out.println(
			dependencyDemo.getBean(A.class).getB() == dependencyDemo.getBean(B.class));
}

是不是很簡單?我們只用了2個map就搞定了Spring的循環依賴

2個Map就能搞定循環依賴,那為什麼Spring要用3個Map呢?

原因其實也很簡單,當我們從singletonFactories中根據BeanName獲取相應的ObjectFactory,然後調用getObject()這個方法返回對應的Bean。在我們的例子中
ObjectFactory的實現很簡單哈,就是將實例化好的對象直接返回,但是在Spring中就沒有這麼簡單了,執行過程比較複雜,為了避免每次拿到ObjectFactory然後調用getObject(),我們直接把ObjectFactory創建的對象緩存起來不就行了,這樣就能提高效率了

比如A依賴B和C,B和C又依賴A,如果不做緩存那麼初始化B和C都會調用A對應的ObjectFactory的getObject()方法。如果做緩存只需要B或者C調用一次即可。

知道了思路,我們把上面的代碼改一波,加個緩存。

public class DependencyDemo {

	// 初始化完畢的Bean
	private final Map<String, Object> singletonObjects =
			new ConcurrentHashMap<>(256);

	// 正在初始化的Bean對應的工廠,此時對象已經被實例化
	private final Map<String, ObjectFactory<?>> singletonFactories =
			new HashMap<>(16);

	// 緩存Bean對應的工廠生產好的Bean
	private final Map<String, Object> earlySingletonObjects =
			new HashMap<>(16);

	// 存放正在初始化的Bean,對象還沒有被實例化之前就放進來了
	private final Set<String> singletonsCurrentlyInCreation =
			Collections.newSetFromMap(new ConcurrentHashMap<>(16));

	public  <T> T getBean(Class<T> beanClass) throws Exception {
		// 類名為Bean的名字
		String beanName = beanClass.getSimpleName();
		// 已經初始化好了,或者正在初始化
		Object initObj = getSingleton(beanName, true);
		if (initObj != null) {
			return (T) initObj;
		}
		// bean正在被初始化
		singletonsCurrentlyInCreation.add(beanName);
		// 實例化bean
		Object object = beanClass.getDeclaredConstructor().newInstance();
		singletonFactories.put(beanName, () -> {
			return object;
		});
		// 開始初始化bean,即填充屬性
		Field[] fields = object.getClass().getDeclaredFields();
		for (Field field : fields) {
			field.setAccessible(true);
			// 獲取需要注入字段的class
			Class<?> fieldClass = field.getType();
			field.set(object, getBean(fieldClass));
		}
		singletonObjects.put(beanName, object);
		singletonsCurrentlyInCreation.remove(beanName);
		earlySingletonObjects.remove(beanName);
		return (T) object;
	}

	/**
	 * allowEarlyReference參數的含義是Spring是否允許循環依賴,默認為true
	 */
	public Object getSingleton(String beanName, boolean allowEarlyReference) {
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null
				&& isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
				singletonObject = this.earlySingletonObjects.get(beanName);
				if (singletonObject == null && allowEarlyReference) {
					ObjectFactory<?> singletonFactory =
							this.singletonFactories.get(beanName);
					if (singletonFactory != null) {
						singletonObject = singletonFactory.getObject();
						this.earlySingletonObjects.put(beanName, singletonObject);
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}
}

我們寫的getSingleton的實現和org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)的實現一模一樣,這個方法幾乎所有分析Spring循環依賴的文章都會提到,這次你明白工作原理是什麼了把

總結一波

  1. 拿bean的時候先從singletonObjects(一級緩存)中獲取
  2. 如果獲取不到,並且對象正在創建中,就從earlySingletonObjects(二級緩存)中獲取
  3. 如果還是獲取不到就從singletonFactories(三級緩存)中獲取,然後將獲取到的對象放到earlySingletonObjects(二級緩存)中,並且將bean對應的singletonFactories(三級緩存)清除
  4. bean初始化完畢,放到singletonObjects(一級緩存)中,將bean對應的earlySingletonObjects(二級緩存)清除

歡迎關注

參考博客

[1]https://mp.weixin.qq.com/s/gBr3UfC1HRcw4U-ZMmtRaQ
[2]https://mp.weixin.qq.com/s/5mwkgJB7GyLdKDgzijyvXw
比較詳細
[1]https://zhuanlan.zhihu.com/p/84267654
[2]https://juejin.im/post/5c98a7b4f265da60ee12e9b2

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

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

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

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

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

※教你寫出一流的銷售文案?

一起玩轉微服務(10)——spring boot介紹

對於Spring,相信大家都非常熟悉,從出現開始,一直是企業級開發的主流。但是隨着軟件的發展和應用開發的不斷演化,它的一些缺點也逐漸胡暴露了出來,下面,我們就一起看一下Spring的發展歷程並且認識一下Spring Boot。

由來

在Spring 1.x的時候,所有的配置都通過XML,隨着項目的擴大,需要頻繁的在java和XML之間切換。 在Spring 2.x的時候,已經開始逐步替換掉XML配置。在Spring 3.x的時候,已經開始提供java的配置方式,在4.x的時候,已經全部推薦使用java配置的方式。隨着動態語言的流行,java的開發顯得格外的繁瑣,體現在配置的複雜、開發效率低下、部署以及與第三方的集成繁雜,這個時候Spring Boot應運而生。

 

 

 Spring Boot 是由 Pivotal 團隊提供的全新框架,其設計目的是用來簡化新 Spring 應用的初始搭建以及開發過程。該框架使用了特定的方式來進行配置,從而使開發人員不再需要定義樣板化的配置。簡化了基於 Spring 的應用開發,通過少量的代碼就能創建一個獨立的、產品級別的 Spring 應用。Spring Boot 為 Spring 平台及第三方庫提供開箱即用的設置,減少與第三方庫集成的複雜度。 Spring Boot 的核心思想就是約定大於配置,多數 Spring Boot 應用只需要很少的 Spring 配置。採用 Spring Boot 可以大大的簡化你的開發模式,所有你想集成的常用框架,它都有對應的組件支持。

 

特性

1.方便地創建獨立的Spring應用,為基於Spring的開發提供更快的入門體驗。2.內置嵌入tomcat,無需生成war文件。3.簡化maven配置。4.自動配置Spring,更快、更方便的與第三方應用整合,比如消息隊列、緩存等在企業級開發中常用的組件。5.提供大型項目中的非功能特性。如:指標、安全、健康檢查及外部配置。6.開箱即用,無需代碼生成,也無需XML配置,同時也能夠通過修改默認值來滿足待定的需求。

四大神器

自動配置

Spring Boot 的自動配置功能可基於類路徑檢測自動為運行中的應用配置依賴關係,不需要提供額外的XML配置。

Starters

Spring Boot 可提供一系列稱為 POM Starters 的精細依賴關係。Spring Boot 熟知如何配置這些依賴關係,同時讓組織能夠擴展 Spring Boot 來配置自定義的依賴關係。

Actuator

Actuator 可提供運行狀況檢查和指標等生產就緒型功能。這些功能通過 Spring Boot 應用內的 REST 終端提供。只需要簡單的配置就可以實現強大的監控和檢查。

開發工具

這些工具旨在縮短開發和測試周期,其中包括一個可在資源變更時觸發瀏覽器刷新的嵌入式 LiveReload 服務器。這些工具還提供了應用自動重啟功能,只要類路徑上的文件發生更改,該功能更即可啟動。重啟技術使用兩種類加載器。未更改的分類(例如來自第三方 JAR 的類)被加載到基礎類加載器,而開發中的分類則被加載到重啟類加載器。當應用重啟時,重啟類加載器會被丟棄,同時創建一個新的類加載器。這種方法意味着應用重啟的速度通常要比“冷啟動”的速度快得多,因為基礎類加載器已準備就緒且已填充完畢。從而快速實現應用的熱部署,對於簡單的修改這種場景能夠非常有效的提高效率。

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

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

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

※推薦評價好的iphone維修中心

架構設計 | 高併發流量削峰,共享資源加鎖機制

本文源碼:GitHub·點這裏 || GitEE·點這裏

一、高併發簡介

在互聯網的業務架構中,高併發是最難處理的業務之一,常見的使用場景:秒殺,搶購,訂票系統;高併發的流程中需要處理的複雜問題非常多,主要涉及下面幾個方面:

  • 流量管理,逐級承接削峰;
  • 網關控制,路由請求,接口熔斷;
  • 併發控制機制,資源加鎖;
  • 分佈式架構,隔離服務和數據庫;

高併發業務核心還是流量控制,控制流量下沉速度,或者控制承接流量的容器大小,多餘的直接溢出,這是相對複雜的流程。其次就是多線程併發下訪問共享資源,該流程需要加鎖機制,避免數據寫出現錯亂情況。

二、秒殺場景

1、預搶購業務

活動未正式開始,先進行活動預約,先把一部分流量收集和控制起來,在真正秒殺的時間點,很多數據可能都已經預處理好了,可以很大程度上削減系統的壓力。有了一定預約流量還可以提前對庫存系統做好準備,一舉兩得。

場景:活動預約,定金預約,高鐵搶票預購。

2、分批搶購

分批搶購和搶購的場景實現的機制是一致的,只是在流量上緩解了很多壓力,秒殺10W件庫存和秒殺100件庫存系統的抗壓不是一個級別。如果秒殺10W件庫存,系統至少承擔多於10W幾倍的流量衝擊,秒殺100件庫存,體系可能承擔幾百或者上千的流量就結束了。下面流量削峰會詳解這裏的策略機制。

場景:分時段多場次搶購,高鐵票分批放出。

3、實時秒殺

最有難度的場景就是準點實時的秒殺活動,假如10點整準時搶1W件商品,在這個時間點前後會湧入高併發的流量,刷新頁面,或者請求搶購的接口,這樣的場景處理起來是最複雜的。

  • 首先系統要承接住流量的湧入;
  • 頁面的不斷刷新要實時加載;
  • 高併發請求的流量控制加鎖等;
  • 服務隔離和數據庫設計的系統保護;

場景:618準點搶購,雙11準點秒殺,電商促銷秒殺。

三、流量削峰

1、Nginx代理

Nginx是一個高性能的HTTP和反向代理web服務器,經常用在集群服務中做統一代理層和負載均衡策略,也可以作為一層流量控制層,提供兩種限流方式,一是控制速率,二是控制併發連接數。

基於漏桶算法,提供限制請求處理速率能力;限制IP的訪問頻率,流量突然增大時,超出的請求將被拒絕;還可以限制併發連接數。

高併發的秒殺場景下,經過Nginx層的各種限制策略,可以控制流量在一個相對穩定的狀態。

2、CDN節點

CDN靜態文件的代理節點,秒殺場景的服務有這樣一個操作特點,活動倒計時開始之前,大量的用戶會不斷的刷新頁面,這時候靜態頁面可以交給CDN層面代理,分擔數據服務接口的壓力。

CDN層面也可以做一層限流,在頁面內置一層策略,假設有10W用戶點擊搶購,可以只放行1W的流量,其他的直接提示活動結束即可,這也是常用的手段之一。

話外之意:平時參与的搶購活動,可能你的請求根本沒有到達數據接口層面,就極速響應商品已搶完,自行意會吧。

3、網關控制

網關層面處理服務接口路由,一些校驗之外,最主要的是可以集成一些策略進入網關,比如經過上述層層的流量控制之後,請求已經接近核心的數據接口,這時在網關層面內置一些策略控制:如果活動是想激活老用戶,網關層面快速判斷用戶屬性,老用戶會放行請求;如果活動的目的是拉新,則放行更多的新用戶。

經過這些層面的控制,剩下的流量已經不多了,後續才真正開始執行搶購的數據操作。

話外之意:如果有10W人參加搶購活動,真正下沉到底層的搶購流量可能就1W,甚至更少,在分散到集群服務中處理。

4、併發熔斷

在分佈式服務的接口中,還有最精細的一層控制,對於一個接口在單位之間內控制請求處理的數量,這個基於接口的響應時間綜合考慮,響應越快,單位時間內的併發量就越高,這裏邏輯不難理解。

言外之意:流量經過層層控制,數據接口層面分擔的壓力已經不大,這時候就是面對秒殺業務中的加鎖問題了。

四、分佈式加鎖

1、悲觀鎖

機制描述

所有請求的線程必須在獲取鎖之後,才能執行數據庫操作,並且基於序列化的模式,沒有獲取鎖的線程處於等待狀態,並且設定重試機制,在單位時間后再次嘗試獲取鎖,或者直接返回。

過程圖解

Redis基礎命令

SETNX:加鎖的思路是,如果key不存在,將key設置為value如果key已存在,則 SETNX 不做任何動作。並且可以給key設置過期時間,過期后其他線程可以繼續嘗試鎖獲取機制。

藉助Redis的該命令模擬鎖的獲取動作。

代碼實現

這裏基於Redis實現的鎖獲取和釋放機制。

import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import javax.annotation.Resource;
@Component
public class RedisLock {

    @Resource
    private Jedis jedis ;

    /**
     * 獲取鎖
     */
    public boolean getLock (String key,String value,long expire){
        try {
            String result = jedis.set( key, value, "nx", "ex", expire);
            return result != null;
        } catch (Exception e){
            e.printStackTrace();
        }finally {
            if (jedis != null) jedis.close();
        }
        return false ;
    }

    /**
     * 釋放鎖
     */
    public boolean unLock (String key){
        try {
            Long result = jedis.del(key);
            return result > 0 ;
        } catch (Exception e){
            e.printStackTrace();
        }finally {
            if (jedis != null) jedis.close();
        }
        return false ;
    }
}

這裏基於Jedis的API實現,這裏提供一份配置文件。

@Configuration
public class RedisConfig {

    @Bean
    public JedisPoolConfig jedisPoolConfig (){
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig() ;
        jedisPoolConfig.setMaxIdle(8);
        jedisPoolConfig.setMaxTotal(20);
        return jedisPoolConfig ;
    }

    @Bean
    public JedisPool jedisPool (@Autowired JedisPoolConfig jedisPoolConfig){
        return new JedisPool(jedisPoolConfig,"127.0.0.1",6379) ;
    }

    @Bean
    public Jedis jedis (@Autowired JedisPool jedisPool){
        return jedisPool.getResource() ;
    }
}

問題描述

在實際的系統運行期間可能出現如下情況:線程01獲取鎖之後,進程被掛起,後續該執行的沒有執行,鎖失效后,線程02又獲取鎖,在數據庫更新后,線程01恢復,此時在持有鎖之後的狀態,繼續執行后就會容易導致數據錯亂問題。

這時候就需要引入鎖版本概念的,假設線程01獲取鎖版本1,如果沒有執行,線程02獲取鎖版本2,執行之後,通過鎖版本的比較,線程01的鎖版本過低,數據更新就會失敗。

CREATE TABLE `dl_data_lock` (
	`id` INT (11) NOT NULL AUTO_INCREMENT COMMENT '主鍵ID',
	`inventory` INT (11) DEFAULT '0' COMMENT '庫存量',
	`lock_value` INT (11) NOT NULL DEFAULT '0' COMMENT '鎖版本',
	PRIMARY KEY (`id`)
) ENGINE = INNODB DEFAULT CHARSET = utf8 COMMENT = '鎖機製表';

說明:lock_value就是記錄鎖版本,作為控制數據更新的條件。

<update id="updateByLock">
    UPDATE dl_data_lock SET inventory=inventory-1,lock_value=#{lockVersion}
    WHERE id=#{id} AND lock_value &lt;#{lockVersion}
</update>

說明:這裏的更新操作,不但要求線程獲取鎖,還會判斷線程鎖的版本不能低於當前更新記錄中的最新鎖版本。

2、樂觀鎖

機制描述

樂觀鎖大多是基於數據記錄來控制,在更新數據庫的時候,基於前置的查詢條件判斷,如果查詢出來的數據沒有被修改,則更新操作成功,如果前置的查詢結果作為更新的條件不成立,則數據寫失敗。

過程圖解

代碼實現

業務流程,先查詢要更新的記錄,然後把讀取的列,作為更新條件。

@Override
public Boolean updateByInventory(Integer id) {
    DataLockEntity dataLockEntity = dataLockMapper.getById(id);
    if (dataLockEntity != null){
        return dataLockMapper.updateByInventory(id,dataLockEntity.getInventory())>0 ;
    }
    return false ;
}

例如如果要把庫存更新,就把讀取的庫存數據作為更新條件,如果讀取庫存是100,在更新的時候庫存變了,則更新條件自然不能成立。

<update id="updateByInventory">
    UPDATE dl_data_lock SET inventory=inventory-1 WHERE id=#{id} AND inventory=#{inventory}
</update>

五、分佈式服務

1、服務保護

在處理高併發的秒殺場景時,經常出現服務掛掉場景,常見某些APP的營銷頁面,出現活動火爆頁面丟失的提示情況,但是不影響整體應用的運行,這就是服務的隔離和保護機制。

基於分佈式的服務結構可以把高併發的業務服務獨立出來,不會因為秒殺服務掛掉影響整體的服務,導致服務雪崩的場景。

2、數據庫保護

數據庫保護和服務保護是相輔相成的,分佈式服務架構下,服務和數據庫是對應的,理論上秒殺服務對應的就是秒殺數據庫,不會因為秒殺庫掛掉,導致整個數據庫宕機。

六、源代碼地址

GitHub·地址
https://github.com/cicadasmile/data-manage-parent
GitEE·地址
https://gitee.com/cicadasmile/data-manage-parent

推薦閱讀:《架構設計系列》,蘿蔔青菜,各有所需

序號 標題
00 架構設計:單服務.集群.分佈式,基本區別和聯繫
01 架構設計:分佈式業務系統中,全局ID生成策略
02 架構設計:分佈式系統調度,Zookeeper集群化管理
03 架構設計:接口冪等性原則,防重複提交Token管理
04 架構設計:緩存管理模式,監控和內存回收策略
05 架構設計:異步處理流程,多種實現模式詳解

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

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

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

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案

深入淺出PyTorch(算子篇)

Tensor

自從張量(Tensor)計算這個概念出現后,神經網絡的算法就可以看作是一系列的張量計算。所謂的張量,它原本是個數學概念,表示各種向量或者數值之間的關係。PyTorch的張量(torch.Tensor)表示的是N維矩陣與一維數組的關係。

torch.Tensor的使用方法和numpy很相似(https://pytorch.org/…tensor-tutorial-py),兩者唯一的區別在於torch.Tensor可以使用GPU來計算,這就比用CPU的numpy要快很多。

張量計算的種類有很多,比如加法、乘法、矩陣相乘、矩陣轉置等,這些計算被稱為算子(Operator),它們是PyTorch的核心組件。

算子的backend一般是C/C++的拓展程序,PyTorch的backend是稱為”ATen”的C/C++庫,ATen是”A Tensor”的縮寫。

Operator

PyTorch所有的Operator都定義在Declarations.cwrap和native_functions.yaml這兩個文件中,前者定義了從Torch那繼承來的legacy operator(aten/src/TH),後者定義的是native operator,是PyTorch的operator。

相比於用C++開發的native code,legacy code是在PyTorch編譯時由gen.py根據Declarations.cwrap的內容動態生成的。因此,如果你想要trace這些code,需要先編譯PyTorch。

legacy code的開發要比native code複雜得多。如果可以的話,建議你盡量避開它們。

MatMul

本文會以矩陣相乘–torch.matmul()為例來分析PyTorch算子的工作流程。

我在深入淺出全連接層(fully connected layer)中有講在GPU層面是如何進行矩陣相乘的。Nvidia、AMD等公司提供了優化好的線性代數計算庫–cuBLAS/rocBLAS/openBLAS,PyTorch只需要調用它們的API即可。

Figure 1是torch.matmul()在ATen中的function flow。可以看到,這個flow可不短,這主要是因為不同類型的tensor(2d or Nd, batched gemm or not,with or without bias,cuda or cpu)的操作也不盡相同。

at::matmul()主要負責將Tensor轉換成cuBLAS需要的格式。前面說過,Tensor可以是N維矩陣,如果tensor A是3d矩陣,tensor B是2d矩陣,就需要先將3d轉成2d;如果它們都是>=3d的矩陣,就要考慮batched matmul的情況;如果bias=True,後續就應該交給at::addmm()來處理;總之,matmul要考慮的事情比想象中要多。

除此之外,不同的dtype、device和layout需要調用不同的操作函數,這部分工作交由c10::dispatcher來完成。

Dispatcher

dispatcher主要用於動態調用dtype、device以及layout等方法函數。用過numpy的都知道,np.array()的數據類型有:float32, float16,int8,int32,…. 如果你了解C++就會知道,這類程序最適合用模板(template)來實現。

很遺憾,由於ATen有一部分operator是用C語言寫的(從Torch繼承過來),不支持模板功能,因此,就需要dispatcher這樣的動態調度器。

類似地,PyTorch的tensor不僅可以運行在GPU上,還可以跑在CPU、mkldnn和xla等設備,Figure 1中的dispatcher4就根據tensor的device調用了mm的GPU實現。

layout是指tensor中元素的排布。一般來說,矩陣的排布都是緊湊型的,也就是strided layout。而那些有着大量0的稀疏矩陣,相應地就是sparse layout。

Figure 2是strided layout的演示實例,這裏創建了一個2行2列的矩陣a,它的數據實際存放在一維數組(a.storage)里,2行2列只是這個數組的視圖。

stride充當了從數組到視圖的橋樑,比如,要打印第2行第2列的元素時,可以通過公式:\(1 * stride(0) + 1 * stride(1)\)來計算該元素在數組中的索引。

除了dtype、device、layout之外,dispatcher還可以用來調用legacy operator。比如說addmm這個operator,它的GPU實現就是通過dispatcher來跳轉到legacy::cuda::_th_addmm。

END

到此,就完成了對PyTorch算子的學習。如果你要學習其他算子,可以先從aten/src/ATen/native目錄的相關函數入手,從native_functions.yaml中找到dispatch目標函數,詳情可以參考Figure 1。

更多精彩文章,歡迎掃碼關注下方的公眾號, 並訪問我的簡書博客:https://www.jianshu.com/u/c0fe8671254e

歡迎轉發至朋友圈,工作號轉載請後台留言申請授權~

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

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

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

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

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

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

MyBatis入門學習-連接oracle實現CURD基本操作

目錄

  • 前言
  • 導入oracle庫
  • 配置
    • 環境配置
    • 配置引用
    • 配置映射
  • 查詢
    • 單條件查詢
    • 多條件查詢
      • 通過類字段傳遞參數
      • 通過Map接口傳參
      • Param註解
  • 插入
  • 更新
  • 刪除
  • 字段映射
  • 參考文獻

前言

本篇記錄使用mybatis連接oracle數據庫實現基本的CURD操作。

導入oracle庫

由於oracle收費, 因此maven沒有oracle庫包,需要我們自己導入,可以手工導入外部包,也可以將oracle的jar導入到maven庫種。具體導入步驟可以查看Maven添加Oracle的依賴及驅動

導入mybatis庫包,我本地使用的是3.5.5版本。最後的配置如下所示


<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis</artifactId>
  <version>3.5.5</version>
</dependency>

<dependency>
  <groupId>com.oracle.jdbc</groupId>
  <artifactId>ojdbc6</artifactId>
  <version>11.2.0.1.0</version>
</dependency>

配置

準備mybatis的配置,在resources目錄下新建一個mybatis-config.xml文件,配置如下

 <?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- 根標籤 -->
<configuration>
    <properties>
        <property name="driver" value="oracle.jdbc.driver.OracleDriver" />
        <property name="url" value="jdbc:oracle:thin:@10.60.45.239:1521:devdb" />
        <property name="username" value="fgmain10001" />
        <property name="password" value="test1" />
    </properties>

    <!-- 環境,可以配置多個,default:指定採用哪個環境 -->
    <environments default="test">
        <!-- id:唯一標識 -->
        <environment id="test">
            <!-- 事務管理器,JDBC類型的事務管理器 -->
            <transactionManager type="JDBC" />
            <!-- 數據源,池類型的數據源 -->
            <dataSource type="POOLED">
                <property name="driver" value="oracle.jdbc.driver.OracleDriver" />
                <property name="url" value="jdbc:oracle:thin:@10.60.45.239:1521:devdb" />
                <property name="username" value="fgmain10001" />
                <property name="password" value="test1" />
            </dataSource>
        </environment>
        <environment id="development">
            <!-- 事務管理器,JDBC類型的事務管理器 -->
            <transactionManager type="JDBC" />
            <!-- 數據源,池類型的數據源 -->
            <dataSource type="POOLED">
                <property name="driver" value="${driver}" /> <!-- 配置了properties,所以可以直接引用 -->
                <property name="url" value="${url}" />
                <property name="username" value="${username}" />
                <property name="password" value="${password}" />
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="singleTransMapper.xml" />
    </mappers>
</configuration>

環境配置

 <environment id="test">
      <!-- 事務管理器,JDBC類型的事務管理器 -->
      <transactionManager type="JDBC" />
      <!-- 數據源,池類型的數據源 -->
      <dataSource type="POOLED">
          <property name="driver" value="oracle.jdbc.driver.OracleDriver" />
          <property name="url" value="jdbc:oracle:thin:@10.60.45.239:1521:devdb" />
          <property name="username" value="fgmain10001" />
          <property name="password" value="test1" />
      </dataSource>
  </environment>

其中environments可以配置多個環境的oracle數據源。

id是環境變量的編號,在<environments>default中可以設置當前的環境值。
dataSource中設置數據源。類型有3種。包括:POOLED池化,UNPOOLED非池化和JNDI從其他配置元加載。
driver配置的類名,oracle填寫oracle.jdbc.driver.OracleDriver
url為配置的數據源,使用239測試庫jdbc:oracle:thin:@10.60.45.239:1521:devdb
username是用戶名。
password是密碼。

配置引用

在value中可以填寫如${變量名}的配置引用,通過在properties/propertie添加對應的實際的配置值。

<configuration>
    <properties>
        <property name="driver" value="oracle.jdbc.driver.OracleDriver" />
    </properties>
    
    <environments default="test">
        ...
        <environment id="test">
            ...
            <dataSource type="POOLED">
                <property name="driver" value="${driver}" /> <!-- 配置了properties,所以可以直接引用 -->
                ...
            </dataSource>
        </environment>
    </environments>
</configuration>

配置映射

配置好數據源后,需要添加對應的表映射,映射包括CRUD對應的SQL語句以及與類之間的映射關係。


<configuration>
  ...
    <mappers>
        <mapper resource="singleTransMapper.xml" />
    </mappers>
</configuration>

在resources目錄下新建一個singleTransMapper.xml文件,MyBatis會將singleTransMapper.xml映射到對應的類

除了resources以外MyBatis還支持classurlpackage共四種配置

class可以配置具體類名,如com.mybatistest.DAO.SingleTransMapper
url可以配置完整的文件路徑,如file:///var/mappers/PostMapper.xml
package可以配置package名稱,註冊所有接口。

查詢


public class SingletransDTO {

    public String EnterpriseNum;

    public String TransNo;

    public String CommandCode;

    public int State;
}

單條件查詢

增加一個查詢單筆的語句,通過輸入流水號,返回查詢到的單筆信息。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="DAO.SingleTransMapper">
    <select id="selectSingle" parameterType="String" resultType="DTO.SingletransDTO">
    select * from se_singletrans where transno = #{transno}
  </select>
</mapper>

namespace需要對應到java中的類,參數和返回類型也需要一致。

在mapper節點下添加select表示select語句
parameterType為輸入的參數
resultType為返回的類型,返回類型需要對應java中的類


public interface SingleTransMapper {
    SingletransDTO selectSingle(String transNo);
}


String resource = "mybatis-config.xml";
//加載資源
InputStream inputStream = Resources.getResourceAsStream(resource);
//創建session
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
try (SqlSession session = sqlSessionFactory.openSession()) {
  //獲取單筆映射對象
    SingleTransMapper mapper = session.getMapper(SingleTransMapper.class);
    //根據流水號查詢
    SingletransDTO blog = mapper.selectSingle("642EHDCS899XKF8P");

    if(blog != null) {
        System.out.println(blog.ENTERPRISENUM);
        System.out.println(blog.TRANSNO);
        System.out.println(blog.COMMANDCODE);
    }else{
        System.out.println("not found");
    }

}catch (Exception exception)
{
    System.out.println(exception.getMessage());
}

多條件查詢

通過類字段傳遞參數

添加一個查詢配置selectSingleByParam

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="DAO.SingleTransMapper">
   
   ...
    <select id="selectSingleByClass" resultType="DTO.SingletransDTO">
    select * from se_singletrans where transno = #{TransNo} and commandCode= #{CommandCode}
  </select>
</mapper>

對應的映射類添加方法對應的方法,MyBatis可以通過反射將類的字段映射到SQL的參數,需要注意的是類的字段名和sql中配置的大小寫需要一致。


public interface SingleTransMapper {
  ...
  SingletransDTO selectSingleByClass(SingleCondition singleCondition);
}
public class SingleCondition {
    /**
     * 流水號
     */
    public String TransNo;

    /**
     * 指令類型
     */
    public String CommandCode;

    public SingleCondition(String transNo, String commandCode)
    {
        TransNo = transNo;
        CommandCode = commandCode;
    }
}

調用構造函數類的多條件查詢方案

...
SingleTransMapper mapper = session.getMapper(SingleTransMapper.class);
SingletransDTO blog = mapper.selectSingleByClass(new SingleCondition( "642EHDCS899XKF8P","10009"));

通過Map接口傳參

另一種方案可以通過傳入HashMap,MayBatis會根據key自動映射到對應的參數。
下面實現通過流水號和指令類型查詢。
添加一個查詢配置selectSingleByMultCondition

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="DAO.SingleTransMapper">
    ...
    <select id="selectSingleByMultCondition" resultType="DTO.SingletransDTO">
    select * from se_singletrans where transno = #{transNo} and commandCode= #{commandCode}
  </select>
</mapper>

添加對應的方法,傳入參數為HashMap<String,Object> param

public interface SingleTransMapper {
  ...
    SingletransDTO selectSingleByMultCondition(HashMap<String,Object> param);
}

修改調用新的多條件查詢方法

...
//獲取單筆映射對象
SingleTransMapper mapper = session.getMapper(SingleTransMapper.class);
//根據流水號查詢
HashMap<String,Object> param = new HashMap<String,Object>();
param.put("transNo","642EHDCS899XKF8P");
param.put("commandCode","10009");
SingletransDTO blog = mapper.selectSingle2(param);
...

需要注意的是,由於HashMap的key是不區分大小寫的,因此需要和配置文件sql的參數大小寫一致。

Param註解

通過類參數和Map進行多條件查詢都需要創建額外的對象,另一種比較好的方式可以通過在方法參數上添加Param註解的方式配置方法參數和SQL參數的映射關係。

添加一個查詢配置selectSingleByParam

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="DAO.SingleTransMapper">
   
   ...
    <select id="selectSingleByParam" resultType="DTO.SingletransDTO">
    select * from se_singletrans where transno = #{param1} and commandCode= #{param2}
  </select>
</mapper>

對應的映射類添加方法對應的方法,這樣MyBatis就知道參數映射規則,就會自動映射,需要注意的數參數和sql中配置的大小寫也需要一致。


public interface SingleTransMapper {
    SingletransDTO selectSingle(String transNo);
    SingletransDTO selectSingleByMultCondition(HashMap<String,Object> param);
    SingletransDTO selectSingleByParam(@Param("param1")String transNo, @Param("param2") String commandCode);
}

調用註解傳參方法

...
//獲取單筆映射對象
SingleTransMapper mapper = session.getMapper(SingleTransMapper.class);
SingletransDTO blog = mapper.selectSingleByParam("642EHDCS899XKF8P","10009");
...

插入

在mapper下添加insert表示插入的sql映射。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="DAO.SingleTransMapper">
  
  ...
  <insert id="insert" parameterType="DTO.SingletransDTO">
    insert into se_singletrans(ENTERPRISENUM,TRANSNO,COMMANDCODE,STATE) values(#{EnterpriseNum},#{TransNo},#{CommandCode},#{State})
  </insert>
</mapper>

添加類對應的insert方法


public interface SingleTransMapper {

    ...
    int insert(SingletransDTO singletransDTO);
}

SqlSession默認會開啟事務,在insert完成后需要調用SqlSessioncommit()方法提交事務。


try (SqlSession session = sqlSessionFactory.openSession()) {
    SingleTransMapper mapper = session.getMapper(SingleTransMapper.class);
    SingletransDTO singletransDTO = new     SingletransDTO();
    singletransDTO.EnterpriseNum  = "QT330001";
    singletransDTO.TransNo = "MYBATIS.INSERT";
    singletransDTO.CommandCode = "10009";
    int count = mapper.insert(singletransDTO);
    session.commit();
    System.out.println("insert result:" +count);
}catch (Exception exception)
{
    System.out.println(exception.getMessage());
}

我們也可以調用SqlSession openSession(boolean autoCommit)傳入參數,自動提交。

更新

在mapper下添加update節點表示插入,插入時可以對插入的字段設置條件,達成某條件是該字段才需要更新。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="DAO.SingleTransMapper">
  
  ...
   <update id="update" parameterType="DTO.SingletransDTO">
    update se_singletrans
    <set>
        <if test="State != null and State!=''"></if>
        STATE=#{State}
    </set>
    where transno = #{TransNo} and commandCode= #{CommandCode}
  </update>
</mapper>

添加類對應的update方法


public interface SingleTransMapper {

    ...
    int update(SingletransDTO singletransDTO);
}

SqlSession默認會開啟事務,和insert一樣,在update完成后需要調用SqlSessioncommit()方法提交事務。


try (SqlSession session = sqlSessionFactory.openSession()) {
    SingleTransMapper mapper = session.getMapper(SingleTransMapper.class);
    SingletransDTO singletransDTO = new SingletransDTO();
    singletransDTO.EnterpriseNum = "QT330001";
    singletransDTO.TransNo = "MYBATIS.INSERT";
    singletransDTO.CommandCode = "10009";
    singletransDTO.State = 2;
    int count = mapper.update(singletransDTO);
    session.commit();
    System.out.println("update result:" +count);
}catch (Exception exception)
{
    System.out.println(exception.getMessage());
}

刪除

在mapper下添加delete節點表示刪除。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="DAO.SingleTransMapper">
  
  ...
   
  <delete id="delete">
    delete from se_singletrans where transno = #{TransNo} and commandCode= #{CommandCode}
  </delete>
</mapper>

添加類對應的delete方法,可以通過參數註解的方式指定參數。


public interface SingleTransMapper {

    ...
    int delete(@Param("TransNo")String transNo, @Param("CommandCode") String commandCode);
}

SqlSession默認會開啟事務,在delete完成后需要調用SqlSessioncommit()方法提交事務。


try (SqlSession session = sqlSessionFactory.openSession()) {
    SingleTransMapper mapper = session.getMapper(SingleTransMapper.class);
    int count = mapper.delete("MYBATIS.INSERT","10009");
    session.commit();
    System.out.println("delete result:" +count);
}catch (Exception exception)
{
    System.out.println(exception.getMessage());
}

字段映射

若字段名和數據庫的字段名不一致,可以通過配置進行映射。添加resultMap節點,配置類字段和數據庫字段的映射關係,若沒有配置的字段,則根據默認MyBatis的映射關係處理,即字段名一樣的自動映射,MyBatis會嘗試進行類型轉換,若轉換異常,則可能拋錯。我們也可以通過typeHandler自定義自己的類型處理器。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="DAO.SingleTransMapper">

  <resultMap id="singleResultMap" type="DTO.SingletransDTO">
    <result property="TransNo"  column="transNo" />
    <result property="CommandCode" column="commandCode"/>
    <result property="SpecialProperty" typeHandler="CustomTypeHandle" column="SpecialColumn"/>
  </resultMap>
  <select id="selectSingleToReusltMap" resultMap="singleResultMap">
    select * from se_singletrans where transno = #{param1} and commandCode= #{param2}
  </select>
</mapper>

關於TypeHandle這裏不做具體闡述,有興趣的可以看下MyBatis自定義類型處理器 TypeHandler

參考文獻

  1. Maven添加Oracle的依賴及驅動
  2. MyBatis自定義類型處理器 TypeHandler

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

【其他文章推薦】

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

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

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

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

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

※教你寫出一流的銷售文案?

【asp.net core 系列】12 數據加密算法

0. 前言

這一篇我們將介紹一下.net core 的加密和解密。在Web應用程序中,用戶的密碼會使用MD5值作為密碼數據存儲起來。而在其他的情況下,也會使用加密和解密的功能。

常見的加密算法分為對稱加密和非對稱加密。所謂的對稱加密是指加密密鑰和解密密鑰是同一個,非對稱加密是值加密密鑰和解密迷藥不同。而我們常應用在保存用戶登錄密碼這個過程中的MD5本質上並不是加密算法,而是一種信息摘要算法。不過MD5盡量保證了每個字符串最後計算出來的值都不一樣,所以在密碼保存中常用MD5做為保密值。

1. 常見對稱加密算法

對稱加密算法,簡單的說就是加密和解密使用相同的密鑰進行運算。對於大多數加密算法,解密和加密是一個互逆的運算。對稱加密算法的安全性取決於密鑰的長度,密鑰越長越安全。當然,不建議使用過長的密鑰。

那麼,我們來看看常見的對稱加密算法有哪些吧,以及C#該如何實現。

1.1 DES 和 DESede 算法

DES算法和DESede算法(又稱三重DES算法) 統稱DES系列算法。DES全稱為Data Encryption Standard,即數據加密標準,是一種使用密鑰加密的塊算法。而DESede就是針對同一塊數據做三次DES加密。這裏就不對原理做過多的介紹了,來看看.net core里如何實現DES加/解密吧。

在Utils項目里,創建目錄Security

在Security目錄下,創建DESHelper類:

namespace Utils.Security
{
    public class DesHelper
    {
        
    }
}

加密解密實現:

using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;

namespace Utils.Security
{
    public static class DesHelper
    {
        static DesHelper()
        {
            DesHandler =  DES.Create("DES");
            DesHandler.Key = Convert.FromBase64String("L1yzjGB2sI4=");
            DesHandler.IV = Convert.FromBase64String("uEcGI4JSAuY=");
        }

        private static DES DesHandler { get; }

        /// <summary>
        /// 加密字符
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static string Encrypt(string source)
        {
            try
            {
                using (var memStream = new MemoryStream())
                using (var cryptStream = new CryptoStream(memStream, DesHandler.CreateEncryptor(DesHandler.Key, DesHandler.IV),
                    CryptoStreamMode.Write))
                {
                    var bytes = Encoding.UTF8.GetBytes(source);
                    cryptStream.Write(bytes, 0, bytes.Length);
                    cryptStream.FlushFinalBlock();
                    
                    return Convert.ToBase64String(memStream.ToArray());
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                return null;
            }
        }

        /// <summary>
        /// 解密
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static string Decrypt(string source)
        {
            try
            {
                using (var mStream = new MemoryStream(Convert.FromBase64String(source)))
                using (var cryptoStream =
                    new CryptoStream(mStream, DesHandler.CreateDecryptor(DesHandler.Key, DesHandler.IV), CryptoStreamMode.Read))
                using (var reader = new StreamReader(cryptoStream))
                {
                    return reader.ReadToEnd();
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                return null;
            }
        }
    }
}

每次調用DesHandler = DES.Create("DES"); 都會重新獲得一個DES算法實現實例,這樣每次獲取的實例中Key、IV這兩個屬性的值也會發生變化。如果直接使用會出現這次加密的數據下次就沒法解密了,為了減少這種情況,所以代碼處手動賦值了Key、IV這兩個屬性。

1.2 AES 加密算法

AES算法(Advanced Encryption Standard)也就是高級數據加密標準算法,是為了解決DES算法中的存在的漏洞而提出的算法標準。現行的AES算法核心是Rijndael算法。當然了,這個不用太過於關心。我們直接看看是如何實現吧:

同樣,在Security目錄創建一個AesHelper類:

namespace Utils.Security
{
    public static class AesHelper
    {
        
    }
}

具體的加解密實現:

using System;
using System.IO;
using System.Security.Cryptography;

namespace Utils.Security
{
    public static class AesHelper
    {
        static AesHelper()
        {
            AesHandler = Aes.Create();
            AesHandler.Key = Convert.FromBase64String("lB2BxrJdI4UUjK3KEZyQ0obuSgavB1SYJuAFq9oVw0Y=");
            AesHandler.IV = Convert.FromBase64String("6lra6ceX26Fazwj1R4PCOg==");
        }

        private static Aes AesHandler { get; }

        public static string Encrypt(string source)
        {
            using (var mem = new MemoryStream())
            using (var stream = new CryptoStream(mem, AesHandler.CreateEncryptor(AesHandler.Key, AesHandler.IV),
                CryptoStreamMode.Write))
            {
                using (var writer = new StreamWriter(stream))
                {
                    writer.Write(source);
                }   
                return Convert.ToBase64String(mem.ToArray());
            }
            
        }

        public static string Decrypt(string source)
        {
            var data = Convert.FromBase64String(source);
            using (var mem = new MemoryStream(data))
            using (var crypto = new CryptoStream(mem, AesHandler.CreateDecryptor(AesHandler.Key, AesHandler.IV),
                CryptoStreamMode.Read))
            using (var reader = new StreamReader(crypto))
            {
                return reader.ReadToEnd();
            }
        }
    }
}

2. 常見非對稱加密算法

非對稱加密算法,指的是加密密鑰和解密密鑰並不相同。非對稱加密算法的秘鑰通常成對出現,分為公開密鑰和私有密鑰。公開密鑰可以以公開的形式發給數據交互方,而不會產生泄密的風險。因為非對稱加密算法,無法通過公開密鑰推算私有密鑰,反之亦然。

通常,非對稱加密算法是用公鑰進行加密,使用私鑰進行解密。

2.1 RSA算法

RSA算法是標準的非對稱加密算法,名字來源是三位發明者的姓氏首字母。RSA公開密鑰密碼體制是一種使用不同的加密密鑰與解密密鑰,“由已知加密密鑰推導出解密密鑰在計算上是不可行的”密碼體制 。其安全性取決於密鑰的長度,1024位的密鑰幾乎不可能被破解。

同樣,在Utils.Security下創建RSAHelper類:

namespace Utils.Security
{
    public static class RsaHelper
    {
        
    }
}

具體實現:

using System;
using System.Security.Cryptography;

namespace Utils.Security
{
    public static class RsaHelper
    {
        public static RSAParameters PublicKey { get; private set; }
        public static RSAParameters PrivateKey { get; private set; }

        static RsaHelper()
        {
            
        }

        public static void InitWindows()
        {
            var parameters = new CspParameters()
            {
                KeyContainerName = "RSAHELPER" // 默認的RSA保存密鑰的容器名稱
            };
            var handle = new RSACryptoServiceProvider(parameters);
            PublicKey = handle.ExportParameters(false);
            PrivateKey = handle.ExportParameters(true);
        }

        public static void ExportKeyPair(string publicKeyXmlString, string privateKeyXmlString)
        {
            var handle  = new RSACryptoServiceProvider();
            handle.FromXmlString(privateKeyXmlString);
            PrivateKey = handle.ExportParameters(true);
            handle.FromXmlString(publicKeyXmlString);
            PublicKey = handle.ExportParameters(false);
        }
        public static byte[] Encrypt(byte[] dataToEncrypt)
        {
            try
            {
                byte[] encryptedData;
                using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider())
                {
                    RSA.ImportParameters(PublicKey);
                    encryptedData = RSA.Encrypt(dataToEncrypt, true);
                }

                return encryptedData;
            }
            catch (CryptographicException e)
            {
                Console.WriteLine(e.Message);
                return null;
            }
        }

        public static byte[] Decrypt(byte[] dataToDecrypt)
        {
            try
            {
                byte[] decryptedData;
                using (var rsa = new RSACryptoServiceProvider())
                {
                    rsa.ImportParameters(PrivateKey);
                    decryptedData = rsa.Decrypt(dataToDecrypt, true);
                }
                return decryptedData;
            }
            catch (CryptographicException e)
            {
                Console.WriteLine(e.ToString());
                return null;
            }
        }
    }
}

因為RSA的特殊性,需要預先設置好公鑰和私鑰。C# 支持多種方式導入密鑰,這裏就不做過多介紹了。

3. 信息摘要算法

這種算法嚴格意義上並不是加密算法,因為它完全不可逆。也就是說,一旦進行使用該類型算法加密后,無法解密還原出數據。當然了,也正是因為這種特性常常被用來做密碼的保存。因為這樣可以避免某些人拿到數據庫與代碼后,可以簡單反推出用戶的密碼。

3.1 MD5算法

最常用的信息摘要算法就是MD5 加密算法,MD5信息摘要算法(英語:MD5 Message-Digest Algorithm),一種被廣泛使用的密碼散列函數,可以產生出一個128位(16字節)的散列值(hash value),用於確保信息傳輸完整一致。

原理不解釋,我們看下如何實現,照例現在Security下創建MD5Helper:

namespace Utils.Security
{
    public static class Md5Helper
    {
        
    }
}

具體實現:

using System.Security.Cryptography;
using System.Text;

namespace Utils.Security
{
    public static class Md5Helper
    {
        private static MD5 Hanlder { get; } = new MD5CryptoServiceProvider();

        public static string GetMd5Str(string source)
        {
            var data = Encoding.UTF8.GetBytes(source);
            var security = Hanlder.ComputeHash(data);
            var sb = new StringBuilder();
            foreach (var b in security)
            {
                sb.Append(b.ToString("X2"));
            }

            return sb.ToString();
        }
    }
}

4 總結

這一篇簡單介紹了四種常用的加密算法的實現,當然最常用的就是 MD5,因為這個是大多數系統用來做密碼保存的加密算法。

更多內容煩請關注我的博客《高先生小屋》

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

【其他文章推薦】

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

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司"嚨底家"!

※幫你省時又省力,新北清潔一流服務好口碑

※別再煩惱如何寫文案,掌握八大原則!

※產品缺大量曝光嗎?你需要的是一流包裝設計!

門多西諾複合大火續燒 面積相當於洛杉磯市

摘錄自2018年8月8日中央通訊社台北報導

美國加州野火繼續傳出災情,「門多西諾複合大火」延燒面積已相當於整個洛杉磯市,上萬名消防員今天仍不斷努力救災控制火勢,上月爆發的一連串野火已造成11人喪命。

加州北部的門多西諾複合大火(Mendocino Complex Fire)由2個分別名為「河流」(River fire)及「牧場」(Ranch fire)的野火組成,延燒面積已達29萬英畝(11萬7359公頃),大小相當於整個洛杉磯市。

門多西諾複合大火昨天(7日)改寫短短8個月前「湯瑪斯野火」(Thomas Fire)紀錄,成為加州有紀錄以來最大野火。湯瑪斯野火去年12月在南加州肆虐,延燒面積達28萬1893英畝。

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

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

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

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

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

※教你寫出一流的銷售文案?

蓄意污染亞馬遜雨林 雪佛龍在厄瓜多憲法法庭敗訴

環境資訊中心外電;姜唯 翻譯;林大利 審校;稿源:ENS

美國雪佛龍(Chevron)公司的世紀污染訴訟案,有了最新進展。厄瓜多憲法法庭稍早駁回了雪佛龍對95億美元污染判決的最終上訴,認定該公司蓄意將數十億加侖的有毒油廢料傾倒在亞馬遜熱帶雨林的原民土地上。

長達151頁的判決書中,以8比0一致判定原告原民團體勝訴,並駁回了雪佛龍的所有說法,例如雪佛龍自己也是詐欺受害者,以及厄瓜多法院對此案無管轄權等都遭到駁回。

前油田運營商德士古(後被雪佛龍收購)蓄意將數十億加侖的有毒油廢料傾倒在亞馬遜熱帶雨林。圖片來源:Caroline Bennett / Rainforest Action Network

環境集體訴訟案 第四次獲勝

厄瓜多憲法法院僅審理憲法問題,是厄瓜多第三大上訴法院,也是該國第四個維持2011年判決結果的法院,連同厄瓜多最高民事法庭和國家法院一致裁定雪佛龍須做出鉅額賠償。

這個針對雪佛龍公司的集體訴訟案,判決書於7月11日發布,原告是守護亞馬遜陣線(Frente de Defensa de la Amazonia),代表厄瓜多亞馬遜北部地區80個原民和農民社區的草根團體。

該訴訟最初由約3萬名熱帶雨林村民於1993年在紐約聯邦法院提出,被告是前油田運營商德士古(Texaco,已被雪佛龍公司收購)。2001年雪佛龍公司接受厄瓜多具有管轄權後,一名美國聯邦法官將此案移交給厄瓜多法院。

「這個判決是厄瓜多人民20年來為了爭取環境正義,對抗世界級污染者、流氓企業的再一次巨大勝利,」環保金人獎得主、1993年訴訟案起草者楊澤(Luis Yanza)表示。他目前也是守護亞馬遜陣線的主席。

楊澤說:「在雪佛龍開始賠償厄瓜多人民前,任何國家都不應該與雪佛龍做生意。」

致癌油廢料污染 雪佛龍八年拖延纏訟

厄瓜多首席律師薩拉查(Patricio Salazar)表示,「司法已認定雪佛龍非法攻擊代表原住民社群的律師,而不是根據是非來訴訟。雪佛龍現在極有可能必須全額支付賠償金,因為它在法律上和道德上都有義務這樣做。」

厄瓜多初審法院依據105份技術證據報告,在2011年判決該公司致癌油廢料污染了1500平方英里的亞馬遜土地。但是八年來,雪佛龍以拖延戰術因應訴訟。

生活在熱帶雨林中的數千人,包括許多原住民,已經死於癌症,而成千上萬的人必須忍受這場世界級的公衛災難。

代表厄瓜多村民的美國律師唐齊格(Steven Donziger)。圖片來源:Steven Donziger

拒支付賠償金 村民仍沒有醫療

收受雪佛龍公司400口油井特許使用費的厄瓜多政府,沒有給受害者太大幫助。

哈佛大學畢業,代表厄瓜多村民的美國律師唐齊格(Steven Donziger)曾走訪受影響地區數十次,他表示受影響地區並沒有醫療服務,許多人甚至一次醫生都沒看,也沒接受任何治療就死於癌症。

「雪佛龍在厄瓜多引發了一場史無前例的人道主義危機。再不清除污染,未來幾年將有數萬人死亡。全世界都必須關注,雪佛龍的股東和管理層必須立即採取行動,解決這個日益嚴重的問題。」唐齊格說。

雪佛龍多年來一直拒絕支付判決賠償金,目前連本帶利已經來到120億美元(約新台幣1361億元)。

雪佛龍公司人員甚至威脅原民,若繼續堅持訴訟,他們可和原告「終身纏訟」。

卡普蘭法官 採信偽證風波

這份最新判決也是對美國紐約南區地方法院法官卡普蘭(Lewis A. Kaplan)的重大打擊。

2014年,這位具有爭議性的法官僅憑一位承認收賄的雪佛龍證人的假證詞便裁定,厄瓜多最高法院對雪佛龍提出的95億美元賠償判決是透過欺詐和脅迫手段取得。卡普蘭拒絕讓公正的事實調查員陪審團參與,也拒絕考量任何有關雪佛龍污染厄瓜多環境的證據。

但新事證顯示,雪佛龍支付厄瓜多前法官格拉(Alberto Guerra)大筆賄款之後,卡普蘭的判決被推翻。雪佛龍幫助格拉一家搬到美國後,格拉承認在雪佛龍律師的指導下作偽證。

卡普蘭僅憑格拉的偽證做出判決,也是全世界唯一做出有利雪佛龍判決的法官。

「出於省錢的錯誤營運決策」 法庭定調蓄意污染

基於比卡普蘭所能取得更完整的證據,17名厄瓜多法官作出有利厄瓜多村民的判決。12名加拿大法官,包括該國最高法院,也針對多個技術性問題作出有利厄瓜多村民的判決。

厄瓜多憲法法庭強調,雪佛龍污染環境造成嚴重後果不是意外造成,而是處心積慮為公司和股東省錢的運營決策,加上20年來蓄意拖延的額外罪行所導致。

厄瓜多原告也在加拿大法院取得了幾項上訴勝利,他們在加國蒐集雪佛龍資產,以迫使其遵守厄瓜多的判決。

Chevron Defeated in Ecuador’s Constitutional Court QUITO, Ecuador, July 31, 2018 (ENS)

In a benchmark pollution case, Ecuador’s Constitutional Court has rejected Chevron’s final appeal of a $9.5 billion pollution judgment that found the company deliberately dumped billions of gallons of toxic oil waste onto Indigenous lands in the Amazon rainforest.

Photo: Caroline Bennett / Rainforest Action Network

The unanimous 8-0 decision, issued in a 151-page document published July 11, was a total victory for the Indigenous groups that brought the case and a rejection of all of Chevron’s claims.

The Court rejected Chevron’s allegations that it was victimized by fraud, and the court threw out the company’s claim that Ecuadorian courts had no jurisdiction over the matter.

Ecuador’s Constitutional Court, which deals only with Constitutional issues, is the third major appellate court in Ecuador and the fourth court overall in the country to uphold the trial-level decision against Chevron, which was issued in 2011. Ecuador’s highest civil court, the National Court of Justice, has ruled unanimously to affirm the judgment against Chevron.

The class action case against Chevron was spearheaded by the Frente de Defensa de la Amazonia, the Amazon Defense Front, a grassroots group representing 80 Indigenous peoples and farmer communities in Ecuador’s northern Amazon region.

The case was originally filed in 1993 in federal court in New York against the former oil field operator Texaco, now part of Chevron Corporation, on behalf of an estimated 30,000 rainforest villagers. But in 2001 a U.S. federal judge moved it to Ecuador’s courts at Chevron’s request after the company accepted jurisdiction there.

“This decision is another huge victory for the people of Ecuador in their historic two-decade battle for environmental justice against the world’s worst corporate polluter and rogue operator,” said Luis Yanza, a Goldman Prize winner who initiated the lawsuit against Chevron in U.S. federal court in 1993, and serves as president of the Frente de Defensa de la Amazonia, Amazon Defense Front.

“No country should ever do business with Chevron until the company first pays for the harm it caused to the people of Ecuador,” Yanza said.

Patricio Salazar, the lead Ecuadorian lawyer on the case, said, “Justice has prevailed over Chevron’s illegal attempts to engage in constant attacks on lawyers who defend the Indigenous communities rather than litigate in good faith on the merits. It is now highly likely that Chevron will pay every last dollar of the judgment against it, as it is legally and ethically obligated to do.”

After eight years of proceedings slowed by Chevron’s strategy of deliberate delay, Ecuador’s trial court relied on 105 technical evidentiary reports to find in 2011 that the company poisoned a 1,500 square mile area of the Amazon with carcinogenic oil waste.

Thousands who live in the rainforest, including many Indigenous peoples, have died of cancer while tens of thousands must endure what is one of the world’s worst ongoing public health catastrophes.

Photo: Jonathan McIntosh / Rainforest Action Network

Ecuador’s government, which received royalties from Chevron’s operation of 400 well sites, has been of little help to the victims. Medical care in the affected region is non-existent, and many people perish from cancer without even visiting a doctor and after receiving no treatment, said Steven Donziger, the Harvard educated U.S. legal representative of the Ecuadorian communities, who has taken dozens of trips to the affected area.

“Chevron has caused a humanitarian crisis in Ecuador of epic proportions that is ongoing to this day,” he said. “Tens of thousands of people will die in the coming years if nothing is done to clean up the pollution. The world must pay attention and Chevron shareholders and management must act immediately to address this worsening problem.”

Chevron has refused for years to pay the Ecuador judgment, now worth $12 billion with interest. Company officials have threatened the Indigenous groups with a “lifetime of litigation” if they persist.

The latest Ecuadorean court decision is also a major blow to controversial Judge Lewis A. Kaplan, a judge of the U.S. District Court for the Southern District of New York.

In 2014, Judge Kaplan ruled that the $9.5 billion Lago Agrio judgment leveled against Chevron by Ecuador’s highest court, was obtained by way of fraud and coercion.

Kaplan relied on false testimony from an admittedly corrupt Chevron witness to find that the Ecuador judgment was procured by fraud. Kaplan refused to seat a jury of impartial fact finders, and he refused to consider any evidence of Chevron’s environmental contamination in Ecuador.

But Kaplan’s decision was disproven after evidence emerged that Chevron paid large sums to Alberto Guerra, a former Ecuadorian judge booted from the bench after he admitted taking bribes. Guerra was moved with his family by Chevron to the United States and later admitted lying on the stand after being coached for 53 days by Chevron lawyers headed by Randy Mastro at Gibson Dunn.

Kaplan based his core findings largely on Guerra’s false testimony. And Kaplan remains the only judge in the world to have ruled in favor of Chevron.

Seventeen Ecuador judges, who had access to a fuller evidentiary record than Kaplan, ruled in favor of the affected communities. Twelve judges from Canada, including the country’s entire Supreme Court, have also ruled in favor of the Ecuadorians on various technical issues.

The Ecuador decision confronts Chevron on the brutal human consequences of both its original environmental crimes, which the Court emphasizes were not the result of an accident, but rather of deliberate operational decision-making designed to save money and enrich the company’s shareholders and executives, and the additional offense of its two-decade campaign of distraction and delay.

The Ecuadorian plaintiffs also have picked up several appellate victories in Canadian courts as they attempt to collect Chevron assets in that country to force compliance with the Ecuador judgment.

※ 全文及圖片詳見:

作者

如果有一件事是重要的,如果能為孩子實現一個願望,那就是人類與大自然和諧共存。

於特有生物研究保育中心服務,小鳥和棲地是主要的研究對象。是龜毛的讀者,認為龜毛是探索世界的美德。

延伸閱讀

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

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

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

※推薦評價好的iphone維修中心

不能放任蜜蜂死 法國禁五款類尼古丁農藥

摘錄自2018年8月10日中央社報導

法國政府為遏止蜜蜂持續大量死亡,公告自9月1日起禁用五種含類尼古丁的農藥。

法國於2016年實施生物多樣性法規,當時就決定要禁用含類尼古丁(neonicotinoid)的殺蟲劑,這種物質會破壞昆蟲的神經系統,導致蜜蜂等花粉媒介昆蟲迷路及死亡,威脅到生物多樣性和糧食生產。

法國這一波禁用的五種殺蟲劑包括益達胺(imidacloprid)、可尼丁(clothianidin)、賽速安(Thiamethoxam)、賽果培(Thiacloprid)和亞滅培(Acetamiprid)。

法國政府對這類殺蟲劑的禁令比歐盟嚴格。費加洛報(Le Figaro)報導,法國生態部表示,國會正在審議糧食法草案,通過後,應可再增加禁用類尼古丁殺蟲劑的種類。

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

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

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

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案

美法院裁決 環保署須下令禁用陶斯松

摘錄自2018年8月10日中央社報導

聯邦第九巡迴上訴法院今天(10日)裁決,美國環境保護署(EPA)須在60天內下令禁用陶斯松(chlorpyrifos)。批評人士指出,這種廣泛使用的農藥會傷害兒童和農民。

第九巡迴上訴法院以2票贊成、1票反對的裁決結果,推翻前環保署長普魯特(Scott Pruitt)2017年3月拒絕接受環保團體請願的決定。當時環保團體呼籲,禁止陶斯松用於水果、蔬菜和堅果等糧食作物。

代表第九巡迴上訴法院撰寫裁決書的法官拉柯夫(Jed Rakoff)指出,「科學證據顯示,殘留在糧食上(的陶斯松)會對兒童神經發育造成損害」,但環保署未能提出有力反駁,因此下令環保署須在60天內下令禁用陶斯松。

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

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

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

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

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

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化