JavaEE 企业级分布式高级架构师(四)SpringMVC学习笔记(1)

基础介绍篇

基础概念介绍

BS和CS开发架构

  • B/S架构:也就是浏览器/服务器架构
  • C/S架构:也就是客户端/服务端结构

说明:我们现在使用Java开发的大多数都是web应用,这些应用几乎全部都是基于B/S架构进行开发的。在B/S架构中,应用系统标准的三层架构分为:表现层、业务层、持久层。这种三层架构在我们的实际开发中使用得非常多。

  • JavaEE制定了一套规范,去进行BS架构的处理,这套规范就是Servlet

应用系统三层架构

  • 表现层
    • 也就是我们常说的 Web 层
    • 它负责接收客户端请求,向客户端响应结果,通常客户端使用http协议请求web层,web层需要接收http请求,完成http响应
    • 表现层包括展示层和控制层:控制层负责接收请求,展示层负责结果的展示
    • 表现层依赖业务层,接收到客户端请求一般会调用业务层处理,并将处理结果响应给客户端
    • 表现层的设计一般都使用 MVC 模型(MVC是表现层的设计模型,和其他层没有关系)
  • 业务层
    • 也就是我们常说的service层(真正的开发人员的核心)
    • 它负责业务逻辑处理,和我们开发项目的需求息息相关。web层依赖业务层,但是业务层不依赖web层
    • 业务层在业务处理时可能会依赖持久层,如果要对数据持久化需要保证事物一致性(也就是我们说的,事务应该放到业务层来控制)
  • 持久层
    • 也就是我们常说的dao层
    • 负责数据持久化,包括数据层即数据库和数据访问层,数据库是对数据进行持久化的载体,数据访问层是业务层和持久层交互的接口,业务层需要通过数据访问层将数据持久化到数据库中。
    • 通俗的讲,持久层就是和数据库交互,对数据库进行增删改查的

MVC设计模式

  • MVC 是模型(model)-视图(view)-控制器(controller)的缩写,是一种用于设计编写 web 应用程序表现层的模式。MVC设计模式的三大角色:
    • Model(模型):模型包含业务模型和数据模型,数据模型用于封装数据,业务模型用于处理业务。
    • View(视图):通常指的就是我们的 jsp 或者 html,作用一般就是展示数据的,通常视图是依据数据模型创建的。
    • Controller(控制器):是应用程序中处理用户交互的部分,作用一般就是处理程序逻辑的。

SpringMVC介绍

SpringMVC是什么

  • SpringMVC是一种基于MVC设计模型的请求驱动类型的轻量级 Web 框架,属于SpringFramework的后续产品,已经融合在 Spring Web Flow 里面。Spring 框架提供了构建Web应用程序的全功能 MVC 模块。
  • 使用Spring可插入的MVC构架,从而使用Spring进行Web开发时,可以选择使用Spring的Spring MVC框架或集成其他MVC开发框架,如Struts1(现在一般不用),Struts2等
  • SpringMVC已经成为目前最主流的MVC框架之一,并且随着 Spring3.0的发布,全面超越Struts2,成为最优秀的MVC框架。
  • 他通过一套注解,让一个简单的Java类成为处理请求的控制器,而无须实现任何接口。同时它还支持Restful编程风格的请求。

SpringMVC与Spring的联系

Spring MVC 全名叫做Spring Web MVC,它是Spring家族Web模块的一个重要成员。这一点,我们可以从spring的整体结构中看得出来:

在这里插入图片描述

SSM项目搭建篇

搭建springmvc工程

  • 创建一个Maven工程

添加依赖

  • 主要添加 MVC、jstl、和servlet-api 依赖
<!-- Spring MVC依赖包 -->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-webmvc</artifactId>
  <version>5.0.7.RELEASE</version>
</dependency>
<!-- jstl -->
<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>jstl</artifactId>
  <version>1.2</version>
</dependency>
<!-- servlet -->
<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>servlet-api</artifactId>
  <version>2.5</version>
  <scope>provided</scope>
</dependency>

开发步骤

配置web.xml
  • 添加前端控制器DispatcherServlet的配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
  <display-name>springmvc-demo</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>
  <!-- 
  	学习前置条件
  	问题1:web.xml中servlet、filter、listener、context-param加载顺序
  	问题2:load-on-startup标签的作用,影响了servlet对象创建的时机
  	问题3:url-pattern标签的配置方式有四种:
  		/dispatcherServlet
  		/servlet/*
  		*
  		/
  		加载顺序是如何的
  	问题4:url-pattern标签配置为/*报错,原因是它拦截了JSP请求,但是又不能处理JSP请求,为什么配置/就不拦截JSP请求?
  	问题5:配置了springmvc去读取spring配置文件之后,就产生了spring父子容器的问题
   -->
  
  <!-- 配置前端控制器 -->
  <servlet>
  	<servlet-name>springmvc</servlet-name>
	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  	<!-- 
  		设置spring配置文件路径
  		如果不设置初始化参数,那么DispatcherServlet会读取默认路径下的配置文件(即:/WEB-INF/springmvc-servlet.xml)
  	 -->
  	<init-param>
  		<param-name>contextConfigLocation</param-name>
  		<param-value>classpath:springmvc.xml</param-value>
  	</init-param>
  	<!-- 
  		指定初始化时机,设置为2,表示Tomcat启动时,DispatcherServlet会跟随着初始化
  		如果不指定初始化时机,DispatcherServlet就会在第一次被请求的时候,才会初始化,而且只会初始化一次
  	 -->
  	<load-on-startup>2</load-on-startup>
  </servlet>
  <servlet-mapping>
  	<servlet-name>springmvc</servlet-name>
  	<!-- 
  		URL-PATTERN的设置
  		不要设置为/*,否则会报错
  		通俗理解:/*会拦截整个项目中的资源访问,包含JSP和静态资源的访问,对于静态资源的访问 spring mvc提供了默认的Handler处理器
  		烦死对于JSP来讲,spring mvc没有提供默认的处理器,我们也没有手动编写对应的处理器,此时按照spring mvc的处理流程分析得知,他短路了
  	 -->
  	<url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>
  • springmvc.xmlDispatcherServlet启动时会去加载springmvc.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"
	xmlns:context="http://www.springframework.org/schema/context"
	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">
	<!-- 
		配置处理器bean的读取
		扫描Controller注解,多个包中间使用半角逗号分隔
	 -->	
	<context:component-scan base-package="com.yw.springmvc.example.controller"/>
	<!-- 
		配置三大组件之处理器适配器和处理器映射器
		内置了RequestMappingHandlerMapping和RequestMappingHandlerAdapter等组件注册
	 -->
	<mvc:annotation-driven/>
	
	<!-- 
		配置三大组件之视图解析器
		InternalResourceViewResolver:默认支持JSP视图解析
	 -->
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/jsp/"/>
		<property name="suffix" value=".jsp"/>	
	</bean>
</beans>
编码
  • Controller 控制器开发方式有多种:
    • 实现HttpRequestHandler接口
    • 实现Controller接口
    • 使用注解方式:在企业开发中,推荐使用注解方式开发处理器
// @Controller:在类上加该注解,指定该类为一个请求处理器,不需要实现任何接口获取继承任何类
@Controller
public class HelloController {
	// @RequestMapping:在方法上或者类上加该注解,指定请求的`url`由该方法处理
	@RequestMapping("hello")
	public ModelAndView hello(){
		ModelAndView mv = new ModelAndView();
		/**
		 * 设置数据模型
		 * 相当于request的setAttribute方法
		 * 底层是个Map,先将K-V数据放入Map中,最终根据视图对象不同,再进行后续处理
		 */
		mv.addObject("msg", "springmvc-demo");
		// 设置视图(逻辑路径)
		mv.setViewName("hello");
		return mv;
	}
}

注意:@Controller注解的类中每个@RequestMapping注解的方法,最终都会转为HandlerMethod类(而这个类才是SpringMVC注解开发方式中真正意义上的处理器)

  • 创建JSP页面:
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
	<h1>${msg}!!!</h1>
</body>
</html>
测试
  • 项目打包成war,发布到Tomcat,访问地址:http://localhost:8080/hello

SSM框架整合

整合思路

  • 将工程的三层结构中的JavaBean分别使用Spring容器(通过XML方式)进行管理:
    • 整合持久层mapper,包括数据源SqlSessionFactorymapper代理对象的整合;
    • 整合业务层Service,包括事务beanbean的配置;
    • 整合表现层Controller,直接使用springmvc的配置;
    • web.xml加载spring容器(包含多个XML文件,还分为父子容器
  • 核心配置文件包括:
    • applicationContext-dao.xml
    • applicationContext-service.xml
    • springmvc.xml
    • web.xml

工程搭建

  • 依赖包:
    • spring(包括springmvc)
    • mybatis
    • mybatis-spring整合包
    • 数据库驱动
    • 第三方连接池
    • JSTL
    • servlet-api
<!-- 持久层依赖 开始 -->
<!-- spring ioc组件需要的依赖包 -->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-beans</artifactId>
  <version>5.0.7.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-core</artifactId>
  <version>5.0.7.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>5.0.7.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-expression</artifactId>
  <version>5.0.7.RELEASE</version>
</dependency>
<!-- spring 事务管理和JDBC依赖包 -->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-tx</artifactId>
  <version>5.0.7.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-jdbc</artifactId>
  <version>5.0.7.RELEASE</version>
</dependency>
<!-- mysql数据库驱动包 -->
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>5.1.35</version>
</dependency>
<!-- dbcp连接池的依赖包 -->
<dependency>
  <groupId>commons-dbcp</groupId>
  <artifactId>commons-dbcp</artifactId>
  <version>1.4</version>
</dependency>
<!-- mybatis依赖 -->
<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis</artifactId>
  <version>3.4.5</version>
</dependency>
<!-- mybatis和spring的整合依赖 -->
<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis-spring</artifactId>
  <version>1.3.1</version>
</dependency>
<!-- 持久层依赖 结束 -->

<!-- 业务层依赖 开始 -->
<!-- 基于AspectJ的aop依赖 -->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aspects</artifactId>
  <version>5.0.7.RELEASE</version>
</dependency>
<dependency>
  <groupId>aopalliance</groupId>
  <artifactId>aopalliance</artifactId>
  <version>1.0</version>
</dependency>
<!-- 业务层依赖 结束 -->

<!-- 表现层依赖 开始 -->
<!-- spring MVC依赖包 -->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-webmvc</artifactId>
  <version>5.0.7.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-web</artifactId>
  <version>5.0.7.RELEASE</version>
</dependency>
<!-- jstl 取决于视图对象是否是JSP -->
<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>jstl</artifactId>
  <version>1.2</version>
</dependency>
<!-- servlet -->
<!-- <dependency> 
  <groupId>javax.servlet</groupId> 
  <artifactId>servlet-api</artifactId> 
  <version>2.5</version> 
  <scope>provided</scope> 
</dependency> -->
<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>javax.servlet-api</artifactId>
  <version>3.1.0</version>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context-support</artifactId>
  <version>5.0.7.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-oxm</artifactId>
  <version>5.0.7.RELEASE</version>
</dependency>
<dependency>
  <groupId>com.thoughtworks.xstream</groupId>
  <artifactId>xstream</artifactId>
  <version>1.4.10</version>
</dependency>
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.9.6</version>
</dependency>
<!-- 表现层依赖 结束 -->

工程整合(配置文件)

整合Mapper
  • db.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://192.168.254.128:3306/ssm?characterEncoding=utf8
jdbc.username=root
jdbc.password=123456
  • applicationContext-dao.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"
	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">
	<!-- 加载db.properties配置文件 -->
	<context:property-placeholder location="classpath:db.properties"/>
	<!-- 配置数据源 -->
	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
		<property name="driverClassName" value="${jdbc.driver}"/>
		<property name="url" value="${jdbc.url}"/>
		<property name="username" value="${jdbc.username}"/>
		<property name="password" value="${jdbc.password}"/>
		<property name="maxActive" value="30"/>
		<property name="maxIdle" value="5"/>
	</bean>
	<!-- 配置SqlSessionFactory -->
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<!-- 加载mybatis的配置文件(如果配置文件中没有配置项,可以忽略该文件) -->
		<property name="configLocation" value="classpath:mybatis/SqlMapConfig.xml"/>	
		<!-- 配置数据源 -->
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	<!-- 配置mapper扫描器,SqlSessionConfig.xml中的mapper配置去掉 -->
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<!-- 指定扫描的包 -->
		<property name="basePackage" value="com.yw.springmvc.example.dao.mapper"/>
	</bean>
</beans>
  • SqlMapConfig.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
</configuration>
  • log4j.properties
# dev env [debug] product env [info]
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p	[%t] - %m%n
整合Service
  • applicationContext-service.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"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:aop="http://www.springframework.org/schema/aop"
	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
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
	<!-- 扫描Service -->
	<context:component-scan base-package="com.yw.springmvc.example.service"/>
	<!-- 
		配置事务
		事务管理器,对mybatis操作数据库进行事务控制,此处使用jdbc的事务控制
	 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<!-- 指定要进行事务管理的数据源 -->
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	<!-- 通知 -->
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<!-- 传播行为 -->
			<tx:method name="save*" propagation="REQUIRED"/>
			<tx:method name="add*" propagation="REQUIRED"/>
			<tx:method name="insert*" propagation="REQUIRED"/>
			<tx:method name="delete*" propagation="REQUIRED"/>
			<tx:method name="del*" propagation="REQUIRED"/>
			<tx:method name="remove*" propagation="REQUIRED"/>
			<tx:method name="update*" propagation="REQUIRED"/>
			<tx:method name="modify*" propagation="REQUIRED"/>
			<tx:method name="find*" read-only="true"/>
			<tx:method name="query*" read-only="true"/>
			<tx:method name="select*" read-only="true"/>
			<tx:method name="get*" read-only="true"/>
		</tx:attributes>
	</tx:advice>
	<!-- aop -->
	<aop:config>
		<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.yw.springmvc.example.service.*Impl.*(..))"/>
	</aop:config>
</beans>
整合Controller
  • web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
  <!-- 配置前端控制器 -->
  <servlet>
  	<servlet-name>springmvc</servlet-name>
	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  	<init-param>
  		<param-name>contextConfigLocation</param-name>
  		<param-value>classpath:spring/springmvc.xml</param-value>
  	</init-param>
  	<load-on-startup>2</load-on-startup>
  </servlet>
  <servlet-mapping>
  	<servlet-name>springmvc</servlet-name>
  	<url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>
  • springmvc.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"
	xmlns:context="http://www.springframework.org/schema/context"
	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">
	<!-- 配置扫描Controller注解 -->	
	<context:component-scan base-package="com.yw.springmvc.example.controller"/>
	<!-- 配置处理器适配器和处理器映射器 -->
	<mvc:annotation-driven/>
	<!-- 配置视图解析器 -->
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/jsp/"/>
		<property name="suffix" value=".jsp"/>	
	</bean>
</beans>
web.xml加载spring父容器
  • 在web.xml中,使用监听器来对spring的配置文件进行加载:
<!-- 加载spring容器 -->
<context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>classpath:spring/applicationContext-*.xml</param-value>
</context-param>
<listener>
	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

编写测试代码

需求
  • 实现商品查询列表,从MySQL数据库查询商品信息。
需求分析
  • 表现层:
    • 请求URL:/queryItem
    • 请求参数:无
    • 请求返回值:ModelAndView指定Model和View(item-list.jsp)
    • Request域(Model):kay为itemList
  • 业务层:业务处理逻辑是实现商品列表的查询。
  • 持久层:只针对表进行增删改查操作
代码实现
  • 持久层代码:根据需求分析,持久层需要查询item表中的记录,使用逆向工程生成代码即可。通过逆向工程,把po类、mapper.xml和mapper.java类生成出来并拷贝到项目中。
  • 业务层代码:根据需求开发Service的接口以及实现类,注意:使用注解@Service开发Service
/**
 * 商品服务接口
 */
public interface ItemService {
	/**
	 * 查询商品信息(全部)
	 */
	List<Item> queryItemList() throws Exception;
}
/**
 * 商品服务实现类
 */
@Service
public class ItemServiceImpl implements ItemService {
	@Autowired
	private ItemMapper itemMapper;
	@Override
	public List<Item> queryItemList() throws Exception {
		ItemExample example = new ItemExample();
		return itemMapper.selectByExample(example);
	}
}
  • 表现层代码
/**
 * 商品item控制器
 */
@Controller
@RequestMapping("item")
public class ItemController {
	@Autowired
	private ItemService itemService;
	
	@RequestMapping("query")
	@ResponseBody
	public List<Item> queryItem(){
		return itemService.queryItemList();
	}
}
  • 部署测试:访问 http://localhost:8080/item/query
// 返回结果:
[{"id":1,"name":"华为手机","price":3000.0,"pic":"","createtime":1454563375000,"detail":null},{"id":2,"name":"苹果笔记本","price":6000.0,"pic":null,"createtime":1423459377000,"detail":null},{"id":3,"name":"黑寡妇电视机","price":2000.0,"pic":"4d4d1b22-9706-429b-86f0-211b3b2742da.jpg","createtime":1423200182000,"detail":null}]
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

讲文明的喜羊羊拒绝pua

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值