当前位置: 首页 > news >正文

kob后端1

kob后端

1.四个层;

SpringBoot中的常用模块
pojo层:将数据库中的表对应成Java中的Class
mapper层(也叫Dao层):将pojo层的class中的操作,映射成sql语句
service层:写具体的业务逻辑,组合使用mapper中的操作
controller层:负责请求转发,接受页面过来的参数,传给Service处理,接到返回值,再传给页面

mapper层

mybatis-plus帮我们实现了一些内容,我们不用自己手写sql语句:需要让UserMapper接口继承BaseMapper
queryWrapper数据库动态查询条件;queryWrapper

QueryWrapper实例

    /**
     * 案例三:年龄范围查询(20-30之间的)
     * @return
     */
    @RequestMapping("/list3")
    public Map<String,Object> getList3(){
        Map<String,Object> result = new HashMap<>();

        //构建一个查询的wrapper
        QueryWrapper<User> wrapper = new QueryWrapper<User>();
        //年龄20-30之间的
        wrapper.between("age",20,30);
        //未删除
        wrapper.eq("del_flag",0);
        //创建时间降序
        wrapper.orderByDesc("create_time");

        List<User> list = userMapper.selectList(wrapper);
        result.put("data",list);
        return result;
    }

不会用默认的Username去查询,而是会根据用户名去database中查找用户名和密码,然后再判断用户名和密码是否匹配;
报错Encoding…是因为密码没有加密,如果想要浏览器密码未加密,需要在密码前加上{noop},表示密码是明文未加密:
在这里插入图片描述
实现密码加密:
方式1:session验证
实现config.SecurityConfig类,用来实现用户密码的加密存储

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

SecurityConfig对象一定会调用passwordEncoder()方法,返回使用的那种加密方法:
几个核心API:
在这里插入图片描述
encode将明文转换成密文;
matches:判断明文和密文是否匹配;
方式2:JWT(目前使用较多)验证,对应前后端分离(因为前后端分离存在跨域问题)
在这里插入图片描述
获取到一个url之后,分为两种页面,一种是类似login页面,只要输入url就可以访问,另一种比如根页面,必须经过授权之后才能访问,否则当访问该页面的时候可能会跳转到其他页面比如Login页面:
第一种session验证方式,用户输入用户名密码提交之后,会先根据用户名到用户名进行查找,找到对应的username和password之后,才将其和用户输入的username_input,password_input进行比对,如果比对成功,则通过了验证,此时会给用户发送一个sessionID,用户收到sessionID之后会将sessionID 存到自己的本地(Cookie),未来每次在访问网站的时候,都会自动的将浏览器(cookie)存储的sessionID发送给服务器;前边服务器在将sessionID发送给用户的时候,自己也会存储一份;sessionID对用户来说相对于一个令牌,用户后续在访问页面的时候,会将sessionID令牌带上,服务器在接收到用户想要访问授权页面的请求的时候,会将sessionID从用户发送的请求中提取出来,判断sessionID是否有效,服务器不仅会存储sessionID,实际上是存储了一个sessionID-user的映射,若判断访问服务器的这个链接是有效的话,那么就从服务端将该sessionID对应的用户信息提取出来,提取到上下文中(每一个url都会对应一个controller,通过controller中的某个接口将这个用户提取出来),之后就可以正常访问controller逻辑了;
在这里插入图片描述

jwt验证的优势:
1.实现了跨域;
2.不需要在服务端上存储;(不需要将令牌存储到多个服务器上,有一个Jwt令牌就可以访问多个服务器)
jwt验证原理:
在这里插入图片描述
同理,用户登陆验证通过之后,用户会得一个jwt_token,而服务器给用户发了一个jwt_token之后,自己是不需要存储任何信息的;
那么,如何判断这个用户是哪个用户呢???
将用户的一些信息加入到jwt_token中,如图所示
在这里插入图片描述
用户的user_id+服务端生成的秘钥,通过某种方式进行加密,得到加密后的字符串,再将user_id和加密后的字符串进行拼接,得到了jwt_token,
服务端如何验证呢?
同样获取到user_id,同理给user_id先拼接上秘钥,然后根据固定的加密方法(同上述步骤一样),判断用户传过来的jwt_token和根据用户传过来的user_id现生成的jwt_token是不是一样的;
防止他人窃取jwt_token
在这里插入图片描述
一般而言,jwt_token是分为access_token和refresh_token传过来的,access_token时间较短,比如5min,而refresh_token时间较长,
同时,用户的请求有Get(明文,不安全)方式的,也有Post(密文,安全)的,用户在请求的时候会优先使用短时间的令牌,如果短时间的令牌过期了,会重新使用post请求根据refresh_token令牌重新得到一个新的access_token令牌,

Controller

GetMapping注解
如GetMapping(“/user/all/”)

给主键id添加自增注解(@TableId(type= IdType.AUTO)

后端API

1.实现/user/account/token/:验证用户名密码,验证成功后返回jwt token(令牌)
2.实现/user/account/info/:根据令牌返回用户信息
3.+实现/user/account/register/:注册账号

1.在service中写API的接口;
2.在service的impl下写对应API的实现;
3.字controller中写service的调用;

在这里插入图片描述
如图创建如下用户目录,在service目录下创建/user/account/,下写API的接口,在impl下创建相同目录/user/account/下写对应API的实现,

@Service
public class LoginServiceImpl implements LoginService {
    @Override
    public Map<String, String> login(String username, String password) {
        return null;
    }
}

在实现类前要加上Service注解;

一些常用注解的解释
Bean层:
@Autowired:自动导入依赖的bean;

Service层:
@Service:一般用于修饰service层的组件,用于service实现类中;

Controller层
@Controller:用于定义控制器类,在spring项目中由控制器负责将用户发来的URL请求转发到对应的服务接口(service层),一般这个注解在类中,通常方法需要配合注解@RequestMapping。用于定义控制器类

@RequestMapping:提供路由信息,负责URL到Controller中的具体函数的映射。

@GetMapping(链接地址):用于将HTTPGET请求映射到特定处理程序方法的注释。具体来说,@GetMapping是一个作为快捷方式的组合注释@RequestMapping(method = RequestMethod.GET)。

@PostMapping(链接地址),是@RequestMapping(method = RequestMethod.POST)的缩写用于将HTTP POST请求映射到特定处理程序方法的注释。

@RestController:用于标注控制层组件(如struts中的action),@ResponseBody和@Controller的合集。

在实现这三个后端API之前,需要先修改Spring Security(Spring Security 是 Spring 家族中的一个安全管理框架)

1.实现service.impl.UserDetailsServiceImpl类,继承自UserDetailsService接口,用来接入数据库信息

package com.kob.backend.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.kob.backend.mapper.UserMapper;
import com.kob.backend.pojo.User;
import com.kob.backend.service.impl.utils.UserDetailsImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UserMapper userMapper;//使用任何bean之前,需要先注入

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    	//设置好动态查询条件,通过userMapper在数据库中根据用户名进行查找,如果查找结果不为空,返回用户详细信息对象;
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", username);
        User user = userMapper.selectOne(queryWrapper);
        if (user == null) {
            throw new RuntimeException("User Does not Exist!");
        }
        return new UserDetailsImpl(user);
    }
}
//用户详细信息UserDetails接口的实现类
package com.kob.backend.service.impl.utils;

import com.kob.backend.pojo.User;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;
@Data
@NoArgsConstructor
@AllArgsConstructor//三个注解表示自动为该类添加get,set,有参构造,无参构造等基本方法
public class UserDetailsImpl implements UserDetails {

    private User user;//这个后添加的,其余全部是实现的接口函数

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return null;
    }

    @Override
    public String getPassword() {
        return user.getPassword();//这里改成获取密码,原本是null
    }

    @Override
    public String getUsername() {
        return user.getUsername();//这里改成获取用户名,原本是null
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;//改成true,账户为过期?
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;//改成true,账户未锁定?
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;//改成true,凭证未过期?
    }

    @Override
    public boolean isEnabled() {
        return true;//改成true,是否启用?
    }
}

2.实现config.SecurityConfig类,用来实现用户密码的加密存储

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

3.实现utils.JwtUtil类,为jwt工具类,用来创建、解析jwt token

package com.kob.backend.service.impl.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.Date;
import java.util.UUID;

@Component
public class JwtUtil {
    public static final long JWT_TTL = 60 * 60 * 1000L * 24 * 14;  // 有效期14天单位时Ms
    public static final String JWT_KEY = "SDFGjhdsfalshdfHFdsjkdsfds1fsfeseasfesfe21232131afasdfac";//设置再服务端的字符串

    public static String getUUID() {
        return UUID.randomUUID().toString().replaceAll("-", "");
    }

    public static String createJWT(String subject) {
        JwtBuilder builder = getJwtBuilder(subject, null, getUUID());
        return builder.compact();
    }

    private static JwtBuilder getJwtBuilder(String subject, Long ttlMillis, String uuid) {
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        SecretKey secretKey = generalKey();
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        if (ttlMillis == null) {
            ttlMillis = JwtUtil.JWT_TTL;
        }

        long expMillis = nowMillis + ttlMillis;
        Date expDate = new Date(expMillis);
        return Jwts.builder()
                .setId(uuid)
                .setSubject(subject)
                .setIssuer("sg")
                .setIssuedAt(now)
                .signWith(signatureAlgorithm, secretKey)
                .setExpiration(expDate);
    }

    public static SecretKey generalKey() {
        byte[] encodeKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);
        return new SecretKeySpec(encodeKey, 0, encodeKey.length, "HmacSHA256");
    }

    public static Claims parseJWT(String jwt) throws Exception {
        SecretKey secretKey = generalKey();
        return Jwts.parserBuilder()
                .setSigningKey(secretKey)
                .build()
                .parseClaimsJws(jwt)
                .getBody();
    }
}

4.实现config.filter.JwtAuthenticationTokenFilter类,用来验证jwt token,如果验证成功,则将User信息注入上下文中

package com.kob.backend.config.filter;

import com.kob.backend.mapper.UserMapper;
import com.kob.backend.pojo.User;
import com.kob.backend.service.impl.utils.UserDetailsImpl;
import com.kob.backend.service.impl.utils.JwtUtil;
import io.jsonwebtoken.Claims;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    @Autowired
    private UserMapper userMapper;

    @Override
    protected void doFilterInternal(HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull FilterChain filterChain) throws ServletException, IOException {
        String token = request.getHeader("Authorization");

        if (!StringUtils.hasText(token) || !token.startsWith("Bearer ")) {
            filterChain.doFilter(request, response);
            return;
        }

        token = token.substring(7);

        String userid;
        try {
            Claims claims = JwtUtil.parseJWT(token);
            userid = claims.getSubject();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        User user = userMapper.selectById(Integer.parseInt(userid));

        if (user == null) {
            throw new RuntimeException("用户名未登录");
        }

        UserDetailsImpl loginUser = new UserDetailsImpl(user);
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(loginUser, null, null);

        SecurityContextHolder.getContext().setAuthentication(authenticationToken);

        filterChain.doFilter(request, response);
    }
}

5.配置config.SecurityConfig类,放行登录、注册等接口

package com.kob.backend.config;

import com.kob.backend.config.filter.JwtAuthenticationTokenFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers("/user/account/token/", "/user/account/register/").permitAll()//这里放行
                .antMatchers(HttpMethod.OPTIONS).permitAll()
                .anyRequest().authenticated();

        http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
    }
}
后端API实现1.实现/user/account/token/:验证用户名密码,验证成功后返回jwt token(令牌)

创建LoginService接口,并写出实现类

package com.kob.backend.service.impl.user.account;

import com.kob.backend.pojo.User;
import com.kob.backend.service.impl.utils.JwtUtil;
import com.kob.backend.service.impl.utils.UserDetailsImpl;
import com.kob.backend.service.user.account.LoginService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

@Service
public class LoginServiceImpl implements LoginService {
    @Autowired//验证用户登陆是否成功;
    private AuthenticationManager authenticationManager;//authenticationManager(身份验证管理器);

    @Override
    public Map<String, String> getToken(String username, String password) {
        //将username和passwd封装成这样的对象之后,就不会存明文了;
        UsernamePasswordAuthenticationToken authenticationToken=new
                UsernamePasswordAuthenticationToken(username,password);//UsernamePasswordAuthenticationToken (用户名密码认证令牌)

        //进行验证;登陆失败会自动处理
        Authentication authenticate = authenticationManager.authenticate(authenticationToken);

        //登陆成功的话将用户名和密码取出;
        UserDetailsImpl loginUser=(UserDetailsImpl)authenticate.getPrincipal();
        User user=loginUser.getUser();

        //通过用户的userid生成jwt令牌
        String jwt= JwtUtil.createJWT(user.getId().toString());

        Map<String,String> map=new HashMap<>();
        map.put("err_message","success");//不需要处理失败的情况,因为登陆失败的话,Authentication会自动抛出异常,不会执行到这里;
        map.put("token",jwt);

        return map;
    }
}

写出对应的调用controller

package com.kob.backend.controller.user.account;

import com.kob.backend.service.user.account.LoginService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

@RestController
public class LoginController {

    @Autowired//将定义的接口注入进来
    private  LoginService loginService;

    @PostMapping("/user/account/token/")//登陆通常使用post请求,因为get请求会将用户名和密码暴露在url中,
     括号内为链接地址,可以随意定义,注意该链接需要放行:在SecurityConfig中的configure方法中将该链接添加上;
    public Map<String,String> getToken(@RequestParam Map<String,String> map){//从post请求中将参数拿出来,需加上注解,将post请求中的参数放到一个map中
        //这里的RequestParam注解后需要的参数是从前端发送的post请求中提取出来的;
        String username=map.get("username");
        String password=map.get("password");
        return loginService.getToken(username,password);
    }

}

当前端发送url地址/user/account/token/的Posting请求时,执行对应该链接的方法getToken,从前端发送过来的请求中获取到username,和password,返回该username,password(如果验证通过)的jwt令牌

后端API实现2.实现/user/account/info/根据令牌获取用户信息

InfoServiceImpl:

package com.kob.backend.service.impl.user.account;

import com.kob.backend.pojo.User;
import com.kob.backend.service.impl.utils.UserDetailsImpl;
import com.kob.backend.service.user.account.InfoService;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

@Service
public class InfoServiceImpl implements InfoService {
    @Override
    public Map<String, String> getinfo() {
    	//获取到当前的用户,
        UsernamePasswordAuthenticationToken authenticationToken=
                (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
               
        //将当前的登陆用户取出;
        UserDetailsImpl loginUser=(UserDetailsImpl)authenticationToken.getPrincipal();
        User user=loginUser.getUser();

        Map<String,String> map=new HashMap<>();
        map.put("err_message","success");
        map.put("id",user.getId().toString());
        map.put("username",user.getUsername());
        map.put("password",user.getPassword());
        map.put("photo",user.getPhoto());
        return map;//返回获取到的用户信息;e
    }
}

InfoServiceController类

package com.kob.backend.controller.user.account;

import com.kob.backend.service.user.account.InfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

@RestController
public class InfoServiceController {
    @Autowired
    private InfoService infoService;

    @GetMapping("/user/account/info/")
    public Map<String,String> getInfo(){
        return infoService.getinfo();
    }
}
3.实现/user/account/register/:注册账号

实现RegisterServiceImpl类:

package com.kob.backend.service.impl.user.account;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.kob.backend.mapper.UserMapper;
import com.kob.backend.pojo.User;
import com.kob.backend.service.user.account.RegisterService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class RegisterServiceImpl implements RegisterService {

    @Autowired
    UserMapper userMapper;

    @Autowired
    PasswordEncoder passwordEncoder;//给密码加密的工具类

    @Override
    public Map<String, String> register(String username, String password, String confirmedpasswd) {
        Map<String, String> map = new HashMap<>();
        if (username == null) {
            map.put("err_message", "用户名不能为空");
            return map;
        }
        if (password == null || confirmedpasswd == null) {
            map.put("err_message", "密码不能为空");
            return map;
        }

        username = username.trim();
        if (username.length() == 0) {
            map.put("err_message", "用户名不能为空");
            return map;
        }
        if (password.length() == 0 || confirmedpasswd.length() == 0) {
            map.put("error_message", "密码不能为空");
        }

        if (username.length() > 100) {
            map.put("error_message", "用户名长度不能大于100");
            return map;
        }
        if (password.length() > 100 || confirmedpasswd.length() > 100) {
            map.put("error_message", "密码长度不能大于100");
            return map;
        }

        if (!password.equals(confirmedpasswd)) {
            map.put("error_message", "两次输入的密码不一致");
            return map;
        }

        QueryWrapper<User> queryWrapper=new QueryWrapper<User>();
        queryWrapper.eq("username",username);
        List<User> userList=userMapper.selectList(queryWrapper);
        if(userList!=null){
            map.put("error_message", "用户名已存在");
            return map;
        }

        String encodedPassword=passwordEncoder.encode(password);
        String photo="https://cdn.acwing.com/media/user/profile/photo/180265_lg_5dc3115a2f.png";//设置一个默认头像;
        User user=new User(null,username,encodedPassword,photo);//id设置为null即可,自增;
        userMapper.insert(user);
        map.put("error_message", "success");
        return map;
    }
}

实现controller层:

package com.kob.backend.controller.user.account;

import com.kob.backend.service.user.account.RegisterService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

@RestController
public class RegisterServiceController {
    @Autowired
    private RegisterService registerService;

    @PostMapping("/user/account/register/")
    public Map<String,String> register(@RequestParam Map<String,String> map){
        String username=map.get("username");
        String password=map.get("password");
        String confirmedPasswd=map.get("confirmedPassword");
        return registerService.register(username,password,confirmedPasswd);
    }
}

至此,就实现了登陆,获取用户信息,注册三种功能;

验证的时候可以在打开一个页面,在setup()中使用ajax向指定的url发送数据,之后相应的数据会显示在控制台;

  setup() {
    $.ajax({
      url: 'http://localhost:3000/user/account/token/',
      type: "post",
      data: {
        username: "zhr",
        password: "123456",
      },
      success(resp) {
        console.log(resp);
      },
      error(resp) {
        console.log(resp);
      }
    });

    $.ajax({
      url: "http://127.0.0.1:3000/user/account/register/",
      type: "post",
      data: {
        username: "zhrr",
        password: "114514",
        confirmedPassword: "114514",
      },
      success(resp) {
        console.log(resp);
      },
      error(resp) {
        console.log(resp);
      }
    })
  }

相关文章:

  • Kotlin~工厂方法、抽象工厂模式
  • C++继承(下)
  • PKI证书签发系统(2.0web版)
  • Ubuntu20.4下安装TensorFlow2.x
  • 【项目部署】Python Django部署到阿里云
  • MySQL数据库索引并没有你想的那么难之第一节
  • 数据结构与算法-单链表
  • 记一次git误操作, 合并冲突别人新增文件显示成“自己新增“绿色文件
  • Dubbo----------------------------配置信息整合SpringBoot的三种方式
  • 基于视觉的车道线识别技术在智能车导航中的应用研究
  • bleu-mp 多进程bleu评估工具
  • webpack多进程打包
  • 索尼IMX316 标定_ToF模块相机校准
  • 【Proteus仿真】【51单片机】智能鱼缸系统设计
  • 瑞吉外卖2.0 Redis 项目优化 Spring Cache MySQL主从复制 sharding-JDBC Nginx
  • 2023-02-04 Elasticsearch 倒排索引的理解 Trie前缀树原理
  • 【DIY小记】VMWare设置主机连接到的Ubuntu虚拟机的网络端口
  • Spring Boot 集成Quartz
  • 【Java学习】JUC并发编程
  • 【入门AUTOSAR网络管理测试】CANoe测试T_STARTx_AppFrame时间
  • 电加热油锅炉工作原理_电加热导油
  • 大型电蒸汽锅炉_工业电阻炉
  • 燃气蒸汽锅炉的分类_大连生物质蒸汽锅炉
  • 天津市维修锅炉_锅炉汽化处理方法
  • 蒸汽汽锅炉厂家_延安锅炉厂家
  • 山西热水锅炉厂家_酒店热水 锅炉
  • 蒸汽锅炉生产厂家_燃油蒸汽发生器
  • 燃煤锅炉烧热水_张家口 淘汰取缔燃煤锅炉
  • 生物质锅炉_炉
  • 锅炉天然气_天燃气热风炉