
本文共 23177 字,大约阅读时间需要 77 分钟。
目录
1 整合流程简介
首先要明白Spring管理非controller层,springmvc管理controller层
1.配置Spring集成MyBatis
1.1开启包扫描,不扫描controller层
1.2集成Mybatis
1.3集成PageHelper
1.4开启事物
2.配置SpringMVC的spring-mvc.xml
2.1开启包扫描,只扫描controller层
2.2开启springmvc的注解驱动
2.3开启静态资源访问
2.4配置视图解析器
3.配置web.xml
3.1配置DispatcherServlet,加载并解析spring-mvc.xml
3.2配置乱码过滤器
3.3配置监听器,tomcat启动时加载applicationContext.xml
4.创建Result,统一返回的数据
5.创建异常处理器
2 项目环境初始化
2.1 引入依赖
<dependencies> <!--jackson--> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.9.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.9.0</version> </dependency> <!-- servlet3.1规范的坐标 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <!--jsp坐标--> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.0</version> <scope>provided</scope> </dependency> <!--Lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.6</version> </dependency> <!--文件上传--> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.1</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.3</version> </dependency> <!--数据校验,支持tomcat7--> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>5.2.1.Final</version> </dependency> <!--spring的坐标--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.6.RELEASE</version> </dependency> <!--spring web的坐标--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>5.1.6.RELEASE</version> </dependency> <!--springmvc的坐标--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.1.6.RELEASE</version> </dependency> <!--Spring集成Junit测试--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.0.5.RELEASE</version> </dependency> <!--Spring集成Aspect切面--> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.13</version> </dependency> <!--Spring 事物--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.0.5.RELEASE</version> </dependency> <!--mybatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.3</version> </dependency> <!--mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.46</version> </dependency> <!--spring连接数据库--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.1.6.RELEASE</version> </dependency> <!--数据库连接池--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.21</version> </dependency> <!--spring集成mybatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.0</version> </dependency> <!--spring集成junit--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.0.5.RELEASE</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <!--分页插件--> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.1.2</version> </dependency></dependencies><build> <plugins> <!--jdk编译插件--> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>utf-8</encoding> </configuration> </plugin> <!--tomcat插件--> <plugin> <groupId>org.apache.tomcat.maven</groupId> <!-- tomcat7的插件, 不同tomcat版本这个也不一样 --> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.1</version> <configuration> <!-- 通过maven tomcat7:run运行项目时,访问项目的端口号 --> <port>80</port> <!-- 项目虚拟路径 如果配置的, 则访问路径为localhost:8080/--> <path>/</path> </configuration> </plugin> </plugins></build>
2.2 创建数据库
/*SQLyog Ultimate v12.09 (64 bit)MySQL - 5.5.40 : Database - spring01**********************************************************************//*!40101 SET NAMES utf8 */;/*!40101 SET SQL_MODE=''*/;/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;CREATE DATABASE /*!32312 IF NOT EXISTS*/`spring01` /*!40100 DEFAULT CHARACTER SET utf8 */;USE `spring01`;/*Table structure for table `account` */DROP TABLE IF EXISTS `account`;CREATE TABLE `account` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(24) DEFAULT NULL, `money` double(10,2) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;/*Data for the table `account` */insert into `account`(`id`,`name`,`money`) values (1,'jack',1000.00),(2,'tom',1000.00),(3,'rose',1000.00);/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
2.3 创建dao\service\domain
创建实体类 Account
package cn.itcast.domain;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;@Data@AllArgsConstructor@NoArgsConstructorpublic class Account { private Integer id; private String name; private Double money;}
创建 Dao
package cn.itcast.dao;import cn.itcast.domain.Account;import java.util.List;public interface AccountDao { @Select("select * from account") List<Account> findAll();}
创建Service
package cn.itcast.service;import cn.itcast.domain.Account;import java.util.List;public interface AccountService { List<Account> findAll() ;}
创建Service实现类
package cn.itcast.service.impl;import cn.itcast.dao.AccountDao;import cn.itcast.domain.Account;import cn.itcast.service.AccountService;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import java.util.List;@Servicepublic class AccountServiceImpl implements AccountService { @Autowired private AccountDao accountDao; public List<Account> findAll() { return accountDao.findAll(); }}
2.4 创建jdbc.properties
jdbc.driver=com.mysql.jdbc.Driverjdbc.url=jdbc:mysql://localhost:3306/spring01jdbc.username=rootjdbc.password=root
3 编写Spring核心配置(3步)
1.配置包扫描
2.集成MyBatis(顺便集成分页插件),集成mybatis需要三个bean
3.配置事物
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!--1.配置包扫描--> <context:component-scan base-package="cn.itcast"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> <!--2.集成MyBatis--> <context:property-placeholder location="classpath:jdbc.properties"/> <bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource"> <property name="driverClassName" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <bean class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"></property> <property name="typeAliasesPackage" value="cn.itcast.domain"></property> <!--4.集成分页--> <property name="plugins"> <array> <bean class="com.github.pagehelper.PageInterceptor"> <property name="properties"> <props> <prop key="helperDialect">mysql</prop> <!--reasonable:分页合理化参数,默认值为false。当该参数设置为 true 时,pageNum<=0 时会查询第一页, pageNum>pages(超过总数时),会查询最后一页。 默认false 时,直接根据参数进行查询。--> <prop key="reasonable">true</prop> </props> </property> </bean> </array> </property> </bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="cn.itcast.dao"></property> </bean> <!--3.配置平台事物管理器,启用事物注解驱动在第一行--> <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven></beans>
测试
package cn.itcast.service.impl;import cn.itcast.service.AccountService;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration("classpath:applicationContext.xml")public class AccountServiceImplTest { @Autowired private AccountService accountService ; @Test public void findAll() { System.out.println(accountService.findAll()); }}
4 配置SpringMVC(4步)
1.配置包扫描,只扫controller
2.启用springmvc注解
3.静态资源放行
4.配置视图解析器
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!--1.配置包扫描,只扫controller--> <context:component-scan base-package="cn.itcast"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> <!--2.启用springmvc注解--> <mvc:annotation-driven/> <!--3.静态资源放行--> <mvc:default-servlet-handler/> <!--4.配置视图解析器--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/"/> <property name="suffix" value=".jsp"/> </bean></beans>
5 配置web.xml
配置web核心三大组件
1.listener:ContextLoaderListener
2.filter:CharacterEncodingFilter
3.servlet:DispatcherServlet
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" ><web-app> <display-name>Archetype Created Web Application</display-name> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <!--配置过滤器,解决中文乱码,支持restful风格代码--> <filter> <filter-name>characterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter> <filter-name>HiddenHttpMethodFilter</filter-name> <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class> </filter> <filter-mapping> <filter-name>characterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter-mapping> <filter-name>HiddenHttpMethodFilter</filter-name> <servlet-name>dispatcherServlet</servlet-name> </filter-mapping> <!--配置监听器 加载spring的核心配置--> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!--配置Servlet,加载springmvc的核心配置--> <servlet> <servlet-name>dispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping></web-app>
6 编写Restful风格控制器
AccountController
测试地址:http://localhost/account
package cn.itcast.controller;import cn.itcast.service.AccountService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;import org.springframework.web.bind.annotation.RestController;@RestController@RequestMapping("/account")public class AccountController { @Autowired private AccountService accountService; /** * 查询所有 * @return */ @GetMapping public Object findAll(){ return accountService.findAll(); }}
7 创建Result统一返回数据格式
Result
package cn.itcast.domain;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;@Data@AllArgsConstructor@NoArgsConstructorpublic class Result implements Serializable { /**操作成功或失败的状态*/ private boolean flag; /**小时提示*/ private String message; /**消息状态码*/ private Integer code; /**数据*/ private Object data;}
常量类
package cn.itcast.constant;public class CodeConstant { //账户模块以1开头,长度是6 /**查询账户成功*/ public static final Integer ACCOUNT_GET_SUCCESS = 100001; /**查询账户失败*/ public static final Integer ACCOUNT_GET_ERROR = 100002;}
package cn.itcast.constant;public class MessageConstant { //账户模块以1开头,长度是6 /**查询账户成功*/ public static final String ACCOUNT_GET_SUCCESS = "查询账户成功"; /**查询账户失败*/ public static final String ACCOUNT_GET_ERROR = "查询账户失败";}
8 统一异常处理
自定义异常
- 设定自定义异常,封装程序执行过程中出现的问题,便于表现层进行统一的异常拦截并进行处理
- BusinessException
- SystemException
- 自定义异常消息返回时需要与业务正常执行的消息按照统一的格式进行处理
定义BusinessException
public class BusinessException extends RuntimeException { //自定义异常中封装对应的错误编码,用于异常处理时获取对应的操作编码 private Integer code; public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } public BusinessException(Integer code) { this.code = code; } public BusinessException(String message, Integer code) { super(message); this.code = code; } public BusinessException(String message, Throwable cause,Integer code) { super(message, cause); this.code = code; } public BusinessException(Throwable cause,Integer code) { super(cause); this.code = code; } public BusinessException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace,Integer code) { super(message, cause, enableSuppression, writableStackTrace); this.code = code; }}
@GetMapping("/{uuid}")public Result get(@PathVariable Integer uuid){ User user = userService.get(uuid); //模拟出现异常,使用条件控制,便于测试结果 if (uuid == 10 ) throw new BusinessException("查询出错啦,请重试!",Code.GET_ERROR); return new Result(null != user ?Code.GET_OK: Code.GET_ERROR,user);}
返回消息兼容异常信息
@Component@ControllerAdvicepublic class ProjectExceptionAdivce { @ExceptionHandler(BusinessException.class) @ResponseBody //对出现异常的情况进行拦截,并将其处理成统一的页面数据结果格式 public Result doBusinessException(BusinessException e){ return new Result(e.getCode(),e.getMessage()); }}
3 纯注解开发SSM
整合思路:
SSM整合可以使用多种方式,我们纯注解的方式,抛弃所有的xml配置文件(包括web.xml),使用Java配置类和注解进行配置。
SSM的整体思路:整合Spring和SpringMVC,整合Spring和MyBatis,两两整合。
3.1 用注解替代applicationContext.xml
同前期设置,添加事务注解驱动
@Configuration
//扫描组件,排除SpringMVC对应的bean,等同于<context:component-scan />@ComponentScan(value = "com.itheima",excludeFilters = { @ComponentScan.Filter(type= FilterType.ANNOTATION,classes = { Controller.class})})@PropertySource("classpath:jdbc.properties")@Import({ JdbcConfig.class,MyBatisConfig.class})//等同于<tx:annotation-driven transaction-manager="txManager"/>,导入的默认名称为transactionManager@EnableTransactionManagementpublic class SpringConfig { //等同于<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> @Bean("transactionManager") public DataSourceTransactionManager getDataSourceTxManager(@Autowired DataSource dataSource){ DataSourceTransactionManager dtm = new DataSourceTransactionManager(); //等同于<property name="dataSource" ref="dataSource"/> dtm.setDataSource(dataSource); return dtm; }}
3.2 用注解替代spring-mvc.xml
- 同前期设置,添加@EnableWebMvc注解
@Configuration@ComponentScan("com.itheima.controller")@EnableWebMvcpublic class SpringMvcConfig implements WebMvcConfigurer { }
- EnableWebMvc
- 支持ConversionService的配置,可以方便配置自定义类型转换器
- 支持@NumberFormat注解格式化数字类型
- 支持@DateTimeFormat注解格式化日期数据,日期包括Date,Calendar,JodaTime(JodaTime要导包)
- 支持@Valid的参数校验(需要导入JSR-303规范)
- 配合第三方jar包和SpringMVC提供的注解读写XML和JSON格式数据
3.3 spring和springmvc纯注解整合
首先在idea创建一个jar工程,不需要去创建任何配置文件,也包括web.xml
首先写spring的配置类
package com.liy.config;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.FilterType;import org.springframework.stereotype.Controller;/***spring配置文件类**configuration 此注解标明配置类的身份*下面的步骤相当于xml配置文件中的开启spring扫描,开启默认注解,不过排除掉controller注解*/@Configuration@ComponentScan(basePackages = "com.liy",useDefaultFilters = true, excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class)})public class SpringConfig {}
然后是springmvc的配置类
package com.liy.config;import com.alibaba.fastjson.support.config.FastJsonConfig;import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;import org.omg.CosNaming.NamingContextExtPackage.AddressHelper;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.FilterType;import org.springframework.http.converter.HttpMessageConverter;import org.springframework.stereotype.Controller;import org.springframework.web.context.WebApplicationContext;import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;import java.nio.charset.Charset;import java.util.List;/** *springmvc 配置文件类 */@Configuration@ComponentScan(basePackages = "com.liy", useDefaultFilters = false, includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class), @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Configuration.class)})public class SpringMVCConfig extends WebMvcConfigurationSupport { //把根目录下的静态资源放开,不过访问静态资源时,请求路径前要加上“/js/” @Override protected void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/js/**").addResourceLocations("classpath:/"); } //给访问jsp的请求加上前缀(“/”) 和后缀 (".jsp") @Override protected void configureViewResolvers(ViewResolverRegistry registry) { registry.jsp("/",".jsp"); } //这里表示,访问/hello3路径后,进入名为hello的视图去 @Override protected void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/hello3").setViewName("hello"); } //加了fastjson的依赖后,这里配置引用fastjson,以及设置编码 @Override protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) { FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter(); converter.setDefaultCharset(Charset.forName("UTF-8")); FastJsonConfig config = new FastJsonConfig(); config.setCharset(Charset.forName("UTF-8")); converter.setFastJsonConfig(config); converters.add(converter); }}
再写个初始化的类,相当于web.xml,启动项目就加载配置文件
package com.liy.config;import org.springframework.web.WebApplicationInitializer;import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;import org.springframework.web.servlet.DispatcherServlet;import javax.servlet.ServletContext;import javax.servlet.ServletException;import javax.servlet.ServletRegistration;/** * 相当于web.xml * */public class WebInit implements WebApplicationInitializer { @Override public void onStartup(ServletContext servletContext) throws ServletException { //扫描springmvc AnnotationConfigWebApplicationContext afw = new AnnotationConfigWebApplicationContext(); afw.register(SpringMVCConfig.class); //添加dispatchservlet ServletRegistration.Dynamic springmvc = servletContext.addServlet("springmvc", new DispatcherServlet(afw)); //添加映射文件路径 springmvc.addMapping("/"); //给springmvc添加启动时机 springmvc.setLoadOnStartup(1); }}
注意这个相当于web.xml的类这能扫描springmvc的配置类,所以要把spring的配置类的注解类型加到springmvc的扫描中
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Configuration.class)1
这样spring和springmvc的纯java注解方式就整合完成了
其实spring的配置类可以省略 ,只要springmvc的配置类扫描所有地方就行
@Configuration@ComponentScan(basePackages = "com.liy")public class SpringMVCConfig extends WebMvcConfigurationSupport {
这样spring的配置类就不需要了
然后写个controller类测试下
@Controllerpublic class HelloController { @Autowired HelloService hs; @GetMapping(value = "/hello",produces = "text/html;charset=utf-8") @ResponseBody public String Hello(String name) { return hs.hello(name); }
页面能看到数据就表示成功了
发表评论
最新留言
关于作者
