Spring中的事務

来源:http://www.cnblogs.com/zhangzongle/archive/2016/10/19/5978301.html
-Advertisement-
Play Games

Spring配置文件中關於事務配置總是由三個組成部分,分別是DataSource、TransactionManager和代理機制這三部分,無論哪種配置方式,一般變化的只是代理機制這部分。 DataSource、 TransactionManager這兩部分只是會根據數據訪問方式有所變化,比如使用Hi ...


Spring配置文件中關於事務配置總是由三個組成部分,分別是DataSource、TransactionManager和代理機制這三部分,無論哪種配置方式,一般變化的只是代理機制這部分。 DataSource、 TransactionManager這兩部分只是會根據數據訪問方式有所變化,比如使用Hibernate進行數據訪問時,DataSource實際為 SessionFactory,TransactionManager的實現為HibernateTransactionManager。

一.事務的4個特性:

原子性:一個事務中所有對資料庫的操作是一個不可分割的操作序列,要麼全做,要麼全部做。 一致性:數據不會因為事務的執行而遭到破壞。
隔離性:一個事務的執行,不受其他事務(進程)的干擾。既併發執行的個事務之間互不幹擾。
持久性:一個事務一旦提交,它對資料庫的改變將是永久的。

二:Spring事務的隔離級別 

1. ISOLATION_DEFAULT: 這是一個PlatfromTransactionManager預設的隔離級別,使用資料庫預設的事務隔離級別.
      另外四個與JDBC的隔離級別相對應
 2. ISOLATION_READ_UNCOMMITTED: 這是事務最低的隔離級別,它充許令外一個事務可以看到這個事務未提交的數據。
      這種隔離級別會產生臟讀,不可重覆讀和幻像讀。
 3. ISOLATION_READ_COMMITTED: 保證一個事務修改的數據提交後才能被另外一個事務讀取。另外一個事務不能讀取該事務未提交的數據
 4. ISOLATION_REPEATABLE_READ: 這種事務隔離級別可以防止臟讀,不可重覆讀。但是可能出現幻像讀。
      它除了保證一個事務不能讀取另一個事務未提交的數據外,還保證了避免下麵的情況產生(不可重覆讀)。
 5. ISOLATION_SERIALIZABLE 這是花費最高代價但是最可靠的事務隔離級別。事務被處理為順序執行。
      除了防止臟讀,不可重覆讀外,還避免了幻像讀。

什麼是臟數據,臟讀,不可重覆讀,幻覺讀?
 臟讀: 指當一個事務正在訪問數據,並且對數據進行了修改,而這種修改還沒有提交到資料庫中,這時,
         另外一個事務也訪問這個數據,然後使用了這個數據。因為這個數據是還沒有提交的數據, 那麼另外一
         個事務讀到的這個數據是臟數據,依據臟數據所做的操作可能是不正確的。
    
 不可重覆讀: 指在一個事務內,多次讀同一數據。在這個事務還沒有結束時,另外一個事務也訪問該同一數據。
         那麼,在第一個事務中的兩次讀數據之間,由於第二個事務的修改,那麼第一個事務兩次讀到的數據
         可能是不一樣的。這樣就發生了在一個事務內兩次讀到的數據是不一樣的,因此稱為是不可重覆讀。
            
 幻覺讀: 指當事務不是獨立執行時發生的一種現象,例如第一個事務對一個表中的數據進行了修改,這種修改涉及
         到表中的全部數據行。同時,第二個事務也修改這個表中的數據,這種修改是向表中插入一行新數據。那麼,
         以後就會發生操作第一個事務的用戶發現表中還有沒有修改的數據行,就好象發生了幻覺一樣。

三:Spring事務類型詳解:

PROPAGATION_REQUIRED--支持當前事務,如果當前沒有事務,就新建一個事務。這是最常見的選擇。

PROPAGATION_SUPPORTS--支持當前事務,如果當前沒有事務,就以非事務方式執行。

PROPAGATION_MANDATORY--支持當前事務,如果當前沒有事務,就拋出異常。

PROPAGATION_REQUIRES_NEW--新建事務,如果當前存在事務,把當前事務掛起。

PROPAGATION_NOT_SUPPORTED--以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。

PROPAGATION_NEVER--以非事務方式執行,如果當前存在事務,則拋出異常。

PROPAGATION_NESTED--如果當前存在事務,則在嵌套事務內執行。如果當前沒有事務,則進行與PROPAGATION_REQUIRED類似的操作。

四.事務的實現方式:

實現方式共有兩種:編碼方式;聲明式事務管理方式。
基於AOP技術實現的聲明式事務管理,實質就是:在方法執行前後進行攔截,然後在目標方法開始之前創建並加入事務,執行完目標方法後根據執行情況提交或回滾事務。
聲明式事務管理又有兩種方式:基於XML配置文件的方式;另一個是在業務方法上進行@Transactional註解,將事務規則應用到業務邏輯中。

五.實現事務的案例

下麵模擬一個用戶(Account)用錢來買股票(Stock),當用戶出錢買股要是錯誤,就需要我們的事務了。

源碼介紹(Spring框架基本jar包全的情況下):

1.首先我們要用到事務的jar包,我用的是:

   spring-tx-4.2.0.RELEASE.jar

2.Account.java

package cn.tx.entity;
/**
 * 賬戶
 * @author zhangzong
 *
 */
public class Account {
    
    private Integer aid;//賬戶編號
    
    private String aname;//用戶姓名
    
    private int balance;//賬戶金額

    public Integer getAid() {
        return aid;
    }

    public void setAid(Integer aid) {
        this.aid = aid;
    }

    public String getAname() {
        return aname;
    }

    public void setAname(String aname) {
        this.aname = aname;
    }

    public int getBalance() {
        return balance;
    }

    public void setBalance(int balance) {
        this.balance = balance;
    }

}
View Code

3.Stock.java

package cn.tx.entity;
/**
 * 股票類
 * @author zhangzong
 *
 */
public class Stock {
    
    private Integer sid;//股票編號
    
    private String sname;//股票名稱
    
    private int count;//股數

    public Integer getSid() {
        return sid;
    }

    public void setSid(Integer sid) {
        this.sid = sid;
    }

    public String getSname() {
        return sname;
    }

    public void setSname(String sname) {
        this.sname = sname;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

}
View Code

4.AccountDao.java

package cn.tx.dao;
//賬戶介面
import cn.tx.entity.Account;

public interface AccountDao {
    
    /**
     * 新增賬戶
     * @param account
     * @return
     */
    public int createAccount(Account account);
    
    /**
     * 對賬戶的操作(錢買股,股收錢)
     * @param id 賬戶的編號
     * @param money 發費的錢財
     * @param isYesOrNo 是買股,還是收錢
     * @return 受影響的行數
     */
    public int updateBalance(int id,int money,boolean isYesOrNo);
    
    /**
     * 根據編號查詢餘額
     * @param id 編號
     * @return 餘額
     */
    public int selectOfBalance(int id);

}
View Code

5.StockDao.java

package cn.tx.dao;
//股票介面
import cn.tx.entity.Stock;

public interface StockDao {
    
    /**
     * 創建股票
     * @param stock 股票對象
     * @return 受影響行數
     */
    public int createStock(Stock stock);
    
    /**
     * 對股票的操作(錢買股,股收錢)
     * @param id 股票編號
     * @param num 變化的數量
     * @param isYesOrNo 是買股,還是收錢
     * @return 受影響的行數
     */
    public int updateCount(int id,int num,boolean isYesOrNo);

}
View Code

6.AccountDaoImpl.java

package cn.tx.dao.impl;
//實現了AccountDao介面的實現類
import org.springframework.jdbc.core.support.JdbcDaoSupport;

import cn.tx.dao.AccountDao;
import cn.tx.entity.Account;
//JdbcDaoSupport是JDBC數據訪問對象的超類
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {

    @Override
    public int createAccount(Account account) {
        String sql = "insert into account(aname,balance) values(?,?)";
        int count = this.getJdbcTemplate().update(sql, account.getAname(),
                account.getBalance());
        return count;
    }

    @Override
    public int updateBalance(int id, int money, boolean isBuyOrNot) {
        String sql = null;
        // isBuyOrNot 為真,金額減少
        if (isBuyOrNot) {
            sql = "update account set balance=balance-? where aid=?";
        } else {
            sql = "update account set balance=balance+? where aid=?";
        }
        int count = this.getJdbcTemplate().update(sql, money, id);
        return count;
    }

    @Override
    public int selectOfBalance(int id) {
        String sql = "select balance from account where aid=?";
        Double money = this.getJdbcTemplate().queryForObject(sql,
                new Object[] { id }, Double.class);
        return money.intValue();

    }

}
View Code

7.StockDaoImpl.java

package cn.tx.dao.impl;
//實現了StockDao介面的實現類
import org.springframework.jdbc.core.support.JdbcDaoSupport;

import cn.tx.dao.StockDao;
import cn.tx.entity.Stock;
//JdbcDaoSupport是JDBC數據訪問對象的超類
public class StockDaoImpl extends JdbcDaoSupport implements StockDao {

    @Override
    public int createStock(Stock stock) {
        String sql="insert into Stock(sname,count) values(?,?)";
        int count = this.getJdbcTemplate().update(sql,stock.getSname(),stock.getCount());
        return count;
    }

    @Override
    public int updateCount(int id, int num, boolean isYesOrNo) {
        String sql=null;
        if (isYesOrNo) {
            sql="update Stock set count+=? where sid=?";
        }else {
            sql="update Stock set count-=? where sid=?";
        }
        int count = this.getJdbcTemplate().update(sql,num,id);
        return count;
    }

}
View Code

8.AccountService.java

package cn.tx.service;
//用戶操作業務介面(用錢買股)
import cn.tx.entity.Account;
import cn.tx.entity.Stock;
import cn.tx.util.StockException;

public interface AccountService {
    /**
     * 新增賬戶
     * @param account
     * @return
     */
    public int createAccount(Account account);
    
    /**
     * 對賬戶的操作(錢買股,股收錢)
     * @param id 賬戶的編號
     * @param money 發費的錢財
     * @param isYesOrNo 是買股,還是收錢
     * @return 收影響的行數
     */
    public int updateBalance(int id,int money,boolean isYesOrNo);
    /**
     * 創建股票
     * @param stock 股票對象
     * @return 受影響行數
     */
    public int createStock(Stock stock);
    /**
     * 對股票的操作(錢買股,股收錢)
     * @param id 股票編號
     * @param num 變化的數量
     * @param isYesOrNo 是買股,還是收錢
     * @return 受影響的行數
     */
    public int updateCount(int id,int num,boolean isYesOrNo);
    /**
     * 購買股票的方法
     * @param aid 賬戶編號
     * @param money 發費的金額
     * @param sid 股票的編號
     * @param num 所買股數
     * @throws StockException 
     */
    public void buyStock(int aid,int money,int sid,int num) throws StockException;
    /**
     * 根據編號查詢餘額
     * @param id 編號
     * @return 餘額
     */
    public int selectOfBalance(int id);
}
View Code

9.AccountServiceImpl.java

package cn.tx.service.impl;
//用戶操作實現類

import cn.tx.dao.AccountDao;
import cn.tx.dao.StockDao;
import cn.tx.entity.Account;
import cn.tx.entity.Stock;
import cn.tx.service.AccountService;
import cn.tx.util.StockException;

public class AccountServiceImpl implements AccountService {

    //植入賬戶介面
    private AccountDao adao;
    //植入股票介面
    private StockDao sdao;
    @Override
    public int createAccount(Account account) {
        // TODO Auto-generated method stub
        return adao.createAccount(account);
    }

    @Override
    public int updateBalance(int id, int money, boolean isYesOrNo) {
        // TODO Auto-generated method stub
        return adao.updateBalance(id, money, isYesOrNo);
    }
    @Override
    public int createStock(Stock stock) {
        // TODO Auto-generated method stub
        return sdao.createStock(stock);
    }
    @Override
    public int updateCount(int id, int num, boolean isYesOrNo) {
        // TODO Auto-generated method stub
        return sdao.updateCount(id, num, isYesOrNo);
    }
    @Override
    public int selectOfBalance(int id) {
        // TODO Auto-generated method stub
        return adao.selectOfBalance(id);
    }

    //@Transactional(isolation=Isolation.DEFAULT,propagation=Propagation.REQUIRED,rollbackFor=StockException.class)
    public void buyStock(int aid,int money,int sid,int num) throws StockException{
         boolean isBuy=true;//預設為錢買股
        //更改賬戶信息
        adao.updateBalance(aid, money, isBuy);
        //模擬異常,如果沒錢或錢為負數
        if(adao.selectOfBalance(aid)<=0){
            throw new StockException("異常發生了。。。。。");
        }
        //更改股票信息
        sdao.updateCount(sid, num, isBuy);
    }

    public AccountDao getAdao() {
        return adao;
    }

    public void setAdao(AccountDao adao) {
        this.adao = adao;
    }

    public StockDao getSdao() {
        return sdao;
    }

    public void setSdao(StockDao sdao) {
        this.sdao = sdao;
    }    

}
View Code

10.StockException.java(製造的一個異常類)

package cn.tx.util;
/**
 * 構造一個檢查異常
 * @author zhangzong
 *
 */
public class StockException extends Exception {

    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    public StockException() {
        super();
    }

    public StockException(String message) {
        super(message);
    }
    
}
View Code

11.applicationContext.xml(Spring的配置文件)---幾種事務的實現都在其中

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
    <!-- 1.dao -->
    <bean id="accountDao" class="cn.tx.dao.impl.AccountDaoImpl">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <bean id="stockDao" class="cn.tx.dao.impl.StockDaoImpl">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- 2.service -->
    <bean id="accountService" class="cn.tx.service.impl.AccountServiceImpl">
        <property name="adao" ref="accountDao"></property>
        <property name="sdao" ref="stockDao"></property>
    </bean>

    <!-- 3.c3p0數據源 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}"></property>
        <property name="jdbcUrl" value="${jdbc.url}"></property>
        <property name="user" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>

    <!-- 4.註冊jdbc屬性 -->
    <context:property-placeholder location="classpath:jdbc.properties" />

    <!--******************************事務配置 ********************************* -->

    <!-- 註冊事務管理器 -->
    <bean id="mytxManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- *****************獲得事務代理************** -->

    <!--方法一: 事務自動代理 :此方法要結合註解使用,在AccountServiceImpl的buyStock上 -->    
    <!--<tx:annotation-driven transaction-manager="mytxManager"/> -->



    <!-- 方法二:TransactionProxyFactoryBean 生成事務代理 -->
    <!-- <bean id="stockServiceProxy"
        class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
        <property name="target" ref="accountService"></property>
        <property name="transactionManager" ref="mytxManager"></property>
        <property name="transactionAttributes">
            <props>  四種隔離級別 傳播屬性 required
                <prop key="create*">ISOLATION_DEFAULT,PROPAGATION_REQUIRED</prop>
                <prop key="buyStock">ISOLATION_DEFAULT,PROPAGATION_REQUIRED,-StockException
                </prop>
            </props>
        </property>
    </bean> -->



    <!-- 方法三 : aop**************** -->
    <tx:advice id="txAdvice" transaction-manager="mytxManager">
        <tx:attributes>
            <tx:method name="add*" isolation="DEFAULT" propagation="REQUIRED" />
            <tx:method name="buyStock" isolation="DEFAULT" propagation="REQUIRED"
                rollback-for="StockException" />
        </tx:attributes>
    </tx:advice>
    <!-- aop配置 -->
    <aop:config>
        <aop:pointcut expression="execution(* *..service.*.*(..))"
            id="stockPointcut" />
        <aop:advisor advice-ref="txAdvice" pointcut-ref="stockPointcut"/>
    </aop:config>

</beans>
View Code

12.jdbc.properties(連接資料庫的配置)

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc\:mysql\:///mybook
jdbc.username=root
jdbc.password=1234
View Code

13.log4j.properties

### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

### direct messages to file mylog.log ###
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=c\:mylog.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

### set log levels - for more verbose logging change 'info' to 'debug' ###

log4j.rootLogger=info, stdout
View Code

14.MyTest.java

package cn.tx.test;
//測試類
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import cn.tx.entity.Account;
import cn.tx.entity.Stock;
import cn.tx.service.AccountService;
import cn.tx.util.StockException;

public class MyTest {
    //測試餘額
    @Test
    public void selectOfbalance(){
        ApplicationContext ctx = new ClassPathXmlApplicationContext(
                "applicationContext.xml");
        AccountService service = (AccountService) ctx.getBean("accountService");
        int balance = service.selectOfBalance(1);
        System.out.println(balance);
    }    
    @Test
    // 購買股票測試
    public void buyStockTest() throws StockException {

        ApplicationContext ctx = new ClassPathXmlApplicationContext(
                "applicationContext.xml");
        AccountService service = (AccountService) ctx.getBean("accountService");
        // 花200 塊 買 2股
        service.buyStock(1, 200, 1, 2);
    }

    @Test
    // 開戶測試
    public void addData() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext(
                "applicationContext.xml");
        AccountService service = (AccountService) ctx.getBean("accountService");
        // 銀行卡賬戶
        Account account = new Account();
        account.setAname("傻瞿亮");
        account.setBalance(1000);

        service.createAccount(account);

        Stock stock = new Stock();
        stock.setSname("腦殘教育");
        stock.setCount(5);

        service.createStock(stock);
    }

}
View Code

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、 hibernate是什麼 (一)hibernate 是一個orm框架,orm (object relation mapping) 對象關係映射框架 o object -> 業務層(只對對象操作) r relation-> 關係資料庫 m mapping 對象關係映射文件 Hibernate有六 ...
  • 一、插入排序 1 #-*- coding:utf-8 -*- 2 ''' 3 描述 4 插入排序的基本操作就是將一個數據插入到已經排好序的有序數據中,從而得到一個新的、個數加一的有序數據,演算法適用於少量數據的排序,時間複雜度為O(n^2)。 5 是穩定的排序方法。插入演算法把要排序的數組分成兩部分:第 ...
  • 轉載於:http://www.cnblogs.com/767355675hutaishi/p/3873770.html 題目大意:眾所周知冒泡排序演算法多數情況下不能只掃描一遍就結束排序,而是要掃描好幾遍。現在你的任務是求1~N的排列中,需要掃描K遍才能排好序的數列的個數模20100713。註意,不同 ...
  • 大家好啊,今天為大家帶來的是自己實現的用C++編寫的簡單進位轉換器,用於10進位數和8進位數,16進位數,2進位數的相互轉換. 首先,說明一下什麼是進位.n進位就是一種用來表示數值的方法,n進位,顧名思義,逢n進1.我們日常生活中使用的基本都是10進位數,逢10進1;現代電腦處理器所能處理的只能是 ...
  • 本章重點介紹java.lang.reflect包下的介面和類 當程式使用某個類時,如果該類還沒有被載入到記憶體中,那麼系統會通過載入,連接,初始化三個步驟來對該類進行初始化. 類的載入時指將類的class文件讀入記憶體,併為之創建一個java.lang.class對象,也就是說,當程式中使用任何類時,系 ...
  • 一、什麼是指針? 指針在百度的解釋:是編程語言中的一個對象,利用地址,它的值直接指向(points to)存在電腦存儲器中另一個地方的值。 也就是說,指針是用於指向某一記憶體單元。 簡而化之,指針便是地址。 二、聲明指針 1.方法:數據類型 *名稱 初始化 2.例如: 3.註意:指針聲明完一定要初始化 ...
  • JAVA Collections工具類sort()排序方法,對Comparable介面 Comparator介面簡述 ...
  • JAVA WEB 作用域 1、page屬性範圍(pageContext) a、在一個頁面設置的屬性,跳轉到其他頁面就無法訪問了(包括重定向和)。 示例:pageScopeDemo01.jsp <%@ page import="java.util.*" language="java" contentT ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...