Tim Cook:汽車是蘋果給用戶準備的聖誕禮物

在 2016 年蘋果公司股東大會上,該公司 CEO Tim Cook 在回答股東關於蘋果汽車的詢問時,他表示用戶等待這一款新品就是等待聖誕禮物,現在距離耶誕節還有一段時間。蘋果公司已經從 Tesla、Ford、GM、Samsung 等公司招募了數百名工程師,組成了電動車研發團隊。

蘋果正在研發電動車已經不是什麼新聞了,但一直沒有官方消息證實,該公司 CEO Tim Cook 在股東大會上回應了股東有關蘋果汽車的問題,他表示現在就像兒童時代,等待平安夜的時間,期待將收到什麼禮物,未來一段時間,就像是等待禮物的平安夜。這意味蘋果公司正在研發電動汽車,但上市還需要等一段時間。   除了電動汽車外,此次股東大會還討論其他議題,蘋果在 2016 的研發支出為 85 億美元,並收購了 19 家公司,目前該公司的員工總數大約為 11.6 萬人。關於近日廣受關注的 FBI 要求解鎖 iPhone 的事件,Tim Cook 再次重申了蘋果公司立場,堅決擁護用戶的個人隱私和資訊安全。蘋果公司的新總部即將完工,大約在 2017 年 1 月部分員工就將搬入新的員工,這是 Steve Jobs 生前最關注的專案之一。

(首圖來源: CC BY 2.0)    (本文授權轉載自《》─〈〉)

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

【其他文章推薦】

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

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

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

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

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

※試算大陸海運運費!

英國建成總長1099公里電動汽車快速充電網 30分鐘充電80%

日前,一條總長為1099公里的電動汽車快速充電網在英國建成,為電動汽車的推廣創造更大的便利。

據悉,該充電網路擁有74個快速充電網站,僅需30分鐘就可以為電動汽車充電 80%。該網路的建立給車主提供了更多選擇,讓長途駕駛更加便捷。此外,網站在多個重點交通樞紐建立,電動車駕駛員可以順利過境。據紐卡斯爾大學研究表明,72%的電動汽車駕駛員會使用快速充電站。據悉,該網路覆蓋了英國大部分地區。

據悉,該專案總投資為580萬英鎊,由歐盟和日產、寶馬、雷諾以及大眾這四家汽車製造商出資籌建。

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

【其他文章推薦】

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

※網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦、台中網頁設計公司推薦專業設計師”嚨底家”!!

※專營大陸快遞台灣服務

※台灣快遞大陸的貨運公司有哪些呢?

2016 第二屆亞太新能源汽車國際峰會 聚焦智慧網聯電動交通

本次由中國汽車工程研究院支援並由上海領研商務諮詢有限公司主辦的 “2016第二屆亞太新能源汽車國際峰會”將於2016年6月2日(星期四)至3日(星期五)在北京召開。

隨著全球能源危機的加重及汽車排放引起的環境問題日益得到世界各國的重視,智慧化,網聯化和電動化已成為未來全球汽車行業發展的必然趨勢。汽車行業是能源、資源消耗較大的產業,同時汽車排放也是環境污染的重要原因。中國已經步入汽車生產和消費大國,節能、環保、低碳是中國汽車產業面臨的長期任務。

據中汽協資料顯示,2015年新能源汽車生產34.04萬輛,銷售33.1萬輛,同比分別增長3.3倍、3.4倍。根據機動車整車出廠合格證統計,2016年1月,中國新能源汽車生產1.61萬輛,同比增長144%。根據全國乘用車市場訊息聯席會剛發佈的統計資料,2016年1月新能源乘用車銷量達13748輛,較之2015年1月同比增長1.8倍。

2015年11月18日,中國國家發改委發佈了《電動汽車充電基礎設施發展指南(2015-2020年)》 ,明確提出到2020年,中國將新增集中式充換電站1.2萬座,分散式充電樁480萬個,以滿足全國500萬輛電動汽車充電需求。今年5月19日中國務院發佈的《中國製造2025》中提出將“節能與新能源汽車”作為重點發展領域,明確了繼續支持電動汽車、燃料電池汽車發展,掌握汽車低碳化、資訊化、智慧化核心技術等的發展戰略。

面臨重大發展機遇的同時,新能源汽車的大規模商業化仍然還存在一些挑戰和瓶頸,如價格偏高、性能不完善、充電基礎設施不完善、充電標準不統一、關鍵零部件技術尚未成熟等等。

在此背景下,2016第二屆亞太新能源汽車國際峰會以 “助力智慧網聯電動交通”為主題,旨在為全球新能源汽車參與者打造一個瞭解中國新能源汽車發展現狀,商業模式和技術發展趨勢的前瞻性產業交流平臺。   

相信本屆在北京召開的”2016第二屆亞太新能源汽車國際峰會”構建的交流平臺和溝通管道,對於借鑒先進理念、整合政策資源、利用各方智慧,推進新能源汽車產業發展、節能減排等各項工作將會產生深遠影響和積極作用,並為中國綠色交通產業起到巨大的推動作用。

期待您的支持和參與。

更多關於2016ANEVS的資訊,請登錄會議官方網站:

組委會聯繫方式:
連絡人:Fiona Hu           
電  話:+86 21-58560121
郵  箱:

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

【其他文章推薦】

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

※網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦、台中網頁設計公司推薦專業設計師”嚨底家”!!

※大陸寄台灣空運注意事項

※大陸海運台灣交貨時間多久?

[springboot 開發單體web shop] 6. 商品分類和輪播廣告展示

目錄

商品分類&輪播廣告

因最近又被困在了OSGI技術POC,更新進度有點慢,希望大家不要怪罪哦。

我們實現了登錄之後前端的展示,如:

接着,我們來實現左側分類欄目的功能。

商品分類|ProductCategory

從上圖我們可以看出,商品的分類其實是有層級關係的,而且這種關係一般都是無限層級。在我們的實現中,為了效果的展示,我們僅僅是展示3級分類,在大多數的中小型電商系統中,三級分類完全足夠應對SKU的分類。

需求分析

先來分析分類都包含哪些元素,以jd為例:

  • logo(logo) 有的分類文字前面會有小標
  • 分類展示主圖(img_url)
  • 主標題(title)
  • 副標題/Slogan
  • 圖片跳轉地址(img_link_url)– 大多數時候我們點擊分類都會分類Id跳轉到固定的分類商品列表展示頁面,但是在一些特殊的場景,比如我們要做一個活動,希望可以點擊某一個分類的主圖直接定位到活動頁面,這個url就可以使用了。
  • 上級分類(parent_id)
  • 背景色(bg_color)
  • 順序(sort)
  • 當前分類級別(type)

開發梳理

在上一小節,我們簡單分析了一下要實現商品分類的一些points,那麼我們最好在每次拿到需求【開發之前】,對需求進行拆解,然後分解開發流程,這樣可以保證我們更好的理解需求,以及在開發之前發現一部分不合理的需求,並且如果需求設計不合理的話,開發人員完全有權,也有責任告知PM。大家的終極目的都是為了我們做的產品更加合理,好用,受歡迎!

  • 首次展示,僅僅讀取一級分類(Root)
  • 根據一級分類查詢二三級子分類

編碼實現

查詢一級分類

Service實現

1.在com.liferunner.service中創建service 接口ICategoryService.java, 編寫查詢所有一級分類的方法getAllRootCategorys,如下:

package com.liferunner.service;
import com.liferunner.dto.CategoryResponseDTO;
import com.liferunner.dto.SecondSubCategoryResponseDTO;
import java.util.List;
/**
 * ICategoryService for : 分類service
 *
 * @author <a href="mailto:magicianisaac@gmail.com">Isaac.Zhang | 若初</a>
 * @since 2019/11/13
 */
public interface ICategoryService {
    /**
     * 獲取所有有效的一級分類(根節點)
     *
     * @return
     */
    List<CategoryResponseDTO> getAllRootCategorys();
}

2.編寫實現類com.liferunner.service.ICategoryService.java

@Service
@Slf4j
public class CategorySericeImpl implements ICategoryService {
    @Autowired
    private CategoryMapper categoryMapper;
    
    @Override
    public List<CategoryResponseDTO> getAllRootCategorys() {
        Example example = new Example(Category.class);
        val conditions = example.createCriteria();
        conditions.andEqualTo("type", CategoryTypeEnum.ROOT.type);
        val categoryList = this.categoryMapper.selectByExample(example);
        //聲明返回對象
        List<CategoryResponseDTO> categoryResponseDTOS = new ArrayList<>();
        if (!CollectionUtils.isEmpty(categoryList)) {
            //賦值
            CategoryResponseDTO dto;
            for (Category category : categoryList) {
                dto = new CategoryResponseDTO();
                BeanUtils.copyProperties(category, dto);
                categoryResponseDTOS.add(dto);
            }
        }
        return categoryResponseDTOS;
    }
}

上述代碼很好理解,創建tk.mybatis.mapper.entity.Example,將條件傳入,然後使用通用Mapper查詢到type=1的一級分類,接着將查到的對象列錶轉換為DTO對象列表。

Controller實現

一般情況下,此類查詢都會出現在網站的首頁,因此我們來創建一個com.liferunner.api.controller.IndexController,並對外暴露一個查詢一級分類的接口:

package com.liferunner.api.controller;

import com.liferunner.service.ICategoryService;
import com.liferunner.service.IProductService;
import com.liferunner.service.ISlideAdService;
import com.liferunner.utils.JsonResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import java.util.Collections;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
/**
 * IndexController for : 首頁controller
 *
 * @author <a href="mailto:magicianisaac@gmail.com">Isaac.Zhang | 若初</a>
 * @since 2019/11/12
 */
@RestController
@RequestMapping("/index")
@Api(value = "首頁信息controller", tags = "首頁信息接口API")
@Slf4j
public class IndexController {
    @Autowired
    private ICategoryService categoryService;

    @GetMapping("/rootCategorys")
    @ApiOperation(value = "查詢一級分類", notes = "查詢一級分類")
    public JsonResponse findAllRootCategorys() {
        log.info("============查詢一級分類==============");
        val categoryResponseDTOS = this.categoryService.getAllRootCategorys();
        if (CollectionUtils.isEmpty(categoryResponseDTOS)) {
            log.info("============未查詢到任何分類==============");
            return JsonResponse.ok(Collections.EMPTY_LIST);
        }
        log.info("============一級分類查詢result:{}==============", categoryResponseDTOS);
        return JsonResponse.ok(categoryResponseDTOS);
    }
}
Test API

編寫完成之後,我們需要對我們的代碼進行測試驗證,還是通過使用RestService插件來實現,當然,大家也可以通過Postman來測試。

{
  "status": 200,
  "message": "OK",
  "data": [
    {
      "id": 1,
      "name": "煙酒",
      "type": 1,
      "parentId": 0,
      "logo": "img/cake.png",
      "slogan": "吸煙受害健康",
      "catImage": "http://www.life-runner.com/shop/category/cake.png",
      "bgColor": "#fe7a65"
    },
    {
      "id": 2,
      "name": "服裝",
      "type": 1,
      "parentId": 0,
      "logo": "img/cookies.png",
      "slogan": "我選擇我喜歡",
      "catImage": "http://www.life-runner.com/shop/category/cookies.png",
      "bgColor": "#f59cec"
    },
    {
      "id": 3,
      "name": "鞋帽",
      "type": 1,
      "parentId": 0,
      "logo": "img/meat.png",
      "slogan": "飛一般的感覺",
      "catImage": "http://www.life-runner.com/shop/category/meat.png",
      "bgColor": "#b474fe"
    }
  ],
  "ok": true
}

根據一級分類查詢子分類

因為根據一級id查詢子分類的時候,我們是在同一張表中做自連接查詢,因此,通用mapper已經不適合我們的使用,因此我們需要自定義mapper來實現我們的需求。

自定義Mybatis Mapper實現

在之前的編碼中,我們都是使用的插件幫我們實現的通用Mapper,但是這種查詢只能處理簡單的單表CRUD,一旦我們需要SQL 包含一部分邏輯處理的時候,那就必須得自己來編寫了,let’s code.
1.在項目mscx-shop-mapper中,創建一個新的custom package,在該目錄下創建自定義mappercom.liferunner.custom.CategoryCustomMapper。

public interface CategoryCustomMapper {
    List<SecondSubCategoryResponseDTO> getSubCategorys(Integer parentId);
}

2.resources目錄下創建目錄mapper.custom,以及創建和上面的接口相同名稱的XML文件mapper/custom/CategoryCustomMapper.xml

<?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="com.liferunner.custom.CategoryCustomMapper">
    <resultMap id="subCategoryDTO" type="com.liferunner.dto.SecondSubCategoryResponseDTO">
        <id column="id" jdbcType="INTEGER" property="id"/>
        <result column="name" jdbcType="VARCHAR" property="name"/>
        <result column="type" jdbcType="INTEGER" property="type"/>
        <result column="parentId" jdbcType="INTEGER" property="parentId"/>
        <collection property="thirdSubCategoryResponseDTOList" ofType="com.liferunner.dto.ThirdSubCategoryResponseDTO">
            <id column="subId" jdbcType="INTEGER" property="subId"/>
            <result column="subName" jdbcType="VARCHAR" property="subName"/>
            <result column="subType" jdbcType="INTEGER" property="subType"/>
            <result column="subParentId" jdbcType="INTEGER" property="subParentId"/>
        </collection>
    </resultMap>
    <select id="getSubCategorys" resultMap="subCategoryDTO" parameterType="INTEGER">
        SELECT p.id as id,p.`name` as `name`,p.`type` as `type`,p.father_id as parentId,
        c.id as subId,c.`name` as subName,c.`type` as subType,c.parent_id as subParentId
        FROM category p
        LEFT JOIN category c
        ON p.id = c.parent_id
        WHERE p.parent_id = ${parentId};
    </select>
</mapper>

TIPS
上述創建的package,一定要在項目的啟動類com.liferunner.api.ApiApplication中修改@MapperScan(basePackages = { "com.liferunner.mapper", "com.liferunner.custom"}),如果不把我們的custom package加上,會造成掃描不到而報錯。

在上面的xml中,我們定義了兩個DTO對象,分別用來處理二級和三級分類的DTO,實現如下:

@Data
@ToString
public class SecondSubCategoryResponseDTO {
    /**
     * 主鍵
     */
    private Integer id;

    /**
     * 分類名稱
     */
    private String name;

    /**
     * 分類類型
     1:一級大分類
     2:二級分類
     3:三級小分類
     */
    private Integer type;

    /**
     * 父id
     */
    private Integer parentId;

    List<ThirdSubCategoryResponseDTO> thirdSubCategoryResponseDTOList;
}
---
    
@Data
@ToString
public class ThirdSubCategoryResponseDTO {
    /**
     * 主鍵
     */
    private Integer subId;

    /**
     * 分類名稱
     */
    private String subName;

    /**
     * 分類類型
     1:一級大分類
     2:二級分類
     3:三級小分類
     */
    private Integer subType;

    /**
     * 父id
     */
    private Integer subParentId;
}
Service實現

編寫完自定義mapper之後,我們就可以繼續編寫service了,在com.liferunner.service.ICategoryService中新增一個方法:getAllSubCategorys(parentId).如下:

public interface ICategoryService {
    ...
    /**
     * 根據一級分類獲取子分類
     *
     * @param parentId 一級分類id
     * @return 子分類list
     */
    List<SecondSubCategoryResponseDTO> getAllSubCategorys(Integer parentId);
}

在com.liferunner.service.impl.CategorySericeImpl實現上述方法:

@Service
@Slf4j
public class CategorySericeImpl implements ICategoryService {
    @Autowired
    private CategoryMapper categoryMapper;

    @Autowired
    private CategoryCustomMapper categoryCustomMapper;
    ...
    @Override
    @Transactional(propagation = Propagation.SUPPORTS)
    public List<SecondSubCategoryResponseDTO> getAllSubCategorys(Integer parentId) {
        return this.categoryCustomMapper.getSubCategorys(parentId);
    }
}
Controller實現
@RestController
@RequestMapping("/index")
@Api(value = "首頁信息controller", tags = "首頁信息接口API")
@Slf4j
public class IndexController {
    @Autowired
    private ICategoryService categoryService;
    ...
    @GetMapping("/subCategorys/{parentId}")
    @ApiOperation(value = "查詢子分類", notes = "根據一級分類id查詢子分類")
    public JsonResponse findAllSubCategorys(
            @ApiParam(name = "parentId", value = "一級分類id", required = true)
            @PathVariable Integer parentId) {
        log.info("============查詢id = {}的子分類==============", parentId);
        val categoryResponseDTOS = this.categoryService.getAllSubCategorys(parentId);
        if (CollectionUtils.isEmpty(categoryResponseDTOS)) {
            log.info("============未查詢到任何分類==============");
            return JsonResponse.ok(Collections.EMPTY_LIST);
        }
        log.info("============子分類查詢result:{}==============", categoryResponseDTOS);
        return JsonResponse.ok(categoryResponseDTOS);
    }
}
Test API
{
  "status": 200,
  "message": "OK",
  "data": [
    {
      "id": 11,
      "name": "國產",
      "type": 2,
      "parentId": 1,
      "thirdSubCategoryResponseDTOList": [
        {
          "subId": 37,
          "subName": "中華",
          "subType": 3,
          "subParentId": 11
        },
        {
          "subId": 38,
          "subName": "冬蟲夏草",
          "subType": 3,
          "subParentId": 11
        },
        {
          "subId": 39,
          "subName": "南京",
          "subType": 3,
          "subParentId": 11
        },
        {
          "subId": 40,
          "subName": "雲煙",
          "subType": 3,
          "subParentId": 11
        }
      ]
    },
    {
      "id": 12,
      "name": "外煙",
      "type": 2,
      "parentId": 1,
      "thirdSubCategoryResponseDTOList": [
        {
          "subId": 44,
          "subName": "XXXXX",
          "subType": 3,
          "subParentId": 12
        },
        {
          "subId": 45,
          "subName": "RRRRR",
          "subType": 3,
          "subParentId": 12
        }
      ]
    }
  ],
  "ok": true
}

以上我們就已經實現了和jd類似的商品分類的功能實現。

輪播廣告|SlideAD

需求分析

這個就是jd或者tb首先的最頂部的廣告圖片是一樣的,每隔1秒自動切換圖片。接下來我們分析一下輪播圖中都包含哪些信息:

  • 圖片(img_url)是最基本的
  • 圖片跳轉連接(img_link_url),這個是在我們點擊這個圖片的時候需要跳轉到的頁面
  • 有的可以直接跳轉到商品詳情頁面
  • 有的可以直接跳轉到某一分類商品列表頁面
  • 輪播圖的播放順序(sort)
  • …

開發梳理

直接查詢出所有的有效的輪播圖片,並且進行排序。

編碼實現

Service 實現

和商品分類實現一樣,在mscx-shop-service中創建com.liferunner.service.ISlideAdService並實現,代碼如下:

public interface ISlideAdService {
    /**
     * 查詢所有可用廣告並排序
     * @param isShow
     * @return
     */
    List<SlideAdResponseDTO> findAll(Integer isShow, String sortRanking);
}
@Service
@Slf4j
public class SlideAdServiceImpl implements ISlideAdService {

    // 注入mapper
    private final SlideAdsMapper slideAdsMapper;

    @Autowired
    public SlideAdServiceImpl(SlideAdsMapper slideAdsMapper) {
        this.slideAdsMapper = slideAdsMapper;
    }

    @Override
    public List<SlideAdResponseDTO> findAll(Integer isShow, String sortRanking) {
        Example example = new Example(SlideAds.class);
        //設置排序
        if (StringUtils.isBlank(sortRanking)) {
            example.orderBy("sort").asc();
        } else {
            example.orderBy("sort").desc();
        }
        val conditions = example.createCriteria();
        conditions.andEqualTo("isShow", isShow);
        val slideAdsList = this.slideAdsMapper.selectByExample(example);
        //聲明返回對象
        List<SlideAdResponseDTO> slideAdResponseDTOList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(slideAdsList)) {
            //賦值
            SlideAdResponseDTO dto;
            for (SlideAds slideAds : slideAdsList) {
                dto = new SlideAdResponseDTO();
                BeanUtils.copyProperties(slideAds, dto);
                slideAdResponseDTOList.add(dto);
            }
        }
        return slideAdResponseDTOList;
    }
}

從上述可以看到,這裏我使用的是構造函數注入SlideAdsMapper,其餘代碼單表查詢沒什麼特別的,根據條件查詢輪播圖,並返回結果,返回的對象是com.liferunner.dto.SlideAdResponseDTO列表,代碼如下:

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@ApiModel(value = "輪播廣告返回DTO", description = "輪播廣告返回DTO")
public class SlideAdResponseDTO{
    /**
     * 主鍵
     */
    private String id;

    /**
     * 圖片地址
     */
    private String imageUrl;

    /**
     *  背景顏色
     */
    private String backgroundColor;

    /**
     * 商品id
     */
    private String productId;

    /**
     * 商品分類id
     */
    private String catId;

    /**
     * 圖片跳轉URL
     */
    private String imageLinkUrl;

    /**
     * 輪播圖類型 用於判斷,可以根據商品id或者分類進行頁面跳轉,1:商品 2:分類 3:鏈接url
     */
    private Integer type;

    /**
     * 輪播圖展示順序 輪播圖展示順序,從小到大
     */
    private Integer sort;

    /**
     * 是否展示 是否展示,1:展示    0:不展示
     */
    private Integer isShow;

    /**
     * 創建時間 創建時間
     */
    private Date createTime;

    /**
     * 更新時間 更新
     */
    private Date updateTime;
}

Controller實現

在com.liferunner.api.controller.IndexController中,新添加一個查詢輪播圖API,代碼如下:

    @Autowired
    private ISlideAdService slideAdService;

    @GetMapping("/slideAds")
    @ApiOperation(value = "查詢輪播廣告", notes = "查詢輪播廣告接口")
    public JsonResponse findAllSlideList() {
        log.info("============查詢所有輪播廣告,isShow={},sortRanking={}=============="
                , 1, "desc");
        val slideAdsList = this.slideAdService.findAll(1, "desc");
        if (CollectionUtils.isEmpty(slideAdsList)) {
            log.info("============未查詢到任何輪播廣告==============");
            return JsonResponse.ok(Collections.EMPTY_LIST);
        }
        log.info("============輪播廣告查詢result:{}=============="
                , slideAdsList);
        return JsonResponse.ok(slideAdsList);
    }

Test API

{
  "status": 200,
  "message": "OK",
  "data": [
    {
      "id": "slide-100002",
      "imageUrl": "http://www.life-runner.com/2019/11/CpoxxF0ZmH6AeuRrAAEZviPhyQ0768.png",
      "backgroundColor": "#55be59",
      "productId": "",
      "catId": "133",
      "type": 2,
      "sort": 2,
      "isShow": 1,
      "createTime": "2019-10-11T21:33:01.000+0000",
      "updateTime": "2019-10-11T21:33:02.000+0000"
    },
    {
      "id": "slide-100003",
      "imageUrl": "http://www.life-runner.com/2019/11/CpoxxF0ZmHuAPlXvAAFe-H5_-Nw961.png",
      "backgroundColor": "#ff9801",
      "productId": "y200008",
      "catId": "",
      "type": 1,
      "sort": 1,
      "isShow": 1,
      "createTime": "2019-10-11T21:33:01.000+0000",
      "updateTime": "2019-10-11T21:33:02.000+0000"
    }
  ],
  "ok": true
}

福利講解

在我們的實現代碼中,有心的同學可以看到,我使用了3種不同的Bean注入方式:

  • 屬性注入
    @Autowired
    private ISlideAdService slideAdService;
  • 構造函數注入
    // 注入mapper
    private final SlideAdsMapper slideAdsMapper;

    @Autowired
    public SlideAdServiceImpl(SlideAdsMapper slideAdsMapper) {
        this.slideAdsMapper = slideAdsMapper;
    }
  • Lombok插件注入(本質也是構造器注入,代碼會動態生成。)
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class ProductServiceImpl implements IProductService {
    // RequiredArgsConstructor 構造器注入
    private final ProductCustomMapper productCustomMapper;
    private final ProductsMapper productsMapper;
    ...
}

那麼,這幾種注入都有什麼區別呢?首先我們下了解一下Spring的注入是干什麼的?

Spring提出了依賴注入的思想,即依賴類不由程序員實例化,而是通過Spring容器幫我們new指定實例並且將實例注入到需要該對象的類中。
依賴注入的另一種說法是”控制反轉”。通俗的理解是:平常我們new一個實例,這個實例的控制權是我們程序員, 而控制反轉是指new實例工作不由我們程序員來做而是交給Spring容器來做。

在傳統的SpringMVC中,大家使用的都是XML注入,比如:

<!--配置bean,配置后該類由spring管理--> 
<bean name="CategorySericeImpl" class="com.liferunner.service.impl.CategorySericeImpl"> 
<!--注入配置當前類中相應的屬性--> 
<property name="categoryMapper" ref="categoryMapper"></property> 
</bean> 
<bean name="categoryMapper" class="com.liferunner.mapper.CategoryMapper"></bean>

注入之後,使用@Autowired,我們可以很方便的自動從IOC容器中查找屬性,並返回。

@Autowired的原理
在啟動spring IoC時,容器自動裝載了一個AutowiredAnnotationBeanPostProcessor後置處理器,當容器掃描到@Autowied、@Resource或@Inject時,就會在IoC容器自動查找需要的bean,並裝配給該對象的屬性。
注意事項:
在使用@Autowired時,首先在容器中查詢對應類型的bean
如果查詢結果剛好為一個,就將該bean裝配給@Autowired指定的數據
如果查詢的結果不止一個,那麼@Autowired會根據名稱來查找。
如果查詢的結果為空,那麼會拋出異常。解決方法時,使用required=false

上述三種註解方式,其實本質上還是注入的2種:set屬性注入 & 構造器注入,使用方式都可以,根據個人喜好來用,本人喜歡使用lombok插件注入是因為,它將代碼整合在一起,更加符合我們Spring自動注入的規範。

源碼下載

下節預告

下一節我們將繼續開發我們電商的核心部分-商品列表和詳情展示,在過程中使用到的任何開發組件,我都會通過專門的一節來進行介紹的,兄弟們末慌!

gogogo!

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

【其他文章推薦】

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

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

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

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

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

※試算大陸海運運費!

結合參數接收響應轉換原理講解SpringBoot常用註解

一、常用註解回顧

1.1 @RequestBody與@ResponseBody


//注意並不要求@RequestBody與@ResponseBody成對使用。
public @ResponseBody  AjaxResponse saveArticle(@RequestBody ArticleVO article)

如上代碼所示:

  • @RequestBody修飾請求參數,註解用於接收HTTP的body,默認是使用JSON的格式
  • @ResponseBody修飾返回值,註解用於在HTTP的body中攜帶響應數據,默認是使用JSON的格式。如果不加該註解,spring響應字符串類型,是跳轉到模板頁面或jsp頁面的開發模式。說白了:加上這個註解你開發的是一個數據接口,不加這個註解你開發的是一個頁面跳轉控制器。

那麼我們有一個問題:如果我們想接收或XML數據該怎麼辦?我們想響應excel的數據格式該怎麼辦?我們後文來回答這個問題。

1.2. @RequestMapping註解

@RequestMapping註解是所有常用註解中,最有看點的一個註解,用於標註HTTP服務端點。它的很多屬性對於豐富我們的應用開發方式方法,都有很重要的作用。如:

  • value: 應用請求端點,最核心的屬性,用於標誌請求處理方法的唯一性;
  • method: HTTP協議的method類型, 如:GET、POST、PUT、DELETE等;
  • consumes: HTTP協議請求內容的數據類型(Content-Type),例如application/json, text/html;
  • produces: HTTP協議響應內容的數據類型。下文會詳細講解。
  • params: HTTP請求中必須包含某些參數值的時候,才允許被註解標註的方法處理請求。
  • headers: HTTP請求中必須包含某些指定的header值,才允許被註解標註的方法處理請求。

@RequestMapping(value = "/article", method = POST)
@PostMapping(value = "/article")

上面代碼中兩種寫法起到的是一樣的效果,也就是PostMapping等同於@RequestMapping的method等於POST。同理:@GetMapping、@PutMapping、@DeleteMapping也都是簡寫的方式。

1.3. @RestController與@Controller

@Controller註解是開發中最常使用的註解,它的作用有兩層含義:

  • 一是告訴Spring,被該註解標註的類是一個Spring的Bean,需要被注入到Spring的上下文環境中。
  • 二是該類裏面所有被RequestMapping標註的註解都是HTTP服務端點。

@RestController相當於 @Controller和@ResponseBody結合。它有兩層含義:

  • 一是作為Controller的作用,將控制器類注入到Spring上下文環境,該類RequestMapping標註方法為HTTP服務端點。
  • 二是作為ResponseBody的作用,請求響應默認使用的序列化方式是JSON,而不是跳轉到jsp或模板頁面。

1.4. @PathVariable 與@RequestParam

PathVariable用於URI上的{參數},如下方法用於刪除一篇文章,其中id為文章id。如:我們的請求URL為“/article/1”,那麼將匹配DeleteMapping並且PathVariable接收參數id=1。而RequestParam用於接收普通表單方式或者ajax模擬表單提交的參數數據。

@DeleteMapping("/article/{id}")
public @ResponseBody AjaxResponse deleteArticle(@PathVariable Long id) {

@PostMapping("/article")
public @ResponseBody AjaxResponse deleteArticle(@RequestParam Long id) {

二、接收複雜嵌套對象參數

有一些朋友可能還無法理解RequestBody註解存在的真正意義,表單數據提交用RequestParam就好了,為什麼還要搞出來一個RequestBody註解呢?RequestBody註解的真正意義在於能夠使用對象或者嵌套對象接收前端數據。

仔細看上面的代碼,是一個paramData對象裡面包含了一個bestFriend對象。這種數據結構使用RequestParam就無法接收了,RequestParam只能接收平面的、一對一的參數。像上文中這種數據結構的參數,就需要我們在java服務端定義兩個類,一個類是ParamData,一個類是BestFriend.

public class ParamData {
    private String name;
    private int id;
    private String phone;
    private BestFriend bestFriend;
    
    public static class BestFriend {
        private String address;
        private String sex;
    }
}
  • 注意上面代碼中省略了GET、SET方法等必要的java plain model元素。
  • 注意成員變量名稱一定要和JSON屬性名稱對應上。
  • 注意接收不同類型的參數,使用不同的成員變量類型

完成以上動作,我們就可以使用@RequestBody ParamData paramData,一次性的接收以上所有的複雜嵌套對象參數了,參數對象的所有屬性都將被賦值。

三、Http數據轉換的原理

大家現在使用JSON都比較普遍了,其方便易用、表達能力強,是絕大部分數據接口式應用的首選。那麼如何響應其他的類型的數據?其中的判別原理又是什麼?下面就來給大家介紹一下:

  • 當一個HTTP請求到達時是一個InputStream,通過HttpMessageConverter轉換為java對象,從而進行參數接收。
  • 當對一個HTTP請求進行響應時,我們首先輸出的是一個java對象,然後由HttpMessageConverter轉換為OutputStream輸出。

當我們在Spring Boot應用中集成了jackson的類庫之後,如下的一些HttpMessageConverter將會被加載。

實現類 功能說明
StringHttpMessageConverter 將請求信息轉為字符串
FormHttpMessageConverter 將表單數據讀取到MultiValueMap中
XmlAwareFormHttpMessageConverter 擴展與FormHttpMessageConverter,如果部分表單屬性是XML數據,可用該轉換器進行讀取
ResourceHttpMessageConverter 讀寫org.springframework.core.io.Resource對象
BufferedImageHttpMessageConverter 讀寫BufferedImage對象
ByteArrayHttpMessageConverter 讀寫二進制數據
SourceHttpMessageConverter 讀寫java.xml.transform.Source類型的對象
MarshallingHttpMessageConverter 通過Spring的org.springframework,xml.Marshaller和Unmarshaller讀寫XML消息
Jaxb2RootElementHttpMessageConverter 通過JAXB2讀寫XML消息,將請求消息轉換為標註的XmlRootElement和XmlType連接的類中
MappingJacksonHttpMessageConverter 利用Jackson開源包的ObjectMapper讀寫JSON數據
RssChannelHttpMessageConverter 讀寫RSS種子消息
AtomFeedHttpMessageConverter 和RssChannelHttpMessageConverter能夠讀寫RSS種子消息

根據HTTP協議的Accept和Content-Type屬性,以及參數數據類型來判別使用哪一種HttpMessageConverter。當使用RequestBody或ResponseBody時,再結合前端發送的Accept數據類型,會自動判定優先使用MappingJacksonHttpMessageConverter作為數據轉換器。但是,不僅JSON可以表達對象數據類型,XML也可以。如果我們希望使用XML格式該怎麼告知Spring呢,那就要使用到produces屬性了。

@GetMapping(value ="/demo",produces = MediaType.APPLICATION_XML_VALUE)

這裏我們明確的告知了返回的數據類型是xml,就會使用Jaxb2RootElementHttpMessageConverter作為默認的數據轉換器。當然實現XML數據響應比JSON還會更複雜一些,還需要結合@XmlRootElement、@XmlElement等註解實體類來使用。同理consumes屬性你是不是也會用了呢。

四、自定義HttpMessageConverter

其實絕大多數的數據格式都不需要我們自定義HttpMessageConverter,都有第三方類庫可以幫助我們實現(包括下文代碼中的Excel格式)。但有的時候,有些數據的輸出格式並沒有類似於Jackson這種類庫幫助我們處理,需要我們自定義數據格式。該怎麼做?下面代碼只是幫助我們理解的一個例子,不要用於生產:

@Service
public class TeamToXlsConverter extends AbstractHttpMessageConverter<Team> {

    private static final MediaType EXCEL_TYPE = MediaType.valueOf("application/vnd.ms-excel");

    TeamToXlsConverter() {
        super(EXCEL_TYPE);
    }

    @Override
    protected Team readInternal(final Class<? extends Team> clazz, final HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
        return null;
    }

    @Override
    protected boolean supports(final Class<?> clazz) {
        return (Team.class == clazz);
    }

    @Override
    protected void writeInternal(final Team team, final HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
        try (final Workbook workbook = new HSSFWorkbook()) {
            final Sheet sheet = workbook.createSheet();
            int rowNo = 0;
            for (final TeamMember member : team.getMembers()) {
                final Row row = sheet.createRow(rowNo++);
                row.createCell(0)
                   .setCellValue(member.getName());
            }
            workbook.write(outputMessage.getBody());
        }
    }
}
  • 實現AbstractHttpMessageConverter接口
  • 指定該轉換器是針對哪種數據格式的?如上文代碼中的”application/vnd.ms-excel”
  • 指定該轉換器針對那些對象數據類型?如上文代碼中的supports函數
  • 使用writeInternal對數據進行輸出處理,上例中是輸出為Excel格式。

    期待您的關注

  • 博主最近新寫了一本書:
  • 本文轉載註明出處(必須帶連接,不能只轉文字):。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】

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

※網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦、台中網頁設計公司推薦專業設計師”嚨底家”!!

※專營大陸快遞台灣服務

※台灣快遞大陸的貨運公司有哪些呢?

[ASP.NET Core 3框架揭秘] 文件系統[1]:抽象的“文件系統”

ASP.NET Core應用 具有很多讀取文件的場景,比如配置文件、靜態Web資源文件(比如CSS、JavaScript和圖片文件等)以及MVC應用的View文件,甚至是直接編譯到程序集中的內嵌資源文件。這些文件的讀取都需要使用到一個IFileProvider對象。IFileProvider對象構建了一個抽象的文件系統,我們不僅可以利用它提供的統一API來讀取各種類型的文件,還能及時監控目標文件的變化。

一、樹形層次結構

IFileProvider對象為我們構建了一個具有層次化目錄結構的文件系統。由於IFileProvider是一個接口,所以由它構建的是一個抽象化的文件系統,這裏所謂的目錄和文件都是一個抽象的概念。具體的文件可能對應一個物理文件,也可能保存在數據庫中,或者來源於網絡,甚至有可能根本就不存在,其內容需要在讀取時動態生成。目錄也僅僅是組織文件的邏輯容器。為了讓讀者朋友們對這個文件系統有一個大體認識,我們先來演示幾個簡單的實例。

文件系統管理的所有文件以目錄的形式進行組織,一個IFileProvider對象可以視為針對一個根目錄的映射。目錄除了可以存放文件之外,還可以包含子目錄,所以目錄/文件在整體上呈現出樹形化層次化結構。接下來我們將一個IFileProvider對象映射到一個物理目錄,並利用它將所在目錄的結構呈現出來。

我們演示實例是一個普通的控制台程序。我們在演示實例中定義了如下一個IFileManager接口,它利用一個唯一的ShowStructure方法將文件系統的整體結構显示出來。該方法具有一個類型為Action<int, string>的參數負責將文件系統的節點(目錄或者文件)名稱呈現出來。這個Action<int, string>對象的兩個參數分別代表縮進的層級和目錄/文件的名稱。

public interface IFileManager
{
    void ShowStructure(Action<int, string> render);
}

我們定義如下這個FileManager類作為對IFileManager接口的默認實現,它利用只讀_fileProvider字段表示的IFileProvider對象來提取目錄結構。目標文件系統的整體結構通過Render方法以遞歸的方式呈現出來,其中涉及到對IFileProvider對象的GetDirectoryContents方法的調用。該方法返回一個IDirectoryContents對象表示指定目錄的內容,如果對應的目錄存在,我們可以遍歷該對象得到它的子目錄和文件。目錄和文件最終體現為一個IFileInfo對象來,至於IFileInfo對象對應的就是一個目錄還是一個文件,則通過其IsDirectory屬性來區分。

public class FileManager : IFileManager
{
    private readonly IFileProvider _fileProvider;
    public FileManager(IFileProvider fileProvider) => _fileProvider = fileProvider;
    public void ShowStructure(Action<int, string> render)
    {
        int indent = -1;
        Render("");
        void Render(string subPath)
        {
            indent++;
            foreach (var fileInfo in _fileProvider.GetDirectoryContents(subPath))
            {
                render(indent, fileInfo.Name);
                if (fileInfo.IsDirectory)
                {
                    Render($@"{subPath}\{fileInfo.Name}".TrimStart('\\'));
                }
            }
            indent--;
        }
    }        
}

接下來我們構建一個本地物理目錄“c:\test\”,並按照如下圖所示的結構在它下面創建相應的子目錄和文件。我們會將這個目錄映射到一個IFileProvider對象上,並進一步利用它創建出上面這個FileManager對象。我們最終調用這個FileManager對象的ShowStructure方法將目錄結構呈現出來。

整個演示程序體現在如下的代碼片段中。我們針對目錄“c:\test\”創建了一個表示物理文件系統的PhysicalFileProvider對象,並將其註冊到創建的ServiceCollection對象上。除此之外,ServiceCollection對象上還添加了針對IFileManager/FileManager的服務註冊。

class Program
{
    static void Main()
    {
        static void Print(int layer, string name)  => Console.WriteLine($"{new string(' ', layer * 4)}{name}");        
        new ServiceCollection()
            .AddSingleton<IFileProvider>(new PhysicalFileProvider(@"c:\test"))
            .AddSingleton<IFileManager, FileManager>()
            .BuildServiceProvider()
            .GetRequiredService<IFileManager>()
            .ShowStructure(Print);
    }
}

我們最終利用ServiceCollection生成的IServiceProvider對象得到FileManager對象,並調用該對象的ShowStructure方法將PhysicalFileProvider對象映射的目錄結構呈現出來。當我們運行該程序之後,控制台上將呈現出如下圖所示的輸出結果,該結果為我們展示了映射物理目錄的真實結構。(S501)

二、讀取文件內容

前面我們演示了如何利用IFileProvider對象將文件系統的結構完整地呈現出來,接下來我們來演示如何利用它來讀取一個物理文件的內容。我們為IFileManager定義如下一個ReadAllTextAsync方法以異步的方式讀取指定文件內容,方法的參數表示文件的路徑。如下面的代碼片段所示,ReadAllTextAsync方法將指定的文件路徑作為參數調用IFileProvider對象的GetFileInfo方法得到一個IFileInfo對象。我們最終調用這個IFileInfo對象的CreateReadStream方法得到讀取文件的輸出流,進而得到文件的真實內容。

public interface IFileManager
{
    ...
    Task<string> ReadAllTextAsync(string path);
}

public class FileManager : IFileManager
{
    ...
    public async Task<string> ReadAllTextAsync(string path)
    {
        byte[] buffer;
        using (var stream = _fileProvider.GetFileInfo(path).CreateReadStream())
        {
            buffer = new byte[stream.Length];
            await stream.ReadAsync(buffer, 0, buffer.Length);
        }
        return Encoding.Default.GetString(buffer);
    }
}

假設我們依然將FileManager使用的IFileProvider映射為目錄“c:\test\”,現在我們在該目錄中創建一個名為data.txt的文本文件,並在該文件中任意寫入一些內容。接下來我們在Main方法中編寫了如下的程序利用依賴注入的方式得到FileManager對象,並讀取文件data.txt的內容。最終的調試斷言旨在確定通過IFileProvider讀取的確實就是目標文件的真實內容。(S502)

class Program
{
    static async Task Main()
    {
        var content = await new ServiceCollection()
            .AddSingleton<IFileProvider>(new PhysicalFileProvider(@"c:\test"))
            .AddSingleton<IFileManager, FileManager>()
            .BuildServiceProvider()
            .GetRequiredService<IFileManager>()
            .ReadAllTextAsync("data.txt");

        Debug.Assert(content == File.ReadAllText(@"c:\test\data.txt"));
    }
}

三、內嵌文件系統

我們一直在強調由IFileProvider結構構建的是一個抽象的具有目錄結構的文件系統,具體文件的提供方式取決於對具體的IFileProvider對象是怎樣一個類型。我們演示實例定義的FileManager並沒有限定具體使用何種類型的IFileProvider,該對象是在應用中通過依賴注入的方式指定的。由於上面的應用程序注入的是一個PhysicalFileProvider對象,所以我們可以利用它來讀取對應物理目錄下的某個文件。假設現在將這個data.txt直接以資源文件的形式編譯到程序集中,我們就需要使用另一個名為EmbeddedFileProvider的實現類型。現在我們直接將這個data.txt文件添加到控制台應用的項目根目錄下。在默認的情況下,當我們編譯項目的時候這樣的文件並不能成為內嵌到目標程序集的資源文件,我們需要利用VS將該文件的“Build Action”屬性按照如下所示的方式設置為“Embedded resource”。

上圖所示的設置將會體現在項目文件(.csproj文件)上。具體來說,項目文件會以如下的形式添加一個<EmbeddedResource>元素將文件data.txt設置為內嵌到編譯後生成的程序集的內嵌資源文件。

<Project Sdk="Microsoft.NET.Sdk">
  ...
  <ItemGroup>
      <EmbeddedResource Include="data.txt"/>   
  </ItemGroup>
</Project>

我們編寫了如下的程序來演示針對內嵌於程序集中的資源文件的讀取。我們首先得到當前入口程序集,並利用它創建了一個EmbeddedFileProvider對象,它代替原來的PhysicalFileProvider對象被註冊到ServiceCollection之中。我們接下來採用了完全一致的編程方式得到FileManager對象並利用它讀取內嵌文件data.txt的內容。為了驗證讀取的目標文件準確無誤,我們採用直接讀取資源文件的方式得到了內嵌文件data.txt的內容,並利用一個調試斷言確定兩者的一致性。(S503)

class Program
{
    static async Task Main()
    {
        var assembly = Assembly.GetEntryAssembly();

        var content1 = await new ServiceCollection()
            .AddSingleton<IFileProvider>(new EmbeddedFileProvider(assembly))
            .AddSingleton<IFileManager, FileManager>()
            .BuildServiceProvider()
            .GetRequiredService<IFileManager>()
            .ReadAllTextAsync("data.txt");

        var stream = assembly.GetManifestResourceStream($"{assembly.GetName().Name}.data.txt");
        var buffer = new byte[stream.Length];
        stream.Read(buffer, 0, buffer.Length);
        var content2 = Encoding.Default.GetString(buffer);

        Debug.Assert(content1 == content2);
    }
}

四、監控文件的變化

在文件讀取場景中,確定加載到內存中的數據與源文件的一致性並自動同步是一個很常見的需求。比如說我們將配置定義在一個JSON文件中,應用啟動的時候會讀取該文件並將其轉換成對應的Options對象。在很多情況下,如果我們改動了配置文件, 最新的配置數據只有在應用重啟之後才能生效。如果我們能夠以一種高效的方式對配置文件進行監控,並在其發生改變的情況下嚮應用發送通知,那麼應用就能在不用重啟的情況下重新讀取配置文件,進而實現Options對象承載的內容和原始配置文件完全同步。

對文件系統實施監控並在其發生改變時發送通知也是IFileProvider對象提供的核心功能之一。接下來我們依然使用前面這個程序來演示如何使用PhysicalFileProvider對某個物理文件實施監控,並在目標文件的內容發生改變的時候重新讀取新的內容。

class Program
{
    static async Task Main()
    {
        using (var fileProvider = new PhysicalFileProvider(@"c:\test"))
        {
            string original = null;
            ChangeToken.OnChange(() => fileProvider.Watch("data.txt"), Callback);
            while (true)
            {
                File.WriteAllText(@"c:\test\data.txt", DateTime.Now.ToString());
                await Task.Delay(5000);
            }

            async void Callback()
            {
                var stream = fileProvider.GetFileInfo("data.txt").CreateReadStream();
                {
                    var buffer = new byte[stream.Length];
                    await stream.ReadAsync(buffer, 0, buffer.Length);
                    string current = Encoding.Default.GetString(buffer);
                    if (current != original)
                    {
                        Console.WriteLine(original = current);
                    }
                }
            }
        }
    }
}

如上面的代碼片段所示,我們針對目錄“c:\test”創建了一個PhysicalFileProvider對象,並調用其Watch方法對指定的文件data.txt實施監控。該方法的返回一個IChangeToken對象,我們正是利用這個對象接收文件改變的通知。我們調用ChangeToken的靜態方法OnChange針對這個對象註冊了一個回調實現對源文件的重新讀取和显示,當源文件發生改變的時候,註冊的回調會自動執行。我們以每隔5秒的間隔對文件data.txt作一次修改,而文件的內容為當前時間。所以當我們的程序啟動之後,每隔5秒鐘當前時間就會以如下圖的方式呈現在控制台上。

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

【其他文章推薦】

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

※網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦、台中網頁設計公司推薦專業設計師”嚨底家”!!

※大陸寄台灣空運注意事項

※大陸海運台灣交貨時間多久?

亞馬遜粉紅豚 外表夢幻屢遭漁夫獵捕

摘錄自2020年3月5日公視報導

腹部泛著粉紅漂亮膚色的亞馬遜淡水豚,動保專家率領的團隊將牠撈捕上岸,進行檢測、採樣、烙印標記以利追蹤的作業後,再把牠放回大自然。亞馬遜淡水豚是世界上體型最大的淡水豚,成年後體色從深灰轉淡變成粉紅,當牠改變行為或經陽光照射,膚色也會跟著改變,就像人會臉紅一樣。

亞馬遜粉紅豚的孕育期長達13個月,河豚寶寶出生後,有兩年時間需要母親照顧,由於哺育期比較長,母河豚每隔三到五年才會生育,加上每年約有2500隻粉紅豚遭到漁夫獵殺,做為獵捕另一種大型鯰魚的誘餌,這種淡水豚已經列入易危名單。當地檢方在2015年提出禁捕鯰魚的禁令,希望保護粉紅豚,不過禁漁令上個月到期,動保專家疾呼應該展延。

動保專家擔心,如果不趕快採取有效行動,亞馬遜粉紅豚恐怕會重演長江白鱀豚於2006年滅絕的悲劇。

物種保育
海洋
生態保育
國際新聞
亞馬遜

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

【其他文章推薦】

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

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

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

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

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

※試算大陸海運運費!

疫情衝擊助減碳 學者籲國際合作

摘錄自2020年3月8日台灣醒報報導

新冠肺炎(COVID-19,武漢肺炎)肆虐全球,但長期來看有助於對抗氣候變遷?美國外交關係協會主任傑飛指出,諸如在家工作、視訊會議或上下班錯開等防疫行為,均可以減緩氣候變遷。此外,為了因應全球供應鏈斷裂,企業或許考慮將生產「在地化」,此舉也可避免受到氣候變遷的衝擊。

不過,赫爾辛基大學能源和清潔空氣中心的梅利維爾擔心,當疫情過去後工商業復甦又恢復到以往的排碳量。以中國為例,2008年金融海嘯一度造成排碳量大減,但之後政府大興土木以刺激經濟,使得排碳量不減反增。

佛蒙特大學岡德環境研究所埃里克森指出,與其在面對危機時被迫減碳,我們還有5至10年的時間,將經濟轉型成低碳產業,同時令經濟衝擊降到最低。對此,埃里克森認為,對抗氣候變遷需要與抗疫相同程度的國際合作。

生活環境
公共衛生
全球變遷
國際新聞
武漢肺炎
減碳

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

【其他文章推薦】

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

※網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦、台中網頁設計公司推薦專業設計師”嚨底家”!!

※專營大陸快遞台灣服務

※台灣快遞大陸的貨運公司有哪些呢?

企鵝出沒澳南島嶼 不時迷路跑到本土

摘錄自2020年3月10日公視報導

南半球的澳洲靠近南極洲附近的島嶼,經常會有野生企鵝出沒,有些企鵝迷路,就會不小心跑到澳洲本土的海灘上,吸引民眾圍觀。野生動物專家呼籲,千萬不要餵食企鵝,否則可能有害健康,也會妨礙牠們回歸大自然。

澳洲南部靠近南極洲的島嶼,總共有八種企鵝出沒,專家近年來發現經常有迷路的企鵝,跑到不該出現的澳洲本土岸邊。在伯斯附近的瑪格麗特河小鎮,就有一處企鵝保育基地,專門收容這些需要幫助的企鵝。

專家說,一般民眾如果發現落單或迷路的企鵝,可以趕快通知野生動物管理人員,但最好不要亂餵食。莫爾解釋,「在牠們獲得照料前,你不需要給牠們水或食物,只有照護人員知道什麼是適當的食物。」

生態保育
國際新聞
澳洲
企鵝

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

【其他文章推薦】

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

※網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦、台中網頁設計公司推薦專業設計師”嚨底家”!!

※大陸寄台灣空運注意事項

※大陸海運台灣交貨時間多久?

※避免吃悶虧無故遭抬價!台中搬家公司免費估價,有契約讓您安心有保障!

德國SMA推出新型儲能2.5逆變器「陽光男孩」

德國太陽能逆變器巨頭SMA推出其新型「陽光男孩」儲能2.5(Sunny Boy Storage 2.5)逆變器,可與特斯拉的Powerwall系統結合,將「陽光男孩」蓄電成本減半。

SMA表示,這款新型逆變器 Sunny Boy Storage 2.5將於5月份投入德國市場,而歐洲和澳大利亞推出時間更晚一些。

此高壓蓄電裝置是一個AC耦合定制串式逆變器,可讓電池與標準的太陽能串式逆變器相連,也可以和家庭和電網相連。「陽光男孩」儲能2.5容量為7kWh,通過德國批發商進行銷售。SMA表示,到今年中期,其他廠商的高壓電池將與SMA的電力解決方案相結合。

該SMA設備單相運行,最大放電功率為2.5kW。可以並聯,可以與Sunny Home Manager的能量管理共同協調,並且可以在戶外安裝。

相比於SMA現有的交流耦合儲能產品「陽光冰島」(Sunny Island)的價格,「陽光男孩」儲能2.5的售價低了56%。因為「陽光冰島」有些必備的部件對於儲能2.5而言根本不需要。根據「陽光冰島」目前網上商店的價格,「陽光男孩」儲能2.5售價不會超過1,000歐元,這對於儲能系統而言並不算貴。

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

【其他文章推薦】

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

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

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

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

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

※試算大陸海運運費!