项目介绍
我完成了一个外卖项目,名叫苍穹外卖,是跟着黑马程序员的课程来自己动手写的。
项目基本实现了外卖客户端、商家端的后端完整业务。
商家端分为员工管理、文件上传、菜品管理、分类管理、套餐管理、店铺营业状态、订单下单派送等的管理、数据统计等,用时7天半;用户端用微信小程序实现的,分为用户登陆退出、套餐菜品浏览、购物车、订单管理、地址薄管理等,用时4天半。
项目使用了Nginx、Swagger、Redis、MySQL、SpringBoot、AOP、HTTPClient、SpringCache、SpringTask、WebSocket、POI等技术。
项目使用了流行的微信小程序实现用户端,保证了技术赶上应用的潮流。
项目难点在于各种第三方调用的实现,比如调用微信进行登录、付款,调用百度地图进行位置查询等,需要自信了解第三方接口的细节,需要有基本的编程语言理解与代码学习能力。解决方案就是自己学习、搜索学习和向其他人、高手学习。
经过了这个项目,我的代码能力提升了,对Spring三层架构的理解又深了一步,离当上一个合格的程序员又近了一步。
业务逻辑分析,请问面试官有感兴趣的地方吗。
假设的业务进行分析,我尽力而为。
JavaSE基础部分
一、Java数据类型
基本数据类型——四类八种
整数型 byte short int long
浮点型 float double
字符型 char
布尔型 boolean
引用数据类型
String字符串
类(对象)
接口类型
数组类型
枚举类型
二、面向对象的三大特性
1.封装
把同一类事物的共性归到同一类中,方便使用。
重要的private:把类的某些信息隐藏在类的内部,不允许外部程序直接访问。
2.继承
子类继承父类,使子类有和父类相同的特征和行为。
3.多态
在继承的基础上才有多态。
优缺点见下面题目。
三、说五个关键字和作用
public 修饰公有成员、方法
static 修饰静态成员、方法
void 表示无返回值
final 修饰最终变量,成为常量
protected 修饰成员、方法,表示同包下或者不同包中继承的子类可以调用
四、接口和抽象类的区别
1.接口是接口,与其他接口是继承关系,可以多继承其他接口;
抽象类是类,与其他类是继承关系,与接口是实现关系,只可以继承一个类,可以多实现接口。
2.接口用于规范,只能声明方法、属性,方法在1.9之前只能是公共的、默认的或静态的,1.9之后才支持私有方法;
抽象类用于共性,除了方法、属性还可以声明构造函数、具体实现的方法、私有方法、静态方法等。
五、访问权限有哪几种
4种。
private 私有的 只有本类内部可以使用。
(default) 缺省/默认的 不加默认为缺省的。只有本包下的类能访问。
protected 被保护的 只有本包,或者不同包下的子类能访问。
public 共有的 本包、不同包、非子类都可以访问。
六、继承的好处
1.易维护性
增加了代码的一致性,增加易维护性。
2.复用性
减小代码和数据的冗余度,增加重用性。
3.条理性
清晰体现类间的层次结构关系,条理更清晰。
七、多态的优缺点
优点
方法参数定义了父类对象,该形参可以用任意子类填充。
1.简化了代码;
2.提升了维护性和扩展性。
缺点
通过父类引用操作子类对象时,只能使用父类中已有的方法,不能操作子类特有的方法。
解决方法:向下转型
八、Override和Overload有什么区别
1.重载方法名称相同,参数的类型或个数不同;重写方法名称、参数类型还有返回值类型必须全部相同。
2.重载对权限没有要求;重写中,被重写的方法不能拥有比父类更加严格的权限
3.重载发生在一个类中;重写发生在继承中。
九、常见的设计模式
在Java中,常见的设计模式同样包括创建型模式、结构型模式和行为型模式。下面是一些在Java中特别常见的设计模式及其简要描述:
创建型模式:
单例模式(Singleton Pattern):确保一个类只有一个实例,并提供一个全局访问点。Java中的java.lang.Runtime就是一个单例模式的经典例子。
工厂模式(Factory Pattern):定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
1.简单工厂
●所有的产品都共有一个工厂,如果新增产品,则需要修改代码,违反开闭原则●是一种编程习惯,可以借鉴这种编程思路
2.工厂方法模式
·给每个产品都提供了一个工厂,让工厂专门负责对应的产品的生产,遵循开闭原则
●项目中用的最多
3.抽象工厂方法模式
·如果有多个纬度的产品需要配合生产时,优先建议采用抽象工厂(工厂的工厂)
一般的企业开发中的较少
结构型模式:
适配器模式(Adapter Pattern):将一个类的接口转换成客户期望的另一个接口。适配器让原本由于接口不兼容而无法协同工作的类可以一起工作。
装饰器模式(Decorator Pattern):动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。Java的IO类库就大量使用了装饰器模式。
行为型模式:
观察者模式(Observer Pattern):定义对象之间的一对多依赖关系,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。在Java的Swing和事件监听中,观察者模式被广泛使用。
策略模式(Strategy Pattern):策略模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。一个系统需要动态地在几种算法中选择一种时,可将每个算法封装到策略类中
策略模式的主要角色如下: 抽象策略(Strategy)类:这是一个抽象角色,通常由一个接口或抽象类实 现。此角色给出所有的具体策略类所需的接口。 具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的 算法实现或行为。 环境(Context)类:持有一个策略类的引用,最终给客户端调用。
策略加工厂
介绍业务(登录、支付、解析excel、优惠等级...)
改造之后,不在service中写业务逻辑,让service调用工厂,然后通过service传递 不同的参数来获取不同的登录策略(登录方式)
提供了很多种策略,都让spring容器管理
提供一个工厂:准备策略对象,根据参数提供对象
其实像这样的需求,在日常开发中非常常见,场景有很多,以下的情景都可以使 用工厂模式+策略模式解决比如: 订单的支付策略 支付宝支付 微信支付 银行卡支付 现金支付,登陆策略
一句话总结:只要代码中有冗长的 if-else 或 switch 分支判断都可以采用策略模 式优化
责任链模式
又名职责链模式,为了避免请求发送者与多个请求处理者耦合在一起,将所有请 求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生 时,可将请求沿着这条链传递,直到有对象处理它为止。 比较常见的springmvc中的拦截器,web开发中的filter过滤器
1.面试题:请解释单例模式,并给出一个线程安全的Java实现。
答案:单例模式确保一个类只有一个实例,并提供一个全局访问点。一个线程安全的Java实现可以使用双重检查锁定(double-checked locking)来确保单例的唯一性,同时避免不必要的同步开销。以下是一个示例:
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
2.面试题:请描述工厂模式,并给出一个Java中的使用场景。
答案:工厂模式是一种创建型设计模式,它提供了一种在不指定具体类的情况下创建对象的方法。在Java中,工厂模式常用于创建那些不需要提前知道具体类型的对象,或者对象的创建过程比较复杂的情况。例如,Java中的Boolean.valueOf(String s)方法就使用了工厂模式来创建Boolean对象。当传入一个字符串时,这个方法会根据字符串的内容返回一个对应的Boolean对象,而无需直接调用new Boolean(String s)。
3.面试题:请解释什么是适配器模式,并给出一个在Java中的使用场景。
答案:适配器模式是一种结构型设计模式,它允许将一个类的接口转换成客户端所期望的另一种接口,从而使得原本由于接口不兼容而无法协同工作的类能够一起工作。在Java中,适配器模式常用于解决旧代码与新代码之间的接口不兼容问题。例如,假设我们有一个旧的类库,它提供了一些方法来完成特定的任务,但新的代码需要调用不同的方法来完成相同的任务。这时,我们可以创建一个适配器类,该类包装旧类库中的对象,并提供新的接口方法,以便新代码可以调用这些方法来完成任务。
4.面试题:请描述装饰器模式,并解释它在Java IO中的应用。
答案:装饰器模式是一种结构型设计模式,它允许用户通过动态地给一个对象添加一些额外的职责来扩展对象的功能。就增加功能来说,装饰器模式相比生成子类更为灵活。在Java IO中,装饰器模式被广泛使用。例如,BufferedReader和BufferedWriter就是典型的装饰器类,它们分别装饰了Reader和Writer对象,为它们添加了缓冲功能,从而提高了IO操作的效率。通过使用装饰器模式,Java IO库可以灵活地组合不同的功能,从而满足各种不同的IO需求。
5.面试题:请解释观察者模式,并给出一个在Java Swing中的使用场景。
答案:观察者模式是一种行为型设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象状态发生改变时,它的所有依赖者(观察者)都会收到通知并自动更新。在Java Swing中,观察者模式被广泛应用于各种事件监听中。例如,当我们点击一个按钮时,按钮会触发一个点击事件,这个事件会被注册在按钮上的所有监听器(观察者)接收到,并执行相应的处理逻辑。通过这种方式,Swing框架实现了用户界面与业务逻辑的解耦,使得代码更加灵活和可维护。
6.面试题:请解释原型模式,并给出一个Java中的使用场景。
答案:原型模式是一种创建型设计模式,它用原型实例指定创建对象的种类,并且通过拷贝这个原型来创建新的对象。在Java中,当我们需要创建大量相同或相似对象时,原型模式可以显著提高性能。例如,在游戏开发中,角色、怪物等对象可能需要频繁创建,使用原型模式可以避免重复初始化,直接复制已有对象的状态来创建新对象。
7.面试题:请描述模板方法模式,并解释其优点。
答案:模板方法模式是一种行为型设计模式,它在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤。其优点包括:提高了代码的复用性,将不变的行为搬到超类,去除了子类中的重复代码;提高了系统的扩展性,新增行为也比较容易,无需修改原有代码;符合开闭原则。
8.9.面试题:请谈谈你对策略模式的理解,并给出一个在Java中的使用场景。
答案:策略模式是一种行为型设计模式,它定义了一系列的算法,并将每一个算法封装起来,使它们可以互相替换。策略模式使得算法可以独立于使用它的客户变化。在Java中,策略模式常用于处理不同算法或行为的替换。例如,排序算法就可以使用策略模式来实现,我们可以定义不同的排序策略(如冒泡排序、快速排序等),然后根据需要选择合适的策略进行排序。
9.面试题:请解释什么是建造者模式,并给出一个Java中的使用场景。
答案:建造者模式是一种创建型设计模式,它将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。建造者模式一步一步创建一个复杂的对象。它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。在Java中,当我们需要创建一个具有多个属性的复杂对象时,可以使用建造者模式来简化对象的创建过程。例如,创建一个具有多个配置选项的服务器对象时,可以使用建造者模式来逐步设置这些配置选项,并最终构建出所需的服务器对象。
十、异常体系结构
Throwable
└ Error
└ Exception 编译时异常/受检异常
└ RuntimeException 运行时异常/非受检异常
十一、集合体系结构和特点
Collection
└ List 有索引,存取一致,有序,元素允许重复
┃ └ ArrayLIst
┃ └ LinkedList
┃ └ Vector
└ Set 无索引,无序,元素不允许重复
└ HashSet
└ TreeSet
└ LinkedHashSet
十二、ArrayList特点,扩容
特点
1.增删慢
每次删除元素,都需要更改数组长度、拷贝以及移动元素位置。当然,增删最后一个元素快。
2.查询快
连续空间存储数组,根据地址+索引能快速访问到数据。
3.可扩容/动态性
ArrayList使用动态数组作为底层的数据结构,能够自动扩容、缩容
4.非线程安全
多个线程同时访问和修改同一个ArrayList实例,可能会导致不可预测的结果
扩容
无参、传参为0、传列表为0时,创建ArrayList容量为0,添加第一个元素后容量为10,下次正常扩容。
正常扩容,列表满时,扩容1.5倍。
十三、HashMap的原理
对于底层数据结构,采用的是哈希表。
哈希表在jdk1.8之前,哈希表是数组+链表;jdk1.8及之后,哈希表是数组+链表+红黑树。
存取原理:
首先根据键,计算哈希值。
根据哈希值,计算下标,存入数组。
数组中,如果该下标下链表为空,则直接存入;
如果链表不为空,则产生了哈希冲突,进一步判断哈希值是否相等。相等,则重复了,替代掉;
如果不相等,则存入链表后面。
取时,首先计算被取元素的哈希值,然后计算其在数组中的下标,在下标中的链表中找到相同哈希值的元素,即可取出。
在jdk1.8之前,解决哈希冲突时采用的是拉链法,即直接使用链表。
1.8之后,解决哈希冲突,用红黑树。如果链表长度大于8,则扩容为红黑树;如果长度小于了6,则又退化为链表。
十四、什么是递归,有什么优缺点
递归:方法自己调用自己
优点:代码简介。
缺点:1.性能开销大;2.栈溢出;3.调试困难。
十五、IO流的分类
字符流、字节流
输入流、输出流
节点流、处理流
其中,文本用字符流,非文本数据、文件拷贝用字节流。
十六、多线程的实现方式
4种方式:
1.继承Thread
2.实现Runnable
3.实现Callable
4.线程池
十七、线程安全问题产生的原因、解决方案
产生原因
1.有多线程要并发
2.要操作同一组数据
解决方案
加同步锁synchronized
加锁Lock(jdk5之后)
十八、线程池的7个参数
1.corePoolSize 核心线程数
2.maxinumPoolSize 最大线程数
3.keepAliveTime 空闲线程的存活时间。
4.unit 存活时间的单位
5.workQueue 任务队列,存放被提交但是尚未被执行的任务。
6.threadFactory 生成线程池中工作线程的线程工厂。
7.handler:拒绝策略,队列、线程都满了时如何处理线程。
十九、UDP和TCP协议的区别
1.UDP无连接,速度快,安全性低,适合高速传输、实时广播通信等。
2.TCP面向连接,速度慢,安全性高,适合传输质量要求高、大文件等的传输,比如邮件发送等。
(还有:TCP只能是一对一的,UDP支持一对一、一对多、多对一)
(还有:TCP首部开销有20个字节;UDP分组首部开销小,只有8个字节)
二十、什么是反射
Java中动态获取类及类的成员、方法的技术。
框架的底层都用了大量的反射。
二十一、死锁
所谓死锁,是指多个进程在运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,若无外力作用,它们都将无法再向前推进。
互斥性:当多个线程对同一把锁,有竞争。在某一时刻,最终只有一个线程可以拥有这把锁
不可抢夺性:当一个线程已经获取到了锁A,其他线程要想获取锁A,这个时候只能等该线程把A释放了之后再获取,不能中途抢夺别的线程的锁。
请求和保持性:当一个线程获取到了锁A,除非该线程自己释放锁A,否则该线程就一直保持占有锁A
循环等待性:在死锁中往往会出现,线程A等着线程B释放锁,同时线程B又在等着线程A来释放他所占有的锁,结果A、B的锁都无法正常释放,也都无法完成各自的进程,陷入了一个循环等待的状态
二十二、线程和进程的区别
一.进程和线程基本概念
1.进程:进程是操作系统中资源分配的基本单位。进程是操作系统对正在运行程序的一种抽象,可以将进程看作程序的一次运行。
2.线程:线程是操作系统中调度执行的基本单位。一个线程是一个“执行流”,每个线程之间都可以按照顺序执行自己的代码,多个线程“同时”执行多份代码。
二.进程和线程之间的区别与联系
1.一个进程可以包含多个线程,线程在进程的内部。
2.进程之间的资源是独立的,线程之间的资源则是共享的。
每个进程都有独立的虚拟地址空间,也有之间独立的文件描述符表,同一进程的多个线程之间则共用这一份虚拟地址空间和文件描述符表。
3.进程是操作系统中资源分配的基本单位,线程是操作系统中调度执行的基本单位。
4.多个进程同时执行时,如果一个进程崩溃,一般不会影响其他进程,而同一进程内的多个线程之间,如果一个线程崩溃,很可能使得整个进程崩溃。
5.进程的上下文切换速度比较慢,而线程的上下文切换速度比较快。
6.进程的创建/销毁/调度开销大,线程的创建/销毁/调度开销相对少很多。
Web
一、什么是AJAX
异步的JavaScript和XML。用来做前端和后端的异步请求的技术。
异步请求:只更新部分前端界面的请求,做到局部更新。
比如注册,提示用户名已存在而整个页面没有动
比如百度图片搜索美女,进度条越变越短,可以一直往下拉
二、maven主要有什么作用
1.依赖管理(管理jar包)
2.构建管理(清理、编译、打包等)
三、 HTTP协议
超文本传输协议,基于TCP协议,用于在Web浏览器和Web服务器之间传输HTML页面、图像、视频、音频和其他类型的文件。
HTTP请求由请求行、请求头和请求体组成。
请求行包含请求方法、URL和HTTP协议版本。
请求头包含一些附加的信息,例如请求的主机名、用户代理、cookie等。
请求体包含客户端发送的数据,例如表单数据等。
只有POST有请求体。
HTTP响应由状态行、响应头和响应体组成。
状态行
包括协议版本号、状态码、结果描述;
响应头
Date: Sun, 17 Mar 2013 08:12:54 GMT
Server: Apache/2.2.8 (Win32) PHP/5.2.5
X-Powered-By: PHP/5.2.5
Set-Cookie: PHPSESSID=c0huq7pdkmm5gg6osoe3mgjmm3; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 4393
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html; charset=utf-8
date:当前GMT时间
server:告诉浏览器,服务器的型号
cinrynt-length告诉浏览器返回数据的长度
content-type:告诉浏览器返回数据的类型
expire:告诉浏览器将返回的资源缓存多长时间
refresh:告诉浏览器多久刷新一次
set-cookie:设置和页面关联的cookie
content-encoding:文档编码方式
allow:服务器支持哪些编码方式
响应体
返回的消息体,可以是html页面、js代码、纯数据。
转自http响应由几部分构成? - 简书
四、接收请求参数有哪些方式
1.原始方法,用HttpServletRequest对象接收,再调用getParameter方法。
2.Spring的简单的参数的方法
五、三层架构
控制层Controller
业务层Service
持久层Dao/Mapper
六、什么是IOC和DI
把创造对象的权利交给容器,就是IOC
有需要使用就注入,就是DI依赖注入
Spring Boot的IOC(Inversion of Control,控制反转)原理主要体现在将对象的创建和依赖关系的管理交给了容器来完成,而不是由开发人员手动管理。
在传统的程序设计中,对象之间的依赖关系由开发者在代码中直接管理,即对象自己创建和管理它所依赖的对象。而在IOC的思想下,控制权被反转,对象的创建和管理由容器来负责,对象只需要声明它所依赖的其他对象,容器会自动注入这些依赖。
具体来说,Spring IOC容器通过xml、注解等其它方式配置类及类之间的依赖关系,完成了对象的创建和依赖的管理注入。在Spring Boot中,推荐使用注解方式来实现IOC。
例如,通过@Bean
注解,可以在配置类中定义一个Bean,并指定其创建方法。Spring Boot会根据这个注解生成IOC容器,并在容器中注册这个Bean。同时,使用@Autowired
、@Inject
等注解,可以将Bean注入到其他需要的地方。
IOC的核心思想是将对象之间的依赖关系交给容器来管理,这样可以降低对象之间的耦合度,提高代码的可维护性和可扩展性。通过使用IOC容器,开发者可以将关注点从对象的创建和依赖管理中解脱出来,更专注于业务逻辑的实现。同时,IOC容器也提供了更灵活的配置方式,可以根据不同的环境和需求进行配置,使系统更易于扩展和维护。
七、MySQL表间关系
一对一、一对多、多对多
一对一
在一个表中用外键关联另一个表
一对多
在多方建立外键,和一个的表中建立联系
多对多
在中间表中建立外键,分别关联主键
八、内连接和外连接查询有什么区别
内连接
获取两表的交集部分
外连接
获取某表的所有数据,以及两表的交集数据
九、事务管理的作用,四大特性
作用
保证多个增删改的操作,要么同时成功,要么同时失败
四大特性
1.原子性
事务是最小单位原子,同成同败
2.一致性
理解1:数据变化、保存一致 eg 银行里存500得500
理解2:预期值和实际值一致
3.隔离性
多个事务之间不要产生影响
4.持久性
操作最终会持久化到数据库中
十、JWT令牌的作用和组成
用来作为登录验证的标记,防篡改
3部分
头部Header,负载Payload,签名Signature
Header
算法和模型
Payload
存储有关用户的数据
Signature
结合前两部分以及一个密钥生成
十一、Spring事务管理如何实现
加@Transactional注解
可以加的范围:方法、类、接口
可自动处理运行时异常
遇到编译时异常,需要回滚,加rollbackFor=异常类.class
事务相互调用,要用到事务传播行为,加propagation=Propagation枚举,会俩SUPPORTS和NOT_SUPPORT就行。
十二、事务传播行为是什么
事务之间相互调用,互相传播行为的行为。
会俩,SUPPORTS和NOT_SUPPORT。
举例小峰老师请吃饭。
7种事务传播机制:
以下事务传播机制都用这个例子解释:有A方法,B方法两个方法,A调用B
(如果A、B中都没有事务,那就不存在什么事务传播机制了,所以其中一个或都有事务才讨论事务传播机制)
方法A是一个事务的方法,方法A执行过程中调用了方法B,那么方法B有无事务以及方法B对事务的要求不同都会对方法A的事务具体执行造成影响,同时方法A的事务对方法B的事务执行也有影响,这种影响具体是什么就由两个方法所定义的事务传播类型所决定。
REQUIRED(Spring默认的事务传播类型 required:需要、依赖、依靠):如果当前没有事务,则自己新建一个事务,如果当前存在事务则加入这个事务
当A调用B的时候:如果A中没有事务,B中有事务,那么B会新建一个事务;如果A中也有事务、B中也有事务,那么B会加入到A中去,变成一个事务,这时,要么都成功,要么都失败。(假如A中有2个SQL,B中有两个SQL,那么这四个SQL会变成一个SQL,要么都成功,要么都失败)
SUPPORTS(supports:支持;拥护):当前存在事务,则加入当前事务,如果当前没有事务,就以非事务方法执行
如果A中有事务,则B方法的事务加入A事务中,成为一个事务(一起成功,一起失败),如果A中没有事务,那么B就以非事务方式运行(执行完直接提交);
MANDATORY(mandatory:强制性的):当前存在事务,则加入当前事务,如果当前事务不存在,则抛出异常。
如果A中有事务,则B方法的事务加入A事务中,成为一个事务(一起成功,一起失败);如果A中没有事务,B中有事务,那么B就直接抛异常了,意思是B必须要支持回滚的事务中运行
REQUIRES_NEW(requires_new:需要新建):创建一个新事务,如果存在当前事务,则挂起该事务。
B会新建一个事务,A和B事务互不干扰,他们出现问题回滚的时候,也都只回滚自己的事务;
NOT_SUPPORTED(not supported:不支持):以非事务方式执行,如果当前存在事务,则挂起当前事务
被调用者B会以非事务方式运行(直接提交),如果当前有事务,也就是A中有事务,A会被挂起(不执行,等待B执行完,返回);A和B出现异常需要回滚,互不影响
NEVER(never:从不): 如果当前没有事务存在,就以非事务方式执行;如果有,就抛出异常。就是B从不以事务方式运行
A中不能有事务,如果没有,B就以非事务方式执行,如果A存在事务,那么直接抛异常
NESTED(nested:嵌套的)嵌套事务:如果当前事务存在,则在嵌套事务中执行,否则REQUIRED的操作一样(开启一个事务)
如果A中没有事务,那么B创建一个事务执行,如果A中也有事务,那么B会把事务嵌套在里面。
十三、AOP是什么,有什么用?有哪些核心概念
不通过修改源代码方式,在主干功能里面添加新功能
AOP(Aspect-Oriented Programming,面向切面编程) 是一种编程范式,旨在提高软件系统的模块化。它通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP 是对 OOP(Object-Oriented Programming,面向对象编程)的补充和完善。
在 AOP 中,切面(Aspect)是一个关注点的模块化,这个关注点可能会横切多个对象。切面用来封装横切多个关注点的通用功能。切面通常与类、接口或方法相关联,并在这些实体执行特定操作(如方法调用前后、异常抛出时等)时触发。
AOP 中的核心概念包括:
- 切面(Aspect):切面是 AOP 的核心概念,它是一个横切关注点的模块化,可以横切多个对象。切面由通知和切点组成。
- 通知(Advice):通知定义了切面在什么时候以及如何应用其增强功能。通知可以分为以下几种类型:
- 前置通知(Before):在目标方法调用之前执行。
- 后置通知(After):在目标方法调用之后执行(无论方法是否成功执行)。
- 返回通知(After-returning):在目标方法成功执行后返回结果时执行。
- 异常通知(After-throwing):在目标方法抛出异常时执行。
- 环绕通知(Around):在目标方法调用前后都执行,并且可以控制目标方法的调用。
- 切点(Pointcut):切点用于定义通知应该应用到哪些连接点。连接点是应用执行过程中能够插入切面的点,通常是方法的调用。
- 连接点(Joinpoint):连接点是程序执行过程中能够应用通知的所有点。在 Spring AOP 中,连接点通常指方法的调用。
- 引入(Introduction):引入允许我们向现有的类添加新方法或属性。
- 织入(Weaving):织入是将切面应用到目标对象并创建新的代理对象的过程。织入可以在编译时、类加载时或运行时完成。
在 Spring 框架中,AOP 主要通过 AspectJ 来实现。AspectJ 是一个面向切面的编程框架,它扩展了 Java 语言以提供横切关注点(cross-cutting concerns)的模块化。Spring AOP 是对 AspectJ 的一个简化,它允许你在 Spring 应用程序中定义切面、通知和切点,并通过 Spring 的 IoC 容器来管理这些切面。
十四、SpringBoot自动配置原理
springboot自动配置原理就是当spring容器启动时,一些配置类、bean对象就自动存入
IOC容器中,不需要我们手动声明,从而简化了开发,省去了繁琐的配置操作。
Spring Boot 的自动装配原理是 Spring Boot 框架中一个非常关键的功能,它大大简化了 Spring 应用程序的开发过程。以下是 Spring Boot 自动装配原理的简要概述:
-
@SpringBootApplication 注解:
- 这是 Spring Boot 的核心注解,通常放在主应用程序类上。
- 它实际上是一个组合注解,包含了
@SpringBootConfiguration
、@EnableAutoConfiguration
和@ComponentScan
三个注解。 @SpringBootConfiguration
:声明当前类是一个配置类,用于定义 Bean。@EnableAutoConfiguration
:启用 Spring Boot 的自动配置功能。这是自动装配的核心注解,告诉 Spring Boot 根据添加的 jar 依赖自动配置你的项目。@ComponentScan
:告诉 Spring 从哪个包开始扫描组件、配置和自动装配 Bean。
-
自动配置的实现:
- Spring Boot 在启动时,会加载
META-INF/spring.factories
文件。这个文件包含了所有自动装配的类。 - Spring Boot 会根据
spring.factories
文件中的配置和类路径下的条件注解(如@ConditionalOnClass
、@ConditionalOnMissingBean
、@ConditionalOnProperty
等)来决定是否加载和装配某个 Bean。 - 条件注解允许 Spring Boot 根据类路径、Bean 的存在性、属性设置等条件来决定是否创建和配置 Bean。
- Spring Boot 在启动时,会加载
-
JavaConfig:
- Spring Boot 支持基于 JavaConfig 的方式来配置 Bean,这使得配置更加灵活和易于维护。
@Configuration
注解用于声明一个类作为配置类,该类中可以定义 Bean。@Bean
注解用于在配置类中声明一个 Bean,并指定其创建方法。
-
组件扫描:
- 通过
@ComponentScan
注解,Spring Boot 可以自动扫描指定包下的组件(如@Component
、@Service
、@Repository
、@Controller
等),并将它们注册为 Spring 的 Bean。 - 这使得开发者无需手动在 XML 配置文件中声明每个 Bean,大大简化了配置过程。
- 通过
-
属性配置:
- Spring Boot 支持多种属性配置方式,如 application.properties 或 application.yml 文件、命令行参数、环境变量等。
- 这些属性可以在应用程序中通过
@Value
注解或@ConfigurationProperties
注解注入到 Bean 中,从而实现灵活的配置管理。
-
内嵌容器:
- Spring Boot 提供了内嵌的容器(如 Tomcat、Jetty、Undertow 等),使得开发者无需单独配置和部署 Web 服务器即可运行 Web 应用程序。
- 这进一步简化了 Spring Boot 应用程序的部署和运维过程。
总之,Spring Boot 的自动装配原理是通过 @SpringBootApplication
注解、spring.factories
文件、条件注解、JavaConfig、组件扫描和属性配置等多种机制共同作用实现的。这些机制使得 Spring Boot 应用程序的开发、配置和部署过程更加简单、高效和灵活。
十五、动态代理
在Spring Boot中,动态代理是AOP(面向切面编程)的一种实现方式,它允许在运行时动态地创建代理类,以在不修改原始类代码的情况下,为其添加额外的功能或行为。Spring Boot主要使用两种动态代理技术:JDK动态代理和CGLIB。
-
JDK动态代理:
- JDK动态代理主要基于接口实现,要求被代理的类必须实现一个或多个接口。
- 在运行时,JVM会根据接口和提供的InvocationHandler来动态地生成代理类的字节码,并加载到JVM中。
- JDK动态代理主要使用
java.lang.reflect.Proxy
类和java.lang.reflect.InvocationHandler
接口来实现。 - 优点:灵活性高,可以根据接口动态生成代理类,无需手动编写代理类代码。
- 缺点:只能对实现了接口的类进行代理,不能对实现类的具体方法进行代理。
-
CGLIB(Code Generation Library):
- CGLIB是一个高性能、高质量的代码生成库,它可以扩展Java类或实现Java接口。
- 对于没有实现接口的类,Spring Boot会使用CGLIB来创建代理。
- CGLIB通过继承被代理类来创建代理对象,因此它可以在运行时动态地扩展类的功能。
- 优点:可以对任何类进行代理,无需实现接口。
- 缺点:由于是通过继承实现的代理,因此不能代理final类或被final方法修饰的方法。
在Spring Boot中,动态代理通常用于实现诸如日志记录、事务管理、安全控制等横切关注点。这些横切关注点可以跨越多个类或方法,使用动态代理可以在不修改原始代码的情况下,为这些类或方法添加额外的功能或行为。
此外,Spring Boot还提供了AOP的注解支持,如@Aspect
、@Pointcut
、@Before
、@After
等,这些注解可以方便地定义切面、切点和通知,从而简化AOP的开发过程。
苍穹外卖具体技术细节
一、Nginx的作用
1.反向代理
前端把请求发送给nginx,再由nginx将请求发送给后端服务器。
2.负载均衡
提高访问速度;进行负载均衡;保证后端服务安全
二、Swagger有什么作用
直接调试后端请求响应
三、Redis常见数据类型
String、Hash、List、Set、Zset
四、Redis和mysql有什么区别
1.数据库类型不同
(1)MySQL是关系型数据库;
(2)Redis是缓存数据库/非关系型数据库
2.数据存放位置不同
(1)MySQL的数据存在磁盘中
(2)Redis的数据存在内存中
3.应用场景不同
(1)MySQL存放在硬盘中,数据读取要I/O操作,速度慢,适合持久化的数据存取;
(2)Redis存放在内存中,用CPU读取,非常快,适合热点数据的存取。
4.存放数据类型不同
(1)MySQL:数值、日期/时间、字符串
(2)Redis:String、Hash、List、Set、Zset
五、HTTPClient的作用
发送HTTP请求
接收响应数据
六、SpringCache有哪些注解
@EnableCaching 加在启动类上开启
@Cacheable 放要缓存的查询上,快速查询redis,没有则查询后加入redis
@CachePut 放新增上,将结果放入redis
@CacheEvict 放删上,去掉一条;放改上,去掉全部重新记
七、SpringTask的作用,cron表达式有那些域
作用
任务调度,在特定时间执行指定的Java代码,定时做任务。
哪些域
秒 分 时 日 月 周 年
SpringTask只支持前6个域,不支持年的域。然后用*表示每个单位都执行,比如每日、每秒等;?表示冲突时的缺省,比如日月和周的冲突;/10表示每10个单位,比如每10分钟、每10个月等。
八、Websocket是什么
实现双向通信的一个网络协议。
九、POI的作用?
操作Java读写Office,项目中主要是Excel文件。
十、mysql导出大量数据
不能将全量数据一次性加载到内存之中。
全量加载不可行,那我们的目标就是如何实现数据的分批加载了。实事上,Mysql本身支持Stream查询,我们可以通过Stream流获取数据,然后将数据逐条刷入到文件中,每次刷入文件后再从内存中移除这条数据,从而避免OOM。
由于采用了数据逐条刷入文件,而且数据量达到百万级,所以文件格式就不要采用excel了,excel2007最大才支持104万行的数据。这里推荐:
以csv代替excel。
考虑到当前SpringBoot持久层框架通常为JPA和mybatis,我们可以分别从这两个框架实现百万级数据导出的方案。
MyBatis实现百万级数据导出
MyBatis实现逐条获取数据,必须要自定义ResultHandler
,然后在mapper.xml文件中,对应的select语句中添加fetchSize="-2147483648"
。
最后将自定义的ResultHandler传给SqlSession来执行查询,并将返回的结果进行处理。
1.我们先定义一个工具类DownloadProcessor
,它内部封装一个HttpServletResponse
对象,用来将对象写入到csv。
2.然后通过实现org.apache.ibatis.session.ResultHandler
,自定义我们的ResultHandler
,它用于获取java对象,然后传递给上面的DownloadProcessor
处理类进行写文件操作:
3.Mapper xml文件核心片段,以下两条select的唯一差异就是在stream获取数据的方式中多了一条属性:fetchSize="-2147483648"
4.获取数据的核心service如下,由于只做个简单演示,就懒得写成接口了。其中 streamDownload
方法即为stream取数据写文件的实现,它将以很低的内存占用从MySQL获取数据;此外还提供traditionDownload
方法,它是一种传统的下载方式,批量获取全部数据,然后将每个对象写入文件。
苍穹外卖业务逻辑
一、员工登录流程
1.前端在登录页面登录,发送请求
2.进入拦截器,拦截器放行所有登录页面的请求
3.进入三层架构,查询用户是否存在,若存在,则加盐加密,返回JWT的token,存放在请求头部。用户不存在,则不能登录。
二、登录验证如何实现的
1.试图访问任意非登录界面,前端发送请求
2.进入拦截器,开始拦截验证JWT。
3.校验成功,则进入访问界面。否则跳到登录界面。
三、项目开发有哪些角色
项目经理
对整个项目分工,负责进度把控。
可能会用到项目管理软件,比如禅道、ones。
除了项目经理,第二牛气的:
产品经理
进行需求调研分析,输出需求调研文档、产品原型。
跟上面领导打交道比较多,但不是领导胜似领导,只是一个岗位名称。意思就是,我出需求,你来实现,是提要求的,所以一般就牛气一点。
(出需求的产品经理,是程序员的天敌)
UI设计师
根据产品原型输出界面效果图。
不一定是小姐姐的哦。
架构师
项目整体架构设计,即项目整体结构的搭建,还有技术选型等。
比如架构是选微服务还是单体,用什么技术,设计什么模块,
以及一些棘手的问题,都是架构师去做。
架构师不一定很牛,有牛逼的也有菜的。有些大公司,有小组,其中就有架构师组。里面的,不一定都是你能佩服到高山仰止的哦~
开发工程师
代码实现。
喏,苦逼打代码的来了。
测试工程师
编写测试用例,输出测试报告。
运维工程师
软件环境搭建,运行项目。
其他
在公司内可能还有其他细化方向,比如还有DBA,即数据库管理员。
他们就只写SQL语句。
四、编辑员工的流程
1.完善之前登录时存储员工id到线程中。
2.完成回显功能,即根据id查询出员工。
3.完成修改功能,即update员工数据。
五、公共字段自动填充实现流程
1.确定使用AOP技术。
2.在com.sky.annotation包下创建自定义注解,比如叫AutoFill
3.用一个枚举类代表更新和新增两种状态,并放入自定义注解中。
4.在com.sky.aspect包下自定义切面类
5.完善切面类的自动填充逻辑,判断更新、新增两种状态,进行不同处理。
6.在mapper中对应的方法上加自定义注解
六、新增菜品的流程
1.实现文件上传。
(1)在配置文件中引入自己的阿里云。
(2)写一个类,比如AliOssProperties,用来读取配置文件。
(3)写一个工具类,用来上传文件。
(4)用一个配置类,比如OssConfiguration,来生成OSS工具类对象。
(5)在三层架构中注入、调用实现文件上传。其中要用UUID随机生成文件名。
2.实现新增菜品的文本细节新增。注意要增入两个表,一个dish,一个dish_flavor。
七、删除菜品的流程
注意业务层分四步:
1.判断是否起售
2.判断是否被套餐关联
3.删除菜品表中数据
4.删除菜品关联的口味数据
八、修改菜品的流程
首先实现根据id查找
然后实现修改
注意修改口味时要先删除原有口味,再单个for循环插入,或多个一起插入。
九、微信登录流程
1.controller层接收前端传过来的授权码code,调用service层,查找授权码是否通过校验。
2.service层校验授权码是否有效,无效则抛出异常;有效,则调用mapper层查询用户。
3.mapper层查找已存在用户并返回service值。
4.service中,若用户存在,直接返回;若不存在,完成用户的自动注册,并返回用户给controller层。
5.controller拿到返回用户,即用户存在或注册好后,封装响应对象,最后返回给前端。
总结3层架构:
controller层:
1.用DTO接收授权码
2.交给service层校验授权码是否有效,拿到返回值用户
3.将返回用户封装给VO,return给前端
service层:
1.准备参数,利用微信第三方,校验授权码是否有效
2.解析响应参数,获取openid。如果为空,说明校验无效,抛异常
3.根据openid,交给mapper查询user列表,如果没有对应的用户,完成自动注册;
4.返回用户
mapper层:
根据openid,查询是否存在用户
十、缓存菜品流程
1.在service层添加代码。添加代码如下:
1.1查询redis。
1.2如果redis中没有数据,则用mapper查询出数据。有则直接跳到1.4步
1.3将mapper查出的数据存入redis。
1.4返回数据
十一、添加购物车流程
1.前端传给controller层菜品/套餐与口味等。
2.controller层交给service层进行添加到购物车。
2.1判断购物车中是否已经存在。调用mapper层,select一下。
2.2如果存在,则只修改数量,交给mapper update一下数量。
2.3如果不存在,则分类添加菜品or套餐给shopcart对象。
2.3.1如果是菜品,查出菜品以及id、图片等
2.3.2如果是套餐,查出套餐以及id、图片等。
2.4添加shopcart对象到数据库,mapper insert进去。
3.mapper层照上操作
十二、用户下单流程
1.查询地址
2.查询购物车
3.封装订单,并添加一条
4.封装订单详情,有可能添加多条
5.清空购物车
6.封装VO并返回
十三、微信支付流程
1.调用微信下单接口;
2.返回预支付交易标识;
3.将组合数据再次签名;
4.推送支付结果;
5.更新订单状态。
十四、来单提醒和用户催单实现流程
来单提醒:
在提交订单的业务层代码里加入WebSocket的注入,然后传输带订单信息的map。
用户催单:
在三层架构里面写催单功能,也用WebSocket来注入、实现双向通信。
十五、导出运营数据实现流程
读取Excel模版到内存中。
准备运营数据
将数据写到Excel模板中。
将Excel文档响应回浏览器(文件下载)
注意的点
ClassLoader能加载的文件位置
ClassLoader能加载的文件位置在resources下。
放入resources后需要的操作
需要用maven构建管理的complie编译一下,才能保证类加载器ClassLoader加载到。
创建的POI与Office对应的下标
下标中getRow(0)与getCell(1)对应的分别是第一列第2行的数据