Spring入門這一篇就夠了

Java3y發表於2019-06-23


前言

前面已經學習了Struts2和Hibernate框架了。接下來學習的是Spring框架…本博文主要是引入Spring框架…

Spring介紹

Spring誕生:

  • 建立Spring的目的就是用來替代更加重量級的的企業級Java技術

  • 簡化Java的開發

    • 基於POJO輕量級和最小侵入式開發

    • 通過依賴注入和麵向介面實現鬆耦合

    • 基於切面和慣例進行宣告式程式設計

    • 通過切面和模板**減少樣板式程式碼 **

侵入式概念

Spring是一種非侵入式的框架

侵入式

  • 對於EJB、Struts2等一些傳統的框架,通常是要實現特定的介面,繼承特定的類才能增強功能

    • 改變了java類的結構

非侵入式

  • 對於Hibernate、Spring等框架,對現有的類結構沒有影響,就能夠增強JavaBean的功能


鬆耦合

前面我們在寫程式的時候,都是面向介面程式設計,通過DaoFactroy等方法來實現鬆耦合

    private CategoryDao categoryDao = DaoFactory.getInstance().createDao("zhongfucheng.dao.impl.CategoryDAOImpl", CategoryDao.class);

   private BookDao bookDao = DaoFactory.getInstance().createDao("zhongfucheng.dao.impl.BookDaoImpl", BookDao.class);

   private UserDao userDao = DaoFactory.getInstance().createDao("zhongfucheng.dao.impl.UserDaoImpl", UserDao.class);

   private OrderDao orderDao = DaoFactory.getInstance().createDao("zhongfucheng.dao.impl.OrderDaoImpl", OrderDao.class);
Spring入門這一篇就夠了

DAO層和Service層通過DaoFactory來實現鬆耦合

  • 如果Serivce層直接new DaoBook(),那麼DAO和Service就緊耦合了【Service層依賴緊緊依賴於Dao】

而Spring給我們更加合適的方法來實現鬆耦合,並且更加靈活、功能更加強大!---->IOC控制反轉

切面程式設計

切面程式設計也就是AOP程式設計,其實我們在之前也接觸過…動態代理就是一種切面程式設計了

當時我們使用動態代理+註解的方式給Service層的方法新增許可權.

    @Override
   @permission("新增分類")
   /*新增分類*/
   public void addCategory(Category category) {
       categoryDao.addCategory(category);
   }


   /*查詢分類*/
   @Override
   public void findCategory(String id) {
       categoryDao.findCategory(id);
   }

   @Override
   @permission("查詢分類")
   /*檢視分類*/
   public List<Category> getAllCategory() {
       return categoryDao.getAllCategory();
   }

   /*新增圖書*/
   @Override
   public void addBook(Book book) {
       bookDao.addBook(book);

   }
  • Controller呼叫Service的時候,Service返回的是一個代理物件

  • 代理物件得到Controller想要呼叫的方法,通過反射來看看該方法上有沒有註解

  • 如果有註解的話,那麼就判斷該使用者是否有許可權來呼叫 此方法,如果沒有許可權,就丟擲異常給Controller,Controller接收到異常,就可以提示使用者沒有許可權了。

AOP程式設計可以簡單理解成:在執行某些程式碼前,執行另外的程式碼

  • Struts2的攔截器也是面向切面程式設計【在執行Action業務方法之前執行攔截器】

Spring也為我們提供更好地方式來實現面向切面程式設計


引出Spring

我們試著回顧一下沒學Spring的時候,是怎麼開發Web專案的

  • 1. 實體類--->class User{ }

  • 2. daoclass-->  UserDao{  .. 訪問db}

  • 3. service--->class  UserService{  UserDao userDao = new UserDao();}

  • 4. actionclass  UserAction{UserService userService = new UserService();}

使用者訪問:

  • Tomcat->action->service->dao

我們來思考幾個問題:

  • ①:物件建立建立能否寫死?

  • ②:物件建立細節

    • action    訪問時候建立

    • service   啟動時候建立

    • dao       啟動時候建立

    • action  多個   【維護成員變數】

    • service 一個   【不需要維護公共變數】

    • dao     一個   【不需要維護公共變數】

    • 物件數量

    • 建立時間

  • ③:物件的依賴關係

    • action 依賴 service

    • service依賴 dao

對於第一個問題和第三個問題,我們可以通過DaoFactory解決掉(雖然不是比較好的解決方法)

對於第二個問題,我們要控制物件的數量和建立事件就有點麻煩了….

Spring框架通過IOC就很好地可以解決上面的問題….

IOC控制反轉

Spring的核心思想之一:Inversion of Control , 控制反轉 IOC

那麼控制反轉是什麼意思呢???物件的建立交給外部容器完成,這個就做控制反轉。

  • Spring使用控制反轉來實現物件不用在程式中寫死

  • 控制反轉解決物件處理問題【把物件交給別人建立】

那麼物件的物件之間的依賴關係Spring是怎麼做的呢??依賴注入,dependency injection.

  • Spring使用依賴注入來實現物件之間的依賴關係

  • 在建立完物件之後,物件的關係處理就是依賴注入

上面已經說了,控制反轉是通過外部容器完成的,而Spring又為我們提供了這麼一個容器,我們一般將這個容器叫做:IOC容器.

無論是建立物件、處理物件之間的依賴關係、物件建立的時間還是物件的數量,我們都是在Spring為我們提供的IOC容器上配置物件的資訊就好了。

那麼使用IOC控制反轉這一思想有什麼作用呢???我們來看看一些優秀的回答…

來自知乎:https://www.zhihu.com/question/23277575/answer/24259844

我摘取一下核心的部分:

ioc的思想最核心的地方在於,資源不由使用資源的雙方管理,而由不使用資源的第三方管理,這可以帶來很多好處。第一,資源集中管理,實現資源的可配置和易管理第二,降低了使用資源雙方的依賴程度,也就是我們說的耦合度

也就是說,甲方要達成某種目的不需要直接依賴乙方,它只需要達到的目的告訴第三方機構就可以了,比如甲方需要一雙襪子,而乙方它賣一雙襪子,它要把襪子賣出去,並不需要自己去直接找到一個賣家來完成襪子的賣出。它也只需要找第三方,告訴別人我要賣一雙襪子。這下好了,甲乙雙方進行交易活動,都不需要自己直接去找賣家,相當於程式內部開放介面,賣家由第三方作為引數傳入。甲乙互相不依賴,而且只有在進行交易活動的時候,甲才和乙產生聯絡。反之亦然。這樣做什麼好處麼呢,甲乙可以在對方不真實存在的情況下獨立存在,而且保證不交易時候無聯絡,想交易的時候可以很容易的產生聯絡。甲乙交易活動不需要雙方見面,避免了雙方的互不信任造成交易失敗的問題。因為交易由第三方來負責聯絡,而且甲乙都認為第三方可靠。那麼交易就能很可靠很靈活的產生和進行了。這就是ioc的核心思想。生活中這種例子比比皆是,支付寶在整個淘寶體系裡就是龐大的ioc容器,交易雙方之外的第三方,提供可靠性可依賴可靈活變更交易方的資源管理中心。另外人事代理也是,僱傭機構和個人之外的第三方。
==========================update===========================

在以上的描述中,誕生了兩個專業詞彙,依賴注入和控制反轉所謂的依賴注入,則是,甲方開放介面,在它需要的時候,能夠講乙方傳遞進來(注入)所謂的控制反轉,甲乙雙方不相互依賴,交易活動的進行不依賴於甲乙任何一方,整個活動的進行由第三方負責管理。

參考優秀的博文①:https://www.tianmaying.com/tutorial/spring-ioc

參考優秀的博文②:這裡寫連結內容

知乎@Intopass的回答:

  1. 不用自己組裝,拿來就用。

  2. 享受單例的好處,效率高,不浪費空間。

  3. 便於單元測試,方便切換mock元件。

  4. 便於進行AOP操作,對於使用者是透明的。

  5. 統一配置,便於修改。


Spring模組

Spring可以分為6大模組:

  • Spring Core  spring的核心功能: IOC容器, 解決物件建立及依賴關係

  • Spring Web  Spring對web模組的支援。

    • 可以與struts整合,讓struts的action建立交給spring

    • spring mvc模式

  • Spring DAO  Spring 對jdbc操作的支援  【JdbcTemplate模板工具類】

  • Spring ORM  spring對orm的支援:

    • 既可以與hibernate整合,【session】

    • 也可以使用spring的對hibernate操作的封裝

  • Spring AOP  切面程式設計

  • SpringEE   spring 對javaEE其他模組的支援

Spring入門這一篇就夠了

上面文主要引出了為啥我們需要使用Spring框架,以及大致瞭解了Spring是分為六大模組的….下面主要講解Spring的core模組!

Core模組快速入門

搭建配置環境

引入jar包:

本博文主要是core模組的內容,涉及到Spring core的開發jar包有五個

  • commons-logging-1.1.3.jar           日誌

  • spring-beans-3.2.5.RELEASE.jar        bean節點

  • spring-context-3.2.5.RELEASE.jar       spring上下文節點

  • spring-core-3.2.5.RELEASE.jar         spring核心功能

  • spring-expression-3.2.5.RELEASE.jar    spring表示式相關表

我主要使用的是Spring3.2版本

編寫配置檔案:

Spring核心的配置檔案applicationContext.xml或者叫bean.xml

那這個配置檔案怎麼寫呢??一般地,我們都知道框架的配置檔案都是有約束的…我們可以在spring-framework-3.2.5.RELEASE\docs\spring-framework-reference\htmlsingle\index.html找到XML配置檔案的約束

<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:p="http://www.springframework.org/schema/p"
   xmlns:context="http://www.springframework.org/schema/context"
   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"
>


</beans>  

我是使用Intellij Idea整合開發工具的,可以選擇自帶的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"
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">


</beans>

前面在介紹Spring模組的時候已經說了,Core模組是:IOC容器,解決物件建立和之間的依賴關係

因此Core模組主要是學習如何得到IOC容器,通過IOC容器來建立物件、解決物件之間的依賴關係、IOC細節。

得到Spring容器物件【IOC容器】

Spring容器不單單隻有一個,可以歸為兩種型別

  • **Bean工廠,BeanFactory【功能簡單】 **

  • 應用上下文,ApplicationContext【功能強大,一般我們使用這個】

通過Resource獲取BeanFactory

  • 載入Spring配置檔案

  • 通過XmlBeanFactory+配置檔案來建立IOC容器

        //載入Spring的資原始檔
       Resource resource = new ClassPathResource("applicationContext.xml");

       //建立IOC容器物件【IOC容器=工廠類+applicationContext.xml】
       BeanFactory beanFactory = new XmlBeanFactory(resource);

類路徑下XML獲取ApplicationContext

  • 直接通過ClassPathXmlApplicationContext物件來獲取

        // 得到IOC容器物件
       ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");

       System.out.println(ac);

在Spring中總體來看可以通過三種方式來配置物件:

  • 使用XML檔案配置

  • 使用註解來配置

  • 使用JavaConfig來配置

XML配置方式

在上面我們已經可以得到IOC容器物件了。接下來就是在applicationContext.xml檔案中配置資訊【讓IOC容器根據applicationContext.xml檔案來建立物件】

  • 首先我們先有個JavaBean的類

/**
* Created by ozc on 2017/5/10.
*/

public class User {

   private String id;
   private String username;


   public String getId() {
       return id;
   }

   public void setId(String id) {
       this.id = id;
   }

   public String getUsername() {
       return username;
   }

   public void setUsername(String username) {
       this.username = username;
   }
}
  • 以前我們是通過new User的方法建立物件的….

    User user = new User();
  • 現在我們有了IOC容器,可以讓IOC容器幫我們建立物件了。在applicationContext.xml檔案中配置對應的資訊就行了

       <!--
       使用bean節點來建立物件
           id屬性標識著物件
           name屬性代表著要建立物件的類全名
       -->

   <bean id="user" class="User"/>

通過IOC容器物件獲取物件:

  • 在外界通過IOC容器物件得到User物件

        // 得到IOC容器物件
       ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");

       User user = (User) ac.getBean("user");

       System.out.println(user);
Spring入門這一篇就夠了

上面我們使用的是IOC通過無參建構函式來建立物件,我們來回顧一下一般有幾種建立物件的方式:

  • 無參建構函式建立物件

  • 帶引數的建構函式建立物件

  • 工廠建立物件

    • 靜態方法建立物件

    • 非靜態方法建立物件

使用無參的建構函式建立物件我們已經會了,接下來我們看看使用剩下的IOC容器是怎麼建立物件的。

帶引數的建構函式建立物件

首先,JavaBean就要提供帶引數的建構函式:

    public User(String id, String username) {
       this.id = id;
       this.username = username;
   }

接下來,關鍵是怎麼配置applicationContext.xml檔案了。

    <bean id="user" class="User">

       <!--通過constructor這個節點來指定建構函式的引數型別、名稱、第幾個-->
       <constructor-arg index="0" name="id" type="java.lang.String" value="1"></constructor-arg>
       <constructor-arg index="1" name="username" type="java.lang.String" value="zhongfucheng"></constructor-arg>
   </bean>
Spring入門這一篇就夠了

在constructor上如果建構函式的值是一個物件,而不是一個普通型別的值,我們就需要用到ref屬性了,而不是value屬性

比如說:我在User物件上維護了Person物件的值,想要在建構函式中初始化它。因此,就需要用到ref屬性了

    <bean id="person" class="Person"></bean> 

   <bean id="user" class="User" >

       <!--通過constructor這個節點來指定建構函式的引數型別、名稱、第幾個-->
       <constructor-arg index="0" name="id" type="java.lang.String" value="1"></constructor-arg>
       <constructor-arg index="1" name="username" type="java.lang.String" ref="person"></constructor-arg>
   </bean>

工廠靜態方法建立物件

首先,使用一個工廠的靜態方法返回一個物件

public class Factory {

   public static User getBean() {

       return new User();
   }

}

配置檔案中使用工廠的靜態方法返回物件

    <!--工廠靜態方法建立物件,直接使用class指向靜態類,指定靜態方法就行了-->
   <bean id="user" class="Factory" factory-method="getBean" >

   </bean>
Spring入門這一篇就夠了

工廠非靜態方法建立物件

首先,也是通過工廠的非非靜態方法來得到一個物件

public class Factory {


   public User getBean() {

       return new User();
   }


}

配置檔案中使用工廠的非靜態方法返回物件

    <!--首先建立工廠物件-->
   <bean id="factory" class="Factory"/>

   <!--指定工廠物件和工廠方法-->
   <bean id="user" class="User" factory-bean="factory" factory-method="getBean"/>
Spring入門這一篇就夠了

c名稱空間

我們在使用XML配置建立Bean的時候,如果該Bean有構造器,那麼我們使用<constructor-arg>這個節點來對構造器的引數進行賦值…

<constructor-arg>未免有點太長了,為了簡化配置,Spring來提供了c名稱空間…

要想c名稱空間是需要匯入xmlns:c="http://www.springframework.org/schema/c"

    <bean id="userService" class="bb.UserService" c:userDao-ref="">

   </bean>

c名稱空間有個缺點:不能裝配集合,當我們要裝配集合的時候還是需要<constructor-arg>這個節點

裝載集合

如果物件上的屬性或者建構函式擁有集合的時候,而我們又需要為集合賦值,那麼怎麼辦?

  • 在建構函式上,普通型別

    <bean id="userService" class="bb.UserService" >
       <constructor-arg >
           <list>
               //普通型別
               <value></value>
           </list>
       </constructor-arg>
   </bean>
  • 在屬性上,引用型別

     <property name="userDao">

        <list>
            <ref></ref>
        </list>
    </property>

註解方式

自從jdk5有了註解這個新特性,我們可以看到Struts2框架、Hibernate框架都支援使用註解來配置資訊…

通過註解來配置資訊就是為了簡化IOC容器的配置,註解可以把物件新增到IOC容器中、處理物件依賴關係,我們來看看怎麼用吧:

使用註解步驟:

  • 1)先引入context名稱空間

    • xmlns:context="http://www.springframework.org/schema/context"

  • 2)開啟註解掃描器

    • <context:component-scan base-package=""></context:component-scan>

    • 第二種方法:也可以通過自定義掃描類以@CompoentScan修飾來掃描IOC容器的bean物件。。如下程式碼:

//表明該類是配置類
@Configuration

//啟動掃描器,掃描bb包下的
   //也可以指定多個基礎包
   //也可以指定型別
@ComponentScan("bb")
public class AnnotationScan {

}

在使用@ComponentScan()這個註解的時候,在測試類上需要@ContextConfiguration這個註解來載入配置類…

  • @ContextConfiguration這個註解又在Spring的test包下..


建立物件以及處理物件依賴關係,相關的註解:

  • @ComponentScan掃描器

  • @Configuration表明該類是配置類

  • @Component   指定把一個物件加入IOC容器--->@Name也可以實現相同的效果【一般少用】

  • @Repository   作用同@Component; 在持久層使用

  • @Service      作用同@Component; 在業務邏輯層使用

  • @Controller    作用同@Component; 在控制層使用

  • @Resource  依賴關係

    • 如果@Resource不指定值,那麼就根據型別來找,相同的型別在IOC容器中不能有兩個

    • 如果@Resource指定了值,那麼就根據名字來找

測試程式碼:

  • UserDao

package aa;

import org.springframework.stereotype.Repository;

/**
* Created by ozc on 2017/5/10.
*/


//把物件新增到容器中,首字母會小寫
@Repository
public class UserDao {

   public void save() {
       System.out.println("DB:儲存使用者");
   }


}
  • userService

package aa;


import org.springframework.stereotype.Service;

import javax.annotation.Resource;


//把UserService物件新增到IOC容器中,首字母會小寫
@Service
public class UserService {

   //如果@Resource不指定值,那麼就根據型別來找--->UserDao....當然了,IOC容器不能有兩個UserDao型別的物件
   //@Resource

   //如果指定了值,那麼Spring就在IOC容器找有沒有id為userDao的物件。
   @Resource(name = "userDao")
   private UserDao userDao;

   public void save() {
       userDao.save();
   }
}
  • userAction

package aa;

import org.springframework.stereotype.Controller;

import javax.annotation.Resource;

/**
* Created by ozc on 2017/5/10.
*/


//把物件新增到IOC容器中,首字母會小寫
@Controller
public class UserAction {

   @Resource(name = "userService")
   private UserService userService;

   public String execute() {
       userService.save();
       return null;
   }
}
  • 測試

package aa;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
* Created by ozc on 2017/5/10.
*/

public class App {

   public static void main(String[] args) {

       // 建立容器物件
       ApplicationContext ac = new ClassPathXmlApplicationContext("aa/applicationContext.xml");

       UserAction userAction = (UserAction) ac.getBean("userAction");

       userAction.execute();
   }
}
Spring入門這一篇就夠了

通過Java方式

由於Spring的自動裝配並不能將第三方庫元件裝配到應用中,於是需要顯式裝配配置。顯示裝配有兩種方式

  • 通過java程式碼裝配bean

  • 通過XML裝配bean

Spring In Action作者首推使用自動裝配的功能,而後是通過java程式碼配置bean,最後才用XML檔案配置的方式..

那麼怎麼通過java程式碼來配置Bean呢??

  • 編寫一個java類,使用@Configuration修飾該類

  • 被@Configuration修飾的類就是配置類

編寫配置類:

    @org.springframework.context.annotation.Configuration
   public class Configuration {

   }

使用配置類建立bean:

  • 使用@Bean來修飾方法,該方法返回一個物件。

  • 不管方法體內的物件是怎麼建立的,Spring可以獲取得到物件就行了。

  • Spring內部會將該物件加入到Spring容器中

  • 容器中bean的ID預設為方法名

@org.springframework.context.annotation.Configuration
public class Configuration {

   @Bean
   public UserDao userDao() {

       UserDao userDao = new UserDao();
       System.out.println("我是在configuration中的"+userDao);
       return userDao;
   }

}
  • 測試程式碼:要使用@ContextConfiguration載入配置類的資訊【引入test包】

package bb;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;

/**
* Created by ozc on 2017/5/11.
*/

//載入配置類的資訊
@ContextConfiguration(classes = Configuration.class)
public class Test2 {

   @Test
   public void test33() {

       ApplicationContext ac =
               new ClassPathXmlApplicationContext("bb/bean.xml");

       UserDao userDao = (UserDao) ac.getBean("userDao");

       System.out.println(userDao);
   }
}
Spring入門這一篇就夠了

三種方式混合使用

註解和XML配置是可以混合使用的,JavaConfig和XML也是可以混合使用的…

如果JavaConfig的配置類是分散的,我們一般再建立一個更高階的配置類(root),然後使用@Import來將配置類進行組合
如果XML的配置檔案是分散的,我們也是建立一個更高階的配置檔案(root),然後使用

在JavaConfig引用XML

  • 使用@ImportResource()

在XML引用JavaConfig

  • 使用<bean>節點就行了

bean物件建立細節

在Spring第一篇中,我們為什麼要引入Spring提出了這麼一些問題:

Spring入門這一篇就夠了

既然我們現在已經初步瞭解IOC容器了,那麼這些問題我們都是可以解決的。並且是十分簡單【物件寫死問題已經解決了,IOC容器就是控制反轉建立物件】

scope屬性

指定scope屬性,IOC容器就知道建立物件的時候是單例還是多例的了。

屬性的值就只有兩個:單例/多例

Spring入門這一篇就夠了
  • 當我們使用singleton【單例】的時候,從IOC容器獲取的物件都是同一個

Spring入門這一篇就夠了
  • 當我們使用prototype【多例】的時候,從IOC容器獲取的物件都是不同的

Spring入門這一篇就夠了

scope屬性除了控制物件是單例還是多例的,還控制著物件建立的時間

  • 我們在User的建構函式中列印出一句話,就知道User物件是什麼時候建立了。

    public User() {

       System.out.println("我是User,我被建立了");
   }
  • 當使用singleton的時候,物件在IOC容器之前就已經建立了

    • Spring入門這一篇就夠了
  • 當使用prototype的時候,物件在使用的時候才建立

    • Spring入門這一篇就夠了

lazy-init屬性

lazy-init屬性只對singleton【單例】的物件有效…..lazy-init預設為false….

有的時候,可能我們想要物件在使用的時候才建立,那麼將lazy-init設定為ture就行了

Spring入門這一篇就夠了

init-method和destroy-method

如果我們想要物件在建立後,執行某個方法,我們指定為init-method屬性就行了。。

如果我們想要IOC容器銷燬後,執行某個方法,我們指定destroy-method屬性就行了。

    <bean id="user" class="User" scope="singleton" lazy-init="true" init-method="" destroy-method=""/>

Bean建立細節總結

    /**
    * 1) 物件建立: 單例/多例
    *  scope="singleton", 預設值, 即 預設是單例 【service/dao/工具類】
    *  scope="prototype", 多例;              【Action物件】
    *
    * 2) 什麼時候建立?
    *    scope="prototype"  在用到物件的時候,才建立物件。
    *    scope="singleton"  在啟動(容器初始化之前), 就已經建立了bean,且整個應用只有一個。
    * 3)是否延遲建立
    *    lazy-init="false"  預設為false,  不延遲建立,即在啟動時候就建立物件
    *    lazy-init="true"   延遲初始化, 在用到物件的時候才建立物件
    *    (只對單例有效)
    * 4) 建立物件之後,初始化/銷燬
    *    init-method="init_user"       【對應物件的init_user方法,在物件建立之後執行 】
    *    destroy-method="destroy_user"  【在呼叫容器物件的destroy方法時候執行,(容器用實現類)】
    */


如果文章有錯的地方歡迎指正,大家互相交流。



來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69900354/viewspace-2648473/,如需轉載,請註明出處,否則將追究法律責任。

相關文章