最后
我还通过一些渠道整理了一些大厂真实面试主要有:蚂蚁金服、拼多多、阿里云、百度、唯品会、携程、丰巢科技、乐信、软通动力、OPPO、银盛支付、中国平安等初,中级,高级Java面试题集合,附带超详细答案,希望能帮助到大家。
还有专门针对JVM、SPringBoot、SpringCloud、数据库、Linux、缓存、消息中间件、源码等相关面试题。
创建foodie-dev-common
模块、foodie-dev-pojo
模块。
foodie-dev-pojo
依赖foodie-dev-common
com.wjw
foodie-dev-common
1.0-SNAPSHOT
创建foodie-dev-mapper
模块,依赖pojo
com.wjw
foodie-dev-pojo
1.0-SNAPSHOT
创建foodie-dev-service
模块,依赖mapper
com.wjw
foodie-dev-mapper
1.0-SNAPSHOT
创建foodie-dev-api
模块(控制层),依赖service
com.wjw
foodie-dev-service
1.0-SNAPSHOT
但实际上api不应该调用mapper
所有的创建完毕之后要执行foodie-dev的Maven的安装命令。
这里没有使用外键,原因如下:
-
会有一定的性能影响。
-
如果要进行热更新(不停机维护),如果有外键可能会导致新更新的代码无法运行,因为要去匹配到现有的外键,所以可能要重启服务器。
-
删除物理外键可以降低耦合度。
-
数据库的分库分表有外键的话很难实现。
在父工程foodie-dev的pom文件中修改
1. 引入依赖 parent
org.springframework.boot
spring-boot-starter-parent
2.1.5.RELEASE
之后子模块就可以不用单独制定版本号了。
2. 设置资源属性
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
3. 引入依赖 dependency
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-logging
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-configuration-processor
true
**创建foodie-dev-api的配置文件application.yml
和启动类:**
package com.wjw;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
-
2 * @Author: 小王同学
-
3 * @Date: 2020/12/20 15:11
-
4
*/
@SpringBootApplication
@MapperScan(basePackages = “com.wjw.mapper”)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
创建controller包下的类HelloController:
使用@RestController
注解使得返回的对象为json格式
package com.wjw.controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.annotations.ApiIgnore;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
/**
-
2 * @Author: 小王同学
-
3 * @Date: 2020/12/20 16:29
-
4
*/
@RestController
@ApiIgnore
public class HelloController {
@GetMapping(“/hello”)
@Transactional
public Object hello() {
return “Hello World”;
}
}
为什么SpringBoot可以近乎0配置?
- 主要就是基于其自动装配特性。
默认的设置来自于@SpringBootApplication
注解,点进该注解中:
我的启动类是在com.wjw包下,当主函数运行后,会自动扫描包下的所有类
@SpringBootConfiguration
注解是一个接口被@Configuration
修饰,表示它是一个容器:
@EnableAutoConfiguration
是开启自动装配的,@Import
是用于做导入的,并且导入的是一个个的Configuration(即容器)
AutoConfigurationImportSelector
是一个选择器,类比jquery,可以选择自动装配的类
getAutoConfigurationEntry
用于自动装配
configurations
被存在于一个List
,来自getCandidateConfigurations
方法
在spring.factories
文件中存了一些自动装配的类
中可以看到内置的tomcat
中内置的是SpringMVC等等等等
导入数据库文件…(略)
1. 父工程foodie-dev的pom中引入数据源驱动与mybatis依赖
mysql
mysql-connector-java
5.1.41
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.1.0
2. 在doofie-dev-api项目的yml中配置数据源和mybatis
最大连接数经验上是核数的1.5倍
############################################################
配置数据源信息
############################################################
spring:
datasource: # 数据源的相关配置
type: com.zaxxer.hikari.HikariDataSource # 数据源类型:HikariCP
driver-class-name: com.mysql.jdbc.Driver # mysql驱动
url: jdbc:mysql://localhost:3306/foodie-shop-dev?useUnicode=true&characterEncoding=UTF-8&autoReconnect
username: root
password: root
hikari:
connection-timeout: 30000 # 等待连接池分配连接的最大时长(毫秒),超过这个时长还没可用的连接则发生SQ
minimum-idle: 5 # 最小连接数
maximum-pool-size: 20 # 最大连接数
auto-commit: true # 自动提交
idle-timeout: 600000 # 连接超时的最大时长(毫秒),超时则被释放(retired),默认:10分钟
pool-name: DateSourceHikariCP # 连接池名字
max-lifetime: 1800000 # 连接的生命时长(毫秒),超时而且没被使用则被释放(retired),默认:30分钟
connection-test-query: SELECT 1
############################################################
mybatis 配置
############################################################
mybatis:
type-aliases-package: com.wjw.pojo # 所有POJO类所在包路径
mapper-locations: classpath:mapper/*.xml # mapper映射文件
同时,在foodie-dev-mapper中创建com.wjw.mapper包,resources中创建mapper包
在foodie-dev-pojo中创建com.wjw.pojo包
3. 内置tomcat
############################################################
web访问端口号 约定:8088
############################################################
server:
port: 8088
tomcat:
uri-encoding: UTF-8
max-http-header-size: 80KB
1.9.1 代码自动生成
打开我提供的另外一个项目,代码自取
运行utils包下的GeneratorDisplay文件,按照注释做填空题,会根据配置文件来生成mapper和pojo:
<?xml version="1.0" encoding="UTF-8"?><jdbcConnection driverClass=“com.mysql.jdbc.Driver”
connectionURL=“jdbc:mysql://localhost:3306/foodie-shop-dev”
userId=“root”
password=“root”>
把自动生成的mapper、pojo、resources/mapper文件夹拷贝到我们的主项目中。
第一个导入foodie-dev-mapper的com.wjw.mapper包下
第三个导入foodie-dev-mapper的resources/mapper下
第二个导入foodie-dev-pojo的com.wjw.pojo包下
1.9.2 完善项目
- 在父工程pom中引入通用mapper工具
tk.mybatis
mapper-spring-boot-starter
2.1.5
- 在foodie-dev-api的yml中引入通用mapper配置
############################################################
mybatis mapper 配置
############################################################
通用 Mapper 配置
mapper:
mappers: com.wjw.my.mapper.MyMapper
not-empty: false # 在进行数据库操作时,判断表达式 username !=null,是否追加 username != ‘’
identity: MYSQL
- 在foodie-dev-mapper项目中引入MyMapper接口类
package com.wjw.my.mapper;
import tk.mybatis.mapper.common.Mapper;
import tk.mybatis.mapper.common.MySqlMapper;
/**
- 继承自己的MyMapper
*/
public interface MyMapper extends Mapper, MySqlMapper {
}
运行Application启动成功。
- 首先先来看一下MyMapper 所继承的父类,如:
interface MyMapper extends Mapper, MySqlMapper
这里有两个父类, Mapper 与MySqlMapper ,我们可以打开MySqlMapper 看一下:
public interface MySqlMapper extends InsertListMapper, InsertUseGeneratedKeysMapper {
}
这里面又继承了了两个mapper,从类名上可以看得出来,是用于操作数据库的,这两个类里又分别包含了如下方法,简单归类一下:
很明显,在传统JavaWeb开发,这两个方法使用是没有问题的,但是我们的数据库表主键设计肯定是全局唯一的,所以不可能使用自增长id,所以这两个方法在我们开发过程中是不会使用的。
- 随后再来看一下Mapper 中所继承的父类,如下:
public interface Mapper extends BaseMapper, ExampleMapper, RowBoundsMapper, Marker {
}
BaseMapper<T>
ExampleMapper<T>
Example类是用于提供给用户实现自定义条件的,也就是where 条件
Restful是一种通信方式,在系统与系统之间可以传递相应的消息,客户端与服务器端通信载体也是可以使用Restful WebService的。
Restful WebService的一个特点是无状态,服务器接收客户端请求时,服务器不需要了解这个request之前做过什么以及将来可能做什么。
使用Restful之后系统有独立性,可以做拆分。
Rest设计规范:
-
GET,多用于查询 ->
/order/{id}
->/getOrder?id=1001
-
POST,多用于更新资源保存信息 ->
/order
->/saveOrder
-
PUT,多用于更新资源 ->
/order/{id}
->/modifyOrder
-
DELETE ->
/order/{id}
->/deleteOrder?id=1001
使用@Transactional
注解来实现事务,查看源码:
默认值是Propagation.REQUIRED
,即如果当前没有事务则新建一个,修饰的方法必须运行在一个事务中,当前有事务的话会加入到现有的事务中,事务一共有7类:
-
REQUIRED:如果当前有事务,则使用该事务,没有事务自己创建一个。举例:领导没饭吃,我会自己买了吃;领导有的吃,会分给我一起吃。
-
SUPPORT:主要用于查询,修饰的方法如果当前有事务,则使用事务,否则不使用。举例:领导有饭吃,我也有饭吃;领导没饭吃,我也没饭吃。
-
MANDATORY:强制必须存在一个事务,如果不存在,抛出异常。举例:领导必须管饭,否则就出异常。
-
REQUIRES_NEW:如果当前有事务,则挂起该事务,并且自己创建一个新的事务给自己用;如果当前没有事务则同REQUIRED。举例:领导有饭吃,我偏自己买了自己吃。
-
NOT_SUPPORTED:如果当前有事务,则把事务挂起,自己不使用事务去操作数据库。举例:领导有饭吃,我太忙了,放一边不吃。
-
NEVER:如果当前有事务存在,则抛出异常。举例:领导有饭给我吃,抛出异常。
-
NESTED:如果当前有事务,则开启子事务(嵌套事务),嵌套事务是独立提交或回滚;如果当前没有事务,则同REQUIRED;但如果主事务提交,会携带子事务一起提交;如果主事务回滚,则会带着子事务一起回滚。相反,子事务异常,则父事务可以回滚或不回滚(借助try/catch)。举例:领导决策不对,老板怪罪,领导带着小弟一同受罪。小弟出了差错,领导可以推卸责任。
==========================================================================
用户名注册登录流程:
邮箱注册流程:
手机号注册登录流程:
分布式系统表的主键一般不设为自增,并且多设计成varchar
类型的,原因是要保证全局唯一性。
先从service层写起,编辑foodie-dec-service项目:
创建UserService接口:
package com.wjw.service;
/**
-
2 * @Author: 小王同学
-
3 * @Date: 2020/12/21 17:38
-
4
*/
public interface UserService {
/**
- 判断用户名是否存在
*/
public boolean queryUsernameIsExist(String username);
}
UserService接口的实现类:
package com.wjw.service.impl;
import com.wjw.mapper.UsersMapper;
import com.wjw.pojo.Users;
import com.wjw.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;
/**
-
2 * @Author: 小王同学
-
3 * @Date: 2020/12/21 17:39
-
4
*/
public class UserServiceImpl implements UserService {
@Autowired
public UsersMapper usersMapper;
/**
-
判断用户名是否存在
-
@param username
*/
@Transactional(propagation = Propagation.SUPPORTS)
@Override
public boolean queryUsernameIsExist(String username) {
Example userExample = new Example(Users.class);
Example.Criteria userCriteria = userExample.createCriteria();
userCriteria.andEqualTo(“username”, username);
Users result = usersMapper.selectOneByExample(userExample);
return result != null;
}
}
在主项目的pom中添加依赖:
commons-codec
commons-codec
1.11
org.apache.commons
commons-lang3
3.4
commons-io
commons-io
1.3.2
在foodie-dev-api项目中定义controller:
package com.wjw.controller;
import com.wjw.service.UserService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
-
2 * @Author: 小王同学
-
3 * @Date: 2020/12/20 16:29
-
4
*/
@RestController
@RequestMapping(“passport”)
public class PassportController {
@Autowired
private UserService userService;
@GetMapping(“/usernameIsExist”)
public int usernameIsExist(@RequestParam String username) {
// 判断用户名不能为空
if (StringUtils.isBlank(username)){
return 500;
}
// 查找注册的用户名是否存在
boolean isExist = userService.queryUsernameIsExist(username);
if (isExist) {
return 500;
}
// 用户名没有重复
return 200;
}
}
在foodie-dev-common模块中创建com.wjw.utils包,并定义响应数据结构。
package com.wjw.utils;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
-
@Description: 自定义响应数据结构
-
本类可提供给 H5/ios/安卓/公众号/小程序 使用
-
前端接受此类数据(json object)后,可自行根据业务去实现相关功能
-
200:表示成功
-
500:表示错误,错误信息在msg字段中
-
501:bean验证错误,不管多少个错误都以map形式返回
-
502:拦截器拦截到用户token出错
-
555:异常抛出信息
-
556: 用户qq校验异常
*/
public class WJWJSONResult {
// 定义jackson对象
private static final ObjectMapper MAPPER = new ObjectMapper();
// 响应业务状态
private Integer status;
// 响应消息
private String msg;
// 响应中的数据
private Object data;
@JsonIgnore
private String ok; // 不使用
public static WJWJSONResult build(Integer status, String msg, Object data) {
return new WJWJSONResult(status, msg, data);
}
public static WJWJSONResult build(Integer status, String msg, Object data, String ok) {
return new WJWJSONResult(status, msg, data, ok);
}
public static WJWJSONResult ok(Object data) {
return new WJWJSONResult(data);
}
public static WJWJSONResult ok() {
return new WJWJSONResult(null);
}
public static WJWJSONResult errorMsg(String msg) {
return new WJWJSONResult(500, msg, null);
}
public static WJWJSONResult errorMap(Object data) {
return new WJWJSONResult(501, “error”, data);
}
public static WJWJSONResult errorTokenMsg(String msg) {
return new WJWJSONResult(502, msg, null);
}
public static WJWJSONResult errorException(String msg) {
return new WJWJSONResult(555, msg, null);
}
public static WJWJSONResult errorUserQQ(String msg) {
return new WJWJSONResult(556, msg, null);
}
public WJWJSONResult() {
}
public WJWJSONResult(Integer status, String msg, Object data) {
this.status = status;
this.msg = msg;
this.data = data;
}
public WJWJSONResult(Integer status, String msg, Object data, String ok) {
this.status = status;
this.msg = msg;
this.data = data;
this.ok = ok;
}
public WJWJSONResult(Object data) {
this.status = 200;
this.msg = “OK”;
this.data = data;
}
public Boolean isOK() {
return this.status == 200;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public String getOk() {
return ok;
}
public void setOk(String ok) {
this.ok = ok;
}
}
之后重新修改刚刚的PassportController
package com.wjw.controller;
import com.wjw.service.UserService;
import com.wjw.utils.WJWJSONResult;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
-
2 * @Author: 小王同学
-
3 * @Date: 2020/12/20 16:29
-
4
*/
@RestController
@RequestMapping(“passport”)
public class PassportController {
@Autowired
private UserService userService;
@GetMapping(“/usernameIsExist”)
public WJWJSONResult usernameIsExist(@RequestParam String username) {
// 判断用户名不能为空
if (StringUtils.isBlank(username)){
return WJWJSONResult.errorMsg(“用户名不能为空”);
}
// 查找注册的用户名是否存在
boolean isExist = userService.queryUsernameIsExist(username);
if (isExist) {
return WJWJSONResult.errorMsg(“用户名已经存在”);
}
// 用户名没有重复
return WJWJSONResult.ok();
}
}
再次用postman测试刚刚的那个接口:
2.4.1 创建用户service
只要是前端传向后端接收的数据体,都可以统一的定义为XxxBO
在foodie-dev-pojo项目下创建bo类:
package com.wjw.pojo.bo;
/**
-
2 * @Author: 小王同学
-
3 * @Date: 2020/12/21 20:41
-
4
*/
public class UserBO {
private String username;
private String password;
private String confirmPassword;
…
}
对应注册时填写的数据
在common模块中引入一些工具:
引入MD5加密工具:
package com.wjw.utils;
import org.apache.commons.codec.binary.Base64;
import java.security.MessageDigest;
public class MD5Utils {
/**
-
@Title: MD5Utils.java
-
@Package com.wjw.utils
-
@Description: 对字符串进行md5加密
*/
public static String getMD5Str(String strValue) throws Exception {
MessageDigest md5 = MessageDigest.getInstance(“MD5”);
String newstr = Base64.encodeBase64String(md5.digest(strValue.getBytes()));
return newstr;
}
public static void main(String[] args) {
try {
String md5 = getMD5Str(“wjw”);
System.out.println(md5);
} catch (Exception e) {
e.printStackTrace();
}
}
}
引入操作日期的工具:
引入表示性别的枚举类:
package com.wjw.enums;
/**
-
2 * @Author: 小王同学
-
3 * @Date: 2020/12/21 21:04
-
4
*/
public enum Sex {
woman(0, “女”),
man(1, “女”),
secret(2, “女”);
public final Integer type;
public final String value;
Sex(Integer type, String value) {
this.type = type;
this.value = value;
}
}
引入生成全局唯一ID的三个包(以后会详细讨论,现在先用):
不要忘了在启动类上添加注解来扫描这个包。
package com.wjw;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import tk.mybatis.spring.annotation.MapperScan;
/**
-
2 * @Author: 小王同学
-
3 * @Date: 2020/12/20 15:11
-
4
*/
@SpringBootApplication
@MapperScan(basePackages = “com.wjw.mapper”)
@ComponentScan(basePackages = {“com.wjw”, “org.n3r.idworker”})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
创建用户对应的service:
@Autowired
private Sid sid;
/**
-
创建用户
-
@param userBO
-
@return
*/
@Transactional(propagation = Propagation.REQUIRED)
@Override
public Users createUser(UserBO userBO) {
String userId = sid.nextShort();
Users user = new Users();
user.setId(userId);
user.setUsername(userBO.getUsername());
try {
user.setPassword(MD5Utils.getMD5Str(userBO.getPassword()));
} catch (Exception e) {
e.printStackTrace();
}
// 默认用户昵称同用户名
user.setNickname(userBO.getUsername());
// 默认头像
user.setFace(USER_FACE);
// 默认生日
user.setBirthday(DateUtil.stringToDate(“1900-01-01”));
// 默认性别 保密
user.setSex(Sex.secret.type);
user.setCreatedTime(new Date());
user.setUpdatedTime(new Date());
usersMapper.insert(user);
// 返回用于在页面里显示用户基本信息
return user;
}
2.4.2 创建用户对应的controller与测试
在PassportController
中创建注册方法:
最后
Java架构学习技术内容包含有:Spring,Dubbo,MyBatis, RPC, 源码分析,高并发、高性能、分布式,性能优化,微服务 高级架构开发等等。
还有Java核心知识点+全套架构师学习资料和视频+一线大厂面试宝典+面试简历模板可以领取+阿里美团网易腾讯小米爱奇艺快手哔哩哔哩面试题+Spring源码合集+Java架构实战电子书+2021年最新大厂面试题。
e.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import tk.mybatis.spring.annotation.MapperScan;
/**
-
2 * @Author: 小王同学
-
3 * @Date: 2020/12/20 15:11
-
4
*/
@SpringBootApplication
@MapperScan(basePackages = “com.wjw.mapper”)
@ComponentScan(basePackages = {“com.wjw”, “org.n3r.idworker”})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
创建用户对应的service:
@Autowired
private Sid sid;
/**
-
创建用户
-
@param userBO
-
@return
*/
@Transactional(propagation = Propagation.REQUIRED)
@Override
public Users createUser(UserBO userBO) {
String userId = sid.nextShort();
Users user = new Users();
user.setId(userId);
user.setUsername(userBO.getUsername());
try {
user.setPassword(MD5Utils.getMD5Str(userBO.getPassword()));
} catch (Exception e) {
e.printStackTrace();
}
// 默认用户昵称同用户名
user.setNickname(userBO.getUsername());
// 默认头像
user.setFace(USER_FACE);
// 默认生日
user.setBirthday(DateUtil.stringToDate(“1900-01-01”));
// 默认性别 保密
user.setSex(Sex.secret.type);
user.setCreatedTime(new Date());
user.setUpdatedTime(new Date());
usersMapper.insert(user);
// 返回用于在页面里显示用户基本信息
return user;
}
2.4.2 创建用户对应的controller与测试
在PassportController
中创建注册方法:
最后
Java架构学习技术内容包含有:Spring,Dubbo,MyBatis, RPC, 源码分析,高并发、高性能、分布式,性能优化,微服务 高级架构开发等等。
还有Java核心知识点+全套架构师学习资料和视频+一线大厂面试宝典+面试简历模板可以领取+阿里美团网易腾讯小米爱奇艺快手哔哩哔哩面试题+Spring源码合集+Java架构实战电子书+2021年最新大厂面试题。
[外链图片转存中…(img-80QZQvjI-1715498247168)]