仿牛客网 01 环境搭建 +SSM 框架入门

2023-10-16 23:30

本文主要是介绍仿牛客网 01 环境搭建 +SSM 框架入门,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

课程介绍

课程目标

  • 学会主流的web开发技术和框架
  • 积累一个真实的web项目的开发经验
  • 掌握热点面试题的答题策略

image

技术架构

  • Spring Boot
  • Spring、Spring MVC、MyBatis
  • Redis、Kafka、Elasticsearch
  • Spring Security、Spring Actuator

springboot是简化spring的操作的。

开发环境

  • 构建工具:Apache Maven 3.6.1
  • 集成开发工具:IntelliJ IDEA
  • 数据库:MySQL、Redis
  • 应用服务器:Apache Tomcat
  • 版本控制工具:Git

搭建开发环境

Apache Maven

  • 可以帮助我们构建项目,管理项目中的jar包

  • Maven仓库:存放构件的位置

    • 本地仓库:默认是~/.m2/repository
    • 远程仓库:中央仓库、镜像仓库、私服仓库
  • 官网:https://maven.apache.org/index.html

maven命令快速入门

更改远程仓库

因为maven默认访问的是远程的私服仓库,非常慢,所以解压在官方下载好的压缩包后,conf文件夹下的setting.xml改成阿里云的镜像仓库。

  <mirrors><!-- mirror| Specifies a repository mirror site to use instead of a given repository. The repository that| this mirror serves has an ID that matches the mirrorOf element of this mirror. IDs are used| for inheritance and direct lookup purposes, and must be unique across the set of mirrors.|<mirror><id>mirrorId</id><mirrorOf>repositoryId</mirrorOf><name>Human Readable Name for this Mirror.</name><url>http://my.repository.com/repo/path</url></mirror>--><mirror><id>aliyunmaven</id><mirrorOf>*</mirrorOf><name>阿里云公共仓库</name><url>https://maven.aliyun.com/repository/public</url></mirror></mirrors>

这里是阿里云maven仓库的配置指南。

image

加入环境变量

为了方便使用maven,我们可以将bin目录加入到系统环境变量PATH当中,可以直接在控制台中启动。

image

image

image

IntelliJ IDEA

  • 目前最流行的Java集成开发工具
  • 官网:www.jetbrains.com/idea
导入和打开的区别:

用idea创建的项目可以直接打开,用eclipse创建的项目需要导入。

image

配置Maven

image

创建项目

image

image

创建好项目,右键运行程序。

image

Spring Initializr

  • 创建Spring Boot项目的引导工具。
  • 按照功能需求归类,只要把相关的功能搜一下,就能把相关依赖的包下载下来,底层是基于maven的。Maven仓库推荐
  • 官网:https://start.spring.io/

进入官网选择好功能,点击按钮生成压缩包,解压后在IDEA中打开项目:

image

image

image

springboot内置tomcat。

Spring Boot入门示例

  • Spring Boot核心作用:

    • 起步依赖(一个依赖其实是很多包的组合)
    • 自动配置,几乎不用做配置就能跑起来程序
    • 端点监控
一个简单的处理客户端请求案例

在上面的spring initializr解压的community项目里面新建一个controller包,新建一个AlphaController类。

image

package com.nowcoder.community.controller;import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;/*** @author shkstart* @create 2023-07-12 17:33*/
@Controller
@RequestMapping("/alpha")
public class AlphaController {@RequestMapping("/hello")@ResponseBodypublic String sayHello(){return "Hello Spring Boot.";}
}

image

修改tomcat的端口

image

加入以下两行:

server.port=8080
server.servlet.context-path=/community

访问地址:http://localhost:8080/community/alpha/hello

Spring入门

spring全家桶

  • Spring Framework
  • Spring Boot
  • Spring Cloud
  • Spring Cloud Data Flow

官网:https://spring.io/

Spring Framework介绍

  • Spring Core

    • loC、AOP
  • Spring Data Access

    • -Transactions、Spring MyBatis
  • Web Servlet

    • -Spring MVC
  • Integration
    -Email、Scheduling、AMQP、Security

image

演示IOC的使用

正常运行是运行的CommunityApplication启动类,但是我们希望在测试代码中的运行环境是和启动类一样的,因此我们需要在测试类上加注解@ContextConfiguration(classes = CommunityApplication.class),并实现ApplicationContextAware接口,重写下面的setApplicationContext方法。

package com.nowcoder.community;import org.junit.jupiter.api.Test;
import org.springframework.beans.BeansException;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.test.context.ContextConfiguration;@SpringBootTest
@ContextConfiguration(classes = CommunityApplication.class)
class CommunityApplicationTests implements ApplicationContextAware {private ApplicationContext applicationContext;@Testvoid contextLoads() {}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext=applicationContext;}//测试传过来的这个spring容器是不是存在@Testpublic void testApplication(){System.out.println(applicationContext);}
}

image

新建一个dao包,在其下面新建一个AlaphaDao接口,写一个查询数据的select方法:

package com.nowcoder.community.dao;/*** @author shkstart* @create 2023-07-13 10:41*/
public interface AlphaDao {String select();
}

接着,再新建一个AlaphaDao接口的AlphaDaoHibernateImpl实现类,如果没有加@Repository注解,则spring装配不了bean到容器里面,当Spring无法将bean装配到容器中时,意味着你无法通过Spring容器来获取该bean的实例。这可能会导致你无法访问数据库,因为通常数据库连接和操作是通过Spring容器中的bean来完成的。

package com.nowcoder.community.dao;import org.springframework.stereotype.Repository;/*** @author shkstart* @create 2023-07-13 10:44*/
@Repository
public class AlphaDaoHibernateImpl implements AlphaDao{@Overridepublic String select() {return "Hibernate";}
}

在测试类下这个方法,我们可以通过容器获取到bean。

    //测试传过来的这个spring容器是不是存在,通过这个容器获取到bean,再使用它的方法。@Testpublic void testApplication(){System.out.println(applicationContext);AlphaDao alphaDao = applicationContext.getBean(AlphaDao.class);System.out.println(alphaDao.select());}

假设这样的一个场景:我们使用的hibernate已经被淘汰了,mybatis更有优势,需要将hibernate替换成mybatis的话,我们该怎么处理呢?

我们现在创建另外一个AlphaDao的实现类AlphaDaoMyBatisImpl,此时就有两个AlphaDao的实现类了,那么在测试类下的那个applicationContext获取到的对象就有歧义,不知道获取到的哪个,因此我们还需要加上@Primary注解,保证在装配bean时优先级最高。

package com.nowcoder.community.dao;import org.springframework.stereotype.Repository;/*** @author shkstart* @create 2023-07-13 10:57*/
@Repository
public class AlphaDaoMyBatisImpl implements AlphaDao{@Overridepublic String select() {return "Mybatis";}
}

image

使用ApplicationContext获取对象的好处就出来了,我们所做的就只是加个注解,创建另外一个实现类,就更新了使用的对象。这样做依赖的就是接口,而不是这个bean本身,降低了bean之间的藕度。

但是现在因为mybatis的优先级最高,所以到的bean对象一直都是mybatis,而这个时候我们想要用hibernate该怎么做?

这里因为AlphaDaoHibernateImpl这个类名比较长,我们可以修改一下注解,自定义名字:@Repository("alphaHibernate")

通过alphaDao = applicationContext.getBean("alphaHibernate", AlphaDao.class);就可以获取到hibernate。

spring管理容器的方法

创建一个service包,处理业务功能。在其下面新建一个AlphaService类:

package com.nowcoder.community.service;import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;/*** @author shkstart* @create 2023-07-14 9:47*/
@Service
public class AlphaService {//构造时调用public AlphaService(){System.out.println("实例化AlphaService");}//构造后调用@PostConstructpublic void init(){System.out.println("初始化AlphaService");}//销毁前调用@PreDestroypublic void destory(){System.out.println("销毁AlphaService");}}

在测试类下获取service这个bean类。

    //获取service@Testpublic void testBeanManagement(){AlphaService alphaService= applicationContext.getBean(AlphaService.class);System.out.println(alphaService);}

image

image

如果想在获取bean的时候不是单例对象,就加类上加一个@Scope("prototype"),每次就会获取一个新的实例,一般默认的是单例@Scope("singletop")

下面进行测试:

    //获取service@Testpublic void testBeanManagement(){AlphaService alphaService= applicationContext.getBean(AlphaService.class);System.out.println(alphaService);alphaService= applicationContext.getBean(AlphaService.class);System.out.println(alphaService);}

image

上面的测试管理的是自己写的类,但是有时候需要管理第三方打包的jar包里面的类该怎么处理呢?这个时候已经不能像刚刚所作的那样,直接加一些注解,它已经封装到jar包了。

一般做法是写一个配置类,再通过配置类bean注解进行声明解决这个问题。

新建一个config包,在其下面新建AlphaConfig类:

package com.nowcoder.community.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.text.SimpleDateFormat;/*** @author shkstart* @create 2023-07-14 10:06*/
@Configuration
public class AlphaConfig {//这个方法的作用就是将返回的对象装配到容器里@Beanpublic SimpleDateFormat simpleDateFormat(){return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");}
}

在测试类下写一个方法:

    //主动从容器中获取,拿到bean去用@Testpublic void testBeanConfig(){SimpleDateFormat simpleDateFormat = applicationContext.getBean(SimpleDateFormat.class);System.out.println(simpleDateFormat.format(new Date()));}

image

这还是一种比较笨拙的方式,在IOC里面有一种思想叫依赖注入。请看下面的代码:

    @Autowiredprivate AlphaDao alphaDao;

这两行代码的意思是@Autowired这个注解将AlphaDao注入给了这个alphaDao属性。

image

这个alphadao如果我们希望注入的是hibernate,加上@Qualifier("alphaHibernate")注解就可以了。

我们一般在service注入dao,在controller下注入service

Spring MVC入门

HTTP

  • HyperText Transfer Protocol
  • 用于传输HTML等内容的应用层协议
  • 规定了浏览器和服务器之间如何通信, 以及通信时的数据格式。

了解协议的网站:

https://www.ietf.org/ 这个是官网,但是解释都太过于学术。

developer.mozilla.org/zh-cn/ 火狐浏览器出的一个网站,有对http详细概述的内容,清晰易懂。

image

image

image

Spring MVC

  • 三层架构

    • 表现层、业务层、数据访问层
  • MVC

    • Model:模型层
    • View:视图层
    • Controller:控制层
  • 核心组件

    • 前端控制器:DispatcherServlet

image

这三者是如何协作的呢?MVC主要解决的是表现层的问题,当浏览器发送请求访问服务器的时候,首先访问的是Controller控制器,接收请求体中的数据,调用业务层处理,然后将数据封装到Model,传给视图层View,视图层利用Model数据生成一个html,然后返回给浏览器,再进行渲染。

这些层的调用都是由DispatcherServlet调用的,基于Spring容器。

image

image

Thymeleaf

  • 模板引擎

    • 生成动态的HTML。
  • Thymeleaf

    • 倡导自然模板,即以HTML文件为模板。
  • 常用语法

    • 标准表达式、判断与循环、模板的布局。
  • 官网:https://www.thymeleaf.org/

image

spring配置类

application.properties加入一行代码:spring.thymeleaf.cache=false

这行代码的作用就是将模板的缓存给关掉,在开发的情况下打开网页就不会还留存上次的数据,用不着自己手动清理。

image

实际上是给一个配置类注入注解,这个ThymeleafConfiguration类又是在给ThymeleafProperties这个类进行配置。

image

演示

处理get请求

AlphaController类下加入下面的方法,这是最笨拙的底层的处理方法。

    /*** 不加注解的,最笨拙的方式。* @param httpServletRequest* @param httpServletResponse*/@RequestMapping("/http")public void http(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse){//获取请求数据System.out.println(httpServletRequest.getMethod());System.out.println(httpServletRequest.getServletPath());Enumeration<String> names = httpServletRequest.getHeaderNames();//获取消息头while (names.hasMoreElements()){String name=names.nextElement();//请求体的keyString value=httpServletRequest.getHeader(name);System.out.println(name+":"+value);}//获取请求体的数据System.out.println(httpServletRequest.getParameter("code"));//返回响应数据httpServletResponse.setContentType("text/html;charset=utf-8");try{PrintWriter writer=httpServletResponse.getWriter();writer.write("<h1>牛客网</h1>");}catch (IOException e){e.printStackTrace();}}

image

image

现在我们演示一下简便的处理方式:

    //Get请求// /students?current=1&limit=20,这里指定用get请求@RequestMapping(path="/students",method= RequestMethod.GET)@ResponseBodypublic String getStudents(@RequestParam(name="current",required=false,defaultValue = "1") int current,@RequestParam(name="limit",required=false,defaultValue = "10") int limit){System.out.println(current);System.out.println(limit);return "some students";}
  • @RequestMapping(path="/students",method= RequestMethod.GET)这一行指定了映射参数和请求方法
  • @RequestParam(name="current",required=false,defaultValue = "1") int current通过这个注解,我们可以设置参数名称,是否必须,和其默认值。
  • http://localhost:8080/community/alpha/students请求下我们没写参数,获取到的是默认值。image

有两种传参的方法,上面演示的是?key=value这种方式,下面是将参数放到路径当中。

    // /student/123 后面的123是id值,是动态的,我们必须要使用{id}接收请求,加上@PathVariable用来给参数id赋值@RequestMapping(path="/student/{id}",method =RequestMethod.GET)@ResponseBodypublic String getStudent(@PathVariable("id") int id ){System.out.println(id);return "a student";}

image

访问静态页面

image

static创建html文件夹,创建一个student的HTML文件:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>增加学生</title>
</head>
<body><form method="post" action="/community/alpha/student"><p>姓名:<input type="text" name="name"></p><p>年龄:<input type="text" name="age"></p><p><input type="submit" value="保存"></p></form>
</body>
</html>

访问静态页面:

image

处理post请求

AlphaController类下测试:

    // Post请求@RequestMapping(path="/student",method = RequestMethod.POST)@ResponseBodypublic String saveStudent(String name,int age){System.out.println(name);System.out.println(age);return "success";}

从刚刚那个静态网页下输入数据,点击保存就发送请求并跳转到第二个页面。

image

image

控制台显示的内容:image

响应一个动态的html数据

AlphaController类下测试:

    //响应html数据//通过model和view数据生成动态的html@RequestMapping(path="/teacher",method = RequestMethod.GET)public ModelAndView getTeacher(){ModelAndView mav= new ModelAndView();//模板的内容mav.addObject("name","张三");mav.addObject("age",30);mav.setViewName("/demo/view");//模板放在templates下,参数里面就是传它的下级路径。return mav;}

mav.setViewName("/demo/view");此时这个路径还不存在,我们还需要去templates文件夹下创建相应的demo文件夹和view这个html文件。

view.html的内容:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://thymeleaf.org">
<head><meta charset="UTF-8"><title>Teacher</title>
</head>
<body><p th:text="${name}"></p><p th:text="${age}"></p>
</body>
</html>
  • <p th:text="${name}"></p>在模板引擎thymeleaf渲染网页的时候会获取变量放到标签p里面。

image

还有另外一种方式可以响应网页,接下来是演示:

@RequestMapping(path = "/school",method = RequestMethod.GET)public String getSchool(Model model){model.addAttribute("name","北京大学");model.addAttribute("age",80);return "/demo/view";}

这个方法把model装到参数里,把view返回给dispatcherServelet,model和view两个都持有,可以获得。

而前一种方法则是model和view统一封装到一个对象里面,效果是一样的。显然第二种方式更简单,最好用这种方式。

image

响应JSON数据

异步请求:就是网页不刷新,但是背后发送了一个请求,访问了数据库。

    @RequestMapping(path="/emp",method = RequestMethod.GET)@ResponseBodypublic Map<String,Object> getEmp(){Map<String,Object> emp=new HashMap<>();emp.put("name","张三");emp.put("age",23);emp.put("salary",8000.00);return emp;}

image

返回多组数据:

    //返回多组员工数据@RequestMapping(path="/emps",method = RequestMethod.GET)@ResponseBodypublic List<Map<String,Object>> getEmps(){List<Map<String,Object>> list=new ArrayList<>();Map<String,Object> emp=new HashMap<>();emp.put("name","张三");emp.put("age",23);emp.put("salary",8000.00);list.add(emp);emp=new HashMap<>();emp.put("name","王五");emp.put("age",28);emp.put("salary",10000.00);list.add(emp);return list;}

image

Mybatis入门

安装数据库

  • 安装MySQL Server

  • 安装MySQL Workbench

  • 官网:

    • https://dev.mysql.com/downloads/mysql
    • https://dev mysql.com/downloads/workbench

在官网下载好这两个软件,从老师给的资料community-init-sql-1.5中复制文件my.ini到mysql根目录下。

image

然后修改其中的参数basedir为MySQL的路径:

image

配置环境变量

image

数据库的使用

右击管理员运行命令行,进行初始化:mysqld --initialize --console

image

安装mysql服务:mysqld install

image

启动服务:net start mysql

image

访问mysql:mysql -uroot -p

image

修改临时密码:alter user root@localhost identified by 'password'

image

mysql语句

新建数据库:create database community;

显示数据库:show databases;

使用数据库:use community

导入sql脚本:source 路径/文件

image

导入init_data.sqlinit_schema.sql两个文件。

安装数据库客户端

点击这个,再右击Edit connection,设置密码和访问库。

image

image

设置字体:

image

取消安全更新:

image

mybatis

MyBatis

  • 核心组件

    • SqISessionFactory:用于创建SqISession的工厂类。
    • SqISession: MyBatis的核心组件,用于向数据库执行SQL。
    • 主配置文件:XML配置文件,可以对MyBatis的底层行为做出详细的配置。
    • Mapper接口:就是DAO接口,在MyBatis中习惯性的称之为Mapper。
    • Mapper映射器:用于编写SQL,并将SQL和实体类映射的组件,采用XML、注解均可实现。
  • 官网

    • http://www.mybatis.org/mybatis-3
    • http://www.mybatis.org/spring

使用MyBatis对用户表进行CRUD操作

从maven仓库获取mysql的maven坐标,复制到pom.xml下:

<!-- https://mvnrepository.com/artifact/com.mysql/mysql-connector-j -->
<dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><version>8.0.33</version>
</dependency>

获取myatis的坐标:

<!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.3.1</version>
</dependency>

在springboot项目文件application.properties中增加下面几行,根据自己的情况修改数据库密码:

# DataSourceProperties
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/community?characterEncoding=utf-8&useSSL=false&serverTimezone=Hongkong
spring.datasource.username=root
spring.datasource.password=2004
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.hikari.maximum-pool-size=15
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.idle-timeout=30000# MybatisProperties
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.type-aliases-package=com.nowcoder.community.entity
mybatis.configuration.useGeneratedKeys=true
mybatis.configuration.mapUnderscoreToCamelCase=true

在加一行logging.level.com.nowcoder.community=debug方便后续进行调试,在查找数据库的时候会很详细的展示sql语句结果提示。

resources包下新建一个mapper文件夹,在项目包下新建entity包,在entity下新建一个User类。

package com.nowcoder.community.entity;import java.util.Date;/*** @author 008* @create 2023-07-15 15:11*/
public class User {private int id;private String username;private String password;private String salt;private String email;private int type;private int status;private String activationCode;private String headerUrl;private Date createTime;
}

按快捷键alt+insert选择getter and setter,为所有属性生成读写器,再按快捷键生成一个toString方法。

dao层生成一个UserMapper接口:

package com.nowcoder.community.dao;import com.nowcoder.community.entity.User;
import org.apache.ibatis.annotations.Mapper;/*** @author 008* @create 2023-07-15 15:16*/
@Mapper
public interface UserMapper {User selectById(int id);User selectByName(String username);User selectByEmail(String email);int insertUser(User user);int updateStatus(int id,int status);int updateHeader(int id,String headerUrl);int updatePassword(int id,String password);
}

不知道为啥这里有一点小问题,会报以下错误:

org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.binding.BindingException: Parameter ‘status’ not found. Available parameters are [arg1, arg0, param1, param2]

搜到的解决方案是在参数前加上相应的@Param("id")注解,传入多个参数,就会报上面的错误。

mapper下新建一个user-mapper.xml,用来写查询语句:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.nowcoder.community.dao.UserMapper"><sql id="insertFields">username, password, salt, email, type, status, activation_code, header_url, create_time</sql><sql id="selectFields">id, username, password, salt, email, type, status, activation_code, header_url, create_time</sql><select id="selectById" resultType="User">select <include refid="selectFields"></include>from userwhere id = #{id}</select><select id="selectByName" resultType="User">select <include refid="selectFields"></include>from userwhere username = #{username}</select><select id="selectByEmail" resultType="User">select <include refid="selectFields"></include>from userwhere email = #{email}</select><insert id="insertUser" parameterType="User" keyProperty="id">insert into user (<include refid="insertFields"></include>)values(#{username}, #{password}, #{salt}, #{email}, #{type}, #{status}, #{activationCode}, #{headerUrl}, #{createTime})</insert><update id="updateStatus">update user set status = #{status} where id = #{id}</update><update id="updateHeader">update user set header_url = #{headerUrl} where id = #{id}</update><update id="updatePassword">update user set password = #{password} where id = #{id}</update></mapper>

通过这上面的代码,我们定义来sql标签,是为了方便后续的代码复用。

<sql id="insertFields">username, password, salt, email, type, status, activation_code, header_url, create_time</sql>

想要在sql语句中引用这一段,用include标签:

    <select id="selectById" resultType="User">select <include refid="selectFields"></include>from userwhere id = #{id}</select>

为了验证上面的方法有没有问题,我们写一个测试类:

package com.nowcoder.community;import com.nowcoder.community.dao.UserMapper;
import com.nowcoder.community.entity.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;import java.util.Date;@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = CommunityApplication.class)
public class MapperTests {@Autowiredprivate UserMapper userMapper;@Testpublic void testSelectUser() {User user = userMapper.selectById(101);System.out.println(user);user = userMapper.selectByName("liubei");System.out.println(user);user = userMapper.selectByEmail("nowcoder101@sina.com");System.out.println(user);}@Testpublic void testInsertUser() {User user = new User();user.setUsername("test");user.setPassword("123456");user.setSalt("abc");user.setEmail("test@qq.com");user.setHeaderUrl("http://www.nowcoder.com/101.png");user.setCreateTime(new Date());int rows = userMapper.insertUser(user);System.out.println(rows);System.out.println(user.getId());}@Testpublic void updateUser() {int rows = userMapper.updateStatus(150, 1);System.out.println(rows);rows = userMapper.updateHeader(150, "http://www.nowcoder.com/102.png");System.out.println(rows);rows = userMapper.updatePassword(150, "hello");System.out.println(rows);}}

开发社区首页

开发流程

  • 开发流程

    • 1次请求的执行过程
  • 分步实现

    • 开发社区首页,显示前10个帖子
    • 开发分页组件,分页显示所有的帖子
  • image

分页查询

entity下新建DiscussPost类:

package com.nowcoder.community.entity;import java.util.Date;/*** @author 008* @create 2023-07-15 16:36*/
public class DiscussPost {private int id;private int userId;private String title;private String content;private int type;private int status;private Date createTime;private int commentCount;@Overridepublic String toString() {return "DiscussPost{" +"id=" + id +", userId=" + userId +", title='" + title + '\'' +", content='" + content + '\'' +", type=" + type +", status=" + status +", createTime=" + createTime +", commentCount=" + commentCount +", score=" + score +'}';}public int getId() {return id;}public void setId(int id) {this.id = id;}public int getUserId() {return userId;}public void setUserId(int userId) {this.userId = userId;}public String getTitle() {return title;}public void setTitle(String title) {this.title = title;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}public int getType() {return type;}public void setType(int type) {this.type = type;}public int getStatus() {return status;}public void setStatus(int status) {this.status = status;}public Date getCreateTime() {return createTime;}public void setCreateTime(Date createTime) {this.createTime = createTime;}public int getCommentCount() {return commentCount;}public void setCommentCount(int commentCount) {this.commentCount = commentCount;}public double getScore() {return score;}public void setScore(double score) {this.score = score;}private double score;public DiscussPost() {}
}

dao下创建DiscussPostMapper接口:

package com.nowcoder.community.dao;import com.nowcoder.community.entity.DiscussPost;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;import java.util.List;/*** @author 008* @create 2023-07-15 16:48*/
@Mapper
public interface DiscussPostMapper {/*** 查询评论(带分页信息)* @param userId* @param offset 起始行号* @param limit 每页显示多少条数据* @return*/List<DiscussPost> selectDiscussPosts(int userId,int offset,int limit);//获取帖子总数//@Param注解用于给参数取别名//如果只有一个参数,并且在<if>里使用,则必须加别名int selectDiscussPostRows(@Param("userId") int userId);
}

mapper下创建一个DiscussPostMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.nowcoder.community.dao.DiscussPostMapper"><sql id="selectFields">id, user_id, title, content, type, status, create_time, comment_count, score</sql><select id="selectDiscussPosts" resultType="DiscussPost">select <include refid="selectFields"></include>from discuss_postwhere status != 2<if test="userId!=0">and user_id = #{userId}</if>order by type desc, create_time desclimit #{offset}, #{limit}</select><select id="selectDiscussPostRows" resultType="int">select count(id)from discuss_postwhere status != 2<if test="userId!=0">and user_id = #{userId}</if></select></mapper>

在测试类下写方法:

@Testpublic void testSelectPosts(){List<DiscussPost> list=discussPostMapper.selectDiscussPosts(149,0,10);for (DiscussPost post:list){System.out.println(post);}int rows=discussPostMapper.selectDiscussPostRows(0);System.out.println(rows);}

即便是service很简单,也要在controller下调用,也是为了保证各层的安全性,方便后续使用。

service下新建DiscussPostService类:

package com.nowcoder.community.service;import com.nowcoder.community.dao.DiscussPostMapper;
import com.nowcoder.community.entity.DiscussPost;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.List;/*** @author 008* @create 2023-07-15 21:50*/
@Service
public class DiscussPostService {@Autowiredprivate DiscussPostMapper discussPostMapper;/*** 查询某页的数据/分页查询* @param userId* @param offset* @param limit* @return*/public List<DiscussPost> findDiscussPosts(int userId,int offset,int limit){return discussPostMapper.selectDiscussPosts(userId, offset, limit);}/*** 查询发言的信息总数* @param userId* @return*/public int findDiscussPostRows(int userId){return discussPostMapper.selectDiscussPostRows(userId);}
}

我们在DiscussPost下有一个userId外键,如果要在网页上显示数据,不可能就显示Id,而是要显示用户名称。有两种方式:

  1. service下关联查询用户,同时查询两种数据。
  2. 单独的查询DiscussPost,针对每一项数据查询User,将两者组合在一起返回给页面。这种方式在使用redis时会很方便,也更直观。

这里采用第二种方式:

service下新建UserService类:

package com.nowcoder.community.service;import com.nowcoder.community.dao.UserMapper;
import com.nowcoder.community.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;/*** @author 008* @create 2023-07-15 22:19*/
@Service
public class UserService {@Autowiredprivate UserMapper userMapper;public User findUserById(int id){return userMapper.selectById(id);}}

从资源上复制静态页面cssimgjsstatic下,将sitemailindex.html复制到templates下。

开发首页

controller下创建HomeController类:

package com.nowcoder.community.controller;import com.nowcoder.community.entity.DiscussPost;
import com.nowcoder.community.entity.User;
import com.nowcoder.community.service.DiscussPostService;
import com.nowcoder.community.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** @author 008* @create 2023-07-15 22:31*/
@Controller
public class HomeController {@Autowiredprivate DiscussPostService discussPostService;@Autowiredprivate UserService userService;/*** 获取首页数据:User、DiscussPost* @param model* @return*/@RequestMapping(path="/index",method = RequestMethod.GET)public String getIndexPage(Model model){List<DiscussPost> list = discussPostService.findDiscussPosts(0, 0, 10);List<Map<String,Object>> discussPosts=new ArrayList<>();if(list!=null){for(DiscussPost post:list){Map<String,Object> map=new HashMap<>();map.put("post",post);User user=userService.findUserById(post.getUserId());map.put("user",user);discussPosts.add(map);}}//放入modelmodel.addAttribute("discussPosts",discussPosts);return "/index";}
}
修改相对路径的查找

修改index.html的内容,使用thymeleaf引擎,即使在相对路径的情况下,也会在static下查找内容:

<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"><link rel="icon" href="https://static.nowcoder.com/images/logo_87_87.png"/><link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" crossorigin="anonymous"><link rel="stylesheet" th:href="@{/css/global.css}" /><title>牛客网-首页</title>
  • 第2行加上xmlns:th="http://www.thymeleaf.org"使用引擎
  • 第8行修改成th:href="@{/css/global.css}"在static文件夹下查找

修改末尾处的两行数据:

	<script th:src="@{/js/global.js}"></script><script th:src="@{/js/index.js}"></script>
处理首页内容的帖子列表部分

删除多余的li,只保留一个li。修改li为动态数据,方便后续遍历。

修改index.html

<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"><link rel="icon" href="https://static.nowcoder.com/images/logo_87_87.png"/><link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/4.3.1/css/bootstrap.min.css" crossorigin="anonymous"><link rel="stylesheet" th:href="@{/css/global.css}" /><title>牛客网-首页</title>
</head>
<body><div class="nk-container"><!-- 头部 --><header class="bg-dark sticky-top"><div class="container"><!-- 导航 --><nav class="navbar navbar-expand-lg navbar-dark"><!-- logo --><a class="navbar-brand" href="#"></a><button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><!-- 功能 --><div class="collapse navbar-collapse" id="navbarSupportedContent"><ul class="navbar-nav mr-auto"><li class="nav-item ml-3 btn-group-vertical"><a class="nav-link" href="index.html">首页</a></li><li class="nav-item ml-3 btn-group-vertical"><a class="nav-link position-relative" href="site/letter.html">消息<span class="badge badge-danger">12</span></a></li><li class="nav-item ml-3 btn-group-vertical"><a class="nav-link" href="site/register.html">注册</a></li><li class="nav-item ml-3 btn-group-vertical"><a class="nav-link" href="site/login.html">登录</a></li><li class="nav-item ml-3 btn-group-vertical dropdown"><a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><img src="http://images.nowcoder.com/head/1t.png" class="rounded-circle" style="width:30px;"/></a><div class="dropdown-menu" aria-labelledby="navbarDropdown"><a class="dropdown-item text-center" href="site/profile.html">个人主页</a><a class="dropdown-item text-center" href="site/setting.html">账号设置</a><a class="dropdown-item text-center" href="site/login.html">退出登录</a><div class="dropdown-divider"></div><span class="dropdown-item text-center text-secondary">nowcoder</span></div></li></ul><!-- 搜索 --><form class="form-inline my-2 my-lg-0" action="site/search.html"><input class="form-control mr-sm-2" type="search" aria-label="Search" /><button class="btn btn-outline-light my-2 my-sm-0" type="submit">搜索</button></form></div></nav></div></header><!-- 内容 --><div class="main"><div class="container"><div class="position-relative"><!-- 筛选条件 --><ul class="nav nav-tabs mb-3"><li class="nav-item"><a class="nav-link active" href="#">最新</a></li><li class="nav-item"><a class="nav-link" href="#">最热</a></li></ul><button type="button" class="btn btn-primary btn-sm position-absolute rt-0" data-toggle="modal" data-target="#publishModal">我要发布</button></div><!-- 弹出框 --><div class="modal fade" id="publishModal" tabindex="-1" role="dialog" aria-labelledby="publishModalLabel" aria-hidden="true"><div class="modal-dialog modal-lg" role="document"><div class="modal-content"><div class="modal-header"><h5 class="modal-title" id="publishModalLabel">新帖发布</h5><button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button></div><div class="modal-body"><form><div class="form-group"><label for="recipient-name" class="col-form-label">标题:</label><input type="text" class="form-control" id="recipient-name"></div><div class="form-group"><label for="message-text" class="col-form-label">正文:</label><textarea class="form-control" id="message-text" rows="15"></textarea></div></form></div><div class="modal-footer"><button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button><button type="button" class="btn btn-primary" id="publishBtn">发布</button></div></div></div></div><!-- 提示框 --><div class="modal fade" id="hintModal" tabindex="-1" role="dialog" aria-labelledby="hintModalLabel" aria-hidden="true"><div class="modal-dialog modal-lg" role="document"><div class="modal-content"><div class="modal-header"><h5 class="modal-title" id="hintModalLabel">提示</h5></div><div class="modal-body" id="hintBody">发布完毕!</div></div></div></div><!-- 帖子列表 --><ul class="list-unstyled"><li class="media pb-3 pt-3 mb-3 border-bottom" th:each="map:${discussPosts}"><a href="site/profile.html"><img th:src="${map.user.headerUrl}" class="mr-4 rounded-circle" alt="用户头像" style="width:50px;height:50px;"></a><div class="media-body"><h6 class="mt-0 mb-3"><a href="#" th:utext="${map.post.title}" >备战春招,面试刷题跟他复习,一个月全搞定!</a><span class="badge badge-secondary bg-primary" th:if="${map.post.type==1}">置顶</span><span class="badge badge-secondary bg-danger" th:if="${map.post.status==1}">精华</span></h6><div class="text-muted font-size-12"><u class="mr-3" th:utext="${map.user.username}">寒江雪</u> 发布于 <b th:text="${#dates.format(map.post.createTime,'yyyy-MM-dd HH:mm:ss')}">2019-04-15 15:32:18</b><ul class="d-inline float-right"><li class="d-inline ml-2">赞 11</li><li class="d-inline ml-2">|</li><li class="d-inline ml-2">回帖 7</li></ul></div></div></li></ul><!-- 分页 --><nav class="mt-5"><ul class="pagination justify-content-center"><li class="page-item"><a class="page-link" href="#">首页</a></li><li class="page-item disabled"><a class="page-link" href="#">上一页</a></li><li class="page-item active"><a class="page-link" href="#">1</a></li><li class="page-item"><a class="page-link" href="#">2</a></li><li class="page-item"><a class="page-link" href="#">3</a></li><li class="page-item"><a class="page-link" href="#">4</a></li><li class="page-item"><a class="page-link" href="#">5</a></li><li class="page-item"><a class="page-link" href="#">下一页</a></li><li class="page-item"><a class="page-link" href="#">末页</a></li></ul></nav></div></div><!-- 尾部 --><footer class="bg-dark"><div class="container"><div class="row"><!-- 二维码 --><div class="col-4 qrcode"><img src="https://uploadfiles.nowcoder.com/app/app_download.png" class="img-thumbnail" style="width:136px;" /></div><!-- 公司信息 --><div class="col-8 detail-info"><div class="row"><div class="col"><ul class="nav"><li class="nav-item"><a class="nav-link text-light" href="#">关于我们</a></li><li class="nav-item"><a class="nav-link text-light" href="#">加入我们</a></li><li class="nav-item"><a class="nav-link text-light" href="#">意见反馈</a></li><li class="nav-item"><a class="nav-link text-light" href="#">企业服务</a></li><li class="nav-item"><a class="nav-link text-light" href="#">联系我们</a></li><li class="nav-item"><a class="nav-link text-light" href="#">免责声明</a></li><li class="nav-item"><a class="nav-link text-light" href="#">友情链接</a></li></ul></div></div><div class="row"><div class="col"><ul class="nav btn-group-vertical company-info"><li class="nav-item text-white-50">公司地址:北京市朝阳区大屯路东金泉时代3-2708北京牛客科技有限公司</li><li class="nav-item text-white-50">联系方式:010-60728802(电话)    admin@nowcoder.com</li><li class="nav-item text-white-50">牛客科技©2018 All rights reserved</li><li class="nav-item text-white-50">京ICP备14055008号-4     <img src="http://static.nowcoder.com/company/images/res/ghs.png" style="width:18px;" />京公网安备 11010502036488号</li></ul></div></div></div></div></div></footer></div><script src="https://code.jquery.com/jquery-3.3.1.min.js" crossorigin="anonymous"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" crossorigin="anonymous"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" crossorigin="anonymous"></script><script th:src="@{/js/global.js}"></script><script th:src="@{/js/index.js}"></script>
</body>
</html>
  • utext的好处是防止有转移字符出现。
  • ${map.user.headerUrl}相当于map.get("user").getHeaderUrl()

封装分页信息

创建Page类:

package com.nowcoder.community.entity;/*** 封装分页相关的信息* @author 008* @create 2023-07-16 12:05*/
public class Page {//当前页码private int current=1;//显示上限private int limit=10;//数据总数(用于计算总数)private int rows;//查询路径(复用分页链接)private String path;public int getCurrent() {return current;}public void setCurrent(int current) {if(current>=1) {this.current = current;}}public int getLimit() {return limit;}public void setLimit(int limit) {if(limit>=1&&limit<=100){//每页最多只能显示100条数据this.limit = limit;}}public int getRows() {return rows;}public void setRows(int rows) {if(rows>=0){this.rows = rows;}}public String getPath() {return path;}public void setPath(String path) {this.path = path;}/*** 获取当前页的起始行* @return*/public int getOffset(){//current*limit-limitreturn (current-1)*limit;}/*** 获取总页数*/public int getTotal(){//rows/limit [+1]if(rows%limit==0){//能被整除就不用+1return rows/limit;}else{return rows/limit+1;}}/*** 获取起始页码:离它最近的两页*/public int getFrom(){int from=current-2;return from<1?1:from;//不能小于第一页}/*** 获取结束页码*/public int getTo(){int to=current+2;int total=getTotal();return to>total?total:to;//不能超过总页数}
}

再整合page到controller里面:

    @RequestMapping(path="/index",method = RequestMethod.GET)public String getIndexPage(Model model,Page page){//方法调用前,SpringMVC会自动实例化Model和Page,并将Page注入Model//所以,在thymeleaf中可以直接访问Page对象中的数据//设置page数据page.setRows(discussPostService.findDiscussPostRows(0));page.setPath("/index");//封装首页数据到一个Map里面List<DiscussPost> list = discussPostService.findDiscussPosts(0, page.getOffset(), page.getLimit());

修改index.html的分页信息:

			<!-- 分页 --><nav class="mt-5" th:if="${page.rows>0}"><ul class="pagination justify-content-center"><li class="page-item"><a class="page-link" th:href="@{${page.path}(current=1)}">首页</a></li><li th:class="|page-item ${page.current==1?'disabled':''}|"><a class="page-link" th:href="@{${page.path}(current=${page.current-1})}">上一页</a></li><li th:class="|page-item ${i==page.current?'active':''}|" th:each="i:${#numbers.sequence(page.from,page.to)}"><a class="page-link" href="#" th:text="${i}">1</a></li><li th:class="|page-item ${page.current==page.total?'disabled':''}|"><a class="page-link" th:href="@{${page.path}(current=${page.current+1})}">下一页</a></li><li class="page-item"><a class="page-link" th:href="@{${page.path}(current=${page.total})}">末页</a></li></ul></nav>
  • th:href="@{${page.path}(current=1)}"=/index?current=1
  • th:each="i:${#numbers.sequence(page.from,page.to)}"这段代码thymeleaf的numbers工具类会生成from-to之间的连续数字,而每一项就是i。
  • 这上面实现了首页时无上一页、末页时无上一页、当前页点亮的功能。

项目调试技巧

响应状态码的含义

https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status

重定向

image

以低耦合的方式进行网页的跳转。

服务端断点调试技巧

点击行号前这段区域加断点,用debug方式启动程序。

image

F8逐行向下执行,F7向内执行,F9直接执行到下一个断点。

管理所有断点:

image

客户端JS断点调试技巧

右击网页,选择检查

image

加断点:

image

选中变量监视值:

image

设置日志级别

Logback Home (qos.ch)

输出日志

新建LoggerTests类:

package com.nowcoder.community;import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = CommunityApplication.class)
public class LoggerTests {private static final Logger logger= LoggerFactory.getLogger(LoggerTests.class);@Testpublic void testLogger(){System.out.println(logger.getName());logger.debug("debug log");logger.info("info log");logger.warn("warn log");logger.error("error log");}
}

测试方法:

image

设置存放日志的路径

application.properties设置存放日志的路径:

logging.file.name=d:/mavenprojects/community/community.log

老师写的那种方法应该过时了,会报红线。

分类别存储日志:

所有素材和源码\第一章素材和源码\源码复制logback-spring.xmlresources下,修改LOG_PATH对应的值。

image

git版本控制

这里就不做多余的笔记了,我的笔记:https://blog.csdn.net/weixin_46066669/article/details/131581007

这篇关于仿牛客网 01 环境搭建 +SSM 框架入门的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/221470

相关文章

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

阿里开源语音识别SenseVoiceWindows环境部署

SenseVoice介绍 SenseVoice 专注于高精度多语言语音识别、情感辨识和音频事件检测多语言识别: 采用超过 40 万小时数据训练,支持超过 50 种语言,识别效果上优于 Whisper 模型。富文本识别:具备优秀的情感识别,能够在测试数据上达到和超过目前最佳情感识别模型的效果。支持声音事件检测能力,支持音乐、掌声、笑声、哭声、咳嗽、喷嚏等多种常见人机交互事件进行检测。高效推

hdu 2602 and poj 3624(01背包)

01背包的模板题。 hdu2602代码: #include<stdio.h>#include<string.h>const int MaxN = 1001;int max(int a, int b){return a > b ? a : b;}int w[MaxN];int v[MaxN];int dp[MaxN];int main(){int T;int N, V;s

搭建Kafka+zookeeper集群调度

前言 硬件环境 172.18.0.5        kafkazk1        Kafka+zookeeper                Kafka Broker集群 172.18.0.6        kafkazk2        Kafka+zookeeper                Kafka Broker集群 172.18.0.7        kafkazk3

安装nodejs环境

本文介绍了如何通过nvm(NodeVersionManager)安装和管理Node.js及npm的不同版本,包括下载安装脚本、检查版本并安装特定版本的方法。 1、安装nvm curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash 2、查看nvm版本 nvm --version 3、安装

数论入门整理(updating)

一、gcd lcm 基础中的基础,一般用来处理计算第一步什么的,分数化简之类。 LL gcd(LL a, LL b) { return b ? gcd(b, a % b) : a; } <pre name="code" class="cpp">LL lcm(LL a, LL b){LL c = gcd(a, b);return a / c * b;} 例题:

Java 创建图形用户界面(GUI)入门指南(Swing库 JFrame 类)概述

概述 基本概念 Java Swing 的架构 Java Swing 是一个为 Java 设计的 GUI 工具包,是 JAVA 基础类的一部分,基于 Java AWT 构建,提供了一系列轻量级、可定制的图形用户界面(GUI)组件。 与 AWT 相比,Swing 提供了许多比 AWT 更好的屏幕显示元素,更加灵活和可定制,具有更好的跨平台性能。 组件和容器 Java Swing 提供了许多

【IPV6从入门到起飞】5-1 IPV6+Home Assistant(搭建基本环境)

【IPV6从入门到起飞】5-1 IPV6+Home Assistant #搭建基本环境 1 背景2 docker下载 hass3 创建容器4 浏览器访问 hass5 手机APP远程访问hass6 更多玩法 1 背景 既然电脑可以IPV6入站,手机流量可以访问IPV6网络的服务,为什么不在电脑搭建Home Assistant(hass),来控制你的设备呢?@智能家居 @万物互联

高并发环境中保持幂等性

在高并发环境中保持幂等性是一项重要的挑战。幂等性指的是无论操作执行多少次,其效果都是相同的。确保操作的幂等性可以避免重复执行带来的副作用。以下是一些保持幂等性的常用方法: 唯一标识符: 请求唯一标识:在每次请求中引入唯一标识符(如 UUID 或者生成的唯一 ID),在处理请求时,系统可以检查这个标识符是否已经处理过,如果是,则忽略重复请求。幂等键(Idempotency Key):客户端在每次

poj 2104 and hdu 2665 划分树模板入门题

题意: 给一个数组n(1e5)个数,给一个范围(fr, to, k),求这个范围中第k大的数。 解析: 划分树入门。 bing神的模板。 坑爹的地方是把-l 看成了-1........ 一直re。 代码: poj 2104: #include <iostream>#include <cstdio>#include <cstdlib>#include <al