JFinal 開箱評測,這次我是認真的

2{icon} {views}

引言

昨天在看服務器容器的時候意外的遇到了 JFinal ,之前我對 JFinal 的印象僅停留在這是一款國人開發的集成 Spring 全家桶的一個框架。

後來我查了一下,好像事情並沒有這麼簡單。

JFinal 連續好多年獲得 OSChina 最佳開源項目,並不是我之前理解的集成 Spring 全家桶,而是自己開發了一套 WEB + ORM + AOP + Template Engine 框架,大寫的牛逼!

先看下官方倉庫對自己的介紹:

這介紹寫的,簡直是深的我心 為您節約更多時間,去陪戀人、家人和朋友 :)

做碼農這一行,誰不想早點把活做完,能正常下班,而不是天天 996 的福報。

介於這麼優秀的框架自己從來沒了解過,這絕對是一個 Java 老司機梭不能容忍的。

那麼今天我就做一次框架的開箱評測,看看到底能不能做到宣傳語上說的 節約更多的時間 ,到底好不好用。

這可能是業界第一個做框架評測的文章的吧,還是先低調一把:本人能力有限,以下內容如有不對的地方還請各位海涵。

接下來的目的是簡單做一個 Demo ,完成最簡單的 CRUD 操作來體驗下 JFinal 。

構建項目

我懷揣着崇敬的心態打開了 JFinal 的官方文檔。

  • 文檔地址:https://jfinal.com/doc

在官網還看到了示例項目,這個必須 down 下來看一眼,這時一件讓我完全沒想到的事兒發生了,竟然還要我註冊登錄,天啊,這都 2020 年了,下載一個 demo 竟然還要登錄,我是瞎了么。

好吧好吧,你是老大你說了算,誰讓我饞你身子呢。

官方對項目的構建演示是使用的 eclipse ,好吧,你又贏了,我用 idea 照着你的步驟來。

過程其實很簡單,就是創建了一個 maven 項目,然後把依賴引入進去,核心依賴就下面這兩個:

<dependency>
    <groupId>com.jfinal</groupId>
    <artifactId>jfinal-undertow</artifactId>
    <version>2.1</version>
</dependency>

<dependency>
    <groupId>com.jfinal</groupId>
    <artifactId>jfinal</artifactId>
    <version>4.9</version>
</dependency>

全量代碼我就不貼了(畢竟太長),代碼都會提交到代碼倉庫,有興趣的同學可以訪問代碼倉庫獲取。

其實用慣了 SpringBoot 的創建項目的過程,已經非常不習慣用這種方式來構建項目了,排除 IDEA 對 SpringBoot 項目構建的支持,直接訪問 https://start.spring.io/ ,直接勾勾選選把自己需要的依賴選上直接下載導入 IDE 就好了。

不過這個沒啥好說的, SpringBoot 畢竟後面是有一個大團隊在支持的,而 JFinal 貌似開發者只有一個人,能做成這樣基本上也可以說是在開源領域國人的驕傲了。

項目啟動

項目依賴搞好了,接下來第一件事兒就是要想辦法啟動項目了,在 JFinal 中,有一個全局配置類,而啟動項目的代碼也在這裏。

這個類需要繼承 JFinalConfig ,而繼承這個類需要實現下面 6 個抽象方法:

public class DemoConfig extends JFinalConfig {
    public void configConstant(Constants me) {}
    public void configRoute(Routes me) {}
    public void configEngine(Engine me) {}
    public void configPlugin(Plugins me) {}
    public void configInterceptor(Interceptors me) {}
    public void configHandler(Handlers me) {}
}

configConstant

這個方法主要是用來配置 JFinal 的一些常量值,比如:設置 aop 代理使用 cglib,設置日誌使用 slf4j 日誌系統,默認編碼格式為 UTF-8 等等。

下面是我選用的官方文檔給出來的一些配置:

public void configConstant(Constants me) {
    // 配置開發模式,true 值為開發模式
    me.setDevMode(true);
    // 配置 aop 代理使用 cglib,否則將使用 jfinal 默認的動態編譯代理方案
    me.setToCglibProxyFactory();
    // 配置依賴注入
    me.setInjectDependency(true);
    // 配置依賴注入時,是否對被注入類的超類進行注入
    me.setInjectSuperClass(false);
    // 配置為 slf4j 日誌系統,否則默認將使用 log4j
    // 還可以通過 me.setLogFactory(...) 配置為自行擴展的日誌系統實現類
    me.setToSlf4jLogFactory();
    // 設置 Json 轉換工廠實現類,更多說明見第 12 章
    me.setJsonFactory(new MixedJsonFactory());
    // 配置視圖類型,默認使用 jfinal enjoy 模板引擎
    me.setViewType(ViewType.JFINAL_TEMPLATE);
    // 配置 404、500 頁面
    me.setError404View("/common/404.html");
    me.setError500View("/common/500.html");
    // 配置 encoding,默認為 UTF8
    me.setEncoding("UTF8");
    // 配置 json 轉換 Date 類型時使用的 data parttern
    me.setJsonDatePattern("yyyy-MM-dd HH:mm");
    // 配置是否拒絕訪問 JSP,是指直接訪問 .jsp 文件,與 renderJsp(xxx.jsp) 無關
    me.setDenyAccessJsp(true);
    // 配置上傳文件最大數據量,默認 10M
    me.setMaxPostSize(10 * 1024 * 1024);
    // 配置 urlPara 參數分隔字符,默認為 "-"
    me.setUrlParaSeparator("-");
}

這裡是一些項目的通用配置信息,在 SpringBoot 中這種配置信息一般是寫在 yaml 或者 property 配置文件裏面,不過這裏這麼配置我個人感覺無所謂,只是稍微有點不適應。

configRoute

這個方法是配置訪問路由信息,我的示例是這麼寫的:

public void configRoute(Routes me) {
    me.add("/user", UserController.class);
}

看到這裏我想到一個問題,每次我新增一個 Controller 都要來這裏配置下路由信息的話,這也太傻了。

如果是小型項目還好,路由信息不回很多,有個十幾條幾十條足夠用了,如果是一些中大型項目,上百或者上千個 Controller ,我要是都配置在這裏,能找得到么,這裏打個問號。

這裡在實際應用中存在一個致命的問題,在發布版本的時候,做過項目的同學都知道,最少四套環境:開發,測試,UAT,生產。每個環境的代碼功能版本都不一樣,難道我發布之前需要手動人工修改這裏么,這怎麼可能管理的過來。

configEngine

這個是用來配置 Template Engine ,也就是頁面模版的,介於我只想單純的簡單的寫兩個 Restful 接口,這裏我就不做配置了,下面是官方提供的示例:

public void configEngine(Engine me) {
    me.addSharedFunction("/view/common/layout.html");
    me.addSharedFunction("/view/common/paginate.html");
    me.addSharedFunction("/view/admin/common/layout.html");
}

configPlugin

這裡是用來配置 JFinal 的 Plugin ,也就是一些插件信息的,我的代碼如下:

public void configPlugin(Plugins me) {
    DruidPlugin dp = new DruidPlugin(p.get("jdbcUrl"), p.get("user"), p.get("password").trim());
    me.add(dp);

    ActiveRecordPlugin arp = new ActiveRecordPlugin(dp);
    arp.addMapping("user", User.class);
    me.add(arp);
}

我的配置很簡單,前面配置了 Druid 的數據庫連接池插件,後面配置了 ActiveRecord 數據庫訪問插件。

讓我覺得有點傻的地方是我如果要增加 ActiveRecord 數據庫訪問的映射關係,需要手動在這裏增加代碼,比如 arp.addMapping("aaa", Aaa.class); ,還是回到上面的問題,不同的環境之間發布系統需要手動修改這裏,項目不大還能人工管理,項目大的話這裡會成為噩夢。

configInterceptor

這個方法是用來配置全局攔截器的,全局攔截器分為兩類:控制層、業務層,我的示例代碼是這樣的:

public void configInterceptor(Interceptors me) {
    me.add(new AuthInterceptor());
    me.addGlobalActionInterceptor(new ActionInterceptor());
    me.addGlobalServiceInterceptor(new ServiceInterceptor());
}

這裏 me.add(...)me.addGlobalActionInterceptor(...) 兩個方法是完全等價的,都是配置攔截所有 Controller 中 action 方法的攔截器。而 me.addGlobalServiceInterceptor(...) 配置的攔截器將攔截業務層所有 public 方法。

攔截器沒什麼好說的,這麼配置感覺和 SpringBoot 裏面完全一致。

configHandler

這個方法用來配置 JFinal 的 Handler , Handler 可以接管所有 Web 請求,並對應用擁有完全的控制權。

這個方法是一個高階的擴展方法,我只是想寫一個簡單的 CRUD 操作,完全用不着,這裏還是摘抄一個官方的 Demo :

public void configHandler(Handlers me) {
    me.add(new ResourceHandler());
}

配置文件

我看官方的配置文件,結尾竟然是 txt ,這讓我第一眼就開始懷疑人生,為啥配置文件要選用 txt 格式的,而裏面的配置格式,卻和 property 文件一模一樣,難道是為了彰顯個性么,這讓我產生了深深的懷疑。

在前面的那個 DemoConfig 配置類中,是可以通過 Prop 來直接獲取配置文件的內容:

static Prop p;

/**
    * PropKit.useFirstFound(...) 使用參數中從左到右最先被找到的配置文件
    * 從左到右依次去找配置,找到則立即加載並立即返回,後續配置將被忽略
    */
static void loadConfig() {
    if (p == null) {
        p = PropKit.useFirstFound("demo-config-pro.txt", "demo-config-dev.txt");
    }
}

在配置文件這裏雖然引入了環境配置的概念,但是還是略顯粗糙,很多需要配置的內容都沒法配置,而這裡能配置的暫時看下來只有數據庫、緩存服務等有限的內容。

Model 配置

說實話,剛開始看到 Model 這一部分的使用的時候驚呆我了,完全沒想到這麼簡單:

public class User extends Model<User> {

}

就這樣,就可以了,裏面什麼都不用寫,完全顛覆了我之前的認知,難道這個框架會動態的去數據庫找字段么,倒不是智能不智能的問題,如果兩個人一起開發同一個項目,我光看代碼都不知道這個 Model 裏面的屬性有啥,必須要對着數據庫一起看,這個會讓人崩潰的。

後來事實證明我年輕了,代碼還是需要的,只是不用自己寫了, JFinal 提供了一個代碼生成器,相關代碼根據數據庫表自動生成的,生成的代碼就不看了,簡單看下這個自動生成器的代碼:

public static void main(String[] args) {
    // base model 所使用的包名
    String baseModelPackageName = "com.geekdigging.demo.model.base";
    // base model 文件保存路徑
    String baseModelOutputDir = PathKit.getWebRootPath() + "/src/main/java/com/geekdigging/demo/model/base";
    // model 所使用的包名 (MappingKit 默認使用的包名)
    String modelPackageName = "com.geekdigging.demo.model";
    // model 文件保存路徑 (MappingKit 與 DataDictionary 文件默認保存路徑)
    String modelOutputDir = baseModelOutputDir + "/..";
    // 創建生成器
    Generator generator = new Generator(getDataSource(), baseModelPackageName, baseModelOutputDir, modelPackageName, modelOutputDir);
    // 配置是否生成備註
    generator.setGenerateRemarks(true);
    // 設置數據庫方言
    generator.setDialect(new MysqlDialect());
    // 設置是否生成鏈式 setter 方法
    generator.setGenerateChainSetter(false);
    // 添加不需要生成的表名
    generator.addExcludedTable("adv", "data", "rate", "douban2019");
    // 設置是否在 Model 中生成 dao 對象
    generator.setGenerateDaoInModel(false);
    // 設置是否生成字典文件
    generator.setGenerateDataDictionary(false);
    // 設置需要被移除的表名前綴用於生成modelName。例如表名 "osc_user",移除前綴 "osc_"後生成的model名為 "User"而非 OscUser
    generator.setRemovedTableNamePrefixes("t_");
    // 生成
    generator.generate();
}

看到這段代碼我心都涼了,居然是整個數據庫做掃描的,還好是用的 MySQL ,開源免費的,如果是 Oracle ,一個項目就需要一台數據庫或者是一個數據庫集群,這個太有錢了。

當然,這段代碼也提供了排除不需要生成的表名 addExcludedTable() 方法,其實沒什麼使用價值,一個 Oracle 集群上可能有 N 多個項目一起跑,上面的表成百上千張,一個小項目如果只用到十來張表,addExcludedTable() 這個方法光把表名 copy 進去估計一两天都搞不完。

數據庫 CRUD 操作

JFinal 把數據的 CRUD 操作集成在了 Model 上,這種做法如何我不做評價,看下我寫的一個樣例 Service 類:

public class UserService {
    private static final User dao = new User().dao();
    // 分頁查詢
    public Page<User> userPage() {
        return dao.paginate(1, 10, "select *", "from user where age > ?", 18);
    }
    public User findById(String id) {
        System.out.println(">>>>>>>>>>>>>>>>UserService.findById()>>>>>>>>>>>>>>>>>>>>>>>>>");
        return dao.findById(id);
    }
    public void save(User user) {
        System.out.println(">>>>>>>>>>>>>>>>UserService.save()>>>>>>>>>>>>>>>>>>>>>>>>>");
        user.save();
    }
    public void update(User user) {
        System.out.println(">>>>>>>>>>>>>>>>UserService.update()>>>>>>>>>>>>>>>>>>>>>>>>>");
        user.update();
    }
    public void deleteById(String id) {
        System.out.println(">>>>>>>>>>>>>>>>UserService.deleteById()>>>>>>>>>>>>>>>>>>>>>>>>>");
        dao.deleteById(id);
    }
}

這裏的分頁查詢看的我有點懵逼,為啥一句 SQL 非要拆成兩半,總感覺後面那半 from user where age > ? 是 Hibernate 的 HQL ,難道這兩者之間有啥不可告人的秘密么。

其他的普通 CRUD 操作寫法倒是蠻正常的,無任何槽點。

Controller

先上代碼吧,就着代碼嘮:

public class UserController extends Controller {

    @Inject
    UserService service;

    public void findById() {
        renderJson(service.findById("1"));
    }

    public void save() {
        User user = new User();
        user.set("id", "2");
        user.set("create_date", new Date());
        user.set("name", "小紅");
        user.set("age", 24);
        service.save(user);
        renderNull();
    }

    public void update() {
        User user = new User();
        user.set("id", "2");
        user.set("create_date", new Date());
        user.set("name", "小紅");
        user.set("age", 19);
        service.update(user);
        renderNull();
    }

    public void deleteById() {
        service.deleteById(getPara("id"));
        renderNull();
    }
}

首先 Service 使用 @Inject 進行注入,這個沒啥好說的,和 Spring 裏面的 @Autowaire 一樣。

這個類裏面所有實際方法的返回類型都是 void 空類型,返回的內容全靠 render() 進行控制,可以返回 json 也可以返回頁面視圖,也罷,只是稍微有點不適應,這個沒啥問題。

但是接下來這個問題就讓我有點方了,感覺都不是問題,成了缺陷了,獲取參數只提供了兩種方法:

一種是 getPara() 系列方法,這種方法只能獲取到表單提交的數據,基本上類似於 Spring 中的 request.getParameter()

另一種是 getModel / getBean ,首先,這兩個方法接受通過表單提交過來的參數,其次是一定要轉成一個 Model 類。

我就想知道一件事情,如果一個請求的類型不是表單提交,而是 application/json ,怎麼去接受參數,我把文檔翻了好幾遍,都沒找到我想要的 request 對象。

可能只是我沒找到,一個成熟的框架,不應該不支持這種常見的 application/json 的數據提交方式,這不可能的。

還有就是,getModel / getBean 這種方式一定要直接轉化成 Model 類,有時候並不是一件好事,如果當前這個接口的入參格式比較複雜,這種 Model 構造起來還是有一定難度的,尤其是有時候只需要獲取其中的少量數據做解析預處理,完全沒必要解析整個請求數據。

小結

通過一個簡單的 CRUD 操作看下來, JFinal 整體上完成了一個 WEB + ORM 框架該有的東西,只是有些地方做的不是那麼好的,當然,這是和 SpringBoot 做比較。

如果是拿來做一些小東西感覺還是可以值得嘗試的,如果是要做一些企業級的應用,就顯得有些捉襟見肘了。

不過這個項目出來的年代是比較早了,從 2012 年至今已經走過了 8 年的時間了,如果是和當年的 SpringMCV + Spring + ORM 這種框架做比較,我覺得我選的話肯定是會選 JFinal 的。

如果是和現在的 SpringBoot 做比較,我覺得我還是傾向於選擇 SpringBoot ,一個是因為熟悉,另一個是因為 JFinal 很多地方,為了方便開發者使用,把相當多的代碼都封裝起來了,這種做法不能說不好,對於初學者而言肯定是好的,文檔簡單看看,基本上半天到一天就能開始上手幹活的,但是對於一些老司機而言,這樣做會讓人覺得束手束腳的,這也不能做那也不能做。

我自己的示例代碼和官方的 Demo 我一起提交到代碼倉庫了,有需要的同學可以回復 「JFinal」 進行獲取。

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

【其他文章推薦】

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

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

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

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

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

東京計畫奧運前 6,000 輛燃料電池車上路

3{icon} {views}

  東京籌備 2020 年奧運會,擬砸 452 億日圓(台幣 122 億元)補助燃料電池車(FCV)和興建加氫站,目前正和豐田(Toyota)、本田(Honda)汽車協商合作,目標在東京奧運前,至少讓 6,000 輛燃料電池車上路趴趴走。   東京都廳能源部門規劃團隊主管 Makoto Fujimoto 表示,將在東京打造 35 座加氫站,正和豐田汽車、本田汽車協商,希望 2020 年前能讓 6,000 輛這種氫燃料電池車上路。Fujimoto 說,購買燃料電池車的日本民眾將獲得 300 萬日圓補助,其中 100 萬日圓由東京都廳補貼,200 萬日圓由中央政府補貼;到 2025 年時,盼在東京的氫燃料電池車將達 10 萬輛小客車、100 輛公車,且具有 80 個加氫站的目標。   此外,Fujimoto 指出,東京都廳將補助興建加氫站的費用 80%,業者負擔費用最高 1 億日圓,相當於一般蓋加油站的費用;若小企業想蓋加氫站,政府可能支付全部興建費用。   日本對燃料電池車的補助,超過中國、美國和歐洲提供購買電動車的補貼誘因,也較現行日本提供三菱汽車純電動車 i-MiEV 的補助 95 萬日圓,多出 2 倍以上。     (Source:)

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

【其他文章推薦】

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

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

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

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

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

數據結構與算法(七):迷宮回溯和八皇后問題

4{icon} {views}

一、迷宮回溯問題

1.問題

一個7*8的數組模擬迷宮,障礙用1表示,通路使用0表示,給定起點(1,1)和終點(6,5),要求給出起點到終點的通路

2.解題思路

  1. 首先,我們需要給程序一個尋向的基本策略,我們先假定尋向順序為“下-右-上-左”,也就是說從起點出發,先往下走,往下走不通就往右…..以此類推
  2. 然後我們需要給走過的路一個標記,暫記為2
  3. 而當從一個方向走到一個只能原路返回的死衚衕時,就給這段路標記為3
  4. 當抵達終點坐標(6,5)時程序結束

3.代碼實現

3.1生成地圖

/**
 * 創建一個二維數組,用於模擬8*7迷宮
 * 使用1表示不可通過的實心方塊,0表示可通過磚塊
 * (6,5)為默認終點,(1,1)為默認起點
 * @return
 */
public static int[][] getMap(){
    int[][] map = new int[8][7];
    //上下全置為1
    for(int i = 0;i <7 ;i++){
        map[0][i] = 1;
        map[7][i] = 1;
    }
    //左右全置為1
    for(int i = 0;i < 8;i++){
        map[i][0] = 1;
        map[i][6] = 1;
    }
    //設置擋板
    map[3][1] = 1;
    map[3][2] = 1;

    //輸出地圖
    System.out.println("地圖的初始情況:");
    showMap(map);

    return map;
}

/**
 * 展示地圖
 * @param map
 */
public static void showMap(int[][] map) {
    for(int i = 0;i < 8;i++){
        for(int j = 0;j < 7;j++){
            System.out.print(map[i][j] + " ");
        }
        System.out.println();
    }
}

3.2 尋路邏輯的實現

對於這個尋路程序,我們可以看見,往四個方向走的過程實際上除了方向外動作上是一樣的;而具體分析同一個方向,每走過一個坐標的動作也是一樣的,我們對流程進行分析:

  1. 出發,先往下走,判斷下一格有沒有障礙(int[x][y]==1
  2. 如果沒有障礙,就繼續往下走,然後重複步驟1到碰到障礙為止
  3. 如果有障礙,就按“下-右-上-左”的順序,換個方向,然後重複步驟1到碰到障礙為止
  4. 如果找到了(6,5)就結束

表現為代碼實際上就是一個遞歸的過程:

  • 找路是方法體
  • 找到了(6,5)或者死衚衕是終止條件
/**
 * 給定起始點,根據地圖找路
 * 使用2表示可以走通的路,使用3表示走過但是不通的路
 * @param map 地圖二維數組
 * @param x 起始點橫坐標
 * @param y 起始點縱坐標
 * @return
 */
public static boolean findWay(int[][] map, int x, int y) {
    //如果走到了終點就終止
    if (map[6][5] == 2){
        return true;
    }else {
        //只有為0的路才能通過
        if (map[y][x] == 0) {
            //如果該點可以走通就打上標記
            map[y][x] = 2;
            if (findWay(map, x, y + 1)) {
                //向下遞歸
                return true;
            } else if (findWay(map, x + 1, y)) {
                //向右遞歸
                return true;
            } else if (findWay(map, x, y - 1)) {
                //向上遞歸
                return true;
            } else if (findWay(map, x - 1, y)) {
                //向左遞歸
                return true;
            } else {
                //都走不通說明是死衚衕
                map[y][x] = 3;
                return false;
            }
        }else {
            //不為0說明要麼是死路要麼是障礙
            return false;
        }
    }
}

3.3 運行結果

findWay()方法中的終止條件從map[6][5] == 2換成其他坐標即可更換終點位置,

棋盤大小和障礙物位置不影響findWay()方法尋路。

二、八皇后問題

1.問題

皇后問題,一個古老而著名的問題,是回溯算法的典型案例。該問題由國際西洋棋棋手馬克斯·貝瑟爾於 1848 年提出:

在 8×8 格的國際象棋上擺放八個皇后,使其不能互相攻擊,即任意兩個皇后都不能處於同一行、同一列或同一斜線上,求有多少種擺法?

2.解題思路

  1. 首先,我們先使用一個長度為8數組來表示八皇后的擺放位置,數組下標+1即表示棋盤的第幾行數組下標對應的存放的数字+1即為棋盤的第幾列。舉個例子:

    arr = {0,2,3,8,4,6,2,7}

    其中,元素0下標為0,即表示第一行第一列;元素2下標為1,即表示第二行第三列……以此類推。

  2. 任意假設任意坐標分標為(x1,y1),(x2,y2),也就是用數組表示為arr[x1]=y1,arr[x2]=y2的兩個皇后不允許在同一列,我們可以理解為:

    arr[x1] != arr[x2];

    而任意坐標的皇后不允許在同一斜線,即(x2-x1)=(y2-y1),也就是斜率不應當相同,我們可以理解為:

    Math.abs(x2-x1) != Math.abs(arr[x2]-arr[x1])

    (注:Math.abs()為求絕對值方法)

3.代碼實現

3.1 檢查擺放位置的代碼實現

在前面明確了如何用數組表示位置,以及如何檢查皇后是否允許擺放后,我們有如下代碼:

//表示皇后位置的數組
int[] arr = new int[8];

/**
 * 檢查第n個皇后是否與前面擺放的皇后衝突
 * @param n
 * @return
 */
public boolean check(int n) {
    //檢查第n層之前的皇后位置
    for (int i = 0; i < n; i++) {
        // arr[i] == arr[n] 檢查是否同一列
        // Math.abs(n - i) == Math.abs(arr[n] - arr[i]) 檢查是否同一斜線
        if (arr[i] == arr[n] ||
            Math.abs(n - i) == Math.abs(arr[n] - arr[i])) {
            return false;
        }
    }
    return true;
}

3.2 完整代碼

接着我們需要考慮如何使用遞歸方法來做到以下效果:

使用一個方法遍歷第n行的每一列,檢查每一列是否可以放置皇后:

  1. 如果可以放置皇后,將位置出入arr[n]中,然後遞歸調用自己,傳入n+1開始遍歷下一行…..以此類推
  2. 如果不可以放置皇后,就跳過該列檢查下一列,如果可以就重複步驟1
  3. 若n行中全部位置都不合適,則結束本層返回上一層n-1層,重複步驟1
  4. 如果最後n=8,即八個皇后全部放置完畢,記一次完成擺放,然後結束遞歸返回第一層,繼續檢查第一層的下一列

最終代碼實現結果如下:

/**
 * @Author:黃成興
 * @Date:2020-06-26 20:53
 * @Description:八皇后問題
 */
public class EightQueens {

    public static void main(String[] args) {
        EightQueens eightQueens = new EightQueens();
        eightQueens.set(0);
        System.out.println("共有擺法:" + eightQueens.count);
    }

    //記錄八皇後有幾種擺法
    int count = 0;

    //表示皇后位置的數組
    int[] arr = new int[8];

    /**
     * 擺放皇后
     * @param n 第幾個皇后
     */
    private void set(int n) {
        //如果放置好了第8個皇后
        if (n == 8){
            show();
            //記錄一種擺放方式
            count++;
            //回到第一層繼續遞歸
            return;
        }

        //遍歷第n行的每一列
        for (int i = 0; i < 8; i++) {
            //將該皇後放置在第n行第i列
            arr[n] = i;
            //檢查放置位置是否合適
            if (check(n)){
                //如果位置合適,就遞歸找下一個(n+1)皇后的擺放位置
                set(n + 1);
            }
            //如果位置不合適,就跳過這一列檢查下一列
        }
    }

    /**
     * 檢查第n個皇后是否與前面擺放的皇后衝突
     * @param n
     * @return
     */
    public boolean check(int n) {
        //檢查第n層之前的皇后位置
        for (int i = 0; i < n; i++) {
            // arr[i] == arr[n] 檢查是否同一列
            // Math.abs(n - i) == Math.abs(arr[n] - arr[i]) 檢查是否同一斜線
            if (arr[i] == arr[n] ||
                Math.abs(n - i) == Math.abs(arr[n] - arr[i])) {
                return false;
            }
        }
        return true;
    }
    

    /**
     * 展示某一擺法中八皇后的擺放位置
     */
    public void show() {
        for (int i : arr) {
            System.out.print(i + " ");
        }
        System.out.println();
    }
}

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

【其他文章推薦】

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

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

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

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

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

兩會進行時:關於新能源汽車的提案,都有什麼?

1{icon} {views}

  3月3日,來自汽車行業的人大代表和政協委員再次聚首北京,為當今的汽車產業建言獻策,而新能源汽車仍將是2015年全國“兩會”汽車界熱議的焦點,政協委員和人大代表等也紛紛為新能源汽車的發展建言獻策。  
柳崇禧:加大基礎設施補貼力度 推動新能源車發展   全國政協委員、中國汽車工業工程公司副總經理柳崇禧帶來了《關於加大充換電基礎設施建設,推動新能源汽車市場應用的提案》。   針對電動汽車的發展受動力電池技術制約,充電設施網路不完善、充電不方便等問題,柳崇禧委員建議,加大充換電基礎設施建設,包括支援國家電網、南方電網等傳統電力供應央企加大充換電基礎設施建設,鼓勵中石化、中石油等傳統汽車能源供應央企參與充換電基礎設施建設。同時,還應鼓勵民營資本進入基礎設施建設,制定法規保障其權益,加大充換電基礎設施建設財政補貼力度,減免稅收。  
李書福:電動汽車不一定環保 呼籲放開汽車限購   全國政協委員、吉利集團董事長李書福共帶來”關於推動計程車體制改革”和”加強資訊化技術下經營交易稅收管理”兩個提案,就推進和深化計程車體制改革提出建議:關鍵要打破計程車牌照的壟斷式管理;建議根據不同城市規模,設立相應計程車體制改革試點城市,對各項改革措施,進行分類指導、科學評估。   另外李書福在接受採訪時呼籲放開汽車限購。他認為,目前某些地方實行的限購政策只是權宜之計。李書福表示,吉利今年會發力新能源汽車。但他認為電動汽車也不一定有利於環境,因為發電要耗費大量的非清潔能源。  
景柱:五大因素致使新能源汽車推廣艱難   全國人大代表、海馬集團董事長景柱認為,電動汽車商業化推廣存在五大癥結:一是以電池為靈魂的四大技術障礙,即功率密度、轉化效率、充電時間、系統安全。二是難以克服的兩大社會障礙:使用習慣、使用環境。三是非規模生產造成的高成本、高故障。四是以高成本為前提的兩大市場因素:特定環境和特定用戶。五是政策導向的四個誤區。   景柱認為,目前我國發展新能源汽車的最佳路徑,是汽車輕量化、節能減排和新能源技術齊頭並進。首先經過深混和插混技術,最終過渡到電動汽車或燃料電池汽車。  
鐘發平:建議國家大力發展混合動力汽車減少汽車污染   全國人大代表、科力遠董事長鐘發平建議國家通過大力發展混合動力汽車來減少汽車尾氣污染,解決當下的霧霾問題。鐘發平認為,相較動力鋰電池,鎳氫動力電池雖然能量密度不如,卻足夠穩定和安全。  
劉義發:小型電動車應成為國民車 實行分類管理   全國人大代表、山東時風集團董事長劉義發建議,綜合小型電動車的種種優勢,順應市場趨勢,將小型電動車作為“國民車”,實行分類管理,引導和規範行業科學發展。  
張天任:建議對微型電動車適當放開政策   全國人大代表、天能集團董事長張天任:建議國家對微型電動車適當放開政策,實行“產品准入從嚴,企業准入從寬”的管理原則,鼓勵和支援多種電池路線、不同電池搭配的新能源電動汽車相容發展,並針對微型電動車建立起科學的技術標準體系。他建議國家將微型電動車納入我國道路交通規劃之中,列入法定交通工具,允許符合標準的微型電動車上牌上路。  
王麒:推廣新能源汽車是治霾重要途徑之一   全國人大代表、啟陽(成都)投資管理有限公司董事長王麒認為推廣新能源汽車是汽車行業對治理霧霾做出貢獻的重要途徑之一,新能源汽車的使用推廣環節應當與生產研發的進度相匹配,要加快新能源汽車的推廣力度,完善新能源汽車相關配套設施建設。  
民進中央:可在有條件的省份率先開展低速電動車試點   民進中央提案呼籲,工信部、發改委和公安部應儘快出臺行業准入辦法,對具有汽車生產資質、產品通過檢測的低速電動車企業實行准入管理。建議將低速電動汽車和燃油汽車、新能源汽車進行分類管理。針對低速電動汽車制定一套嚴格的管理辦法,包括車輛生產、檢測、保險、上路管理等多個環節。在道路資源和安全可控範圍內,將低速電動車納入現行的道路交通管理體系,除高速公路和限行道路外,所有鄉村公路和城鎮的部分道路應向低速電動汽車開放。車輛可採用專用明顯標誌的車牌便於識別,駕照可考慮使用C4/C5駕照。   同時,民進中央提案建議,在有條件的省份率先開展低速電動車試點工作,探索一種以市場需求為拉動,政府不投資和補貼,技術路線由低到高、先易後難、自主研發,在發展中規範、在規範中提高的發展模式。  
全國工商聯:建議儘快廢除計程車專營制度   全國工商聯向大會遞交提案,建議儘快廢除計程車專營制度,讓計程車行業回歸市場經濟的軌道。政府應明確職責定位,徹底斬斷與行業之間的利益關聯,從行業中完全退場。此外,計程車市場應對社會開放,破除“數量管制”、建立“品質管制”體系。  
丁金宏:國家應出臺統一的城市限車標準   全國政協委員、人口學家丁金宏教授此次帶來了“限制大城市汽車消費”的提案,建議國家出臺統一標準,“城市規模達到多大,汽車擁有量達到多高時,就採取不同的措施,包括限購、重點區域限行、擁擠收費等,綜合採取多項措施,而不是多個城市單獨去做。”他說,限車如果僅僅從消費者權利和汽車發展利益出發,不能保證城市健康發展。  
施傑:應嚴管佔用應急車道 加大處罰   全國政協委員、國浩律師(成都)事務所律師施傑在提案中建議,對違反交規的駕駛行為,除了加大處罰力度以外,還應搭建一個公共監督平臺,讓公眾在履行自己監督義務時有暢道的管道。   施傑認為,對佔用應急車道行為除了加大罰款、扣分力度以外,同時還可實行“責任形式多元化”,用法律的“籠子”把佔用高速應急車道的行為給牢牢關住。 他認為,相關交通管理部門可以通過公眾微信帳號、微博平臺,收集公眾提供的照片、錄影等證據,一經核實便納入交管系統。對違規車輛進行處罰。同時對舉報者給予一定獎勵,當然,若發生虛報謊報情形也會加以懲處。讓公眾成為“移動電子眼”,以便做到無縫監督。  
宗慶後:建議儘快取消汽車限購限行   全國人大代表、娃哈哈集團董事長宗慶後:建議取消限購限行,加強對尾氣排放的檢測監管和交通基礎設施改善,從而減輕汽車帶來的環境壓力和交通壓力。   在宗慶後看來,現在限行限購都存在不合理的地方。“有些地方想用限購限行解決堵車問題、空氣污染問題,但限了購也限了行,路依舊在堵,空氣依舊污染,只是給老百姓造成了一些困惑而已。”他指出,目前汽車尾氣排放存在三個問題,一個是汽油的品質問題;二是噴油嘴的問題,噴油嘴不好不能噴得很均勻,不能充分燃燒,尾氣就大了;三是尾氣過濾問題。  
蔡繼明:計程車行業可試點放開平等競爭   全國人大代表、清華大學政治經濟學研究中心主任蔡繼明指出,專車和計程車一樣是平等競爭,而打破計程車行業壟斷的改革,需要改變現行法規,至少應試點在某個區域、一定時間內暫時停止執行相關法律檔。  
王鳳英:應加強交通綜合治理法制建設   全國人大代表、長城汽車總裁王鳳英提出要加強城市交通綜合治理法制建設。她建議站在國家法律、制度層面進行具有前瞻性的頂層設計,建立健全具有中國特色的、符合國情的相關法律和配套制度體系,保障各項交通治理政策措施的推出有法可依、有法必依;並在符合法律框架的前提下,積極採用最新的資訊技術、智慧交通等科技手段,對城市發展規劃、道路路網建設、市政交通管理等進行系統性創新管理,從而全面、高效地從根本上解決城市交通擁堵難題。     文章來源:第一電動網

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

【其他文章推薦】

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

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

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

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

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

Spring Bean生命周期的各階段介紹

1{icon} {views}

一.xml方式配置bean

二.Aware接口

  2.1 BeanNameAware

  2.2 BeanFactoryAware

  2.3 ApplicationContextAware

  2.4 Aware各接口的執行順序

  2.4 Aware接口總結

三.BeanPostProcessor接口

四.InitializingBean接口

五.init-method方法

六.DestructionAwareBeanPostProcessor接口

七.DisposableBean接口

八.destory-method方法

九.生命周期大雜燴

  9.1 實現多接口的Student類

  9.2 BeanPostProcessor前後置處理

  9.3 DestructionAwareBeanPostPrecessor接口

  9.4 配置xml文件

  9.5 測試代碼

  9.6 輸出結果

十.總結

 

 

 

 

  Spring Bean的生命周期是一個老生常談的問題了,網上一搜一大把,無非就是畫一幅流程圖(比如下面這幅圖),然後用語言介紹創建bean后執行各Aware接口,然後BeanPostProcessor…..最終Bean創建成功了,就可以使用這個Bean了,然後在容器銷毀的時候,又會執行一些操作。

  其實對於上面的提到的流程圖,注意上面的圖只是Spring Bean的大概流程(省略了一部分),主要涉及到了5個接口,分別是XxxAware、BeanPostProcessor、InitiailizingBean、Destruction、DisposableBean接口,本文將會對這幾個接口,以及init-method、destroy-method做相關的使用介紹,在明白怎麼使用后,再把他們串起來,這樣的話,對於Spring Bean的生命周期就差不多知道咋回事了,而不用死記硬背。

 

一. xml方式配置Bean

  在說Aware、BeanPostProcessor、InitiailizingBean、Destruction、DisposableBean這些接口前,先簡單回顧一下使用xml配置並獲取一個Student類的bean過程,後面介紹各個接口的使用方式時時,也是按照這個形式;

1.1 創建Student類

  平淡無奇的Student類:

package cn.ganlixin.entity;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;

@Data
@Slf4j
public class Student {

    private Integer id;
    private String name;
}

  

1.2 創建配置文件

  平淡無奇的applicationContext.xml配置文件,創建一個student bean,利用setter方式設置初始值:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="cn.ganlixin.entity.Student" id="student">
        <property name="id" value="99"/>
        <property name="name" value="張三"/>
    </bean>
</beans>

  

1.3 測試

  創建一個Main類,用於測試

package cn.ganlixin;

import cn.ganlixin.entity.Student;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;

@Slf4j
public class Test {

    public static void main(String[] args) {
        BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));

        Student student = beanFactory.getBean("student", Student.class);
        log.info("測試程序獲取到的student bean:{}", student);
    }
}

  下面是運行程序的輸出,可以看到和預期相符,創建一個Student的bean,id和name默認值為99、張三;

INFO  [main] cn.ganlixin.Test - 測試程序獲取到的student bean:Student(id=99, name=張三)

   

二.Aware接口

  Aware接口有很多實現類,本文只介紹BeanNameAware、BeanFactoryAware、ApplicationContextAware,關係如下:

  

 

2.1 BeanNameAware

  創建一個Student類,讓該類實現BeanNameAware接口,並且重寫setBeanName方法

@Data
@Slf4j
public class Student implements BeanNameAware {

    private Integer id;
    private String name;

    /**
     * 實現了BeanNameAware接口后,需重寫setBeanName方法,接收的參數就是bean的id
     *
     * @param s bean的id
     */
    @Override
    public void setBeanName(String s) {
        log.info("beanName:{}, student bean:{}", s, this);
        this.id = 100;
        log.info("將beanName:{}的id改為100", s);
    }
}

  配置文件和測試程序都不改變,運行測試程序,輸出內容如下:

INFO  [main] cn.ganlixin.entity.Student - beanName:student, student bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.entity.Student - 將beanName:student的id改為100
INFO  [main] cn.ganlixin.Test - 測試程序獲取到的student bean:Student(id=100, name=張三)

  可以看到,實現BeanNameAware接口后,重寫setBeanName的方法中,獲取到的student bean,是已經初始化的bean(屬性都已經有值了),並且setBeanName方法中可以對當前的bean進行各種操作,包括修改bean的某些屬性,最後獲取到的bean是已經修改后的bean。

  這裏只是簡單介紹了一下BeanNameAware接口的用法,使用BeanNameAware接口,可以對當前Bean進行操作

 

2.2 BeanFactoryAware

  創建Student類,實現BeanFactoryAware接口,並且重寫setBeanFactory方法

@Data
@Slf4j
public class Student implements BeanFactoryAware {

    private Integer id;
    private String name;

    /**
     * 實現BeanFactoryAware接口后,需重寫setBeanFactroy方法
     *
     * @param beanFactory 創建該bean的beanFactory
     */
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        // 可以在setBeanFactory方法中獲取、修改beanFactory中的所有bean
        
        log.info("student this bean:{}", this);
        Student student = beanFactory.getBean("student", Student.class);
        log.info("通過beanFactory獲取student bean:{}", student);

        // 將name設置為李四
        this.name = "李四";
    }
}

  運行輸出如下:

INFO  [main] cn.ganlixin.entity.Student - student this bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.entity.Student - 通過beanFactory獲取student bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.Test - 測試程序獲取到的student bean:Student(id=99, name=李四)

  通過上面的代碼輸出結果可以看出,實現BeanFactoryAware接口后,可以在setBeanFactory方法中操作BeanFactory的所有bean,操作的範圍要比BeanNameAware要大。

 

2.3 ApplicationContextAware

  ApplicationContext,有多種稱呼,比如“應用容器”、“環境”、“上線文”…

  創建Student類,實現ApplicationContextAware接口,並且重寫setApplicationContext接口:

@Data
@Slf4j
public class Student implements ApplicationContextAware {

    private Integer id;
    private String name;

    /**
     * 實現ApplicationContextAware接口后,徐重寫setApplicationContext方法
     *
     * @param applicationContext 該bean所在的上下文(applicationContext、容器)
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        log.info("Student this:{}", this);

        final Student student = applicationContext.getBean("student", Student.class);
        final Environment environment = applicationContext.getEnvironment();
        log.info("student bean:{}", student);
        log.info("env -> user.dir:{}", environment.getProperty("user.dir"));
    }
}

  需要修改一下測試程序,測試程序中加載配置時使用的XmlBeanFactory,而XmlBeanFactory不會回調ApplicationContextAware接口的setApplicationContext方法,下面使用ClassPathXmlApplicationContext類來加載配置:

@Slf4j
public class Test {

    public static void main(String[] args) {
        //BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));

        // 使用ApplicationContext來加載配置
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student = context.getBean("student", Student.class);
        log.info("測試程序獲取到的student bean:{}", student);
    }
}

  運行測試程序:

INFO  [main] cn.ganlixin.entity.Student - Student this:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.entity.Student - student bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.entity.Student - env -> user.dir:/Users/ganlixin/code/java-code-all/spring
INFO  [main] cn.ganlixin.Test - 測試程序獲取到的student bean:Student(id=99, name=張三)

  實現ApplicationContextAware接口后,在setApplicationContext方法中,入參是當前的applicationContext,也就是說,可以在該方法中對Spring容器進行設置,操作的範圍又要比BeanFactoryAware的setBeanFactory要廣得多。

 

2.4 Aware各接口執行的先後順序

  既然有這幾個Aware接口,如果一個類同時實現了這3個接口,那麼執行順序是怎樣的呢?下面就來測試一下。

  創建Student類,分別實現BeanNameAware、BeanFactoryAware、ApplicationContextAware接口,並重寫其接口的方法:

@Data
@Slf4j
public class Student implements BeanNameAware, BeanFactoryAware, ApplicationContextAware {

    private Integer id;
    private String name;

    /**
     * 實現了BeanNameAware接口后,需重寫setBeanName方法,接收的參數就是bean的id
     *
     * @param s bean的id
     */
    @Override
    public void setBeanName(String s) {
        log.info("call BeanNameAware.setBeanName()");
    }

    /**
     * 實現BeanFactoryAware接口后,需重寫setBeanFactroy
     *
     * @param beanFactory 創建該bean的bean工廠
     */
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        log.info("call BeanFactoryAware.setBeanFactory()");
    }

    /**
     * 實現ApplicationContextAware接口后,徐重寫setApplicationContext方法
     *
     * @param applicationContext 該bean所在的上下文(applicationContext、容器)
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        log.info("call ApplicationContextAware.setApplicationContext()");
    }
}

  仍舊使用ClassPathXmlApplicationContext類來加載配置,運行輸出結果如下:

INFO  [main] cn.ganlixin.entity.Student - call BeanNameAware.setBeanName()
INFO  [main] cn.ganlixin.entity.Student - call BeanFactoryAware.setBeanFactory()
INFO  [main] cn.ganlixin.entity.Student - call ApplicationContextAware.setApplicationContext()
INFO  [main] cn.ganlixin.Test - 測試程序獲取到的student bean:Student(id=99, name=張三)

  

2.4 Aware接口總結

  上面演示了Spring中幾個Aware接口的用法和特點,下面總結一下:

  1.實現BeanNameAware接口后,重寫setBeanName方法,可以對單個Bean進行擴展修改;

  2.實現BeanFactoryAware接口后,重寫setBeanFactory方法,可以對bean工廠中的所有Bean進行擴展修改;

  3.實現ApplicationContextAware接口后,重寫setApplicationContext方法后,可以對整個容器進行擴展修改;

  4.這幾個接口的執行順序分別是BeanNameAware->BeanFactoryAware->ApplicationContextAware;

 

三.BeanPostProcessor接口

  BeanPostProcessor和前面的Aware接口有些區別,通過下面的例子就能看出區別在哪裡!

  下面舉個例子,創建MyBeanPostProcessor類,實現BeanPostProcessor接口,注意,這裏沒有在Student類上實現BeanPostProcessor接口。

@Slf4j
public class MyBeanPostProcessor implements BeanPostProcessor {

    /**
     * 實現了BeanPostProcessor接口后,重寫postProcessBeforeInitialization,在各種Aware接口執行完畢后執行該方法
     *
     * @param bean     本次處理的bean
     * @param beanName 本次處理的beanName(bean id)
     * @return 返回的是在本方法中處理后的bean
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        log.info("MyBeanPostProcessor.postProcessBeforeInitialization, beanName:{}, bean:{}", beanName, bean);
        return bean;
    }

    /**
     * 實現了BeanPostProcessor接口后,重寫postProcessBeforeInitialization,在initMethod方法執行完畢后執行該方法
     *
     * @param bean     本次處理的bean
     * @param beanName 本次處理的beanName(bean id)
     * @return 返回的是在本方法中處理后的bean
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        log.info("MyBeanPostProcessor.postProcessAfterInitialization, beanName:{}, bean:{}", beanName, bean);
        return bean;
    }
}

  創建兩個類,分別是Student和User類,其中Use類沒有實現Aware接口,Student類實現了前面提到的3個Aware接口

@Data
public class User {
    private Integer id;
    private String name;
}

  

@Data
@Slf4j
public class Student implements BeanNameAware, BeanFactoryAware, ApplicationContextAware {

    private Integer id;
    private String name;

    /**
     * 實現了BeanNameAware接口后,需重寫setBeanName方法,接收的參數就是bean的id
     *
     * @param s bean的id
     */
    @Override
    public void setBeanName(String s) {
        log.info("call BeanNameAware.setBeanName()");
    }

    /**
     * 實現BeanFactoryAware接口后,需重寫setBeanFactroy
     *
     * @param beanFactory 創建該bean的bean工廠
     */
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        log.info("call BeanFactoryAware.setBeanFactory()");
    }

    /**
     * 實現ApplicationContextAware接口后,徐重寫setApplicationContext方法
     *
     * @param applicationContext 該bean所在的上下文(applicationContext、容器)
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        log.info("call ApplicationContextAware.setApplicationContext()");
    }
}

  

  xml配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="cn.ganlixin.entity.Student" id="student">
        <property name="id" value="99"/>
        <property name="name" value="張三"/>
    </bean>

    <bean class="cn.ganlixin.entity.User" id="user">
        <property name="id" value="88"/>
        <property name="name" value="王五"/>
    </bean>

    <!-- 將實現了BeanPostProcessor接口的類也聲明為bean -->
    <bean class="cn.ganlixin.processor.MyBeanPostProcessor"/>
</beans>

  

  測試:

INFO  [main] cn.ganlixin.entity.Student - call BeanNameAware.setBeanName()
INFO  [main] cn.ganlixin.entity.Student - call BeanFactoryAware.setBeanFactory()
INFO  [main] cn.ganlixin.entity.Student - call ApplicationContextAware.setApplicationContext()
INFO  [main] cn.ganlixin.processor.MyBeanPostProcessor - MyBeanPostProcessor.postProcessBeforeInitialization, beanName:student1, bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.processor.MyBeanPostProcessor - MyBeanPostProcessor.postProcessAfterInitialization, beanName:student1, bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.processor.MyBeanPostProcessor - MyBeanPostProcessor.postProcessBeforeInitialization, beanName:user, bean:User(id=88, name=王五)
INFO  [main] cn.ganlixin.processor.MyBeanPostProcessor - MyBeanPostProcessor.postProcessAfterInitialization, beanName:user, bean:User(id=88, name=王五)
INFO  [main] cn.ganlixin.Test - 測試程序獲取到的student bean:Student(id=99, name=張三)

  從上面的運行結果可以得出以下結論:

  1.因為只有Student實現了Aware接口,所以創建student bean的時候會調用對應的Aware接口方法,而User類沒有實現Aware接口,所以並沒有調用Aware接口方法;

  2.Student和User類都沒有繼承BeanPostProcessor接口,但是在創建student和user bean的時候,都掉用了MyBeanPostProcessor類中的前置和後置處理(繼承自BeanPostProcessor接口);

  3.BeanPostProcessor接口的前置和後置處理,是在Aware接口之後調用;

  4.很重要的一點,需要將BeanPostProcessor接口實現類聲明為bean,使用<bean>配置或者使用@Component註解,不然BeanPostProcessor不起作用。

 

四.InitializingBean接口

  創建Student類,實現InitializingBean接口,然後重寫afterPropertiesSet方法:

@Data
@Slf4j
public class Student implements InitializingBean {

    private Integer id;
    private String name;

    @Override
    public void afterPropertiesSet() throws Exception {
        // 同樣可以在這裏修改bean的屬性值
        log.info("InitialingBean.afterPropertiesSet, this:{}", this);
    }
}

  修改xml配置文件,創建student bean,測試:

INFO  [main] cn.ganlixin.entity.Student - InitialingBean.afterPropertiesSet, this:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.Test - 測試程序獲取到的student bean:Student(id=99, name=張三)

  

五.init-method

  創建Student類,增加一個額外的方法display()

@Data
@Slf4j
public class Student {

    private Integer id;
    private String name;

    public void display() {
        log.info("Student.display call, this:{}", this);
    }
}

  修改配置文件,在<bean>標籤中增加init-method屬性,值為display,也就是Student的display方法名:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="cn.ganlixin.entity.Student" id="student" init-method="display">
        <property name="id" value="99"/>
        <property name="name" value="張三"/>
    </bean>
</beans>

  運行測試:

INFO  [main] cn.ganlixin.entity.Student - Student.display call, this:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.Test - 測試程序獲取到的student bean:Student(id=99, name=張三)

  上面,輸出了display中的內容,這是在設置bean的時候調用的。

 

六.DestructionAwareBeanPostProcessor接口

  DestructionAwareBeanPostProcessor接口,從名稱上可以看出來是DestructionAware + BeanPostProcessor的組合,其實也的確是這樣,但是需要注意的就是,spring並沒有提供DestructionAware接口!!

  下面是DestructionAwareBeanPostProcessor接口的定義:

public interface DestructionAwareBeanPostProcessor extends BeanPostProcessor {

    /**
     * Destruction執行的操作
     *
     * @param bean     處理的bean
     * @param beanName bean的名稱
     * @throws BeansException
     */
    void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException;

    /**
     * 是否需要執行postProcessBeforeDestruction方法
     *
     * @param bean 執行Destruction的bean
     * @return 是否需要執行postProcessBeforeDestruction方法
     */
    default boolean requiresDestruction(Object bean) {
        return true;
    }
}

  DestructionAwareBeanPostProceesor繼承自BeanPostProcessor接口,所以也可以重寫前值和後置處理。

  下面介紹使用示例,創建MyDestructionAwareBeanPostProceesor,繼承DestructionAwareBeanPostProceesor接口:

@Slf4j
public class MyDestructionAwareBeanPostProcessor implements DestructionAwareBeanPostProcessor {

    @Override
    public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
        log.info("DestructionAwareBeanPostProcessor.postProcessBeforeDestruction, \n\tbeanName:{}, bean:{}", beanName, bean);
    }
    
    @Override
    public boolean requiresDestruction(Object bean) {
        return true; // 返回true,一律執行postProcessBeforeDestruction方法
        // 如果返回false,則不執行postProcessBeforeDestruction方法
    }
}

  修改配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="cn.ganlixin.entity.Student" id="student">
        <property name="id" value="99"/>
        <property name="name" value="張三"/>
    </bean>


    <bean class="cn.ganlixin.entity.User" id="user">
        <property name="id" value="88"/>
        <property name="name" value="王五"/>
    </bean>

    <!-- 將實現了DestructionAwareBeanPostProcessor接口的實現類聲明為bean> -->
    <bean class="cn.ganlixin.processor.MyDestructionAwareBeanPostProcessor"/>
</beans>

  測試程序:

@Slf4j
public class Test {

    public static void main(String[] args) {
        // 使用ApplicationContext來加載配置
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student = context.getBean("student", Student.class);
        User user = context.getBean("user", User.class);

        log.info("測試程序獲取到的student bean:{}", student);

        // 獲取bean工廠,然後調用destroyBean銷毀bean
        AutowireCapableBeanFactory factory = context.getAutowireCapableBeanFactory();
        factory.destroyBean(student);
    }
}

  運行測試程序,輸出如下:

INFO  [main] cn.ganlixin.Test - 測試程序獲取到的student bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.processor.MyDestructionAwareBeanPostProcessor - DestructionAwareBeanPostProcessor.postProcessBeforeDestruction, 
	beanName:cn.ganlixin.entity.Student, bean:Student(id=99, name=張三)

  可以看到,在手動調用destroyBean方法來銷毀student bean的時候,調用了MyDestructionAwareBeanPostProcessor中定義的方法。

  需要注意的是,雖然這裏使用destroyBean來銷毀了student bean,如果又通過getBean來獲取student bean,則會重新創建student bean。

 

七.DisposableBean接口 

  前面介紹了DestructionAwareBeanPostProcessor接口,可以對所有的bean設置銷毀(destruction)后的處理操作。

  而這裏介紹的DisposableBean接口,就是對單獨的Bean進行destrction后的處理,也就是說不是應用到所有的bean上。

  簡單介紹一下用法,創建Student類和User類,User類正常(不實現任何接口),Student類實現DisposableBean接口,然後重寫destroy方法:

@Data
@Slf4j
public class Student implements DisposableBean {

    private Integer id;
    private String name;

    @Override
    public void destroy() throws Exception {
        log.info("DisposableBean.destroy, this:{}", this);
    }
}

@Data
public class User {
    private Integer id;
    private String name;
}

  創建配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="cn.ganlixin.entity.Student" id="student">
        <property name="id" value="99"/>
        <property name="name" value="張三"/>
    </bean>

    <bean class="cn.ganlixin.entity.User" id="user">
        <property name="id" value="88"/>
        <property name="name" value="王五"/>
    </bean>
</beans>

  測試程序:

@Slf4j
public class Test {

    public static void main(String[] args) {
        // 使用ApplicationContext來加載配置
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student = context.getBean("student", Student.class);
        User user = context.getBean("user", User.class);

        log.info("測試程序獲取到的student bean:{}", student);
        log.info("測試程序獲取到的user bean:{}",user);

        // 獲取bean工廠,然後調用destroyBean銷毀bean
        AutowireCapableBeanFactory factory = context.getAutowireCapableBeanFactory();
        factory.destroyBean(student);
        factory.destroyBean(user);
    }
}

  運行輸出:

INFO  [main] cn.ganlixin.Test - 測試程序獲取到的student bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.Test - 測試程序獲取到的user bean:User(id=88, name=王五)
INFO  [main] cn.ganlixin.entity.Student - DisposableBean.destroy, this:Student(id=99, name=張三)

  可以看到,雖然測試代碼中destroy了student和user兩個bean,但是只有student bean在銷毀時觸發了DisposableBean的destory方法。

 

八.destroy-method方法

  和init-method相對應的就是destory-method方法了,創建Student類,增加clean方法(自定義):

@Data
@Slf4j
public class Student {

    private Integer id;
    private String name;

    public void clean() {
        log.info("Student.clean, this:{}", this);
    }
}

  修改配置文件,<bean>標籤中使用destroy-method屬性,值為clean方法

<bean class="cn.ganlixin.entity.Student" id="student" destroy-method="clean">
    <property name="id" value="99"/>
    <property name="name" value="張三"/>
</bean>

  測試程序:

@Slf4j
public class Test {

    public static void main(String[] args) {
        // 使用ApplicationContext來加載配置
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student = context.getBean("student", Student.class);

        log.info("測試程序獲取到的student bean:{}", student);

        // 刪除bean
        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) context.getAutowireCapableBeanFactory();
        registry.removeBeanDefinition("student");
    }
}

  輸出:

INFO  [main] cn.ganlixin.Test - 測試程序獲取到的student bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.entity.Student - Student.clean, this:Student(id=99, name=張三)

  

九.聲明周期大雜燴

  上面對每一種接口都做了介紹,這裏就將所有接口都做一下整合,嘗試在一個測試程序中測試所有接口,這個過程中就會對Bean的生命周期有清晰的認識:

9.1 實現多接口的Student類

  創建Student類,實現Aware、InitializingBean、DisposableBean接口,並且增加display、clean方法,作為init-method和destory-method。

package cn.ganlixin.entity;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

@Data
@Slf4j
public class Student implements BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean, DisposableBean {

    private Integer id;
    private String name;

    @Override
    public void setBeanName(String s) {
        log.info("BeanNameAware.setBeanName, this:{}", this);
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        log.info("BeanFactoryAware.setBeanFactory, this:{}", this);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        log.info("ApplicationContextAware.setApplicationContext, this:{}", this);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        log.info("InitialingBean.afterPropertiesSet, this:{}", this);
    }

    @Override
    public void destroy() throws Exception {
        log.info("DisposableBean.destory, this:{}", this);
    }

    public void display() {
        log.info("init-method, Student.display, this:{}", this);
    }

    public void clean() {
        log.info("destroy-method, Student.clean, this:{}", this);
    }
}

 

9.2 BeanPostProcessor前後置處理

  創建MyBeanPostProcessor接口實現類,並重寫前置和後置處理方法:

package cn.ganlixin.processor;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

@Slf4j
public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        log.info("MyBeanPostProcessor.postProcessBeforeInitialization, beanName:{}, bean:{}", beanName, bean);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        log.info("MyBeanPostProcessor.postProcessAfterInitialization, beanName:{}, bean:{}", beanName, bean);
        return bean;
    }
}

 

9.3 DestructionAwareBeanPostPrecessor接口

  創建MyDestructionAwareBeanPostProcessor類,並重寫其中的方法(不重寫BeanPostProcessor的前後置處理方法):

package cn.ganlixin.processor;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;

@Slf4j
public class MyDestructionAwareBeanPostProcessor implements DestructionAwareBeanPostProcessor {

    @Override
    public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
        log.info("DestructionAwareBeanPostProcessor.postProcessBeforeDestruction, \n\tbeanName:{}, bean:{}", beanName, bean);
    }

    @Override
    public boolean requiresDestruction(Object bean) {
        return true; // 返回true,一律執行postProcessBeforeDestruction方法
        // 如果返回false,則不執行postProcessBeforeDestruction方法
    }
}

  

9.4 配置xml文件  

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 創建student bean,指定init-method和destroy-method -->
    <bean class="cn.ganlixin.entity.Student" id="student" init-method="display" destroy-method="clean">
        <property name="id" value="99"/>
        <property name="name" value="張三"/>
    </bean>


    <!-- 將實現了DestructionAwareBeanPostProcessor接口的實現類聲明為bean-->
    <bean class="cn.ganlixin.processor.MyDestructionAwareBeanPostProcessor"/>

    <!-- 將實現了BeanPostProcessor接口的類也聲明為bean-->
    <bean class="cn.ganlixin.processor.MyBeanPostProcessor"/>
</beans>

  

9.5 測試代碼

package cn.ganlixin;

import cn.ganlixin.entity.Student;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

@Slf4j
public class Test {

    public static void main(String[] args) {
        // 使用ApplicationContext來加載配置
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student = context.getBean("student", Student.class);

        log.info("測試程序獲取到的student bean:{}", student);

        // 刪除bean
        BeanDefinitionRegistry factory = (BeanDefinitionRegistry) context.getAutowireCapableBeanFactory();
        factory.removeBeanDefinition("student");
    }
}

  

9.6 輸出結果

INFO  [main] cn.ganlixin.entity.Student - BeanNameAware.setBeanName, this:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.entity.Student - BeanFactoryAware.setBeanFactory, this:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.entity.Student - ApplicationContextAware.setApplicationContext, this:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.processor.MyBeanPostProcessor - MyBeanPostProcessor.postProcessBeforeInitialization, beanName:student, bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.entity.Student - InitialingBean.afterPropertiesSet, this:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.entity.Student - init-method, Student.display, this:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.processor.MyBeanPostProcessor - MyBeanPostProcessor.postProcessAfterInitialization, beanName:student, bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.Test - 測試程序獲取到的student bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.processor.MyDestructionAwareBeanPostProcessor - DestructionAwareBeanPostProcessor.postProcessBeforeDestruction, 
	beanName:student, bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.entity.Student - DisposableBean.destory, this:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.entity.Student - destroy-method, Student.clean, this:Student(id=99, name=張三)

  

十.總結

  看了上面這個輸出結果,再結合下面這個圖,基本就能掌握Bean的大致生命周期了。

  

 

   原文地址:https://www.cnblogs.com/-beyond/p/13188675.html

   

 

  

 

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

【其他文章推薦】

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

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

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

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

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

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

2{icon} {views}

電動車熱潮可說成也特斯拉(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/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

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

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

特斯拉更新軟體 不再擔心電動車電量問題

3{icon} {views}

特斯拉(Tesla)執行長 Elon Musk 日前才剛透過 Twitter 宣稱,他要終結特斯拉電動車主的「里程焦慮」(range anxiety,就是車主擔心電動車上路後會面臨電量不夠的問題),這顯然不是說說而已。特斯拉 19 日發布了 Model S 電動轎車軟體更新「Version 6.2」,Musk 更誇下海口,宣稱本次更新可讓 Model S 車主不需擔心電量問題!   Musk 19 日在對記者召開的電話會議上表示,Version 6.2 多了「里程保險」(range assurance)的程式,這項程式能夠與特斯拉的超級充電站溝通,在汽車快要沒電之際對車主示警,然後找到最近的充電站。   Version 6.2 另一個重要更新則是「旅程規劃」程式,可將超級充電站、Model S 與導航系統的網絡全面整合,為車主設計出最佳的長途旅行路線。也就是說,Model S 的系統會隨時確認目前的地理位置、最接近的超級充電站、電池所剩電量以及距離目的地還有多遠。Version 6.2 預計 2 週內就能正式釋出。    

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

【其他文章推薦】

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

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

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

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

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

寫給.NET開發者的Python教程(一):引言

3{icon} {views}

距離上一篇博文已過去8個月了,這段時間發生了很多事情導致沒能持續更新博客。這段時間除了工作繁忙,業餘時間都投入到AI技術的學習中,後面一段時間將會給大家分享我作為一個.NET開發人員在深度學習領域學習的收穫和成果。

《寫給.NET開發者的Python教程》這個系列是第一個想和大家分享的內容,主要是從C#角度學習Python,希望能給.NET開發者們掃清AI學習的第一個攔路虎。所有的教程都會以AI開發的必備的Jupyter Notebook環境編寫和提供給大家。

之後的系列會給大家分享如何從頭開始使用深度學習框架訓練AI模型和打比賽的做法和經驗。

為什麼學Python?

 

學習Python不是鼓勵大家拋棄.NET平台轉向Python,而是為了讓大家能夠在這個AI當紅的年代,能對新的技術領域有所了解,甚至多掌握一門手藝。於我而言,學習Python的理由有幾個:

  1. 學習AI技術。雖然AI領域能夠使用很多語言和平台(如R語言、ML.NET),但目前最主流的方式是使用基於Python的AI框架,例如PyTorch和Tensorflow。大多數AI庫和開源項目都基於Python,業界開發和部署AI模型也大都使用Python。
  2. 擁抱新語言。學一門新語言可以開拓視野,增加解決問題的靈活性。有個朋友每年都會學習一門新語言,這一點非常棒。學習新東西可以保持對技術的熱情和新鮮感,不同的語言和平台各有千秋,擅長不同領域,能夠成為一個多面手對我們的職業發展大有裨益。
  3. 提升競爭力。對於.NET開發者而言,我們會越來越多遇見項目中集成AI技術的場景,學會Python有助於我們了解AI技術方案,更好的參与AI方案的開發、集成和部署。舉個例子,目前主流的AI服務託管方案都是將AI模型部署為Web服務,然而Python的諸多Web框架性能和併發能力都不如ASP.NET Core,如果能採用ASP.NET Core作為Web服務,本機通過ML.NET或TensorFlow.NET調用AI模型或許會是更好的選擇。

根據StackOverflow 2020年的開發者報告,Python仍舊是近幾年開發者最想學習的語言:https://insights.stackoverflow.com/survey/2020#technology-most-loved-dreaded-and-wanted-languages-wanted

 

 

系列內容規劃

《寫給.NET開發者的Python教程》主要會以C#和Python語言特性對照的方式,為大家介紹Python的語法,每一行Python代碼都會有C#的對照例子。同時對於Python的類庫和框架的使用,也會提供.NET對照的介紹,為大家提供一份簡明概要的指引教程。

《寫給.NET開發者的Python教程》系列博文的規劃如下:

  1. 引言
  2. C# vs Python:語言特性、Conda和Jupyter Notebook環境
  3. 基本類型和變量
  4. 運算符、條件判斷和循環語句
  5. 數據結構
  6. 函數和面向對象
  7. 模塊、包和庫
  8. 常用功能:文件、時間、字符串、Json&XML序列化和反序列化
  9. 機器學習神器:Numpy、Pandas、Pillow和Matplotlib

最後,這個系列的更新頻率暫定一周一節

 

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

【其他文章推薦】

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

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

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

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

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

交通部:電動自行車最快年底掛牌證納管

3{icon} {views}

交通部道安會與警政署交通組宣布,最快年底新出廠電動自行車將掛牌證納管,全台 15 萬輛已出場電動自行車也將逐步掛牌。   交通部路政司表示,時速超過 25 公里的普通輕型電動機車及小型輕型電動機車,被歸入輕型機車,必須掛牌、考照,時速 25 公里以下的電動自行車與電動輔助自行車,前者以電力為主,後者人力踩踏為主、電力為輔,只需認證不用掛牌,電動自行車也不能載人。   交通部路政司監理科長趙晉緯說,最快今年底前,要求電動自行車需配掛類似識別管理證的「牌證」,但電動自行車不用繳牌照稅也不用領牌費,因此不算是「掛牌」,只是領牌證,號碼將是 1 碼英文對上 5 碼數字,另外,97 年起共有 15 萬電動自行車出廠,也將陸續分批掛牌。  

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

【其他文章推薦】

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

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

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

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

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

特斯拉Q1財報優 今年或賣出5.5萬輛車

6{icon} {views}

特斯拉6日公佈首季財報,銷售金額比去年同期增加 50%,營收高達 11 億美元,每股淨損 36 美分,優於分析師預期,股價盤後上漲 2.4%,上個月則已累積大漲達 13%。同時,特斯拉預計新的太陽能電池廠將在今年第 3 季啟用,明年第一季前汽車產能估計將快速暴增。   特斯拉首季賣出的車輛達 10045 輛,稍優於原先發佈的 10030 輛,預期 2015 年總銷售車輛將可達 5.5 萬輛。同時,特斯拉的最新車款 Model X 也將於第三季開始販售。特斯拉表示,受到美元強勢走升的影響,今年首季提列的匯損金額高達 2200 萬美元,預計第二季 Model X 的售價仍會受美元影響,因此其將在第三季後調漲部分歐洲市場的電動車售價。   目前特斯拉仍積極在美國擴產,第三季新的太陽能電池廠就即將啟用,汽車產能到了 2016 年首季可望大幅增加。去年馬斯克稱將在內華達州建造一個超級電池工廠,到了 2020 年將生產約 50 萬顆的鋰電池。

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

【其他文章推薦】

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

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

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

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

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