❝问:linux中怎么看日志,怎么看进程,怎么看磁盘大小,怎么看内存大小❞
#看日志
tail -f xx.log
#看进程
jps / ps -ef | grep xx /netstat -tnlp | grep xx
#看磁盘大小
du / df
#看内存大小
free
更多Linu命令见文章:我在工作中用到的Linux命令
❝问:Spring事务 A,B 。A调B, A异常,B会回滚么❞
「事务传播类型:」
务传播行为类型 | 说明 |
|---|---|
PROPAGATION_REQUIRED | 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。 |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行。 |
PROPAGATION_MANDATORY | 使用当前的事务,如果当前没有事务,就抛出异常。 |
PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。 |
默认传播类型为:PROPAGATION_REQUIRED,上面问题,A,B会合并成一个事务,所以A异常,B会回滚,B异常,A会回滚。

❝问:Spring中 @Autowired与@Resource区别❞
推荐文章:https://mp.weixin.qq.com/s/IglQITCkmx7Lpz60QOW7HA
❝问:单体服务到微服务的演变史❞
演变:单体服务 -> SOA -> 微服务
「单体服务」:
概念:所有功能全部打包在一起。应用大部分是一个war包或jar包。我参与网约车最开始架构是:一个乘客项目中有 用户、订单、消息、地图等功能。随着业务发展,功能增多,这个项目会越来越臃肿。
好处:容易开发、测试、部署,适合项目初期试错。
坏处:
随着项目越来越复杂,团队不断扩大。坏处就显现出来了。
「SOA」:
对单体应用的改进:引入SOA(Service-Oriented Architecture)面向服务架构,拆分系统,用服务的流程化来实现业务的灵活性。服务间需要某些方法进行连接,面向接口等,它是一种设计方法,其中包含多个服务, 服务之间通过相互依赖最终提供一系列的功能。一个服务 通常以独立的形式存在于操作系统进程中。各个服务之间 通过网络调用。但是还是需要用些方法来进行服务组合,有可能还是个单体应用。
所以要引入微服务,是SOA思想的一种具体实践。
微服务架构 = 80%的SOA服务架构思想 + 100%的组件化架构思想。
「微服务」:
那么微服务有哪些「特性」呢:
独立运行在自己进程中。
一系列独立服务共同构建起整个系统。
一个服务只关注自己的独立业务。
轻量的通信机制RESTful API。
使用不同语言开发。
全自动部署机制
「微服务优点」
「微服务缺点」
「什么是刚性事务?」
刚性事务:遵循ACID原则,强一致性。
柔性事务:遵循BASE理论,最终一致性;与刚性事务不同,柔性事务允许一定时间内,不同节点的数据不一致,但要求最终一致。
BASE 是 Basically Available(基本可用)、Soft state(软状态)和 Eventually consistent (最终一致性)三个短语的缩写。BASE理论是对CAP中AP的一个扩展,通过牺牲强一致性来获得可用性,当出现故障允许部分不可用但要保证核心功能可用,允许数据在一段时间内是不一致的,但最终达到一致状态。满足BASE理论的事务,我们称之为“柔性事务”。
关于如何设计划分服务,我觉得可以学习下DDD领域驱动设计,有很好的指导作用。
❝问:AOP怎么实现的Redis缓存注解❞
「1.定义注解」
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomizeCache {
String key();
String value();
long expireTimes() default 120L; //默认过期时间120s
int semaphoreCount() default Integer.MAX_VALUE; //默认限制线程并发数
}
「2.AOP切面编程」
@Component
@Aspect
@Slf4j
public class CacheAspect {
@Autowired
private RedisTemplate redisTemplate;
@Pointcut("@annotation(com.lvshen.demo.redis.cache.CustomizeCache)")
public void cachePointcut() {
}
@Around("cachePointcut()")
public Object doCache(ProceedingJoinPoint point) {
Object value = null;
Semaphore semaphore = null;
MethodSignature signature = (MethodSignature) point.getSignature();
try {
//获取方法上注解的类容
Method method = point.getTarget().getClass().getMethod(signature.getName(), signature.getMethod().getParameterTypes());
CustomizeCache annotation = method.getAnnotation(CustomizeCache.class);
String keyEl = annotation.key();
String prefix = annotation.value();
long expireTimes = annotation.expireTimes();
int semaphoreCount = annotation.semaphoreCount();
//解析SpringEL表达式
SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression(keyEl);
StandardEvaluationContext context = new StandardEvaluationContext();
//添加参数
Object[] args = point.getArgs();
DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
String[] parameterNames = discoverer.getParameterNames(method);
for (int i = 0; i < parameterNames.length; i++) {
context.setVariable(parameterNames[i], args[i].toString());
}
//解析
String key = prefix + "::" + expression.getValue(context).toString();
//判断缓存中是否存在
value = redisTemplate.opsForValue().get(key);
if (value != null) {
log.info("从缓存中读取到值:{}", value);
return value;
}
//自定义组件,如:限流,降级。。。
//创建限流令牌
semaphore = new Semaphore(semaphoreCount);
boolean tryAcquire = semaphore.tryAcquire(3000L, TimeUnit.MILLISECONDS);
if (!tryAcquire) {
//log.info("当前线程【{}】获取令牌失败,等带其他线程释放令牌", Thread.currentThread().getName());
throw new RuntimeException(String.format("当前线程【%s】获取令牌失败,等带其他线程释放令牌", Thread.currentThread().getName()));
}
//缓存不存在则执行方法
value = point.proceed();
//同步value到缓存
redisTemplate.opsForValue().set(key, value, expireTimes, TimeUnit.SECONDS);
} catch (Throwable t) {
t.printStackTrace();
} finally {
if (semaphore == null) {
return value;
} else {
semaphore.release();
}
}
return value;
}
}
「3.使用」
@CustomizeCache(value = "member", key = "#name")
public List<Member> listByNameSelfCache(String name) {
return memberMapper.listByName(name);
}
❝问:Spring Boot注解❞
「@EnableAutoConfiguration」:是自动配置的注解;
「@Configuration」:用于定义配置类;
「@ConditionalOnBean(A.class)」:仅仅在当前上下文中存在A对象时,才会实例化一个Bean;
关于Conditional开头的注解还有很多,有兴趣的可以去Spring官网:https://spring.io/projects/spring-boot
或者SpringBoot中文社区看看:https://springboot.io/
❝问:Bean的生命周期❞
简略的说,单列Bean生命随容器存在而存在。非单例的Bean不引用时就会被垃圾回收器回收。

❝问:AOP原理❞
AOP的思想是,不去动原来的代码,而是基于原来代码产生代理对象,通过代理的方法,去包装原来的方法,就完成了对以前方法的增强。AOP的底层原理就是动态代理的实现。
关于AOP的使用,比如我之前用AOP思想做的缓存注解等。
「切入点」:通过一个表达式告诉SpringAOP去哪个地方进行增强。也可以把这个表达式理解为一个查询条件,系统会根据这个查询条件查询到我们要进行增强的代码位置。
「连接点」:就是SpringAOP通过告诉它的切入点的位置找的的具体的要增强的代码的位置,这个代码位置就是连接点。
「切面」:切面由一组(增强处理和切入点)共同构成。
「目标对象」:目标对象就是被增强的目标类。我们也称之为委托类。
「AOP代理」:代理类就是AOP代理,里面包含了目标对象以及一些增强处理。系统会用AOP代理类代替委托类去执行功能。
「织入」:织入就是将我们的增强处理增强到指定位置的过程。
具体使用可以看看问题:「如何用AOP实现缓存注解」的代码。
❝问:Spring的动态代理❞
动态代理其实就是Java中的一个方法,这个方法可以实现:「动态创建一组指定的接口的实现对象(在运行时,创建实现了指定的一组接口的对象)」
分为JDK动态代理和Cglib动态代理
当目标对象实现了接口,默认使用JDK动态代理,也可以强制使用Cglib动态代理。
当目标对象没有实现接口,必须使用Cglib动态代理。
下面是代码:
public interface UserService {
void addUser(String name, String password);
void delUser(String name);
}
public class UserServiceImpl implements UserService{
@Override
public void addUser(String name, String password) {
System.out.println("调用addUser()...");
System.out.println(String.format("参数为:name[%s],password[%s]",name,password));
}
@Override
public void delUser(String name) {
System.out.println("调用delUser()");
System.out.println(String.format("参数为:name[%s]",name));
}
}
「JdkProxy」
public class JdkProxy implements InvocationHandler {
//需要代理的目标对象
private Object target;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("JDK动态代理,监听开始...");
Object invoke = method.invoke(target, args);
System.out.println("JDK动态代理,监听结束...");
return invoke;
}
public Object getJdkProxy(Object targetObject) {
this.target = targetObject;
//实例化
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),targetObject.getClass().getInterfaces(),this);
}
}
测试JdKProxy
@org.junit.Test
public void testJdkProxy() {
JdkProxy jdkProxy = new JdkProxy();
UserService userService = (UserService) jdkProxy.getJdkProxy(new UserServiceImpl());
userService.addUser("lvshen","123456");
}

「CglibProxy」
public class CglibProxy implements MethodInterceptor {
private Object target;
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("CGLIB动态代理,监听开始...");
Object invoke = method.invoke(target, objects);
System.out.println("CGLIB动态代理,监听结束...");
return invoke;
}
public Object getCglibProxy(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
//指定父类
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
Object result = enhancer.create();
return result;
}
}
测试CglibProxy
public class Test {
@org.junit.Test
public void testCglibProxy() {
CglibProxy cglibProxy = new CglibProxy();
UserService service = (UserService) cglibProxy.getCglibProxy(new UserServiceImpl());
service.addUser("zhouzhou","654321");
}
}

❝问:Spring与SpringBoot的区别❞
「SpringBoot特点」:
❝问:springboot中bootstrap.properties与application.properties的区别❞
「区别:」
application.properties(application.yml)系统级别的一些参数配置,这些参数一般是不会变动的bootstrap.properties(bootstrap.yml)定义应用级别的配置在「SpringBoot」 有两种上下文:
「bootstrap」 加载优先于 applicaton
「bootstrap」 里面的属性会优先加载,默认也不能被本地相同配置覆盖
「应用场景:」
spring.application.name和 spring.cloud.config.server.git.uri❝问:applicationContext与beanFactory的区别❞
两者都能获取bean.
beanFactory:懒加载,调用getBean是才实例化对象
applicationContext:预加载,启用applicationContext就实例化对象了
ApplicationContext 包含 BeanFactory 的所有特性,通常推荐使用前者。但是也有一些限制情形,比如移动应用内存消耗比较严苛,在那些情景中,使用更轻量级的 BeanFactory 是更合理的。然而,在大多数企业级的应用中,ApplicationContext 是你的首选。
public class HelloWorldApp {
public static void main(String[] args) {
XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("beans.xml"));
HelloWorld obj = (HelloWorld) factory.getBean("helloWorld");
obj.getMessage();
}
}
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
HelloWorld obj = (HelloWorld) context.getBean("helloWorld");
obj.getMessage();
}
❝问:Mybatis中 #{} 与 ${}的区别❞
使用#{parameterName}引用参数的时候,Mybatis会把这个参数认为是一个字符串,并自动加上'',例如传入参数是“Smith”,那么在下面SQL中:
Select * from emp where name = #{employeeName}
使用的时候就会转换为:
Select * from emp where name = 'Smith';
同时使用${parameterName}的时候在下面SQL中
Select * from emp where name = ${employeeName}
就会直接转换为:
Select * from emp where name = Smith
简单说#{}是经过预编译的,是安全的。而${}是未经过预编译的,「仅仅是取变量的值,是非安全的,存在SQL注入」。
sql注入问题:
当使用#{}时
DEBUG [http-nio-8080-exec-5] - ==> Preparing: select * from user where account = ? and password = ? DEBUG [http-nio-8080-exec-5] - ==> Parameters: 20200801(String), 111111 or account = 'admin' (String) DEBUG [http-nio-8080-exec-5] - <== Total: 0 返回结果:null
当使用${}时
DEBUG [http-nio-8080-exec-5] - ==> Preparing: select * from user where account = ? and password = ? DEBUG [http-nio-8080-exec-5] - ==> Parameters: 201301001(String), 111111 or account = 'admin' (String) DEBUG [http-nio-8080-exec-5] - <== Total: 0 转换为实际的SQL语句:select * from user where account = '20200801' and password = '111111 or account = 'admin''
❝问:介绍下DDD领域驱动设计,是说的什么,里面分为哪些模块❞
这个说起来比较复杂。这种设计模式更加趋近于现实世界的状态,要求我们写代码时要区分业务代码与非业务代码。

推荐文章:https://www.cnblogs.com/cuiqq/p/10961337.html
❝问:设计模式分为哪种类❞

❝问:充血模型与贫血模型❞
一、贫血模型
所谓贫血模型,是指Model 中,仅包含状态(属性),不包含行为(方法),采用这种设计时,需要分离出DB层,专门用于数据库操作。
@Data
public class Employee {
public string Id ;
public string Name ;
public string Sex ;
public DateTime? BirthDay;
/// 直属上级的Id
public string ParentId ;
}
//实现方法略
public class EmpDAO {
public static bool AddEmployee(Employee emp);
public static bool UpdateEmployee(Employee emp);
public static bool DeleteEmployee(Employee emp);
public static Employee GetEmployeeById(string Id);
}
二、充血模型
Model 中既包括状态,又包括行为,是最符合面向对象的设计方式。
@Data
public class Employee {
public string Id ;
public string Name ;
public string Sex ;
public DateTime;
/// 直属上级的Id
public string ParentIdl;
private Employee _parent;
public static Employee query(string id){
Employee emp = new Employee();
//实现略,仅需填充emp的熟悉即可
return emp;
}
//保存对象,实现略
public bool Save() {
return true;
}
// 删除对象,实现略
public bool Drop(){
return true;
}
}
❝问:手写单列模式❞
见文章:那些能让人秀出花的单列模式
public class LazySimpleSingleton {
private static volatile LazySimpleSingleton instance = null;
private LazySimpleSingleton(){
if (instance != null) {
throw new RuntimeException("该构造方法禁止获取");
}
}
public static LazySimpleSingleton getInstance() {
if (instance == null) {
synchronized (LazySimpleSingleton.class) {
if (instance == null) {
instance = new LazySimpleSingleton();
}
}
}
return instance;
}
}
❝问:手写冒泡排序❞
❝问:评测题目: 三个线程A、B、C,实现一个程序让线程A打印“A”,线程B打印“B”,线程C打印“C”, 三个线程输出ABCABCABC......ABC,循环10次“ABC”❞
见文章:阿里多线程面试题-按线程顺序输出
❝问:Description:给出有序数组(非递减)和闭区间, 找出数组中在区间之内的元素起始位置和结束位置
❝问:写一个二分查找算法❞
public static int getIndex(int[] arr, int key) {
int mid = arr.length / 2;
if (arr[mid] == key) {
return mid;
}
int start = 0;
int end = arr.length - 1;
while (start <= end) {
mid = (end - start) / 2 + start;
if (arr[mid] == key) {
return mid;
} else if (arr[mid] > key) {
end = mid - 1;
} else {
start = start + 1;
}
}
//找不到,返回-1
return -1;
}