大三后端暑期实习面经总结——SSM&微服务框架篇


博主现在大三在读,从三月开始找暑期实习,暑假准备去tx实习啦!总结下了很多面试真题,希望能帮助正在找工作的大家!相关参考都会标注原文链接,尊重原创!


目录

    • 1. mvc、mvp、mvvm
      • MVC架构
      • MVP模式
      • MVVM模式
    • 2. Java服务器有哪些
    • 3. 跨域及其解决方式
    • 4. MyBatis优缺点
    • 5. #{}和${}的区别是什么
    • 6. 简述Mybatis的插件运行原理,如何编写一个插件
    • 7. Spring是什么
    • 8. 对AOP的理解
    • 9. 对IoC的理解
    • 10. 如何实现一个IoC容器
    • 11. BeanFactory和ApplicationContext的区别
    • 12. Spring Bean的生命周期
    • 13. Spring支持的bean作用域
    • 14. Spring中的bean是线程安全的吗
    • 15. Spring中的设计模式及使用场景
    • 16. Spring事务的实现方式
    • 17. Spring事务隔离级别
    • 18. spring事务传播机制
    • 19. Spring事务什么时候失效
      • 1. 数据库引擎不支持事务
      • 2. 没有被 Spring 管理
      • 3. 方法不是 public 的
      • 4. 自身调用问题
      • 5. 数据源没有配置事务管理器
      • 6. 不支持事务
      • 7. 异常被吃了
      • 8. 异常类型错误
    • 20. 什么是bean的自动装配及实现方式
    • 21. spring、springmvc、springboot区别
    • 22. SpringMVC执行流程
    • 23. springmvc九大组件
    • 24. springboot自动配置原理
    • 25. 如何理解springboot的stater
    • 26. 编写一个求两数和的controller
    • 27. springboot实现热部署方式
    • 28. 什么是 ORM 框架?
    • 29. 为什么要使用 spring?
    • 30. spring 有哪些主要模块?
    • 31. @RequestMapping的作用
    • 32. mybatis一级&二级缓存


参考:

  • https://zhuanlan.zhihu.com/p/64147696
  • https://www.bilibili.com/video/BV1Eb4y1R7zd?p=2&t=4948
  • https://blog.csdn.net/hellojoy/article/details/103896422
  • https://www.cnblogs.com/zyndev/p/13697313.html

1. mvc、mvp、mvvm

MVC架构

MVC是Model-View-Controller的缩写,它将应用程序划分为三个部分:

  • Model: 模型,管理数据

  • View: 视图,展示数据

  • Controller: 控制器,处理数据

大三后端暑期实习面经总结——SSM&微服务框架篇_第1张图片

MVP模式

MVP是Model-View-Presenter的缩写,它从MVC衍生而来,从名称上看只是将C换成了P。其他都一样。而事实上呢?它们也确实这样,P承担了C的任务而已。

同MVC的区别是:它们两个的数据流向不一样
大三后端暑期实习面经总结——SSM&微服务框架篇_第2张图片

  • MVC框架中,V的数据从Model中拿

  • MVP框架中,V的数据从Presenter中拿。

MVVM模式

MVVM是Model-View-ViewModel的缩写,为了解决前端的响应式编程而生,由于前端网页混合了HTML、CSS和JavaScript,而且页面众多,代码的组织和维护难度复杂,所以通过ViewModel实现View和Model的双向绑定。

image-20210528083638325

2. Java服务器有哪些

Web服务器是运行及发布Web应用的容器,只有将开发的Web项目放置到该容器中,才能使网络中的所有用户通过浏览器进行访问。开发Java Web应用所采用的服务器主要是与JSP/Servlet兼容的Web服务器,比较常用的有Tomcat、Resin、JBoss、WebSphere 和 WebLogic 等,下面将分别进行介绍。

Tomcat

目前最为流行的Tomcat服务器是Apache-Jarkarta开源项目中的一个子项目,是一个小型、轻量级的支持JSP和Servlet 技术的Web服务器,也是初学者学习开发JSP应用的首选。

Resin

Resin是Caucho公司的产品,是一个非常流行的支持Servlet和JSP的服务器,速度非常快。Resin本身包含了一个支持HTML的Web服务器,这使它不仅可以显示动态内容,而且显示静态内容的能力也毫不逊色,因此许多网站都是使用Resin服务器构建。

JBoss

JBoss是一个种遵从JavaEE规范的、开放源代码的、纯Java的EJB服务器,对于J2EE有很好的支持。JBoss采用JML API实现软件模块的集成与管理,其核心服务又是提供EJB服务器,不包含Servlet和JSP的Web容器,不过它可以和Tomcat完美结合。

WebSphere

WebSphere是IBM公司的产品,可进一步细分为 WebSphere Performance Pack、Cache Manager 和WebSphere Application Server等系列,其中WebSphere Application Server 是基于Java 的应用环境,可以运行于 Sun Solaris、Windows NT 等多种操作系统平台,用于建立、部署和管理Internet和Intranet Web应用程序。

WebLogic

WebLogic 是BEA公司的产品,可进一步细分为 WebLogic Server、WebLogic Enterprise 和 WebLogic Portal 等系列,其中 WebLogic Server 的功能特别强大。WebLogic 支持企业级的、多层次的和完全分布式的Web应用,并且服务器的配置简单、界面友好。对于那些正在寻求能够提供Java平台所拥有的一切应用服务器的用户来说,WebLogic是一个十分理想的选择。


3. 跨域及其解决方式

什么是跨域?

要明白什么是跨域之前,首先要明白什么是同源策略

同源策略是一个重要的安全策略,用来限制从一个源加载的文档或脚本与来自另一个源的资源进行交互。它能帮助阻隔恶意文档,减少可能被攻击的媒介。

那怎样判断是否是同源呢?

  • 如果协议,端口(如果指定了)和主机对于两个页面是相同的,则两个页面具有相同的源,也就是同源

大三后端暑期实习面经总结——SSM&微服务框架篇_第3张图片

跨域,指的是浏览器不能执行其他网站的脚本,它是由浏览器的同源策略所造成的,是浏览器对于JavaScript所定义的安全限制策略。

1️⃣ 使用Filter方式进行设置

使用Filter过滤器来过滤服务请求,向请求端设置Response Header(响应头部)的Access-Control-Allow-Origin属性声明允许跨域访问。

@WebFilter
public class CorsFilter implements Filter {
       

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
       
        HttpServletResponse response = (HttpServletResponse) res;  
        response.setHeader("Access-Control-Allow-Origin", "*");  
        response.setHeader("Access-Control-Allow-Methods", "*");  
        response.setHeader("Access-Control-Max-Age", "3600");  
        response.setHeader("Access-Control-Allow-Headers", "*");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        chain.doFilter(req, res);  
    }  
}

2️⃣ 继承 HandlerInterceptorAdapter

@Component
public class CrossInterceptor extends HandlerInterceptorAdapter {
     

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
     
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "*");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        return true;
    }
}

3️⃣ 实现WebMvcConfigurer

@Configuration
@SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection")
public class AppConfig implements WebMvcConfigurer {
     

    @Override
    public void addCorsMappings(CorsRegistry registry) {
     
        registry.addMapping("/**")  // 拦截所有的请求
                .allowedOrigins("http://www.abc.com")  // 可跨域的域名,可以为 *
                .allowCredentials(true)
                .allowedMethods("*")   // 允许跨域的方法,可以单独配置
                .allowedHeaders("*");  // 允许跨域的请求头,可以单独配置
    }
}

4️⃣ 使用Nginx配置

location / {
     
   add_header Access-Control-Allow-Origin *;
   add_header Access-Control-Allow-Headers X-Requested-With;
   add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,OPTIONS;

   if ($request_method = 'OPTIONS') {
     
     return 204;
   }
}

5️⃣ 使用 @CrossOrgin 注解

如果只是想部分接口跨域,且不想使用配置来管理的话,可以使用这种方式

  • 在Controller使用

    @CrossOrigin
    @RestController
    @RequestMapping("/user")
    public class UserController {
           
    
    	@GetMapping("/{id}")
    	public User get(@PathVariable Long id) {
           
    		
    	}
    
    	@DeleteMapping("/{id}")
    	public void remove(@PathVariable Long id) {
           
    
    	}
    }
    
  • 在具体接口上使用

    @RestController
    @RequestMapping("/user")
    public class UserController {
           
    
    	@CrossOrigin
    	@GetMapping("/{id}")
    	public User get(@PathVariable Long id) {
           
    		
    	}
    
    	@DeleteMapping("/{id}")
    	public void remove(@PathVariable Long id) {
           
    
    	}
    }
    

6️⃣ Spring Cloud Gateway 跨域配置

spring: 
  cloud:
    gateway:
      globalcors:
        cors-configurations:
          '[/**]':
            // 允许跨域的源(网站域名/ip),设置*为全部
            // 允许跨域请求里的head字段,设置*为全部
            // 允许跨域的method,默认为GET和OPTIONS,设置*为全部
            allow-credentials: true
            allowed-origins:
              - "http://xb.abc.com"
              - "http://sf.xx.com"
            allowed-headers: "*"
            allowed-methods:
              - OPTIONS
              - GET
              - POST
              - DELETE
              - PUT
              - PATCH
            max-age: 3600

注意: 通过gateway 转发的其他项目,不要进行配置跨域配置

有时即使配置了也不会起作用,这时你可以根据浏览器控制的错误输出来查看问题,如果提示是 response 中 header 出现了重复的 Access-Control-* 请求头,可以进行如下操作

import java.util.ArrayList;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.NettyWriteResponseFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component("corsResponseHeaderFilter")
public class CorsResponseHeaderFilter implements GlobalFilter, Ordered {
     

  @Override
  public int getOrder() {
     
    // 指定此过滤器位于NettyWriteResponseFilter之后
    // 即待处理完响应体后接着处理响应头
    return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER + 1;
  }

  @Override
  public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
     
    return chain.filter(exchange).then(Mono.defer(() -> {
     
      exchange.getResponse().getHeaders().entrySet().stream()
          .filter(kv -> (kv.getValue() != null && kv.getValue().size() > 1))
          .filter(kv -> (
              kv.getKey().equals(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)
                  || kv.getKey().equals(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS)
                  || kv.getKey().equals(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS)
                  || kv.getKey().equals(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS)
                  || kv.getKey().equals(HttpHeaders.ACCESS_CONTROL_MAX_AGE)))
          .forEach(kv -> {
     
            kv.setValue(new ArrayList<String>() {
     {
     
              add(kv.getValue().get(0));
            }});
          });
      return chain.filter(exchange);
    }));
  }
}

4. MyBatis优缺点

优点

  1. 基于SQL语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响,SQL写在xml里,解除sql与程序代码的耦合,便于统一管理;提供xml标签,支持编写动态SQL语句,并可重用
  2. 与JDBC相比,减少了50%以上的代码量,消除了JDBC大量冗余的代码,不需要手动开关连接
  3. 很好的与各种数据库兼容,因为MyBatis使用JDBC来连接数据库,所以只要JDBC支持的数据库 MyBatis都支持
  4. 能够与Spring很好的集成
  5. 提供映射标签,支持对象与数据库的ORM字段关系映射;提供对象关系映射标签,支持对象关系组件维护

缺点

  1. SQL语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写SQL语句的功底有一定要求。
  2. SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。

5. #{}和${}的区别是什么

#{}是预编译处理,是占位符;${}是字符串替换,是拼接符

  • Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement来赋值
  • Mybatis在处理${}时,就是把${}替换成变量的值,调用Statement来赋值
  • #{}的变量替换是在DBMS中、变量替换后,#{}对应的变量自动加上单引号
  • ${}的变量替换是在DBMS外、变量替换后,${}对应的变量不会加上单引号
  • 使用#{}可以有效的防止SQL注入,提高系统安全性

6. 简述Mybatis的插件运行原理,如何编写一个插件

Mybatis的插件本质上指的就是Mybatis的拦截器,它只能拦截ParameterHandlerResultSetHandlerStatementHandlerExecutor四个接口,所以Mybatis拦截器实现的功能是很有限的。

拦截原理:Mybatis使用jdk的动态代理,为需要拦截的接口生成代理对象以实现接口方法的拦截功能,每当执行这四种接口对象的方法时,就会进入拦截方法,具体就是 InvocationHandler中的invoke()方法,拦截指定的方法

大三后端暑期实习面经总结——SSM&微服务框架篇_第4张图片


7. Spring是什么

1️⃣ 轻量级开源的J2EE框架

  • 它是一个容器框架,可以用来存放JavaBean对象
  • 它是一个中间层框架(万能胶),可以起连接作用,整合其他框架使用,让企业开发更快更简洁

2️⃣ 轻量级的控制反转(IoC)和面向切面(AOP)的容器框架

  • 从大小和开销来说spring都是轻量级
  • 通过控制反转IoC达到了松耦合的目的
  • 提供了面向切面编程AOP的丰富支持——允许通过分离应用的业务逻辑与系统级服务来进行内聚性的开发
  • 包含并管理应用JavaBean的配置和声明周期,这个意义上是一个容器
  • 将简单的组件配置组合成为复杂的应用,这个意义上是一个框架

8. 对AOP的理解

一个系统是由不同的组件组成的,每个组件都负责一块特定的业务,实现自己的核心功能;此外,这些组件可能还会有额外的任务。比如日志、事务管理、安全保障等业务,这些业务常常需要融入到不同组件的核心功能中,会跨越系统的多个组件,这些额外的需求在AOP中被称为横切关注点

当我们需要为这些分散的组件引入公共的需求的时候,OOP就显得无能为力,因为面向对象编程的思想会将这额外的业务定义成一个个业务类然后插入到每个组件的核心业务中,十分麻烦,会有大量代码的重复,不利于各个模块的复用。AOP就能很好的解决这些问题。

AOP:将程序中的交叉业务逻辑(比如安全、日志、事务等)封装成一个切面,然后注入到目标对象的具体业务逻辑中。AOP可以对某个对象或者某个对象的某些功能进行增强,比如对对象方法的增强,可以在方法的前后添加额外的功能

使用“横切”技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处都基本相似。比如权限认证、日志、事务处理。Aop 的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。正如Avanade公司的高级方案构架师Adam Magee所说,AOP的核心思想就是“将应用程序中的商业逻辑同对其提供支持的通用服务进行分离。”


9. 对IoC的理解

从三个方面来回答:容器概念控制反转依赖注入

1️⃣Ioc容器:本质上就是一个map(key-value),里面存放了各种对象:

  • xml中配置的bean结点

  • @Repository、@Service、@Controller、@Conponent标注的类

在项目启动的时候会读取配置文件中的的bean结点,扫描以上注解标时的类,根据全限定类名通过反射创建对象存放到map中;

2️⃣ 控制反转:没有引入IoC容器之前,对象A依赖于对象B,那么对象A在初始化或者运行到某一点的时候,自己必须主动去创建对象B或者使用已经创建的对象B。无论是创建还是使用对象B,控制权都在自己手上。

引入IoC容器之后,对象A与对象B之间失去了直接联系,当对象A运行到需要对象B的时候,IoC容器会主动创建一个对象B注入到对象A需要的地方。

通过前后的对比,不难看出来:对象A获得依赖对象B的过程,由主动行为变为了被动行为,控制权颠倒过来了,这就是“控制反转这个名称的由来

全部对象的控制权全部上缴给"第三方"IoC容器,所以IoC容器成了整个系统的关键核心,它起到了一种类似“粘合剂的作用,把系统中的所有对象粘合在一起发挥作用,如果没有这个“粘合剂,对象与对象之间会彼此失去联系,这就是有人把IoC容器比喻成“粘合剂"的由来。

3️⃣ 依赖注入:DI是IoC的一种实现方式,就是由IoC容器在运行期间,动态地将某种依赖关系注入到对象之中。


10. 如何实现一个IoC容器

大三后端暑期实习面经总结——SSM&微服务框架篇_第5张图片


11. BeanFactory和ApplicationContext的区别

ApplicationContextBeanFactory的子接口,体统了更完整的功能:

  • 继承MessageSource,因此支持国际化
  • 统一的资源文件访问方式
  • 提供在监听器中注册bean的事件
  • 同时加载多个配置文件
  • 载入多个(有继承关系)上下文,是的每一个上下文都专注于一个特定的层次,比如应用的web层

区别:

  • BeanFactory采用延迟加载的形式来注入Bean,即只有在使用到某个Bean,也就是调用getBean()方法时,在对Bean进行加载实例化。这样我们在容器启动的时候就不能发现spring存在的配置问题,比如Bean的某个属性没有注入,直到BeanFactory加载后第一次调用getBean()方法才会抛出异常
  • ApplicationContext在容器启动时一次性创建了所有的Bean。这样就能在容器启动的时候发现Spring中存在的配置问题,有利于检查所依赖属性是否注入。ApplicationContext启动后预载入所有的单实例Bean,通过预载入单实例Bean,确保需要使用的时候无需等待,因为已经创建好了
  • 相对于基本的BeanFactoryApplicationContext唯一的不足是占用内存空间,当应用程序配置了很多的Bean时,程序启动较慢
  • BeanFactory通常以编程的方式创建,ApplicationContext可以以声明的方式创建(使用ContextLoader)
  • 两者都支持后置处理器BeanPostProcessor、BeanFactoryPostProcessor,但区别是:BeanFactory需要手动注册,而ApplicationContext是自动注册

12. Spring Bean的生命周期

大三后端暑期实习面经总结——SSM&微服务框架篇_第6张图片

1️⃣ 实例化

  • 首先扫描路径解析类得到BeanDefinition,它描述了bean对象的一系列信息
  • 然后通过构造方法实例化出一个对象(如果由多个构造方法,需要推断构造方法)

2️⃣ 属性赋值

  • 对加了@Autowired等注解的对象的进行属性的赋值

3️⃣ 注入Aware接口

  • Spring会检测该对象是否实现了xxxAware接口,并将相关的xxxAware实例注入给bean

4️⃣ 前置处理

  • 当经过上述几个步骤后,bean对象已经被正确构造,但如果你想要对象被使用前再进行一些自定义的处理,就可以继承BeanPostProcessor接口实现其前置处理方法,这个方法会先于InitialzationBean执行,因此称为前置处理

    //当前正在初始化的bean对象会被传递进来,我们就可以对这个bean作任何处理。
    //这个函数会先于InitialzationBean执行,因此称为前置处理。
    postProcessBeforeInitialzation( Object bean, String beanName )
    

5️⃣ 初始化

  • 对bean实例进行初始化:InitializingBean

6️⃣ 后置处理

  • 在bean初始化完成后可以进行后置处理,它与前置处理不同,由于该函数并不会把当前bean对象传进来,因此在这一步没办法处理对象本身,只能增加一些额外的逻辑,AOP通常就在此实现

    //当前正在初始化的bean对象会被传递进来,我们就可以对这个bean作任何处理。
    //这个函数会在InitialzationBean完成后执行,因此称为后置处理。
    postProcessAfterInitialzation( Object bean, String beanName )
    

7️⃣ 使用bean

  • 如果bean是单例的,还会将其放入单例池然后使用

8️⃣ 销毁bean

  • Spring容器关闭时调用DisposableBeandestory()方法

13. Spring支持的bean作用域

当通过spring容器创建一个Bean实例时,不仅可以完成Bean实例的实例化,还可以为Bean指定特定的作用域。Spring支持如下5种作用域:

  • singleton:默认的单例模式,每个容器中只有一个bean实例,单例的模式由BeanFactory(第一次注入时创建)自身来维护。该对象的生命周期与Spring IoC容器一致
  • prototype:原型模式,为每一个bean都提供一个实例,每次注入都会创建一个对象
  • request:每个HTTP请求都会为bean创建一个实例,请求结束该实例回收
  • session:与request类似,每个session中有一个bean实例,session过期后该实例回收
  • application:在ServletContext的生命周期中复用一个单例对象
  • websocket:在websocket的声明周期中复用一个单例对象
  • global-session:每个全局的HTTP Session,使用session定义的Bean都将产生一个新实例。典型情况下,仅在使用portlet context的时候有效。同样只有在Web应用中使用Spring时,该作用域才有效

其中比较常用的是singleton和prototype两种作用域。对于singleton作用域的Bean,每次请求该Bean都将获得相同的实例。容器负责跟踪Bean实例的状态,负责维护Bean实例的生命周期行为;如果一个Bean被设置成prototype作用域,程序每次请求该id的Bean,Spring都会新建一个Bean实例,然后返回给程序。在这种情况下,Spring容器仅仅使用new 关键字创建Bean实例,一旦创建成功,容器不在跟踪实例,也不会维护Bean实例的状态。

如果不指定Bean的作用域,Spring默认使用singleton作用域。Java在创建Java实例时,需要进行内存申请;销毁实例时,需要完成垃圾回收,这些工作都会导致系统开销的增加。因此,prototype作用域Bean的创建、销毁代价比较大。而singleton作用域的Bean实例一旦创建成功,可以重复使用。因此,除非必要,否则尽量避免将Bean被设置成prototype作用域。


14. Spring中的bean是线程安全的吗

在Spring中,bean默认作用域是单例的,在spring容器中全局共享,框架并没有对bean进行多线程的封装,会存在多线程访问的问题,并不安全

首先搞清楚什么是有状态、什么是无状态

  • 有状态指的是有数据存储的功能
  • 无状态就是不会保存数据

如果Bean是有状态的,那么就需要由我们来进行线程安全的保证,最简单的方法就是改变bean的作用域,将singleton改为protopyte,这样每次请求bean就相当于new Bean(),这样就可以保证线程安全了

此外,还可以用ThreadLocal来确保安全,例如Dao层与数据库进行交互,会操作Connection对象,Connection对象就是带有状态的,比如数据库事务的操作,Spring的事务管理器就是使用ThreadLocal为不同线程维护了一套独立的connection副本来保证线程之间不会相互影响

不要在bean中声明任何有状态的实例变量或类变量,如果必须如此,那么就使用 Threadloca把变量变为线程私有的,如果bean的实例变量或类变量需要在多个线程之间共享,那么就只能使用 synchronized、Lock、CAS等这些实现线程同步的方法了。


15. Spring中的设计模式及使用场景

1️⃣ 简单工厂:由一个工厂类根据传入的参数,动态决定创建哪一个产品类

spring中的BeanFactory就是简单工厂模式的体现,根据传入一个唯一的标识来获得Bean对象,但是否是在传入参数后创建还是传入参数前创建这个要根据具体情况来定。


2️⃣ 工厂方法

实现了FactoryBean接口的bean是一类叫做factory的bean,其特点是:spring在使用getBean()获得bean时,会自动调用该类bean的getObject()方法,所以返回的不是这个bean,而是bean.getObject()的返回值


3️⃣ 适配器模式

spring定义了一个适配接口,使得每一种Controller有一种对应的适配器实现类,让适配器代替controller执行相应的方法。这样在扩展 Controller时,只需要增加一个适配器类就完成了SpringMVC的扩展了


4️⃣ 装饰器模式:动态地给一个对象添加一些额外的职责。就増加功能来说,Decorator模式相比生成子类更为灵活。

Spring中用到的包装器模式在类名上有两种表现:一种是类名中含有Wrapper,另一种是类名中含有Decorator


5️⃣ 动态代理

织入:把切面应用到目标对象并创建新的代理对象的过程。

切面在运行时被织入,一般情况下,在织入切面时,AOP容器会未目标对象动态创建一个代理对象,SpringAOP就是以这种方式织入切面的


6️⃣ 观察者模式

spring的事件驱动模型使用的是观察者模式,Spring中observer模式常用的地方是listener的实现


7️⃣ 策略模式

Spring框架的资源访问Resource接口。该接口提供了更强的资源访问能力,Spring框架本身大量使用了Resource接口来访问底层资源。


16. Spring事务的实现方式

大三后端暑期实习面经总结——SSM&微服务框架篇_第7张图片

1️⃣ 编程式

编程式事务管理对基于 POJO 的应用来说是唯一选择。我们需要在代码中调用beginTransaction()、commit()、rollback()等事务管理相关的方法,这就是编程式事务管理。

2️⃣ 声明式

  1. 基于 TransactionProxyFactoryBean 的声明式事务管理
  2. 基于 @Transactional 的声明式事务管理
  3. 基于 Aspectj AOP 配置事务

基于 Aspectj AOP 配置事务具体流程

1️⃣ 开启Spring的事务处理功能

创建一个 DataSourceTransactionManager 对象,传入上述创建好的dataSource对象


<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
bean>

2️⃣ 配置事务通知

这里使用事务tx命名空间配置

常用的两个属性

  • name属性规定配置事务的方法

  • propagation属性配置事务的传播特性,默认为REQUIRED

REQUIRED 支持当前事务,如果当前没有事务,就新建一个事务
REQUIRED_NEW 新建事务,如果当前存在事务,把当前事务挂起
SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行
NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
MANDATORY 支持当前事务,如果当前没有事务,就抛出异常
NEVER 以非事务方式执行,如果当前存在事务,则抛出异常
NESTED 支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务

<tx:advice id="txAdvice" transaction-manager="transactionManager">
    
    
    <tx:attributes>
        <tx:method name="add" propagation="REQUIRED"/>
        <tx:method name="delete" propagation="REQUIRED"/>
        <tx:method name="update" propagation="REQUIRED"/>
        <tx:method name="query" read-only="true"/>
        <tx:method name="*" propagation="REQUIRED"/>
    tx:attributes>
tx:advice>

3️⃣ 结合AOP实现事务织入


<aop:config>
    <aop:pointcut id="txPointCut" expression="execution(* mapper.*.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
aop:config>

17. Spring事务隔离级别

大三后端暑期实习面经总结——SSM&微服务框架篇_第8张图片


18. spring事务传播机制

事务传播机制 是指多个事务相互调用时,事务是如何在这些方法之间传播的问题

例如:方法A是一个事务的方法,执行过程中调用了方法B,那么方法B有无事务以及方法B对事务的要求不同都会对方法A事务的具体执行造成影响,同时方法A的事务对方法B的事务执行也会有影响,这种影响具体是什么是由两个方法所定义的事务传播类型决定的

传播类型 介绍:当前指A,新建的事务都指B
REQUIRED 如果当前有事务,则加入当前事务;如果当前没有事务,就新建一个事务
如果A有事务,则将B事务加入A的事务中作为一整个事务一起执行;如果A没有事务,则给B新建一个新事务
REQUIRED_NEW 新建事务,如果当前存在事务,把当前事务挂起
B会创建一个新的事务,如果A有事务,则将A的事务挂起执行,也就是各自执行各自的事务
SUPPORTS 如果当前有事务,则加入当前事务;如果当前没有事务,就以非事务方式执行
如果A有事务,则将B事务加入A的事务中作为一整个事务一起执行;如果A没有事务,B以非事务方式运行
NOT_SUPPORTED 以非事务方式执行,如果当前存在事务,就把当前事务挂起
B以非事务方式运行,如果A有事务,则将A的事务挂起执行
MANDATORY 如果当前有事务,则加入当前事务;如果当前没有事务,就抛出异常
如果A有事务,则将B事务加入A的事务中作为一整个事务一起执行;如果A没有事务,则抛出异常
NEVER 以非事务方式执行,如果当前存在事务,则抛出异常
如果A有事务,则抛出异常
NESTED 如果当前事务存在,则开启一个嵌套事务,如果当前没有事务,就新建一个事务
如果A有事务(父事务),则给B开启一个嵌套事务(也称子事务),如果父事务回滚,子事务也会回滚;如果A无事务,则开启一个新事务

大三后端暑期实习面经总结——SSM&微服务框架篇_第9张图片


19. Spring事务什么时候失效

spring事务的原理是AOP,进行了切面增强,所以失效的根本原因就是AOP不起作用了,常见情况如下:

1. 数据库引擎不支持事务

这里以 MySQL 为例,其 MyISAM 引擎是不支持事务操作的,InnoDB 才是支持事务的引擎,一般要支持事务都会使用 InnoDB。

从 MySQL 5.5.5 开始的默认存储引擎是:InnoDB,之前默认的都是:MyISAM,所以这点要值得注意,底层引擎不支持事务再怎么搞都是白搭。

2. 没有被 Spring 管理

如下面例子所示:

// @Service
public class OrderServiceImpl implements OrderService {
     

    @Transactional
    public void updateOrder(Order order) {
     
        // update order
    }

}

如果此时把 @Service 注解注释掉,这个类就不会被加载成一个 Bean,那这个类就不会被 Spring 管理了,事务自然就失效了。

3. 方法不是 public 的

@Transactional 只能用于 public 的方法上,否则事务不会失效,如果要用在非 public 方法上,可以开启 AspectJ 代理模式。
大三后端暑期实习面经总结——SSM&微服务框架篇_第10张图片

4. 自身调用问题

来看两个示例:

@Service
public class OrderServiceImpl implements OrderService {
     

    public void update(Order order) {
     
        updateOrder(order);
    }

    @Transactional
    public void updateOrder(Order order) {
     
        // update order
    }

}

update方法上面没有加 @Transactional 注解,调用有 @Transactional 注解的 updateOrder 方法,updateOrder 方法上的事务管用吗?

再来看下面这个例子:

@Service
public class OrderServiceImpl implements OrderService {
     

    @Transactional
    public void update(Order order) {
     
        updateOrder(order);
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void updateOrder(Order order) {
     
        // update order
    }

}

这次在 update 方法上加了 @Transactional,updateOrder 加了 REQUIRES_NEW 新开启一个事务,那么新开的事务管用么?

这两个例子的答案是:不管用!

因为它们发生了自身调用,就调该类自己的方法,而没有经过 Spring 的代理类,默认只有在外部调用事务才会生效,这也是老生常谈的经典问题了。

这个的解决方案之一就是在的类中注入自己,用注入的对象再调用另外一个方法,这个不太优雅,另外一个可行的方案可以参考《Spring 如何在一个事务中开启另一个事务?》这篇文章。

5. 数据源没有配置事务管理器

@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
     
    return new DataSourceTransactionManager(dataSource);
}

如上面所示,当前数据源若没有配置事务管理器,那也是白搭!

6. 不支持事务

来看下面这个例子:

@Service
public class OrderServiceImpl implements OrderService {
     

    @Transactional
    public void update(Order order) {
     
        updateOrder(order);
    }

    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void updateOrder(Order order) {
     
        // update order
    }
}

Propagation.NOT_SUPPORTED: 表示不以事务运行,当前若存在事务则挂起,详细的可以参考《事务隔离级别和传播机制》这篇文章。

都主动不支持以事务方式运行了,那事务生效也是白搭!

7. 异常被吃了

这个也是出现比较多的场景:

@Service
public class OrderServiceImpl implements OrderService {
     
    @Transactional
    public void updateOrder(Order order) {
     
        try {
     
            // update order
        } catch {
     

        }
    }
}

把异常吃了,然后又不抛出来,事务就不生效了

8. 异常类型错误

上面的例子再抛出一个异常:

// @Service
public class OrderServiceImpl implements OrderService {
     
    @Transactional
    public void updateOrder(Order order) {
     
        try {
     
            // update order
        } catch {
     
            throw new Exception("更新错误");
        }
    }
}

这样事务也是不生效的,因为默认回滚的是:RuntimeException,如果你想触发其他异常的回滚,需要在注解上配置一下,如:

@Transactional(rollbackFor = Exception.class)

这个配置仅限于 Throwable 异常类及其子类。


20. 什么是bean的自动装配及实现方式

image-20210430101639885
大三后端暑期实习面经总结——SSM&微服务框架篇_第11张图片大三后端暑期实习面经总结——SSM&微服务框架篇_第12张图片


21. spring、springmvc、springboot区别

Spring是一个IOC容器,用于管理Bean,通过DI依赖注入的方式实现控制反转,可以方便的整合各种框架,且提供了AOP机制弥补了OOP的代码重复问题,更方便的将不同类不同方法中的共同处理抽取成为切面,自动注入给方法执行,例如异常、日志

SpringMVC是spring对web框架的一个解决方案,提供了一个总的前端控制器servlet,用于接受请求,然后定义了一套路由策略(url到heandler的映射)以及适配执行handler,将handler处理的结果使用视图解析器生成视图展现给前端

SpringBoot是spring提供的一个快速开发工具包,让程序员能更方便、快速的开发spring+springmvc的应用,约定大于配置,通过stater机制整合了一系列的解决方案,使得redis、mongodb、es可以开箱即用


22. SpringMVC执行流程

大三后端暑期实习面经总结——SSM&微服务框架篇_第13张图片

1)用户发送请求至前端控制器 DispatcherServlet

2)DispatcherServlet 收到请求调用 HandlerMapping 处理器映射器。

3)处理器映射器找到具体的处理器(可以根据xml配置、注解进行査找),生成处理器及处理器拦截器(如果有则生成)并以 HandlerExecutionChain 对象的形式返回给 DispatcherServlet

4)DispatcherServlet 调用 HandlerAdapter 处理器适配器。

5)HandlerAdapter 经过适配调用具体的处理器(Controller,也叫后端控制器)

6)Controller 执行完成返回 ModelAndview

7)HandlerAdapter 将 controller 执行结果 ModelAndview 返回给 DispatcherServlet

8)DispatcherServlet 将 ModelAndview 传给 ViewReslover 视图解析器。

9)ViewReslover 解析后返回具体view

10)DispatcherServlet 根据 view 进行渲染视图(即将模型数据填充至视图中)

11)DispatcherServlet响应用户。


23. springmvc九大组件

大三后端暑期实习面经总结——SSM&微服务框架篇_第14张图片


24. springboot自动配置原理

大三后端暑期实习面经总结——SSM&微服务框架篇_第15张图片

SpringFactoriesLoader.loadFactoryNames是spring的一种spi机制

结论

  1. SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值
  2. 将这些值作为自动配置类导入容器,自动配置类就生效,帮我们进行自动配置工作;
  3. 整个J2EE的整体解决方案和自动配置都在springboot-autoconfigure的jar包中;
  4. 它会给容器中导入非常多的自动配置类 (xxxAutoConfiguration), 就是给容器中导入这个场景需要的所有组件,并配置好这些组件 ;
  5. 有了自动配置类,免去了我们手动编写配置注入功能组件等的工作;

25. 如何理解springboot的stater

大三后端暑期实习面经总结——SSM&微服务框架篇_第16张图片


26. 编写一个求两数和的controller

package com.zsr.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
@ResponseBody
public class HelloController {
     
    @RequestMapping("/hello")
    public int hello(@RequestParam int a, @RequestParam int b) {
     
        return a + b;
    }
}

访问:http://localhost:8080/hello?a=1&b=2


27. springboot实现热部署方式

SpringBoot热部署实现有两种方式:

1️⃣ 使用spring loaded

<build>
    <plugins>
        <plugin>
            
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-maven-pluginartifactId>
            <dependencies>
                
                
                <dependency>
                    <groupId>org.springframeworkgroupId>
                    <artifactId>springloadedartifactId>
                    <version>1.2.6.RELEASEversion>
                dependency>
            dependencies>
        plugin>
    plugins>
build>

添加完毕后需要使用mvn指令运行:

首先找到IDEA中的Edit configurations ,然后进行如下操作:(点击左上角的"+",然后选择maven将出现右侧面板,在红色划线部位输入如图所示指令,你可以为该指令命名(此处命名为MvnSpringBootRun))

2️⃣ spring-boot-devtools

在Spring Boot 项目中添加 spring-boot-devtools依赖即可实现页面和代码的热部署


 <dependency>
     <groupId>org.springframework.bootgroupId>
     <artifactId>spring-boot-devtoolsartifactId>
 dependency>

28. 什么是 ORM 框架?

对象-关系映射(Object-Relational Mapping,简称ORM),面向对象的开发方法是当今企业级应用开发环境中的主流开发方法,关系数据库是企业级应用环境中永久存放数据的主流数据存储系统。对象和关系数据是业务实体的两种表现形式,业务实体在内存中表现为对象,在数据库中表现为关系数据。内存中的对象之间存在关联和继承关系,而在数据库中,关系数据无法直接表达多对多关联和继承关系。因此,对象-关系映射(ORM)系统一般以中间件的形式存在,主要实现程序对象到关系数据库数据的映射。


29. 为什么要使用 spring?

1.简介

  • 目的:解决企业应用开发的复杂性
  • 功能:使用基本的JavaBean代替EJB,并提供了更多的企业应用功能
  • 范围:任何Java应用

简单来说,Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。

2.轻量

从大小与开销两方面而言Spring都是轻量的。完整的Spring框架可以在一个大小只有1MB多的JAR文件里发布。并且Spring所需的处理开销也是微不足道的。此外,Spring是非侵入式的:典型地,Spring应用中的对象不依赖于Spring的特定类。

3.控制反转

Spring通过一种称作控制反转(IoC)的技术促进了松耦合。当应用了IoC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。你可以认为IoC与JNDI相反——不是对象从容器中查找依赖,而是容器在对象初始化时不等对象请求就主动将依赖传递给它。

4.面向切面

Spring提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务(transaction)管理)进行内聚性的开发。应用对象只实现它们应该做的——完成业务逻辑——仅此而已。它们并不负责(甚至是意识)其它的系统级关注点,例如日志或事务支持。

5.容器

Spring包含并管理应用对象的配置和生命周期,在这个意义上它是一种容器,你可以配置你的每个bean如何被创建——基于一个可配置原型(prototype),你的bean可以创建一个单独的实例或者每次需要时都生成一个新的实例——以及它们是如何相互关联的。然而,Spring不应该被混同于传统的重量级的EJB容器,它们经常是庞大与笨重的,难以使用。

6.框架

Spring可以将简单的组件配置、组合成为复杂的应用。在Spring中,应用对象被声明式地组合,典型地是在一个XML文件里。Spring也提供了很多基础功能(事务管理、持久化框架集成等等),将应用逻辑的开发留给了你。

所有Spring的这些特征使你能够编写更干净、更可管理、并且更易于测试的代码。它们也为Spring中的各种模块提供了基础支持。


30. spring 有哪些主要模块?

Spring框架至今已集成了20多个模块。这些模块主要被分如下图所示的核心容器、数据访问/集成,、Web、AOP(面向切面编程)、工具、消息和测试模块。

大三后端暑期实习面经总结——SSM&微服务框架篇_第17张图片


31. @RequestMapping的作用

RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。

RequestMapping注解有六个属性,下面我们把她分成三类进行说明。

value, method:

  • value:指定请求的实际地址,指定的地址可以是URI Template 模式(后面将会说明);
  • method:指定请求的method类型, GET、POST、PUT、DELETE等;

consumes,produces

  • consumes:指定处理请求的提交内容类型(Content-Type),例如application/json, text/html;
  • produces:指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回;

params,headers

  • params: 指定request中必须包含某些参数值是,才让该方法处理。
  • headers:指定request中必须包含某些指定的header值,才能让该方法处理请求。

32. mybatis一级&二级缓存

大三后端暑期实习面经总结——SSM&微服务框架篇_第18张图片

MyBatis系统中默认定义了两级缓存:一级缓存二级缓存

  • 一级缓存默认是开启,它是SqlSession级别的缓存,也称为本地缓存

  • 二级缓存需要手动开启和配置,它是基于namespace级别的缓存,也称全局缓存,一级缓存作用域太低,所以诞生了二级缓存;只要开启了二级缓存,我们在同一个Mapper中的查询,可以在二级缓存中拿到。大师数据查出的数据都会被默认先放在一级缓存中,只有会话提交或者关闭以后,一级缓存中的数据才会转到二级缓存中(注意:使用二级缓存属性类需要实现Serializable序列化接口,可用来保存对象的状态)

  • 为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通实现Cache接口来自定义二级缓存

你可能感兴趣的:(面试题,java,spring)