• SSH案例--入门级


    1.项目功能展示

    (1)注册

      

    (2)修改地址与级别信息,点击修改

       

    (3)再添加一位成员,进行删除

    点击第二行的删除

    (4)登录模块测试

    输入数据库中没有的信息:

    输入数据库中存在的信息:

      

    2. Web.xml

      Spring提供了ContextLoaderListener,该监听器实现了ServletContextListener接口,他在Web应用程序启动时被触发。当他创建时会自动查找WEB-INF/下的applicationContext.xml,所以当只有一个配置文件且文件名为applicationContext.xml时,则只需要在web.xml文件中配置ContextLoaderListener监听器即可.当有多个配置文件需要载入,则应该使用<context-param>元素指定配置文件的文件名,ContextLoaderListener加载时,会查找名为contextConfigLocation的初始化参数。当Web应用程序启动时先读取web.xml文件,然后创建spring容器,之后根据配置文件内容,装配Bean实例。

    <?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>ssh</display-name>
      <filter>
          <filter-name>struts2</filter-name>
          <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
      </filter>
      <filter-mapping>
          <filter-name>struts2</filter-name>
          <url-pattern>/*</url-pattern>
      </filter-mapping>
      
      
     <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
      </listener>
      
      <context-param>
          <param-name>contextConfigLocation</param-name>
          <param-value>/WEB-INF/applicationContext.xml</param-value>
      </context-param>
    
      <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>
    </web-app>
    View Code

    3. 实体类与其映射文件

    (1)Member.java

    package com.entity;
    
    public class Member {
        private long id;
        private String name;
        private String password;
        private String address;
        private String email;
        private int level;
        public Member(){}
        public Member(String name,String password,String address,String email,int level){
            this.name = name;
            this.password = password;
            this.address = address;
            this.email = email;
            this.level = level;
        }
        public long getId() {
            return id;
        }
        public void setId(long id) {
            this.id = id;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public String getPassword() {
            return password;
        }
        public void setPassword(String password) {
            this.password = password;
        }
        public String getAddress() {
            return address;
        }
        public void setAddress(String address) {
            this.address = address;
        }
        public String getEmail() {
            return email;
        }
        public void setEmail(String email) {
            this.email = email;
        }
        public int getLevel() {
            return level;
        }
        public void setLevel(int level) {
            this.level = level;
        }
        
    }
    View Code

    (2)Member.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">
    <hibernate-mapping>
        <class name="com.entity.Member" table="MEMBER">
            <id name="id" type="long">
                <column name="ID" />
                <generator class="identity" />
            </id>
            <property name="name" type="java.lang.String">
                <column name="NAME" />
            </property>
            <property name="password" type="java.lang.String">
                <column name="PASSWORD" />
            </property>
            <property name="address" type="java.lang.String">
                <column name="ADDRESS" />
            </property>
            <property name="email" type="java.lang.String">
                <column name="EMAIL" />
            </property>
            <property name="level" type="int">
                <column name="LEVEL" />
            </property>
        </class>
    </hibernate-mapping>
    View Code

    4.DAO层

    (1)接口MemberDAO.java

    package com.dao;
    
    import java.util.List;
    
    import com.entity.Member;
    
    public interface MemberDAO {
        public void add(Member member);
        public void update(Member member);
        public void delete(long id);
        public Member findByName(String name,String password);
        public Member findById(long id);
        public List<Member> findAll();
    
    }
    View Code

    (2)实现类MemberDAOImpl.java(sessionFactory通过构造方法注入,使其可以操作数据库)

    package com.dao;
    
    import java.util.List;
    
    import com.entity.Member;
    import org.hibernate.*;
    import org.springframework.beans.factory.annotation.Autowired;
    public class MemberDAOImpl implements MemberDAO {
        
        private SessionFactory sessionFactory;
        
        //构造方法注入SessionFactory对象
        public MemberDAOImpl(SessionFactory sessionFactory) {
            this.sessionFactory=sessionFactory;
        }
        
        //使用SessionFactory对象返回Session对象
        public Session currentSession(){
            return sessionFactory.openSession();
        }
        @Override
        public void add(Member member) {
            Session session=null;
            try {
                session=currentSession();
                Transaction tx = session.beginTransaction();
                session.save(member);
                tx.commit();
            } catch (HibernateException e) {
                e.printStackTrace();
            }finally{
                session.close();
            }
    
        }
    
        @Override
        public void update(Member member) {
            Session session=null;
            try {
                session=currentSession();
                Transaction tx = session.beginTransaction();
                session.update(member);
                tx.commit();
            } catch (HibernateException e) {
                e.printStackTrace();
            }finally{
                session.close();
            }
    
        }
    
        @Override
        public void delete(long id) {
            Session session=null;
            try {
                session=currentSession();
                Transaction tx = session.beginTransaction();
                Member mb = (Member)session.get(Member.class, id);
                session.delete(mb);
                tx.commit();
            } catch (HibernateException e) {
                e.printStackTrace();
            }finally{
                session.close();
            }
    
        }
    
        @Override
        public Member findByName(String name, String password) {
            Session session=null;
            Member result = null;
            try {
                session=currentSession();
                Transaction tx = session.beginTransaction();
                String hsql = " from Member m where m.name = :mname and m.password = :mpassword";
                Query query = session.createQuery(hsql);
                query.setParameter("mname", name);
                query.setParameter("mpassword", password);
                result = (Member)query.uniqueResult();
                tx.commit();
            } catch (HibernateException e) {
                e.printStackTrace();
            }finally{
                session.close();
            }
            return result;
        }
    
        @Override
        public Member findById(long id) {
            Session session=null;
            Member result = null;
            try {
                session=currentSession();
                Transaction tx = session.beginTransaction();
                String hsql = " from Member m where m.id = :id";
                Query query = session.createQuery(hsql);
                query.setParameter("id",id);
                result = (Member)query.uniqueResult();
                tx.commit();
            } catch (HibernateException e) {
                e.printStackTrace();
            }finally{
                session.close();
            }
            return result;
        }
    
        @Override
        public List<Member> findAll() {
            Session session=null;
            List<Member> list = null;
            try {
                session=currentSession();
                Transaction tx = session.beginTransaction();
                String hsql = " from Member";
                Query query = session.createQuery(hsql);
                list = query.list();
                tx.commit();
            } catch (HibernateException e) {
                e.printStackTrace();
            }finally{
                session.close();
            }
            return list;
        }
    
    }
    View Code

    5.业务逻辑层

    (1)接口MemberService.java

    package com.service;
    import java.util.List;
    import com.entity.Member;
    public interface MemberService {
        public void add(Member member);
        public void update(Member member);
        public void delete(long id);
        public Member findByName(String name,String password);
        public Member findById(long id);
        public List<Member> findAll();
    }
    View Code

    (2)实现类MemberServiceImpl.java(设值注入DAO层组件)

    package com.service;
    
    import java.util.List;
    
    import com.dao.MemberDAO;
    import com.entity.Member;
    
    public class MemberServiceImpl implements MemberService {
        private MemberDAO memberDao;
        //设值注入DAO对象
        public void setMemberDao(MemberDAO memberDao) {
            this.memberDao = memberDao;
        }
    
        @Override
        public void add(Member member) {
            if(memberDao.findById(member.getId())==null)
                memberDao.add(member);
    
        }
    
        @Override
        public void update(Member member) {
            if(memberDao.findById(member.getId())!=null)
                memberDao.update(member);
    
        }
    
        @Override
        public void delete(long id) {
            if(memberDao.findById(id)!=null)
                memberDao.delete(id);
    
        }
    
        @Override
        public Member findByName(String name, String password) {
            return memberDao.findByName(name, password);
        }
    
        @Override
        public Member findById(long id) {
            return memberDao.findById(id);
        }
    
        @Override
        public List<Member> findAll() {
            return memberDao.findAll();
        }
    
    }
    View Code

    6. Action类

    (1)注册MemberRegisterAction.java(设值注入业务逻辑组件)

    package com.action;
    
    import com.entity.Member;
    import com.opensymphony.xwork2.ActionSupport;
    import com.service.MemberService;
    
    public class MemberRegisterAction extends ActionSupport {
        private Member member;
        private MemberService memberService;
        public Member getMember() {
            return member;
        }
        public void setMember(Member member) {
            this.member = member;
        }
        //注入业务逻辑组件
        public void setMemberService(MemberService memberService) {
            this.memberService = memberService;
        }
        public String execute(){
            memberService.add(member);
            return SUCCESS;
        }
        
    }
    View Code

    (2)删除MemberDeleteAction.java(设置注入业务逻辑组件,注意传入参数id的相应getter和setter方法)

    package com.action;
    
    import com.opensymphony.xwork2.ActionSupport;
    import com.service.MemberService;
    
    public class MemberDeleteAction extends ActionSupport {
        private MemberService memberService;
    
        public void setMemberService(MemberService memberService) {
            this.memberService = memberService;
        }
        
        private long id;
    
    
        public long getId() {
            return id;
        }
        public void setId(long id) {
            this.id = id;
        }
    
        public String execute(){
            memberService.delete(getId());
            return SUCCESS;
        }
    }
    View Code

    (3)查询MemberQueryAction.java(用于查看所有会员信息)

    package com.action;
    
    import java.util.List;
    
    import org.apache.struts2.ServletActionContext;
    
    import com.entity.Member;
    import com.opensymphony.xwork2.ActionSupport;
    import com.service.MemberService;
    
    public class MemberQueryAction extends ActionSupport {
        private MemberService memberService;
    
        public void setMemberService(MemberService memberService) {
            this.memberService = memberService;
        }
        public String execute(){
            List<Member> list = memberService.findAll();
            ServletActionContext.getRequest().setAttribute("memberList", list);
            return SUCCESS;
        }
    }
    View Code

    (4)修改MemberUpdateAction.java(有两个方法,execute()和showMember(),当需要修改时,showMember()方法转向update.jsp.并设置好需要修改的对象,修改完成之后,由spring托管的memberUpdateAction组件执行execute方法进行update操作)

    package com.action;
    
    import com.entity.Member;
    import com.opensymphony.xwork2.ActionSupport;
    import com.service.MemberService;
    
    public class MemberUpdateAction extends ActionSupport {
        private MemberService memberService;
        private Member member;
        private long id;
        public void setMemberService(MemberService memberService) {
            this.memberService = memberService;
        }
        public Member getMember() {
            return member;
        }
        public void setMember(Member member) {
            this.member = member;
        }
        public long getId() {
            return id;
        }
        public void setId(long id) {
            this.id = id;
        }
        public String showMember(){
            Member mb = memberService.findById(getId());
            setMember(mb);
            return SUCCESS;
        }
        public String execute(){
            memberService.update(member);
            return SUCCESS;
        }
    }
    View Code

    (5)登录MemberLoginAction.java(将输入的信息与数据库中进行比较)

    package com.action;
    
    import com.entity.Member;
    import com.opensymphony.xwork2.ActionSupport;
    import com.service.MemberService;
    
    public class MemberLoginAction extends ActionSupport {
        private Member member;
        private MemberService memberService;
        public Member getMember() {
            return member;
        }
        public void setMember(Member member) {
            this.member = member;
        }
        //注入业务逻辑组件
        public void setMemberService(MemberService memberService) {
            this.memberService = memberService;
        }
        
        public String execute(){
            Member mb =memberService.findByName(member.getName(), member.getPassword());
            if(mb!=null)
                return SUCCESS;
            else
                return ERROR;
        }
    }
    View Code

    7.struts.xml

      spring托管将Struts2中的Action的实例化工作交由Spring容器统一管理,同时使Struts2中的Action实例能够访问Spring提供的业务逻辑资源,spring容器依赖注入的优势也可以体现,Struts2提供的spring插件struts2-spring-plugin-2.3.16.3.jar配合<constant name="struts.objectFactory" value="spring">使得Struts2的action由Spring来负责进行实例化.

    <?xml version="1.0" encoding="UTF-8" ?>
    
    <!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
        "http://struts.apache.org/dtds/struts-2.3.dtd">
    
    <struts>
    
        <constant name="struts.devMode" value="true" />
        
        <constant name="struts.objectFactory" value="spring"></constant>
        
        <package name="default" namespace="/" extends="struts-default">
            <!-- class属性值是spring定义的Bean,当执行execute()返回success时,控制转到另一个动作memberQuery -->
            <action name="memberRegister" class="memberRegisterAction">
                <result name="success" type="redirectAction">/memberQuery</result>
            </action>
            
            <action name="memberLogin" class="memberLoginAction">
                <result name="success">/welcome.jsp</result>
                <result name="error">/register.jsp</result>
            </action>
            
            <action name="memberQuery" class="memberQueryAction">
                <result name="success">/displayAll.jsp</result>
            </action>
            
            <action name="memberDelete" class="memberDeleteAction">
                <result name="success" type="redirectAction">/memberQuery</result>
            </action>
            
            <action name="memberShow" class="memberUpdateAction" method="showMember">
                <result name="success">/update.jsp</result>
            </action>
            
            <action name="memberUpdate" class="memberUpdateAction">
                <result name="success" type="redirectAction">/memberQuery</result>
            </action>
        </package>
    </struts>
    View Code

    8.applicationContext.xml

      spring 框架提供了多种数据源类,可以使用spring提供的DriverManagerDataSource类还可以使用第三方数据源,如C3P0的ComboPooledDataSource数据源类,注意相应的两个jar包c3p0-0.9.2.1.jar mchange-commons-java-0.2.3.4.jar.

      Hibernate4已经完全实现自己的事物管理,所以spring4不提供HibernateDaoSupport和HibernateTemplete的支持。可以在应用程序的spring上下文中,像配置其他bean那样来配置HibernateSession工厂。如果要使用XML文件定义对象与数据库之间的映射,则需要在spring中配置LocalSessionFactoryBean。hibernateProperties属性配置了Hibernate如何进行操作的细节。"hibernate.current_session_context_class"是为当前Session提供一个策略,Session由org.springframework.orm.hibernate4.SpringSessionContext.currentSession得到。将sessionFactory注入到其他Bean中,如注入到DAO组件中,使其获得SessionFactory的引用后,就可以实现对数据库的访问。

    <?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-4.3.xsd  
            http://www.springframework.org/schema/context 
            http://www.springframework.org/schema/context/spring-context-4.3.xsd">
    
    
        <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
            <property name="driverClass" value="com.mysql.jdbc.Driver"/>
            <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/zzh?useUnicode=true&amp;characterEncoding=UTF-8"/>
            <property name="user" value="root"/>
            <property name="password" value="root"/>
            <property name="maxPoolSize" value="40"/>
            <property name="minPoolSize" value="1"/>
            <property name="initialPoolSize" value="2"/>
            <property name="maxIdleTime" value="20"/>
        </bean>
        
        <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
            <property name="dataSource" ref="dataSource"/>
            <property name="mappingResources">
                <list>
                    <value>com/entity/Member.hbm.xml</value>
                </list>
            </property>
            <property name="hibernateProperties">
                <props>
                    <prop key="hibernate.show_sql">true</prop>
                    <prop key="hibernate.hbm2ddl.auto">update</prop>
                    <prop key="hibernate.temp.use_jdbc_metadata_defaults">false</prop>
                    <prop key="hibernate.current_session_context_class">
                        org.springframework.orm.hibernate4.SpringSessionContext
                    </prop>
                    <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                </props>
            </property>
        </bean>
        
        
        <bean id="memberDao" class="com.dao.MemberDAOImpl">
        <!-- 构造方法注入会话工厂组件sessionFactory -->
            <constructor-arg>
                <ref bean="sessionFactory"/>
            </constructor-arg>
        </bean>
        
        
        <bean id="memberService" class="com.service.MemberServiceImpl">
        <!-- 设值注入DAO组件 -->
            <property name="memberDao" ref="memberDao"></property>
        </bean>
        
        
        <bean id="memberRegisterAction" class="com.action.MemberRegisterAction">
        <!-- 设值注入业务逻辑组件 -->
            <property name="memberService" ref="memberService"></property>
        </bean>
        
        <bean id="memberLoginAction" class="com.action.MemberLoginAction">
            <property name="memberService" ref="memberService"></property>
        </bean>
        
        <bean id="memberQueryAction" class="com.action.MemberQueryAction">
            <property name="memberService" ref="memberService"></property>
        </bean>
        
        <bean id="memberDeleteAction" class="com.action.MemberDeleteAction">
            <property name="memberService" ref="memberService"></property>
        </bean>
        
        <bean id="memberUpdateAction" class="com.action.MemberUpdateAction">
            <property name="memberService" ref="memberService"></property>
        </bean>
    </beans>
    View Code

    9.jsp

    (1)注册register.jsp

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <%@ taglib prefix="s" uri="/struts-tags" %>
    <!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=UTF-8">
    <title>会员注册页面</title>
    </head>
    <body>
        <s:form action="memberRegister" method="post">
            <h4><s:text name="欢迎注册会员"></s:text></h4>
            <s:property value="exception.message"/>
            <s:textfield name="member.name" label="会员姓名" tooltip="Enter your name!" required="true"></s:textfield>
            <s:password name="member.password" label="会员口令" tooltip="Enter your password!"></s:password>
            <s:textfield name="member.address" label="会员地址"></s:textfield>
            <s:textfield name="member.email" label="会员邮箱"></s:textfield>
            <s:textfield name="member.level" label="会员级别"></s:textfield>
            <s:submit value="提交"/>
        </s:form>
    </body>
    </html>
    View Code

    (2)所有会员信息displayAll.jsp

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <%@ taglib prefix="s" uri="/struts-tags" %>
    <!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=UTF-8">
    <title>显示会员信息</title>
    </head>
    <body>
        <h4>会员信息</h4>
        <table border='1'>
            <tr>
                <td>会员id</td><td>会员名</td><td>密码</td><td>地址</td><td>邮箱</td><td>级别</td><td>删除</td><td>修改</td>
            </tr>
                <s:iterator value="#request.memberList" id="mb">
                    <tr>
                        <td><s:property value="#mb.id"/></td>
                        <td><s:property value="#mb.name"/></td>
                        <td><s:property value="#mb.password"/></td>
                        <td><s:property value="#mb.address"/></td>
                        <td><s:property value="#mb.email"/></td>
                        <td><s:property value="#mb.level"/></td>
                        
                        <td>
                            <a href="<s:url action="memberDelete"><s:param name="id"><s:property value="#mb.id"/></s:param>
                            </s:url>">删除</a>
                        </td>
                        <td>
                            <a href="<s:url action="memberShow"><s:param name="id"><s:property value="#mb.id"/></s:param>
                            </s:url>">修改</a>
                        </td>
                    </tr>
                </s:iterator>
        </table>
        <a href="register.jsp">返回注册页面</a>
    </body>
    </html>
    View Code

    (3)修改页面update.jsp

      注意:不允许修改会员id,当需要将会员id传递给更新会员动作,所以使用隐藏表单域标签来接收显示会员页面传递来的会员id在update.jsp页面提交时再传递给更新会员的动作memberUpdate。

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <%@ taglib prefix="s" uri="/struts-tags" %>
    <!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=UTF-8">
    <title>修改会员信息</title>
    </head>
    <body>
        <s:form action="memberUpdate" method="post">
            <h4><s:text name="修改会员信息"></s:text></h4>
            <s:actionerror/>
            
            <s:hidden name="member.id" value="%{member.id}"></s:hidden>
            <s:textfield name="member.name" label="会员姓名" required="true"></s:textfield>
            <s:textfield name="member.password" label="会员口令"></s:textfield>
            <s:textfield name="member.address" label="会员地址"></s:textfield>
            <s:textfield name="member.email" label="会员邮箱"></s:textfield>
            <s:textfield name="member.level" label="会员级别"></s:textfield>
            <s:submit value="提交"/>
        </s:form>
    </body>
    </html>
    View Code

    (4)登录login.jsp

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <%@ taglib prefix="s" uri="/struts-tags" %>
    <!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=UTF-8">
    <title>会员登录</title>
    </head>
    <body>
        <s:form action="memberLogin" method="post">
            <s:textfield name="member.name" label="会员姓名"></s:textfield>
            <s:password name="member.password" label="会员口令"></s:password>
            <s:submit value="提交"></s:submit>
        </s:form>
    </body>
    </html>
    View Code

    (5)welcome.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=UTF-8">
    <title>贵宾你好!</title>
    </head>
    <body>
        合法成员
    </body>
    </html>
    View Code

    10. END

      github地址:(https://github.com/byzhaozihan/s2sh_entry-level)

      本篇案例算是最简单的ssh整合,注入形式也都一目了然,如果对你有帮助,请点赞或关注我,谢谢观看!

  • 相关阅读:
    url处理函数
    各种排序方法(冒泡,快速,插入,选择),二分查找
    js二叉树,前序/中序/后序(最大最小值,排序)
    vs中nuget命令的用法
    父子页面间调用
    Ubuntu16.04.2 LTS下使用编译安装程序(使用configure、make、 make install)
    windows与虚拟机的linux共享一个文件夹
    Linux下安装nginx
    Linux下安装VSCode
    Ubuntu下安装jdk
  • 原文地址:https://www.cnblogs.com/zhaozihan/p/5893785.html
Copyright © 2020-2023  润新知