0、概述:

技术架构:

Spring Boot

Spring 、SpringMVC、Mybatis

Redis、Kafka、Elasticsearch(搜索引擎)

Spring Security、Spring Actuator

开发环境:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eCCMSSVl-1620266608035)(C:\Users\wangfei\AppData\Roaming\Typora\typora-user-images\image-20201207203248729.png)]

1、技术准备

1.搭建开发环境

1.maven

Apache Maven

  • 管理jar包
  • maven仓库:存放构件的位置
    • 本地仓库:默认是~/.m2/repository
    • 远程仓库:中央仓库、镜像仓库、私服仓库
    • 示例:安装、配置、常用命令

cd mavendemo1 带有pom.xml路径下

mvn compile 编译的结果在target中(test不会编译)

重新编译 mvn clean mvn compile

mvn clean compile

mvn clean test maven clean之后再测试一下(test包含compile)

2.Intellij IDEA

配置maven(查找jar包 mvnrepository 根据使用量)

快速开始archetype-quickstart

Build Project Ctrl +F9

3.Spring Initializr(解决包依赖问题)

  • 创建Spring Boot项目的引导工具(底层基于maven)

    2.1.5

  • 创建“牛客社区"项目

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HkTjkLCM-1620266608037)(C:\Users\wangfei\AppData\Roaming\Typora\typora-user-images\image-20201213204958968.png)]

4.Spring Boot入门示例

  • Spring Boot核心作用

    • 起步依赖、自动配置、端点监控
  • 示例

    • 一个简单的处理客户端请求案例

      • controller.AlphaController:

      • @Controller
        @RequestMapping("/alpha")
        //方法前面的uri名字  即对应的Controller
        public class AlphaController{
            
            @RequestMapping("/hello")
            @ResponseBody
            public String SayHello(){
                return "hello Spring boot";
            }
        }
        

修改端口的配置:

server.port=8080
server.servlet.context-path=/community  
#项目访问路径

自动编译、自动重启:DevTools的作用

2.Spring入门

1.Spring Framework
  • Spring Core
    • IOC、AOP(用来管理对象的一种思想)
    • Spring管理的对象,我们通常叫做Bean,IOC基于面向对象的思想;AOP基于面向切面编程的思想(对面向对象的补充)(能够管理一切Bean、第三方的框架拿过来也能进行整合)
  • Spring Data Access(Spring访问数据库的功能)
  • Web Servlet(Web开发)
  • Integration(web集成)
    • Email、Scheduling、AMQP、Security

(GA正式版本)

springIOC(基石)
  • Inversion of Control
    • 控制反转,是一种面向对象编程的设计思想
  • Dependency Injection
    • 依赖注入,是IOC思想的实现方式
  • IOC Container
    • IOC容器,是实现依赖注入的关键,本质上是一个工厂

降低Bean对象之间的耦合

Spring容器两份数据:才可以自动实例化Bean供你调用

1、管理哪些Bean,Bean的类型

2、配置文件里的配置,通过配置描述这些Bean之间的关系

(关联关系体现在配置文件中)

解释Spring Boot入门案例:

springboot应用启动了,底层启动了tomcat;自动帮我们创建了Spring容器

@SpringBootApplication
public class CommunityApplication{
    
    public static void main(String[] args){
        SpringApplication.run(CommunityApplication.class,args);
    }
}

在web项目当中,spring容器不需要我们自己手动创建,在

SpringApplication.run(CommunityApplication.class,args);

底层自动创建Spring容器。

Spring容器创建完,它会自动扫描某些包下的某些Bean,将这些Bean装配到容器中,那么哪些类会被扫描到呢?

Spring应用启动的时候需要配置的,注解@SpringBootApplication标识的类表明这是一个配置文件。启动了自动配置

@SpringBootApplication  

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BhxwHYsp-1620266608041)(C:\Users\wangfei\AppData\Roaming\Typora\typora-user-images\image-20201213212906234.png)]

启动了组件扫描,扫描条件:

1、配置类所在的包下所有的类,

2、标明了注解@Controller @Service @Component @Repository 都能被扫描到,业务@Service,控制层@Controller,数据库访问@Repository,任何地方都能用Component。

Springboot中的IOC容器:

如何得到IOC容器,实现ApplicationContextAware

IOC的顶层接口BeanFactory,一般多用ApplicationContext

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dMkOWXIL-1620266608044)(C:\Users\wangfei\AppData\Roaming\Typora\typora-user-images\image-20210102190143916.png)]

测试应用上下文即IOC容器:

①test类中的测试:

test包里面对应的CommunityApplicationTests.java

@SpringBootTest  //Springboot的测试
//测试时以主程序作为配置类
@ContextConfiguration(classes = CommunityApplication.class)    
②实现获取容器的接口ApplicationContextAware
public class CommunityApplicationTests implements ApplicationContextAware {}
③通过传入ApplicationContext获取容器ApplicationContext
 @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
④证明容器得到Bean
1.测试应用上下文Spring容器  证明ApplicationContext的实质实现类是org.springframework.web.context.support.GenericWebApplicationContext@282cb7c7

GenericWebApplicationContext

2.先有一个dao层,其中对应的接口和不同的接口实现类  如AlphaDao接口和AlphaDaoMyBatisImplAlphaDaoHibernateImpl
   通过getBean(Interface.class)的方法获取Bean,能够获取到实现了接口的方法,但只限一个;
   如果有多个可在其标注@Primary注解,否则需要:
   通过getBean(InterfaceImpl.class)的方法获取Bean
    
     @Test
    public void testApplicationContext() {
        System.out.println(applicationContext);

        //依赖的是接口,实现类变了,接口不用动,但是只会挑选一个,当重复加@Primary注解,实现只加载这一个
        //降低耦合,使得实现类和接口不发生耦合
        AlphaDao alphaDao = applicationContext.getBean(AlphaDao.class);

        AlphaDao alphaDao1 = applicationContext.getBean(AlphaDaoMyBatisImpl.class);
        AlphaDao alphaDao2 = applicationContext.getBean(AlphaDaoHibernateImpl.class);

        System.out.println(alphaDao.select());
        System.out.println(alphaDao1.select());
        System.out.println(alphaDao2.select());

    }
3.通过接口实现类的名字获取(字符串的方式),获取的时候是Object对象,要么强转,要么后面加上类型限定(即接口)
    @Repository("alphaHibernate")
    
     alphaDao3 = applicationContext.getBean("alphaHibernate",AlphaDao.class);
        System.out.println(alphaDao3.select());

接口及其实现类:

public interface AlphaDao {
    String select();
}

@Repository("alphaHibernate")
public class AlphaDaoHibernateImpl implements AlphaDao {
    @Override
    public String select() {
        return "hibernate";
    }
}

@Repository
@Primary
//加上primary会被优先的装配
public class AlphaDaoMyBatisImpl implements AlphaDao {
    @Override
    public String select() {
        return "MyBatis";
    }
}
⑤证明容器管理Bean(自动化创建,销毁)
 @Test
    public void testBeanManagement() {
        AlphaService alphaService = applicationContext.getBean(AlphaService.class);
        System.out.println(alphaService);
    }

该程序启动时Bean被实例化,程序停止的时候被销毁

说明Bean只被实例化一次,只被销毁一次,说明是单例的

执行两次的Bean是同一个
     @Test
    public void testBeanManagement() {
        AlphaService alphaService = applicationContext.getBean(AlphaService.class);
        System.out.println(alphaService);

        alphaService = applicationContext.getBean(AlphaService.class);
        System.out.println(alphaService);
    }

结果:
com.nowcoder.community.service.AlphaService@3659d7b1
com.nowcoder.community.service.AlphaService@3659d7b1

如果不想单例的,那么需要在Bean的注解上,添加@Scope注解,保证不是单例的(默认singleton)

@Scope("prototype")

改了之后,Bean的hashCode不一致

⑥测试配置Config

测试配置类:放在配置包config下

配置类

@Configuration
public class AlphaConfig {

    @Bean
    //这个方法返回的对象将会被装配到容器里
    public SimpleDateFormat simpleDateFormat() {
        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    }

}

@Bean注解表示返回的对象会被装配到容器里

测试:

@Test
    public void testBeanConfig() {
        SimpleDateFormat simpleDateFormat =
                applicationContext.getBean(SimpleDateFormat.class);
        System.out.println(simpleDateFormat.format(new Date()));

    }

2021-01-02 20:13:48

这些都是主动获取

⑦验证自动注入

最好需要自动注入

@Autowired
    private AlphaDao alphaDao;

    @Autowired
    private AlphaService alphaService;

    @Autowired
    private SimpleDateFormat simpleDateFormat;

    @Test
    public void testDi() {
        System.out.println(alphaDao);
        System.out.println(alphaService);
        System.out.println(simpleDateFormat);
    }
 //需要注入其他的实现类,再加一个注解Qualifier(name)
    @Autowired
    @Qualifier("alphaHibernate")
    private AlphaDao alphaDao;

com.nowcoder.community.dao.AlphaDaoHibernateImpl@25e49cb2
com.nowcoder.community.service.AlphaService@7f7af971
java.text.SimpleDateFormat@4f76f1a0

由controller来处理浏览器的请求,controller会调用业务组件service,业务组件service会调用dao访问数据库,

controller–>service–>dao

因此可以在service类中添加private 的dao接口类 并添加@Autowired注解

@Service
public class AlphaService {
    @Autowired
    private AlphaDao alphaDao;

    public String find() {
        return alphaDao.select();
    }
}

同样的道理,controller层调用service

在controller类中添加private 的service类 并添加@Autowired注解,注意controller有效的前提是需要标注访问路径

//@RestController
@RequestMapping("/alpha")
//方法前面的uri名字
@Controller
public class AlphaController {

    @Autowired
    private AlphaService alphaService;

    //controller的前提是需要标注访问路径
    @RequestMapping("/data")
    @ResponseBody
    public String getData() {
        return alphaService.find();
    }

}

测试是否自动注入成功: 启动主程序:

测试的代码:

config包:

@Configuration
public class AlphaConfig {

    @Bean
    //这个方法返回的对象将会被装配到容器里
    public SimpleDateFormat simpleDateFormat() {
        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    }

}

controller:

//@RestController
@RequestMapping("/alpha")
//方法前面的uri名字
@Controller
public class AlphaController {

    @Autowired
    private AlphaService alphaService;


    //controller的前提是需要标注访问路径
    @RequestMapping("/data")
    @ResponseBody
    public String getData() {
        return alphaService.find();
    }


    @RequestMapping("/hello")
    @ResponseBody
    public String hello() {
        return "nowCoder";
    }
}

dao包:

public interface AlphaDao {
    String select();
}

@Repository("alphaHibernate")
public class AlphaDaoHibernateImpl implements AlphaDao {
    @Override
    public String select() {
        return "hibernate";
    }
}

@Repository
@Primary
//加上primary会被优先的装配
public class AlphaDaoMyBatisImpl implements AlphaDao {
    @Override
    public String select() {
        return "MyBatis";
    }
}

service包:

@Service
//@Scope("prototype")  
//默认singleton 单例;prototype非单例的
public class AlphaService {

    @Autowired
    private AlphaDao alphaDao;

    public AlphaService() {
        System.out.println("实例化AlphaService");
    }

    @PostConstruct
    //证明容器会自动管理Bean,比如初始化创建,销毁,要让容器自动调用,
    // @PostConstruct注解表明方法在构造后调用
    //初始化方法
    public void init() {
        System.out.println("初始化AlphaService");
    }

    @PreDestroy
    public void destroy() {
        System.out.println("销毁AlphaService");
    }

    public String find() {
        return alphaDao.select();
    }
}

主启动类:

@SpringBootApplication
public class CommunityApplication {

    public static void main(String[] args) {
        SpringApplication.run(CommunityApplication.class, args);
    }
}

主测试类:

@SpringBootTest
//测试时以主程序作为配置类
@ContextConfiguration(classes = CommunityApplication.class)
public class CommunityApplicationTests implements ApplicationContextAware {


    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Test
    public void testApplicationContext() {
        System.out.println(applicationContext);

        //依赖的是接口,实现类变了,接口不用动,但是只会挑选一个,当重复加@Primary注解,实现只加载这一个
        //降低耦合,使得实现类和接口不发生耦合
        AlphaDao alphaDao = applicationContext.getBean(AlphaDao.class);

        AlphaDao alphaDao1 = applicationContext.getBean(AlphaDaoMyBatisImpl.class);
        AlphaDao alphaDao2 = applicationContext.getBean(AlphaDaoHibernateImpl.class);
        AlphaDao alphaDao3 = applicationContext.getBean(AlphaDaoTestImpl.class);


        System.out.println(alphaDao.select());
        System.out.println(alphaDao1.select());
        System.out.println(alphaDao2.select());
        System.out.println(alphaDao3.select());

        alphaDao3 = applicationContext.getBean("alphaHibernate", AlphaDao.class);
        System.out.println(alphaDao3.select());

    }

    @Test
    public void testBeanManagement() {
        AlphaService alphaService = applicationContext.getBean(AlphaService.class);
        System.out.println(alphaService);

        alphaService = applicationContext.getBean(AlphaService.class);
        System.out.println(alphaService);
    }

    @Test
    public void testBeanConfig() {
        SimpleDateFormat simpleDateFormat =
                applicationContext.getBean(SimpleDateFormat.class);
        System.out.println(simpleDateFormat.format(new Date()));

    }

    //需要注入其他的实现类,再加一个注解Qualifier(name)
    @Autowired
    @Qualifier("alphaHibernate")
    private AlphaDao alphaDao;

    @Autowired
    private AlphaService alphaService;

    @Autowired
    private SimpleDateFormat simpleDateFormat;

    @Test
    public void testDi() {
        System.out.println(alphaDao);
        System.out.println(alphaService);
        System.out.println(simpleDateFormat);
    }
}

2.Spring MVC

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ahzdM7XO-1620266608045)(C:\Users\wangfei\AppData\Roaming\Typora\typora-user-images\image-20210102204517081.png)]

学习文档网页:

https://developer.mozilla.org/zh-CN/

FastStone Draw

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eFo7IFH4-1620266608046)(C:\Users\wangfei\AppData\Roaming\Typora\typora-user-images\image-20210102204818420.png)]

为了解耦更有利于维护

三层架构和MVC不是一个东西:

三层架构是指整个web分层:表现层;业务层;数据层;

MVC:

Model 模型层;(数据)

View:视图层;

Controller控制层

MVC主要解决表现层的问题,当浏览器访问服务器的时候,访问的是Controller组件,会接收请求中的数据,调用业务层处理,处理完后得到的数据封装到Model里面,传给视图层,视图层利用model数据生成一个HTML返回给浏览器。(View渲染)

核心组件:

前端控制器:DispatcherServlet

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VCHBAtf3-1620266608047)(C:\Users\wangfei\AppData\Roaming\Typora\typora-user-images\image-20210102205617165.png)]

模板引擎:(需要的材料:模板文件+Model)生成HTML

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EebijgNS-1620266608049)(C:\Users\wangfei\AppData\Roaming\Typora\typora-user-images\image-20210102205741747.png)]

https://www.thymeleaf.org/

启动关闭缓存,降低服务器压力

spring.io的appendix有thymleaf

获取请求
@RequestMapping("/http")
public void http(HttpServletRequest request, HttpServletResponse response) {

    //获取请求数据
    System.out.println(request.getMethod());
    System.out.println(request.getServletPath());

    //请求行所有数据
    Enumeration<String> headerNames = request.getHeaderNames();
    while (headerNames.hasMoreElements()) {
        String name = headerNames.nextElement();//key
        String value = request.getHeader(name);//value
        System.out.println(name + ":" + value);
    }
    //传入参数code
    System.out.println(request.getParameter("code"));

    //返回响应数据,网页版本
    response.setContentType("text/html;charset=utf-8");
    //输出流
    try (
            PrintWriter writer = response.getWriter();//java7的默认会关闭,前提有close方法
    ) {
        writer.write("<h1>牛客网 </h1>");
    } catch (IOException e) {
        e.printStackTrace();
    }
}
Get请求1 路径方法中传入参数(默认)

形参中的:

@RequestParam(name,required,defaultValue)

注解:

/students?current=1&limit=20 加参数 1-10 3-20

@RequestMapping(path = "/students", method = RequestMethod.GET)

路径是/students下 ,method为get方法

形参:(先准备传入,再设置)

get请求获取参数怎么获取?

在方法形参中传入表示要取的这个参数current、limit,DispatcherServlet 检测到current和limit之后,会把
@RequestParam(name,required,defaultValue)一个一个对应赋值;请求参数中的name = "current"意思是request当中名为current的值给current,required=false,意思是也可以不传入这个参数,defaultValue 不传的时候默认值

public String getStudents(
        @RequestParam(name = "current", required = false, defaultValue = "1") int current,
        @RequestParam(name = "limit", required = false, defaultValue = "10") int limit) {

代码:

// /students?current=1&limit=20   加参数  1-10   3-20
@RequestMapping(path = "/students", method = RequestMethod.GET)
@ResponseBody
public 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";
}
Get请求2 参数是路径的一部分

{parameter} &@PathVariable

例如:/student/123

get请求表示从服务器获取数据,post请求表示从浏览器向服务器提交数据
直接把参数编排到路径当中,当参数成为路径中的一部分就不是上面那样获取

注解:

@RequestMapping(path = "/student/{id}", method = RequestMethod.GET)  

/path:"/student/{id}",id表示变量 变量化后可从中取值

形参:

public String getStudent(@PathVariable("id") int id) {

@PathVariable表示路径变量,括号里写上变量的名字,注解会从路径中的得到变量赋给形参id(保持一致)

// /student/123 
//path:"/student/{id}",id表示变量
@RequestMapping(path = "/student/{id}", method = RequestMethod.GET)
@ResponseBody
public String getStudent(@PathVariable("id") int id) {
    System.out.println(id);
    return "a student";
}
Post请求

通常用post请求,从浏览器向服务器提交数据,后台应该怎么获取呢?浏览器向服务器提交数据,浏览器首先应该具有一个表单的网页
get请求也能传参,但是参数在明面上,且输入框有限,

resources/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>

静态资源访问路径:community/html/student.html

http://localhost:8080/community/html/student.html

填写完表单之后,点击保存提交转到DispatchServlet控制分发后的controller下:

community/alpha/student

http://localhost:8080/community/alpha/student

controller的方法:

@RequestMapping 照样表示访问路径,method为RequestMethod.Post 表示提交

只需要直接声明参数,声明的参数与form表单的参数一致就会自动传入进来。(这里传入之后打印在控制台)

注解:@RequestMapping(path = "/student", method = RequestMethod.POST)

形参:public String saveStudent(String name, int age) {

在方法列表形参中写入与form表单一样的值即可

@RequestMapping(path = "/student", method = RequestMethod.POST)
    @ResponseBody
    public String saveStudent(String name, int age) {
        System.out.println(name);
        System.out.println(age);
        //只要直接声明参数,声明的参数与form表单的参数一致就会自动传入进来
        return "success";
    }
返回网页1

响应HTML数据,DispatcherServlet会调用某个controller方法,controller方法会返回ModelAndView数据,ModelAndView数据传给模板引擎,由模板引擎进行渲染,生成动态的HTML.

//需要new ModelAndView

mav.setViewName("/demo/view");

return的是templates下demo包里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下demo包里view.html
    return mav;
}

返回view所指向的html 路径:templates/demo/view.html

第一个段落写入姓名,第二个写入年龄,这些是动态的值,需要借助模板引擎解决:
th即tymeleaf的缩写,:p的文本表示这里的一个变量,变量用el表达式来写,name是从ModelAndView中取的值
模板引擎渲染的时候,会获取name变量放进p里面

<!DOCTYPE html>
<!---->
<!--需要声明这是一个模板,而不只是一个网页,只需要声明其来自模板网址就行-->
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Teacher</title>
</head>
<body>
    <p th:text="${name}"></p>
    <p th:text="${age}"></p>
</body>
</html>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f1558SL0-1620266608050)(C:\Users\wangfei\AppData\Roaming\Typora\typora-user-images\image-20210103204230388.png)]


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jTnR09Sx-1620266608053)(C:\Users\wangfei\AppData\Roaming\Typora\typora-user-images\image-20210103204426599.png)]

返回网页2

形参:public String getSchool(Model model)

return的还是路径,String表示view的返回路径,model数据怎么传呢,因为返回的view,不可能带model,model放进方法体的形参列表中,Model不是我们创建的,
是DispatchServlet在调用方法时,会自动实例化Model对象,这是一个Bean,DispatchServlet持有该对象的引用,可以往这里面写入数据,它也能得到,

区别:

与前者不同,前者是把Model和View都装进ModelAndView,后者是把Model装进参数里,视图View直接返回。返回的值给了DispatcherServlet,而Model的引用也被DispatcherServlet持有,所以这两个数都能得到。

相比第二种方法更简易,不用手动new ModelAndView对象

//与前者类似,
@RequestMapping(path = "/school", method = RequestMethod.GET)
public String getSchool(Model model) {
    model.addAttribute("name", "北京大学");
    model.addAttribute("age", 80);
    return "demo/view";
}
响应Json数据

响应JSON数据(异步请求)
java对象–>JSON字符串 -->(浏览器利用JSON数据封装成) JS对象
任何语言有字符串类型,任何语言都能解析,方便跨语言 异步请求(成功、失败响应结果)

异步:不刷新页面能够访问服务器得到一次非html的响应

将json字符串封装成Map返回

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ezXXdbt6-1620266608055)(C:\Users\wangfei\AppData\Roaming\Typora\typora-user-images\image-20210103211239341.png)]

属性和属性值构成

响应Json数据2

多个json对象的Map格式封装进List中

@RequestMapping(path = "/emps", method = RequestMethod.GET)
@ResponseBody
public 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", 24);
    emp.put("salary", 7000.00);
    list.add(emp);

    emp = new HashMap<>();
    emp.put("name", "王五");
    emp.put("age", 25);
    emp.put("salary", 9000.00);
    list.add(emp);
    return list;
}

结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rz4ium68-1620266608057)(C:\Users\wangfei\AppData\Roaming\Typora\typora-user-images\image-20210103211835354.png)]

3.Mybatis

1.首先安装Mysql

Mysql Server和Mysql Workbench

先解压  配置my.ini
配置bin的环境变量
mysqld --initialize --console
mysqld install
目前自动配的文件在
D:\work\mysql-8.0.22-winx64\data  中

net start mysql 发生系统错误2 系统找不到指定的文件
以管理员身份运行:
mysqld --remove
mysqld --install

修改密码:
ALTER USER root@localhost IDENTIFIED by '123456'
导入sql命令Source
source 路径   左斜线

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H1i6Ezj3-1620266608058)(C:\Users\wangfei\AppData\Roaming\Typora\typora-user-images\image-20210103134316792.png)]

2.MyBatis

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nNrFkGeb-1620266608060)(C:\Users\wangfei\AppData\Roaming\Typora\typora-user-images\image-20210103134633890.png)]

https://mybatis.org/mybatis-3/

http://mybatis.org/spring/

i-DDL 可查看建表语句

核心组件:

-SqlSessionFactory:用于创建SqlSession的工厂类

-SqlSession:MyBatis的核心组件,用于向数据库执行SQL,相当于JDBC中的Connection

-主配置文件:XML配置文件,可以对MyBatis的底层行为连接参数,连接池的配置

Tips:在springboot中,前三个自动初始化了(xml配置可以配置进properties中)

-Mapper接口:就是DAO接口,在Mybatis中习惯的称为Mapper

-Mapper映射器:用于编写SQL,并将SQL和实体类映射的组件,采用XML、注解均可实现

示例1:

entity/User.java

–>dao/UserMapper.Interface

–>resources/mapper/user-mapper.xml

1.先需要一个与table对应的实体类,在entity包中含有get、set、toString方法;

2.再写UserMapper,添加@Mapper注解,其中的方法即实现对数据的操作:比如selectById(int id);insertUser(User user)

mybatis的好处就在于不需要写接口实现类,只需要接口,其中利用配置自动底层实现生产实现类。

3.再写UserMapper的配置文件

user-mapper.xml 放在mapper路径下

1:entity/User

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;
	...
}

2:dao/UserMapper

//@Mapper 和下面组件一样交给IOC管理
@Repository 加这个不会显示warning

//@Mapper
@Repository
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);

}

3:user-mapper.xml 配置实体类和UserMapper的

head部分:复制即可:统一形式

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

namespace=""

表示是为哪个mapper服务的,需要标注全限定类名,指定到mapper–>

<mapper namespace="com.nowcoder.community.dao.UserMapper">
</mapper>
测试select

1.selectById:

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

2.selectByName:

<select id="selectByName" resultType="User">
        select <include refid="selectFields"></include>
        from user
        where username=#{username}
    </select>

3.selectByEmail

<select id="selectByEmail" resultType="User">
        select id,username,password,salt,email,
            type,status,activation_code,header_url,create_time
        from user
        where email=#{email}
    </select>

当反复涉及到字段名称,可以用sql管理

sql id是必须要的 后面用<include refid="selectFields">指定特定的语句字段

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

对应的测试类:

//@ComponentScan(basePackages =“com.nowcoder.community.dao”) 添加防止组件扫描不到,也可以放在配置类中

测试的时候,要注意会用到UserMapper的接口的方法,故需在测试类中自动注入UserMapper

@SpringBootTest
//测试时以主程序作为配置类
@ContextConfiguration(classes = CommunityApplication.class)
  
public class MapperTest {

    @Autowired
    private UserMapper userMapper;

    @Test
    public void testSelectUser() {
        User user = userMapper.selectById(101);
        System.out.println(user);

        User user1 = userMapper.selectByName("liubei");
        System.out.println(user1);

        User user2 = userMapper.selectByEmail("nowcoder101@sina.com");
        System.out.println(user2);

    }

同理:

测试Insert

#{username} 等等确保和User类对应 是驼峰法 从中取值再插入进mysql的数据库中;

而insertFields是对应数据库的_小写写法

 <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>
 <sql id="insertFields">       username,password,salt,email,type,status,activation_code,header_url,create_time
    </sql>

测试类

int rows = userMapper.insertUser(user);

返回的是改变的行数;

@Test
public void testInsertUser() {
    User user = new User();
    user.setUsername("test");
    user.setPassword("123456");
    user.setSalt("abc");
    user.setEmail("test@qq.com");
    user.setHeaderUri("http://www.nowcoder.com/101.png");
    user.setCreateTime(new Date());

    int rows = userMapper.insertUser(user);
    System.out.println(rows);
    System.out.println(user.getId());
}
测试Update
<update id="updateStatus">
    update user set status=#{status} where id=#{id}
</update>

<update id="updatePassword">
    update user set password=#{password} where id=#{id}
</update>

<update id="updateHeader">
    update user set header_url=#{headerUrl} where id=#{id}
</update>

测试类:

 @Test
    public 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, "1234567");
        System.out.println(rows);

    }

总的user-mapper.xml配置类:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- #为那个mapper服务的,需要标注全限定类名-->
<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 user
        where id=#{id}
    </select>

    <select id="selectByName" resultType="User">
        select <include refid="selectFields"></include>
        from user
        where username=#{username}
    </select>

    <select id="selectByEmail" resultType="User">
        select id,username,password,salt,email,
            type,status,activation_code,header_url,create_time
        from user
        where 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="updatePassword">
        update user set password=#{password} where id=#{id}
    </update>

    <update id="updateHeader">
        update user set header_url=#{headerUrl} where id=#{id}
    </update>

</mapper>

4.开发社区首页

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zIFsdQhD-1620266608061)(C:\Users\wangfei\AppData\Roaming\Typora\typora-user-images\image-20210103161545547.png)]

设计过程:

entity/DiscussPost.java

–>dao/UserMapper.Interface

–>resources/mapper/user-mapper.xml

1.entity:
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;
    private double score;
    ...
}
2.dao

//@Param用于给参数取别名,
//如果只有一个参数,并且在里使用,则必须加别名

@Repository
public interface DiscussPostMapper {

    List<DiscussPost> selectDiscussPosts(int userId, int offset, int limit);

    //@Param用于给参数取别名,
    //如果只有一个参数,并且在<if>里使用,则必须加别名
    int selectDiscussPostRows(@Param("userId") int userId);


}
3.mapper:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- #为那个mapper服务的,需要标注全限定类名-->
<mapper namespace="com.nowcoder.community.dao.DiscussPostMapper">
</mapper>

select语句对应的sql:复杂查询;

包含 正常显示(status!=2)

​ 条件判断是否传入id <if test=“userId!=” /if>

​ id不为0需要拼接userId

​ 排序:order by type 类型 降序 保证置顶

​ creat_time 降序 保证最新在前面

​ 筛选,分页 offset 和limit

 <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_post
        where status!=2
        <if test="userId!=0">
            and user_id=#{userId}
        </if>
        order by type desc,create_time desc
        limit #{offset},#{limit}
    </select>

查询总的帖子数量

<select id="selectDiscussPostRows" resultType="int">
        select count(id)
        from discuss_post
        where status!=2
        <if test="userId!=0">
            and user_id=#{userId}
        </if>
    </select>
4.service

一定不要跨层调用 必须service调dao,在查询过程中会封装成DiscussPost,但是会显示userId,不需要显示userId
两种做法:

1.关联查询,在查询该表的时候同时查询用户表
2.单独的查一下user再与DiscussPost合并一起封装,在后面与redis缓存数据的时候方便,性能更高。这里选择第二种

DiscussPostService:

@Service
public class DiscussPostService {

    @Autowired
    private DiscussPostMapper discussPostMapper;

    public List<DiscussPost> findDiscussPosts(int userId, int offset, int limit) {
        return discussPostMapper.selectDiscussPosts(userId, offset, limit);
    }

    public int findDiscussPostRows(int userId) {
        return discussPostMapper.selectDiscussPostRows(userId);
    }

}

UserService:

@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    public User findUserById(int id) {
        return userMapper.selectById(id);
    }
}
5.controller

在controller层中调service,包含DiscussPostService

UserService

①路径:@RequestMapping(path = "/index", method = RequestMethod.GET)

②方法:Model和View分开:

public String getIndexPage(Model model) {
    model.addAttribute("discussPosts", discussPosts);
     return "/index";
}

③从discussPostService中获取到的帖子封装进List中;

 List<DiscussPost> list = discussPostService.findDiscussPosts(0, 0, 10);

④由于需要重新组装,新建一个集合List存放Map

List<Map<String, Object>> discussPosts = new ArrayList<>();

⑤如果list不为空,循环遍历post

if (list != null) {
            for (DiscussPost post : list) {
                
            }

⑥将post放入map中;

Map<String, Object> map = new HashMap<>();
                map.put("post", post);

⑦由UserService的findUserById获取user用户,放入map中,并将map保存在discussPosts这个list中

User user = userService.findUserById(post.getUserId());
map.put("user", user);
discussPosts.add(map);

Controller总代码:

@Controller
public class HomeController {

    @Autowired
    private DiscussPostService discussPostService;

    @Autowired
    private UserService userService;

    @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);
            }
        }
        model.addAttribute("discussPosts", discussPosts);
        return "/index";
    }
}
6.html

由于controller返回路径是index

利用thymeleaf模板引擎重新构建动态的html `th

(1)修改的地方:

①引入模板引擎

xmlns:th="http://www.thymeleaf.org"

②将相对路径的css,js修改

th:"@{}"

<link rel="stylesheet" href="css/global.css" />
<script src="/js/global.js"></script>
<script src="/js/index.js"></script>

修改:

<link rel="stylesheet" th:href="@{/css/global.css}" />
<script th:src="@{/js/global.js}"></script>
<script th:src="@{/js/index.js}"></script>

③帖子详情的各个参数

  • 去掉重复的ul 利用foreach循环输出: 每次遍历得到map数据
<th:each="map:${discussPosts}">

在ul后面添加: th:each="${}" 括号里面的即由Model返回的discussPostsJson数据,命名为map进行后面使用。

  • 用户头像:th:src="${}"

map.user.headerUrl 相当于

map.getUser().getHeadUrl() 由于是json数据即.底层是get

<img src="http://images.nowcoder.com/head/1t.png">

修改为:

<img th:src="${map.user.headerUrl}">
  • 用户发帖标题内容
<a href="site/discuss-detail.html">

修改为:

<th:utext="${map.post.title}">
  • 置顶和加精
<span class="badge badge-secondary bg-primary">置顶</span>
<span class="badge badge-secondary bg-danger">精华</span>

修改为:

<th:if="${map.post.type==1}">
<th:if="${map.post.status==1}">
  • 用户发帖名称
<a href="site/discuss-detail.html">

内容用#占位,th:utext="${}"

<a href="#" th:utext="${map.user.username}">
  • 用户发帖时间

thymeleaf有格式化语法:

#dates.format() 第一个为取的参数,第二个是时间格式

<b th:text="${#dates.format(map.post.createTime,'yyyy--MM--dd HH:mm:ss')}">2019-04-15 15:32:18</b>

<u class="mr-3">寒江雪</u> 发布于 <b>2019-04-15 15:32:18</b>

改为:
<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>					
(2)有bug调了好久的地方

1.首先 网页加载很慢,怀疑是js,css的样式出问题

通过network分析确实未加载出来,更换在线链接src

改完下面回来之后发现也能显示

2.帖子列表内容不出来

通过重点排查html中的帖子详情代码;

发现1;头像未改为thymeleaf格式;

​ 2.createTime少些一个a

​ 3.controller层map名字写错dicussPosts 少写一个s

head部分:
	<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" crossorigin="anonymous">

尾部部分:
	<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>

中途修改的src:

<script src="https://cdn.staticfile.org/jquery/3.3.1/jquery.min.js"></script>

<link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/4.3.1/css/bootstrap.min.css">

<script src="https://cdn.staticfile.org/jquery/3.3.1/jquery.min.js"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" crossorigin="anonymous"></script>

<script src="https://cdn.staticfile.org/twitter-bootstrap/4.1.0/js/bootstrap.min.js"></script>


7.从page开始加强

优化html从entity开始添加page对象进行有效控制来回的jason数据

package com.nowcoder.community.entity;

//封装分页相关的信息
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) {
            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;
    }

    //获取当前页的起始行
    public int getOffset() {
        // current * limit - limit
        return (current - 1) * limit;
    }

    //获取总页数
    public int getTotal() {
        // rows / limit [+1]
        if (rows % limit == 0) {
            return 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的处理 保证可以翻页

@Controller
public class HomeController {

    @Autowired
    private DiscussPostService discussPostService;

    @Autowired
    private UserService userService;

    @RequestMapping(path = "/index", method = RequestMethod.GET)
    public String getIndexPage(Model model, Page page) {
        // 方法调用前,SpringMVC会自动实例化Model和Page,并将Page注入Model.
        // 所以,在thymeleaf中可以直接访问Page对象中的数据.
        page.setRows(discussPostService.findDiscussPostRows(0));
        page.setPath("/index");

        List<DiscussPost> list = discussPostService.findDiscussPosts(0, page.getOffset(), page.getLimit());
        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);
            }
        }
        model.addAttribute("discussPosts", discussPosts);
        return "/index";
    }

}

<!-- 分页 -->
				<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>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4eqQRxss-1620266608063)(C:\Users\wangfei\AppData\Roaming\Typora\typora-user-images\image-20210104213150009.png)]

响应状态码的含义

查询手册:https://developer.mozilla.org/zh-CN/

200成功

302

302给一个建议,通过客户端调用,进行重定向,非常低耦合的方式进行页面调转

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-luUqzcZg-1620266608065)(C:\Users\wangfei\AppData\Roaming\Typora\typora-user-images\image-20210105000627776.png)]

404 路径写错了

500 服务端接收到了请求,但是服务器出现了问题。

应该检查服务端的程序

服务端断点调试技巧

F8 下一行

F7 方法内部

F9 改变断点,程序自上而下执行,直到下一个断点为止

管理断点

分别查看以及设置是否可用

客户端断点调试技巧

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ApnL1C8A-1620266608067)(C:\Users\wangfei\AppData\Roaming\Typora\typora-user-images\image-20210105191601261.png)]

下一行 F10

进入 F11

执行到底 F8

设置日志级别,并将日志输出到不同的终端

springboot默认启动日志:logback

http://logback.qos.ch/

http://logback.qos.ch/manual/index.html

跟踪级别,调试级别,普通级别,警告级别,错误级别

public interface Logger {

  // Printing methods: 
  public void trace(String message);
  public void debug(String message);
  public void info(String message); 
  public void warn(String message); 
  public void error(String message); 
}

比选中的更高的

配置类:

#logger  日志级别,该包下面的设置信息为debug
logging.level.com.nowcoder.community=debug

写一个测试类

@SpringBootTest
//测试时以主程序作为配置类
@ContextConfiguration(classes = CommunityApplication.class)
public class LoggerTests {

    //slf4j
    private static final Logger logger = LoggerFactory.getLogger(LoggerTests.class);


    @Test
    public void testLogger() {
        System.out.println(logger.getName());
        logger.debug("debug logger");
        logger.info("info logger");
        logger.warn("warn logger");
        logger.error("error logger");

    }
}

改变配置为warn

将test包注意引为:否则失效

import org.junit.jupiter.api.Test;

启动线程池:info

出现error trycatch error级别

开发过程中调试过程用,后面不需要用 debug

把日志输出打印到文件中

logging.file.name=D:/SpringbootPro/nowcoder/data/community.log

日志按照不同级别打印:

5mB以后再重复新开

需要log对应的xml文件:(第三章)

logback-spring.xml 必须放在resources根目录下

只需要改变包名,路径,内存大小等

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <contextName>community</contextName>
    <property name="LOG_PATH" value="D:/SpringbootPro/nowcoder/data/"/>
<!--    存放目录-->
    <property name="APPDIR" value="community"/>

    <!-- error file -->
    <appender name="FILE_ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/${APPDIR}/log_error.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/${APPDIR}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>5MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <append>true</append>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>%d %level [%thread] %logger{10} [%file:%line] %msg%n</pattern>
            <charset>utf-8</charset>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>error</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!-- warn file -->
    <appender name="FILE_WARN" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/${APPDIR}/log_warn.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/${APPDIR}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>5MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <append>true</append>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>%d %level [%thread] %logger{10} [%file:%line] %msg%n</pattern>
            <charset>utf-8</charset>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>warn</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!-- info file -->
    <appender name="FILE_INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/${APPDIR}/log_info.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/${APPDIR}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>5MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <append>true</append>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>%d %level [%thread] %logger{10} [%file:%line] %msg%n</pattern>
            <charset>utf-8</charset>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>info</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!-- console -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d %level [%thread] %logger{10} [%file:%line] %msg%n</pattern>
            <charset>utf-8</charset>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>debug</level>
        </filter>
    </appender>

    <logger name="com.nowcoder.community" level="debug"/>

    <root level="info">
        <appender-ref ref="FILE_ERROR"/>
        <appender-ref ref="FILE_WARN"/>
        <appender-ref ref="FILE_INFO"/>
        <appender-ref ref="STDOUT"/>
    </root>

</configuration>

5.版本控制

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dKJ4vyIr-1620266608073)(C:\Users\wangfei\AppData\Roaming\Typora\typora-user-images\image-20210105200020797.png)]

1.对代码进行备份

2.可以把代码每一次变更都记录下来

3.团队成员互相协作

代码上传备份

  • 认识Git

中央服务器的形式管理代码,所有人把代码存放在一个版本控制的中央服务器,其有1,2,3不同版本的代码。

AB都需要从中下载修改再上传

但是AB每个主机上也有一个仓库

服务器 远程仓库;本地 本地仓库

代码是先存放到本地仓库,觉得没有问题了再上传推送到远程仓库。

都有 谁新用谁的

集中化的版本控制系统—>分布式

Git Bash 模拟Linux环境命令行

Git CMD Windows命令行

Git GUI 界面 很少

查看

git version 查看版本 我的是2.28.0

  • Git常用命令

git config --list

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yCt5Jc6Q-1620266608075)(C:\Users\wangfei\AppData\Roaming\Typora\typora-user-images\image-20210105201640336.png)]

git config --global user.name "rexocean"
git config --global user.email "15531124244@163.com"

上传本地仓库:

要让git管理 需要cd进到目录下(文件名下面就是src等等)

需要初始化

git init

这个命令会在该路径下产生.git 文件

查看git当前的的状态

git status

还没有添加到库里面

添加git add *

再次查看 全是绿的

只是临时存,没有提交,

提交 m写备注

git commit -m `Test1`

在用git status检查

修改已经提交的文件中代码

改完以后再查看状态 git status

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P19DQb3z-1620266608079)(C:\Users\wangfei\AppData\Roaming\Typora\typora-user-images\image-20210105202958017.png)]

显示是已修改的 是红色的 还未提交

继续提交仓库中 git add *

并提交

要传到远程仓库:

传递过程中要有ssl安全连接 需要创建一个密钥

牛客网测试:

新建项目 保证提交有位置

访问路径:

https://git.nowcoder.com/473369408/community1.git

声明 取别名origin

git remote add origin https://git.nowcoder.com/473369408/community1.git
git push -u origin master

输入密码就是绑定的邮箱和密码

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2sJx2uQ8-1620266608081)(C:\Users\wangfei\AppData\Roaming\Typora\typora-user-images\image-20210105213510407.png)]

从远程下载下来看

在项目上点克隆或下载 复制http

https://git.nowcoder.com/473369408/community1.git

打开要存储的位置

git clone https://git.nowcoder.com/473369408/community1.git

下载完成

测试github下的密钥

项目git地址:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EYYxNouz-1620266608082)(C:\Users\wangfei\AppData\Roaming\Typora\typora-user-images\image-20210105214337756.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H3IZiINr-1620266608083)(C:\Users\wangfei\AppData\Roaming\Typora\typora-user-images\image-20210105224454656.png)]

git

https://blog.csdn.net/erlian1992/article/details/77200588ssh

https://blog.csdn.net/erlian1992/article/details/77200700

git imooc课程:

仓库:管理项目的中心,每个项目看成一个仓库

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐