SSH框架整合

SSH整合

搞清楚SSH整合中,Struts2、Spring、Hibernate三个框架的分工。

 

Struts2负责表现层(Web层)。

Spring负责业务逻辑层(Service层)。

Hibernate负责数据访问层(Dao层)。

 

Hibernate + Spring + Struts 2

数据库依赖,数据库连接池依赖

日志依赖

Jackson等依赖

 

  • 整合Hibernate
  1. 使用Maven搭建一个Web项目
  2. 在pom.xml中引入Hibernate和数据库依赖

<!-- 配置hibernate依赖包 -->

<dependency>

    <groupId>org.hibernate</groupId>

    <artifactId>hibernate-core</artifactId>

    <version>5.2.10.Final</version>

</dependency>

 

<!-- 配置mysql驱动包 -->

<dependency>

    <groupId>mysql</groupId>

    <artifactId>mysql-connector-java</artifactId>

    <version>5.1.43</version>

</dependency>

  1. 创建所需要的

com.zking.pojo

com.zking.action

com.zking.dao

com.zking.service

  1. 创建数据库表

在MySQL中创建数据库表:

Create table t_user(

user_id int primary key auto_increment,

user_name varchar(50),

pass_word varchar(50)

);

  1. 创建实体类及映射文件

根据数据库表的字段在com.zking.pojo中创建实体类(User.java)和 映射文件(User.hbm.xml)。

 

User.java代码:

/**

 * 用户实体类

 *

 * @author DML

 * @date 2018年8月18日

 *

 */

public class User implements Serializable {

 

private static final long serialVersionUID = 7206188332382708460L;

 

private Integer userId;

private String userName;

private String password;

 

public User() {

super();

}

public User(String userName, String password) {

super();

this.userName = userName;

this.password = password;

}

public User(Integer userId, String userName, String password) {

super();

this.userId = userId;

this.userName = userName;

this.password = password;

}

public Integer getUserId() {

return userId;

}

public void setUserId(Integer userId) {

this.userId = userId;

}

public String getUserName() {

return userName;

}

public void setUserName(String userName) {

this.userName = userName;

}

public String getPassword() {

return password;

}

public void setPassword(String password) {

this.password = password;

}

@Override

public String toString() {

return "User [userId=" + userId + ", userName=" + userName + ", password=" + password + "]";

}

 

}

 

User.hbm.xml代码:

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"

"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<!-- Generated 2018-8-18 12:59:17 by Hibernate Tools 3.5.0.Final -->

<hibernate-mapping package="com.zking.ssh.pojo">

    <class name="User" table="T_USER">

        <id name="userId" column="USER_ID">

            <generator class="native" />

        </id>

        <property name="userName" column="USER_NAME" />

        <property name="password" column="PASS_WORD" />

    </class>

</hibernate-mapping>

  1. 配置hibernate.cfg.xml核心配置文件

在src或resources下创建核心配置文件hibernate.cfg.xml,主要配置:数据源、hibernate配置、映射文件加载。(使用hibernate工具创建cfg.xml文件)

 

<hibernate-configuration>

    <!-- 如果这里出现name="sessionFactory"则会报JNDI错 -->

    <session-factory>

 

<!-- 第一:设置数据库配置(数据源),必须配置项 -->

<property name = "connection.driver_class">com.mysql.jdbc.Driver</property>

<property name = "connection.url">jdbc:mysql://localhost:3306/db_abc</property>

<property name = "connection.username">root</property>

<property name = "connection.password">root</property>

 

<!-- 第二:设置hibernate配置,可选配置项 -->

<property name = "dialect">org.hibernate.dialect.MySQLDialect</property>

<property name = "show_sql">true</property>

<property name = "format_sql">true</property>

 

<!-- 第三:设置加载映射文件,必须配置项 -->

<mapping resource="com/zking/pojo/User.hbm.xml" />

 

</hibernate-configuration>

 

  1. 测试hibernate框架

创建测试类,对hibernate框架的配置进行测试。

@Test

public void testHibernate(){

Configuration cfg = new Configuration().configure();

SessionFactory sessionFactory = cfg.buildSessionFactory();

Session session = sessionFactory.openSession();

Transaction tx = null;

try{

tx = session.beginTransaction();

    // 测试添加一条记录

    session.save(new User("admins","123"));

    tx.commit();

    }catch(Exception e){

    tx.rollback();

}finally{

        session.close();

    sessionFactory.close();

}

}

 

查看结果是否完成了添加功能。

如果出现了报错先解决报错,再走Spring的整合。

 

  • 整合Spring

思想:将Hibernate交由Spring(大管家)管理。

  1. 在pom.xml中引入Spring的依赖

<dependency>

    <groupId>org.springframework</groupId>

    <artifactId>spring-context</artifactId>

    <version>4.3.10.RELEASE</version>

</dependency>

  1. 配置Spring核心配置文件:applicationContext.xml

2.1 src下创建Spring核心配置文件:applicatoinContext.xml

也可以配置多个applicationContext-***.xml,用于实例化各包中的类对象。如:

applicationContext-action.xml

applicationContext-entity.xml

applicationContext-biz.xml

applicationContext-dao.xml

……

为每个包配置一个applicationContext.xml文件,好处:分包实例化对象。也可以写在一个applicationContext.xml中,在web.xml中加载一次即可。

 

2.2 在rescources中创建数据库连接池配置文件:db.properties

//配置数据源参数(需引入C3P0依赖)

driver_Class=com.mysql.jdbc.Driver

url=jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8

uname=root

upass=root //注意不要写password,否则与数据源配置name有冲突

 

//置连接池参数

initPoolSize=3     //最小连接数

maxPoolSize=20 //最大连接数

 

注意:整合时使用数据库连接池技术,hibernate.cfg.xml中的设置数据库配置代码可以删除。

 

在pom.xml中引入数据库连接池技术C3PO依赖:

<!-- 引入C3P0数据库连接池 -->

<dependency>

    <groupId>com.mchange</groupId>

    <artifactId>c3p0</artifactId>

<version>0.9.5.2</version>

</dependency>

 

2.3 配置applicationContext.xml(Spring整合重点)

<!-- 1. 载入db.properties -->

<context:property-placeholder location="classpath:db.properties" />

<!-- 注意:如果敲不出<context>说明该xml的<beans>中没有引入xmlns:context -->

 

<!-- 2. 配置数据源(需引入C3P0,同时删除hibernate.hbm.xml中的数据源配置 -->

<!-- 要引用C3PO中的ComboPooledDataSource类 -->

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">

    <!-- 读取db.properties中参数 -->

    <property name="user" value="${uname}"></property>

    <property name="password" value="${upass}"></property>

    <property name="jdbcUrl" value="${url}"></property>

    <property name="driverClass" value="${driver_Class}"></property>

    <property name="initialPoolSize" value="${initPoolSize}"></property>

    <property name="maxPoolSize" value="${maxPoolSize}"></property>

<!-- 注意:db.properties中的key尽量不要与这里name的值一致,会引起冲突 -->

</bean>

 

<!-- 3. 配置SessionFactory -->

<!-- 实例化LocalSessionFactoryBean类,需要在pom.xml中引入spring-orm支持依赖 -->

<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">

    <!-- 3.1 引入数据源 -->

    <property name="dataSource" ref="dataSource"></property>

    <!-- 3.2 加载hibernate核心配置文件 (hibernate核心文件交给spring来加载)-->

    <property name="configLocation" value="classpath:hibernate.cfg.xml"></property>

    <!-- 3.3 加载hibernate映射文件 -->

    <property name="mappingLocations" value="classpath:com/zking/pojo/*.hbm.xml"></property>

<!--

*.hbm.xml代表加载pojo下面所有的.hbm.xml文件

        删除掉hibernate.cfg.xml中<mapping>对应的映射文件

        提示:hibernate.cfg.xml中的show_sql和format_sql其实也可以写到db.properties中,那么hibernate.cfg.xml文件就可以删除掉了。

    -->

</bean>

 

<!-- 4. 配置事务 -->

<bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">

    <property name="sessionFactory" ref="sessionFactory"></property>

</bean>

 

<!-- 5. 配置事务的属性 -->

<!--

在<beans>标签中复制xmlns:aop="http://www.springframework.org/schema/aop"

修改成:xmlns:tx="http://www.springframework.org/schema/tx"

复制最后一行:http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context-4.3.xsd

改成:http://www.springframework.org/schema/tx

http://www.springframework.org/schema/tx/spring-tx-4.3.xsd

这样<tx:>就可以使用。

<tx:advice>表示通知/增强,AOP在项目中是通过事务来体现,而事务的底层是通知。

-->

<tx:advice id="myAdvice" transaction-manager="transactionManager">

<!-- 事务的属性 -->

    <tx:attributes>

        <!--当方法以add开头,REQUIRED表示如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中

1、PROPAGATION_REQUIRED:如果存在一个事务,则支持当前事务。如果没有事务则开启。
2、PROPAGATION_SUPPORTS:如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行。
3、PROPAGATION_MANDATORY:如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。
4、PROPAGATION_REQUIRES_NEW:总是开启一个新的事务。如果一个事务存在,则将这个存在的事务挂起。
5、PROPAGATION_NOT_SUPPORTED:总是非事务地执行,并挂起任何存在的事务。
6、PROPAGATION_NEVER:总是非事务地执行,如果存在一个活动事务,则抛出异常。
7、 PROPAGATION_NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中,如果没有活动事务,则按TransactionDefinition.PROPAGATION_REQUIRED属性执行

-->

<tx:method name="add*" propagation="REQUIRED"/>

<tx:method name="update*" propagation="REQUIRED"/>

<tx:method name="delete*" propagation="REQUIRED"/>

<tx:method name="*"/>

</tx:attributes>

</tx:advice>

 

<!-- 6. 配置事务的切点 -->

<!-- 指明哪些包里的哪些类,哪些类中的哪些方法要事务 -->

<aop:config>

    <!-- * com.zking.dao.*.*(..)表示com.zking.dao包下所有的类中的所有方法这是一种固定写法 -->

    <aop:pointcut  id="myCut"  expression="execution(* com.zking.dao.*.*(..))"/>

<!--注意:需要再次引入一个依赖:spring-aspects,用于解析上面这个表达式-->

<!-- 通知关联事务的属性 -->

<aop:advisor advice-ref="myAdvice"  pointcut-ref="myCut"/>

</aop:config>

 

问题:将dao包下所有的dao方法做为通知切入到事务中。

 

在pom.xml中引入依赖:

<!-- 引入spring-orm依赖 -->

<dependency>

    <groupId>org.springframework</groupId>

    <artifactId>spring-orm</artifactId>

    <version>4.3.10.RELEASE</version>

</dependency>

 

<!-- 引入Spring 的AspectJ依赖,解析事务切点 -->

<dependency>

    <groupId>org.springframework</groupId>

    <artifactId>spring-aspects</artifactId>

    <version>4.3.10.RELEASE</version>

</dependency>

  1. 配置Dao

3.1 编写接口:IUserDao

public interface IUserDao{

// 添加对象

public void addUser(User user);

}

 

3.2 编写实现类:UserDaoImpl,实现接口IUserDao

该实现类中有CRUD操作,hibernate中做CRUD操作是通过session对象,而session对象是通过sessionFactory获得,因此主要是如何获得sessionFactory。另注意,不要想着new对象,所以对象实例化交由xml中配置。

public class UserDaoImpl implements IUserDao{

    // 声明sessionFactory,已在applicationContext.xml中实例化

private SessionFactory sessionFactory;

// 省略sessionFactory的set和get方法

 

// 获得session的方法

    public Session getSession(){

    return sesionFactory.getCurrentSession();

}

 

@override

public void addUser(User user){

    // 添加方法

getSession().save(stu);

}

}

  1. 在applicationContext-dao.xml中配置对象

<!--配置UserDaoImpl-->

<bean id="UserDaoImpl" class="com.zking.dao.UserDaoImpl">

    <!--注入:ref="sessionFactory"引用的是跨文件的id,引用applicationContext-public.xml中id="sessionFactory"对象 -->

    <property name="sessionFactory" ref="sessionFactory"></property>

</bean>

  1. 测试Spring框架

@Test

public void test(){

    // 加载spring配置文件

    ApplicationContext ac = new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml", "applicationContext-dao.xml"});

// 测试能否得到sessionFactory对象

// Object obj = ac.getBean("sessionFactory");

// System.out.println(obj);

 

    // 里氏替换

IUserDao iUserDao = (IUserDao)ac.getBean("UserDaoImpl");

iUserDao.addUser(new User("小红", "女", 20));

}

 

注意:如果运行报错sessionFactory错误,那么有可能是数据源、hibernate核心配置文件、映射文件出了问题。因为这三者出问题,都会报sessionFactory错误,applicationContext.xml配置文件中已表现出。

 

 

  • 整合Struts2
  1. 在pom.xml中导入struts依赖

<!-- 引入struts2框架依赖 -->

<dependency>

    <groupId>org.apache.struts</groupId>

    <artifactId>struts2-core</artifactId>

    <version>2.3.33</version>

</dependency>

 

<!-- 引入struts2对Spring的支持插件 -->

<dependency>

<groupId>org.apache.struts</groupId>

<artifactId>struts2-spring-plugin</artifactId>

<version>2.3.33</version>

</dependency>

 

  1. 配置web.xml文件

<!-- 启动服务器,加载Struts2核心过滤器 -->

<filter>

<filter-name>struts2</filter-name>

<filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>

</filter>

<filter-mapping>

    <filter-name>struts2</filter-name>

    <url-pattern>*.action</url-pattern>

</filter-mapping>

  1. Web层页面

<form action="UserActionadd.action" method="post">

    <!-- 注意:这里的stu.sname中stu一定要与action中实体类名相同stu表示action中的对象 -->

    <input type="text" name="stu.sname" /><br/>

    <input type="text" name="stu.ssex" /><br/>

    <input type="text" name="stu.sage" /><br/>

    <input type="submit" value="保存" />

</form>

  1. 配置biz包

4.1 配置IUserBiz(UserDao的biz接口),里面的方法与IUserDao一致

public interface IUserBiz{

    public void addUser(User stu);

}

 

4.2 创建biz实现类:UserBizImpl,实现IUserBiz接口,重写addUser()方法

public class UserBizImpl implements IUserBiz{

// dao包中UserDaoImpl的父接口

    private IUserDao iUserDao;

    public void setIUserDao(IUserDao iUserDao){

    this.iUserDao = iUserDao;

}

public IUserDao getIUserDao(){

return iUserDao;

}

 

@override

public void addUser(User stu){

    iUserDao.addUser(stu);

}

}

 

同时需要在applicationContext-biz.xml中配置UserBizImpl,为iUserDao注入一个对象。

<bean id="UserBizImpl" class="com.zking.biz.UserBizImpl">

    <!-- ref="UserDaoImpl"为applicationContext-dao.xml中的对象-->

    <property name="iUserDao" ref="UserDaoImpl">

</bean>

  1. 编写Action

在com.zking.action包中创建表单对应的Action对象。

Public class UserAction{

private User stu;   // 注意这里的stu与form表单中的stu一致

public User getStu() {

return stu;

}

public void setStu(User stu) {

this.stu = stu;

}

 

private IUserBiz iUserBiz;   // 用来调用addUser()

public IUserBiz getIUserBiz() {

return iUserBiz;

}

public void setIUserBiz(IUserBiz iUserBiz) {

this.iUserBiz = iUserBiz;

}

 

public String add() throws Exception {

iUserBiz.addUser(stu);

return "success";

}

}

  1. 配置applictaionContext-action.xml

<!-- 配置UserAction -->

<bean id="UserAction" class="com.zking.action.UserAction" scope="prototype">

    <property name="iUserBiz" ref="UserBizImpl"></property>

</bean>

  1. 配置struts.xml

在resources中创建struts.xml配置文件,配置文件的跳转。并准备一个success.jsp,先确保流程跳转无误。

<struts>

<package name="myPackage" extends="struts-default">

    <!-- 注意:class的值不再是一个类的全栈名,而是<bean>中的id -->

        <action name="UserAction*" class="UserAction" method="{1}">

            <result name="success">/success.jsp</result>

</action>

    </package>

</struts>

  1. 在web.xml中加载Spring

<!--加载Spring-->

<context-param>

<param-name>contextConfigLocation</param-name>

<param-value>classpath:applicationContext-*.xml</param-value>

</context-param>

 

<!--添加监听-->

<listener>

<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

    <!--注意:需要导入spring-web依赖-->

</listener>

注意:<context-param>在web.xml中的dtd约束中有要求:放在过滤器的上面。

 

引入spring-web依赖

<dependency>

<groupId>org.springframework</groupId>

    <artifactId>spring-web</artifactId>

<version>5.0.8.RELEASE</version>

</dependency>

 

 

 

总结:SSH框架最主要的本质是:“高内聚、低耦合”。在ssh中使用Struts是作为系统的整体基础架构,主要负责MVC的分离,在Struts框架的模型部分,控制业务跳转,利用Hibernate框架对持久层提供支持,Spring做管理层,管理struts和hibernate。具体做法是:用面向对象的分析方法根据需求提出一些模型,将这些模型实现为基本的Java对象,然后编写基本的DAO(Data Access Objects)接口,并给出Hibernate的DAO实现,采用Hibernate架构实现的DAO类来实现Java类与数据库之间的转换和访问,最后由Spring做管理,管理struts和hibernate。

 

 

Web.xml

<!-- 配置字符集拦截器,由spring提供 -->

<filter>

    <filter-name>encodingFilter</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-mapping>

    <filter-name>encodingFilter</filter-name>

    <url-pattern>/*</url-pattern>

</filter-mapping>

 

Struts.xml

<struts>

    <!-- 包含struts-default.xml文件 -->

    <include file="struts-default.xml"></include>

    <!-- 设置字符集为UTF-8 -->

    <constant name="struts.i18n.encoding" value="UTF-8"></constant>

    <!-- 设置struts2允许的后缀名 -->

    <constant name="struts.action.extension" value="do,action"></constant>

    <!-- 是否允许浏览器缓存静态内容 -->

    <constant name="struts.serve.static.browserCache" value="false"></constant>

    <!-- 配置文件变化时是否重新加载 -->

    <constant name="struts.configuration.xml.reload" value="true"></constant>

    <!-- 是否开启开发者模式 -->

    <constant name="struts.devMode" value="true"></constant>

    <!-- 是否允许动态方法调用 -->

    <constant name="struts.enable.DynamicMethodInvocation" value="false"></constant>

    <!-- 由spring担任工程bean -->

<constant name="struts.ObjectFactory" value="spring"></constant>

 

<!--使用动态方法配置:value="true表示使用动态方法调用。-->

<constant name="struts.enable.DynamicMethodInvocation" value="true" />

<package name=my extends=struts-default namespace=/>                                        

<action name="user"  class="userAction">                   

<result name="success">login.jsp</result>   

<allowed-methods>login</allowed-methods>                          

</action>

</package>

 

<!--使用通配符-->

<package name="pack1" extends="struts-default" namespace="/">

    <global-allowed-methods>regex:.*</global-allowed-methods>

    <action name="action_*" class="com.zking.action.Login4Action" method="{1}">

……

    </action>

</package>

 

    <!-- 映射action -->

    <package name="user" namespace="/" extends="struts-default">

        <action name="hello" class="com.xxx.controller.UserAction" method="test">

            <result name="index">index.jsp</result>

        </action>

    </package>

</struts>

 

ApplicationContext.xml

<beans xmlns="http://www.springframework.org/schema/beans"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xmlns:aop="http://www.springframework.org/schema/aop"

    xmlns:c="http://www.springframework.org/schema/c"

    xmlns:cache="http://www.springframework.org/schema/cache"

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

    xmlns:jdbc="http://www.springframework.org/schema/jdbc"

    xmlns:jee="http://www.springframework.org/schema/jee"

    xmlns:lang="http://www.springframework.org/schema/lang"

    xmlns:mvc="http://www.springframework.org/schema/mvc"

    xmlns:p="http://www.springframework.org/schema/p"

    xmlns:task="http://www.springframework.org/schema/task"

    xmlns:tx="http://www.springframework.org/schema/tx"

    xmlns:util="http://www.springframework.org/schema/util"

    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd

        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd

        http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd

        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd

        http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd

        http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd

        http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd

        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd

        http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd

        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd

        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

 

    <!-- 开启注解编程 -->

    <!-- <mvc:annotation-driven></mvc:annotation-driven> -->

    <context:annotation-config></context:annotation-config>

 

    <!-- 指定需要开启(扫描)注解编程的包 -->

    <context:component-scan base-package="com.zking">

    </context:component-scan>

</beans>

 

log4j2-test.properties文件

log4j.rootLogger=ERROR,Console,OneFile,HtmlFile

log4j.logger.org.apache.cxf=DEBUG

log4j.logger.org.hibernate.type.descriptor.sql.BasicBinder=TRACE

log4j.logger.org.hibernate.tool.hbm2ddl=DEBUG

#log4j.logger.org.hibernate.SQL=DEBUG

#log4j.logger.org.hibernate.type.descriptor.sql.BasicExtractor=TRACE

 

log4j.appender.Console=org.apache.log4j.ConsoleAppender

log4j.appender.Console.Target=System.out

log4j.appender.Console.layout=org.apache.log4j.PatternLayout

log4j.appender.Console.Threshold=ALL

log4j.appender.Console.layout.ConversionPattern=[%p][%d{yyyy-MM-dd HH\:mm\:ss,SSS}][%c]%m%n

 

log4j.appender.OneFile=org.apache.log4j.RollingFileAppender

log4j.appender.OneFile.File=d:/OASystem/oasys.txt

log4j.appender.OneFile.MaxFileSize=10MB

log4j.appender.OneFile.Threshold=ERROR

log4j.appender.OneFile.layout=org.apache.log4j.PatternLayout

log4j.appender.OneFile.layout.ConversionPattern=[%p][%d{yyyy-MM-dd HH\:mm\:ss,SSS}][%c]%m%n

 

log4j.appender.HtmlFile=org.apache.log4j.DailyRollingFileAppender

log4j.appender.HtmlFile.file=../logs/oasys/oasys

log4j.appender.HtmlFile.DatePattern='_'yyyy-MM-dd'.html'

log4j.appender.HtmlFile.layout=org.apache.log4j.HTMLLayout

log4j.appender.HtmlFile.Threshold=ERROR

 

 

   

 

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值