JAVA Web学习(24)___第18章 Spring核心之IOC

第18章 Spring核心之IOC

18.1 Sring概述

18.1.1 初识Sring

Spring 有可能成为所有企业应用程序的一站式服务点,然而,Spring 是模块化的,允许你挑选和选择适用于你的模块,不必要把剩余部分也引入。下面的部分对在 Spring 框架中所有可用的模块给出了详细的介绍。
Spring 框架提供约 20 个模块,可以根据应用程序的要求来使用。
Spring 体系结构
Spring 模块可以分类成:数据访问和集成、Web远程调用、面向切面编程、Instrumentation,Spring核心容器、测试(参考资料Spring4实战)

总共六大模块,可以分类成以下。

  • 数据访问和集成:

JDBC:spring-jdbc-4.3.18.RELEASE.jar

Transaction:spring-tx-4.3.18.RELEASE.jar

ORM:spring-orm-4.3.18.RELEASE.jar

OXM:spring-oxm-4.3.18.RELEASE.jar

Messaging:spring-messaging-4.3.18.RELEASE.jar

JMS:spring-jms-4.3.18.RELEASE.jar

  • Web远程调用:

Web:spring-web-4.3.18.RELEASE.jar

Web Servlet:spring-webmvc-4.3.18.RELEASE.jar

Web portlet:spring-webmvc-portlet-4.3.18.RELEASE.jar

Web Socket:spring-websocket-4.3.18.RELEASE.jar

  • 面向切面编程:(核心)

Aop:spring-aop-4.3.18.RELEASE.jar

Aspects:spring-aspects-4.3.18.RELEASE.jar

  • Instrumentation(工具):这个使用场景非常有限,主要提供了为JVM添加代理的功能

Instrumentation:spring-instrument-4.3.18.RELEASE.jar

Instrument Tomcat:spring-instrument-tomcat-4.3.18.RELEASE.jar

  • Spring 核心容器:(核心)

Beans:spring-beans-4.3.18.RELEASE.jar

Core:spring-core-4.3.18.RELEASE.jar

Context:spring-context-4.3.18.RELEASE.jar

Expression:spring-expression-4.3.18.RELEASE.jar

Context support:spring-context-support-4.3.18.RELEASE.jar

  • 测试:

Test:spring-test-4.3.18.RELEASE.jar

18.1.2 Spring的获取

http://repo.spring.io/release/org/springframework/spring下载最新版本的 Spring 框架的二进制文件。

18.1.3 简单配置Spring

Spring的jar包相关功能说明

jar包名称 说 明
spring.jar 整个Spring模块,包含表中所有jar的功能
spring-core.jar Spring的核心模块,包含Ioc容器
spring-aop.jar Spring的Aop模块
spring-context.jar Sping的上下文,包含ApplicationContext容器
spring-dao.jar Spring的DAO模块,包含对DAO与JDBC的支持
spring-orm.jar Sping的ORM模块,支持Hibernate、JDBC的支持
spring-web.jar Sping的Web模块,包含Web application context
spring-webmvc.jar Spring的MVC框架模块

18.1.4 使用BeanFactory管理bean

  • 1.创建web项目,导入jar包 commons-logging-1.0.4.jarspring.jar
  • 2.创建User.java 实体类
public class User {
       private String name;//用户姓名
        private Integer age;//年龄
        private String sex;//性别

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }
}
  • 3.创建操作类Manger.java,此处使用Junit Test测试
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;


public class Manger {
    @Test
     public  void main() {
            Resource resource = new ClassPathResource("applicationContext.xml");            //装载配置文件
            BeanFactory factory = new XmlBeanFactory(resource);
            User user = (User) factory.getBean("user");                                    //获取Bean
            System.out.println("用户姓名——"+user.getName());//输出用户的姓名
            System.out.println("用户年龄——"+user.getAge());//输出用户的年龄
            System.out.println("用户性别——"+user.getSex());//输出用户的性别
        }   
}

  • 4.创建applicationContext.xml,在源码目录下Src目录
<?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-2.5.xsd">
    <!-- User Bean -->
    <bean name="user" class="com.hwp.spring.User">
        <property name="name">
            <value>小强</value>
        </property>
        <property name="age">
            <value>26</value>
        </property>
        <property name="sex">
            <value>男</value>
        </property>
    </bean>
</beans>

<property> 中的name要和实体类对应上,<bean>中class要完整的包名、类名

18.1.5 ApllicationContext的应用

Spring ApplicationContext 容器
Application Context 是 BeanFactory 的子接口,也被成为 Spring 上下文。
Application Context 是 spring 中较高级的容器。和 BeanFactory 类似,它可以加载配置文件中定义的 bean,将所有的 bean 集中在一起,当有请求的时候分配 bean。 另外,它增加了企业所需要的功能,比如,从属性文件中解析文本信息和将事件传递给所指定的监听器。这个容器在 org.springframework.context.ApplicationContext interface 接口中定义。
ApplicationContext 包含 BeanFactory 所有的功能,一般情况下,相对于 BeanFactory,ApplicationContext 会更加优秀。当然,BeanFactory 仍可以在轻量级应用中使用,比如移动设备或者基于 applet 的应用程序。
最常被使用的 ApplicationContext 接口实现:
FileSystemXmlApplicationContext:该容器从 XML 文件中加载已被定义的 bean。在这里,你需要提供给构造器 XML 文件的完整路径。
ClassPathXmlApplicationContext:该容器从 XML 文件中加载已被定义的 bean。在这里,你不需要提供 XML 文件的完整路径,只需正确配置 CLASSPATH 环境变量即可,因为,容器会从 CLASSPATH 中搜索 bean 配置文件。
WebXmlApplicationContext:该容器会在一个 web 应用程序的范围内加载在 XML 文件中已被定义的 bean。
实例

  • 1.创建web工程,导包

commons-logging-1.0.4.jar和spring.jar

  • 2.创建package,com.hwp.spring,在文件夹下创建HelloWorld.java和MainApp.java两个类

  • HelloWorld.java

public class HelloWorld {
     private String message;
       public void setMessage(String message){
          this.message  = message;
       }
       public void getMessage(){
          System.out.println("Your Message : " + message);
       }
    }
  • 2.MainApp.java
public class Manger {
//此处使用Junit测试
    @Test
    public  void main() {
         ApplicationContext context = new FileSystemXmlApplicationContext
                    ("src\\Beans.xml");
              HelloWorld obj = (HelloWorld) context.getBean("helloWorld");
              obj.getMessage();
    }   
}
  • 3.在源码目录下即Src目录下,创建Beans.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-3.0.xsd">

   <bean id="helloWorld" class="com.hwp.spring.HelloWorld">
       <property name="message" value="Hello World!"/>
   </bean>

</beans>
  • 4.运行后控制台输出
Your Message : Hello World!

18.2 依赖注入

18.2.1 什么是控制反转与依赖注入

依赖注入有3种实现类型

  • 1.接口注入(Spring不支持)
    基于接口将调用与实现分离。这种依赖注入方式必须实现容器所规定的接口,使程序代码和容器的API绑定在一起,这不是理想的依赖注入方式。
  • 2.Setter注入(Spring支持)
    基于JavaBean的Setter方法为属性赋值。在实际开发中得到了最广泛的应用。
public class HelloWorld {
     private String message;

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
      
    }
  • 3.构造器注入(Spring支持)
    基于构造方法为属性赋值。容器通过调用类的构造方法
public class HelloWorld {
     private String message;
public HelloWorld(String message) {
        super();
        this.message = message;
    }
    
    }

18.2.2 bean的配置

<?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-3.0.xsd">

   <bean id="helloWorld" class="com.hwp.spring.HelloWorld">
       <property name="message" value="Hello World!"/>
   </bean>

</beans>

id属性为bean的名称,class属性为对应的类名,这样通过BeanFactory容器的getBean("test")方法就可以获取到该类的实例。

18.2.3 Setter注入

  • 1.创建User.java 实体类
public class User {
       private String name;//用户姓名
        private Integer age;//年龄
        private String sex;//性别

  ...                       //省略Setter()和Getter()方法
}
  • 2.创建applicationContext.xml,在源码目录下Src目录
<?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-2.5.xsd">
    <!-- User Bean -->
    <bean name="user" class="com.hwp.spring.User">
        <property name="name">
            <value>小强</value>
        </property>
        <property name="age">
            <value>26</value>
        </property>
        <property name="sex">
            <value>男</value>
        </property>
    </bean>
</beans>
  • 3.创建操作方法

public class Manger {
    @Test
     public  void main() {
            Resource resource = new ClassPathResource("applicationContext.xml");            //装载配置文件
            BeanFactory factory = new XmlBeanFactory(resource);
            User user = (User) factory.getBean("user");                                    //获取Bean
            System.out.println("用户姓名——"+user.getName());//输出用户的姓名
            System.out.println("用户年龄——"+user.getAge());//输出用户的年龄
            System.out.println("用户性别——"+user.getSex());//输出用户的性别
        }   
}

控制台输出信息

用户姓名——小强
用户年龄——26
用户性别——男

如果当JavaBean的某个元素是List集合类型或数组时,需要使用<list>标签为List集合类型或数组的每一个元素赋值
示例代码
List

 <!-- 配置List属性 -->
  <bean id="person3" class="com.kejian.spring.bean.collectionbean.PersonForList">
    <property name="name" value="Peter"></property>
    <property name="age" value="30"></property>
    <property name="cars">
    <list>
      <ref bean="car1"/>
      <ref bean="car2"/>
    </list>
    </property>
  </bean>

Map

  <!-- 配置Map属性 -->
  <bean id="person4" class="com.kejian.spring.bean.collectionbean.PersonForMap">
    <property name="name" value="Mike"></property>
    <property name="age" value="35"></property>
    <property name="cars">
      <map>
        <entry key="ACar" value-ref="car1"></entry>
        <entry key="BCar" value-ref="car2"></entry>
      </map>
    </property>
  </bean>

properties

  <!-- 通过props来配置properties -->
  <bean id="datasource" class="com.kejian.spring.bean.collectionbean.Datasource">
    <property name="properties">
      <props>
        <prop key="user">root</prop>
        <prop key="password">root</prop>
        <prop key="url">jdbc:mysql.///test</prop>
        <prop key="driver">jdbc.mysql.driver</prop>
      </props>
    </property>
  </bean>

18.2.4 构造器注入

  • 1.Javabean
public class User{
    private String name;
    private int age;
    private String sex;
    
    public User(String name,int age,String sex){
    this.name=name;
    this.age=age;
    this.sex=sex;
    }
    public void printlnfo(){
            System.out.println("用户姓名——"+name);//输出用户的姓名
            System.out.println("用户年龄——"+age);//输出用户的年龄
            System.out.println("用户性别——"+sex);//输出用户的性别
    }
}
  • 2.创建applicationContext.xml,在源码目录下Src目录
<?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-2.5.xsd">
    <!-- User Bean -->
    <bean name="user" class="com.hwp.spring.User">
        <constructor-arg>
            <value>小强</value>
        </constructor-arg>  
        <constructor-arg>
            <value>20</value>
        </constructor-arg>  
        <constructor-arg>
            <value>男</value>
        </constructor-arg>  
    </bean>
</beans>

容器通过多个<constructor-arg>标签完成了对构造方法的传参,但是如果标签的赋值顺序与构造方法中参数的顺序类型不同,程序会产生异常,可以使用<constructor-arg>元素的index属性和type属性解决此类问题。
示例代码

<beans>

   <bean id="exampleBean" class="examples.ExampleBean">
      <constructor-arg type="int" value="2001 index="0"/>
      <constructor-arg type="java.lang.String" value="Zara" index="1"/>
   </bean>

</beans>

18.2.5 引用其他的bean

  • 1.创建web项目,导入jar包,commons-logging-1.0.4.jar、spring-webmvc.jar和spring.jar
  • 2.创建javabean,User.java
public class User {
    private String name;//用户姓名
    private Integer age;//年龄
    private String sex; //性别
    ...                 //省略Setter、Getter方法
    public void  println() {
            System.out.println("名字---"+name);
            System.out.println("年纪---"+age+"");
            System.out.println("性别---"+sex);
        
    }
    
}

    1. 创建Manger.java 继承于AbstractController类
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;

public class Manger extends AbstractController {
    private User user;
    
    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    @Override
    protected ModelAndView handleRequestInternal(HttpServletRequest arg0,
            HttpServletResponse arg1) throws Exception {
        user.println();
        return null;
    }

}

  • 4.创建applicationContext.xml
<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-2.5.xsd">
    <!-- User Bean -->
    <bean id="user" class="com.hwp.spring.User">
        <property name="name" >
            <value>小强</value>
        </property>
        <property name="age">
            <value>26</value>
        </property>
        <property name="sex">
            <value>男</value>
        </property>
    </bean>
    <!-- 注入JavaBean -->
    <bean name="/main.do" class="com.hwp.spring.Manger">
        <property name="user">
            <ref local="user"/>
        </property>
    </bean>
</beans>
  • 5.设置web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
  <display-name>18_3spring</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  <servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>/WEB-INF/applicationContext.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>
</web-app>
  • 6.编写index.jsp
<!-- 主要代码 -->
 <body>
  <a href="http://localhost:8080/18_3spring/main.do">执行JavaBean的注入</a>
  </body>

代码部署后,点击超链接,控制台输出信息

名字---小强
年纪---26
性别---男

18.2.6 匿名内部javabean的创建

18.3 自动装配

18.3.1 按bean名称装配

<bean>元素的byname属性以属性名区分自动装配,

  • 1.定义User类
    public class User{
    private String name; //用户名
    private int age;     //年龄   
    private String sex;  //性别
    ...                  // 省略的Setter()和Getter()方法
    }
  • 2.定义PrintlInfo类,将User对象注入到PrintInfo对象中,按我的个人理解是实体类嵌套一层实体类
public class PrintlInfo{
    private User user;
    ... //省略Setter()、Getter()方法
    public void PrintUser(){
       System.out.println("打印的属性");
       System.out.println("----------");
       System.out.println("姓名---"+user.getName());   //输出用户的姓名
       System.out.println("年纪---"+user.getAge());      //输出用户的年龄
       System.out.println("性别---"+user.getSex());      //输出用户的性别
    }
}
  • 3.applicationContext.xml,存放在在源码目录下即Src目录下
<?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-2.5.xsd">
    <!-- User Bean -->
    <bean id="user" class="com.mr.user.User">
        <property name="name">
            <value>小强</value>
        </property>
        <property name="age">
            <value>26</value>
        </property>
        <property name="sex">
            <value>男</value>
        </property>
    </bean>
    <bean autowire="byName" id="printInfo" class="com.mr.user.PrintInfo" />
</beans>
  • 4.测试类,此处使用junit测试
public class Manger {
    @Test
    public  void main() {
         Resource resource = new ClassPathResource("applicationContext.xml");            //装载配置文件
            BeanFactory factory = new XmlBeanFactory(resource);
            PrintInfo printInfo = (PrintInfo) factory.getBean("printInfo");                 //获取Bean
            printInfo.PrintUser();//输出JavaBean的属性值
        }   
}

测试,控制台输出

PrintInfo打印的User属性


用户姓名--小强
用户年龄--26
用户性别--男

18.3.2 按bean类型装配

按类型装配,除了applicationContext.xml中以下代码变动,其他的代码一样

    
    <bean autowire="byType" id="printInfo" class="com.mr.user.PrintInfo" />

18.3.3 自动装配的其他方式

在Spring中还有另外3种自动装配方式,通过设置autowire的不同属性值来实现。

  • 1.no属性
    即不启用自动装配。Autowire默认的值。不使用Autowire,引用关系显示声明,spring的reference也建议不用autoware,因为这会破坏模块关系的可读性。
  • 2.constructor属性
    通byType一样,也是通过类型查找依赖对象。与byType的区别在于它不是使用Seter方法注入,而是使用构造子注入。如果容器中没有找到与构造器参数类型一致的bean,那么抛出异常。
  • 3.autodetect属性
    在byType和constructor之间自动的选择注入方式。通过bean类的自省机制(introspection)来决定是使用constructor还是byType方式进行自动装配。如果发现默认的构造器,那么将使用byType方式,否则采用
    constructor。
    Sping自动装配的优缺点
    优点
  • 自动装配可以大大地减少属性和构造器参数的指派。
  • 自动装配也可以在解析对象时更新配置。

缺点

  • 在property和constructor-arg设置中的依赖总是重载自动装配,我们无法对原始类型(如int,long,boolean等就是首字母小写的那些类型),还有String,Classes做自动装配。这是受限于设计。
  • 自动装配跟直接装配(explicit wiring)相比较,在准确性方便还是差那么点,虽然没有明确地说明,但是Spring还是尽量避免这种模棱两可的情况,导致出现没预料到的结果。
  • Spring容器生成文档的工具可能会不能使用装配的信息。
  • 容器中多个bean的定义可能要对setter和构造器参数做类型匹配才能做依赖注入,虽然对于array,collection和map来说不是啥问题,但是对于只有单一值的依赖来讲,这就有点讲不清楚了,所以如果没有唯一的bean定义,那只能抛出异常

18.4 bean的作用域

18.4.1 了解Spring中的bean

Bean 定义
被称作 bean 的对象是构成应用程序的支柱也是由 Spring IoC 容器管理的。bean 是一个被实例化,组装,并通过 Spring IoC 容器所管理的对象。这些 bean 是由用容器提供的配置元数据创建的,例如,已经在先前章节看到的,在 XML 的表单中的 定义。

bean 定义包含称为配置元数据的信息,下述容器也需要知道配置元数据:

  • 如何创建一个 bean
  • bean 的生命周期的详细信息
    bean 的依赖关系
    上述所有的配置元数据转换成一组构成每个 bean 定义的下列属性。
属性 描述
class 这个属性是强制性的,并且指定用来创建 bean 的 bean 类。
name 这个属性指定唯一的 bean 标识符。在基于 XML 的配置元数据中,你可以使用 ID 和/或 name 属性来指定 bean 标识符。
scope 这个属性指定由特定的 bean 定义创建的对象的作用域,它将会在 bean 作用域的章节中进行讨论。
constructor-arg 它是用来注入依赖关系的,并会在接下来的章节中进行讨论。
properties 它是用来注入依赖关系的,并会在接下来的章节中进行讨论。
autowiring mode 它是用来注入依赖关系的,并会在接下来的章节中进行讨论。
lazy-initialization mode 延迟初始化的 bean 告诉 IoC 容器在它第一次被请求时,而不是在启动时去创建一个 bean 实例。
initialization 方法 在 bean 的所有必需的属性被容器设置之后,调用回调方法。它将会在 bean 的生命周期章节中进行讨论。
destruction 方法 当包含该 bean 的容器被销毁时,使用回调方法。它将会在 bean 的生命周期章节中进行讨论。

18.4.2 singleton的作用域

singleton 是默认的作用域,也就是说,当定义 Bean 时,如果没有指定作用域配置项,则 Bean 的作用域被默认为 singleton。
当一个bean的作用域为Singleton,那么Spring IoC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。

也就是说,当将一个bean定义设置为singleton作用域的时候,Spring IoC容器只会创建该bean定义的唯一实例。

Singleton是单例类型,就是在创建起容器时就同时自动创建了一个bean的对象,不管你是否使用,他都存在了,每次获取到的对象都是同一个对象。注意,Singleton作用域是Spring中的缺省作用域。你可以在 bean 的配置文件中设置作用域的属性为 singleton,如下所示:

<!-- A bean definition with singleton scope -->
<bean id="..." class="..." scope="singleton">
    <!-- collaborators and configuration for this bean go here -->
</bean>

18.4.3 prototype的作用域

18.5 对bean的特殊处理

18.5.1 初始化与销毁

18.5.2 自定义属性编辑器

Spring内置的属性编辑器说明

属性名称 说 明 属性名称 说 明
ByteArrayPropertyEdior 字节数组编辑器 CustomDateEditor Date类型编辑器
CharacterEdior 字符类型编辑器 CustomNumberEditor 数值类型编辑器
CharArrayEdior 字符编辑器 FileEditor 文件类型编辑器
ClassEdiotor 字节数组编辑器 InputStramEditor 输入流类型编辑器
CustomBooleanEditor Boolean类型编辑器 LocaleEditor 地域信息编辑器
CustomCollectionEditor Collection集合类型编辑器 PropertiesEditor 属性类型编辑器
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容