Spring

作者: 技术灭霸 | 来源:发表于2020-04-25 23:48 被阅读0次

1、Spring和SpringMVC区别?

Spring是IOC和AOP的容器框架,SpringMVC是基于Spring功能之上添加的Web框架,想用SpringMVC必须先依赖Spring。

Spring可以说是一个管理bean的容器,也可以说是包括很多开源项目的总称,spring mvc是其中一个开源项目

2、Spring和SpringBoot区别

Spring Boot基本上是Spring框架的扩展,它消除了设置Spring应用程序所需的XML配置,为更快,更高效的开发生态系统铺平了道路。

Spring Boot中的一些特征:

  • 创建独立的Spring应用。
  • 嵌入式Tomcat、Jetty、 Undertow容器(无需部署war文件)。
  • 提供的starters 简化构建配置
  • 尽可能自动配置spring应用。
  • 提供生产指标,例如指标、健壮检查和外部化配置
  • 完全没有代码生成和XML配置要求

3、讲一下SpringBoot的启动流程?Spring Boot Starter原理?

image.png

Spring Boot Starter原理
搭建一个基于Spring的Web应用:
1、pom文件中引入相关jar包,包括spring、springmvc、redis、mybaits、log4j、mysql-connector-java 等等相关jar ...
2、配置web.xml,Listener配置、Filter配置、Servlet配置、log4j配置、error配置 ...
3、配置数据库连接、配置spring事务
4、配置视图解析器
5、开启注解、自动扫描功能
6、配置完成后部署tomcat、启动调试 ......

现在:
引入maven实质上就是导入jar包,spring-boot启动的时候会找到starter jar包中的resources/META-INF/spring.factories文件,根据spring.factories文件中的配置,找到需要自动配置的类

4、Spring Bean的生命周期(区别于Spring的启动过程)

实例化bean -> 属性填充 -> 调用Aware相关接口 -> 前置处理器 -> 检查并调用InitializingBean的AfterPropertiesSet方法 -> 反射执行bean的init-method方法 -> 后置处理器 -> 循环依赖检查 -> 初始化 -> 使用 -> 销毁
image.png

Spring启动流程及Bean生命周期梳理

image.png

4、微服务存在什么优缺点?

特点:1、单一职责 2、自治
优点:1、逻辑清晰 2、简化部署 3、可扩展 4、灵活组合 5、技术异构 6、高可靠

5、spring 中 init-method 和 destroy-method 的使用方式

init-method:bean 构造器执行之后执行
destroy-method:bean 被销毁之前执行

6、Springboot自动装配怎么实现?

@SpringBootApplication -》@SpringBootConfiguration -》@EnableAutoConfiguration

  1. 说白了就是将主配置类(即@SpringBootApplication标注的类)的所在包及子包里面所有组件扫描加载到Spring容器。所以包名一定要注意。
  2. @Import注解就是给Spring容器中导入一些组件,这里传入了一个组件的选择器:AutoConfigurationImportSelector。
  3. 里面有一个selectImports方法,将所有需要导入的组件以全类名的方式返回;这些组件就会被添加到容器中。
    会给容器中导入非常多的自动配置类(xxxAutoConfiguration);就是给容器中导入这个场景需要的所有组件,并配置好这些组件

7、 SpringBoot中starter原理

stater机制帮我们完成了项目起步所需要的的相关jar包。

starter中简单来讲就是引入了一些相关依赖和一些初始化的配置。

为什么配置了META-INF/spring.factories配置文件就可以加载?这里才是springboot实现starter的关键点,springboot的这种配置加载方式是一种类SPI(Service Provider Interface)的方式,SPI可以在META-INF/services配置接口扩展的实现类,springboot中原理类似,只是名称换成了spring.factories而已。

8、为什么依赖的依赖变少了?SpringBoot是如何管理这些依赖的?

1.1 从pom文件出发
1.2 SpringBoot将所有的常见开发功能,分成了一个个场景启动器(starter),这样我们需要开发什么功能,
就导入什么场景启动器依赖即可。

9、Spring InitializingBean和ApplicationListener的区别

  • 使用spring中的InitializingBean接口,监听spring的启动过程,在spring装载完所有的bean的时候,会自动调用实现InitializingBean的afterPropertiesSet()方法
  • 实现ApplicationListener接口,并实现 onApplicationEvent()方法

10、dubbo服务暴露和服务引用怎么和spring结合

服务暴露

  • dubbo通过自定义Schema配置dubbo里面的核心组件
  • 通过DubboNamespaceHandler注册的service把xml解析成ServiceBean,这个Bean是继承上面讲的ServiceConfig,然后通过利用Spring的生命周期在实例化bean的时候会调用org.springframework.beans.factory.InitializingBean#afterPropertiesSet方法。它最终会调用com.alibaba.dubbo.config.ServiceConfig#export方法进行服务暴露

服务引用

  • 而对于服务引用Dubbo也是同样的套路。
  • 它也是通过DubboNamespaceHandler注册的的reference把xml解析成ReferenceBean。这个Bean是继承自上面提到的ReferenceConfig。然后通过利用Spring的生命周期在实例化bean的时候会调用org.springframework.beans.factory.InitializingBean#afterPropertiesSet方法。它最终会调用com.alibaba.dubbo.config.ReferenceConfig#get方法远程服务调用代理类的创建

11、Spring跨域的三种实现

第一种办法:

@Configuration
public class CorsConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
                .allowCredentials(true)
                .maxAge(3600)
                .allowedHeaders("*");
    }
}

这种方式是全局配置的,网上也大都是这种解决办法,但是很多都是基于旧的spring版本,比如:

Spring Boot如何解决前端的Access-Control-Allow-Origin跨域问题

文中WebMvcConfigurerAdapter在spring5.0已经被标记为Deprecated

spring5最低支持到jdk1.8,所以注释中明确表明,你可以直接实现WebMvcConfigurer接口,无需再用这个适配器,因为jdk1.8支持接口中存在default-method。

第二种办法:

@WebFilter(filterName = "CorsFilter ")
@Configuration
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-Credentials", "true");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, PATCH, DELETE, PUT");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
        chain.doFilter(req, res);
    }
}

这种办法,是基于过滤器的方式,方式简单明了,就是在response中写入这些响应头

第三种办法:

public class GoodsController {

    @CrossOrigin(origins = "http://localhost:4000")
    @GetMapping("goods-url")
    public Response queryGoodsWithGoodsUrl(@RequestParam String goodsUrl) throws Exception {}
}   

没错就是@CrossOrigin注解,点开注解

@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CrossOrigin {}

从元注解@Target可以看出,注解可以放在method、class等上面,类似RequestMapping,也就是说,整个controller下面的方法可以都受控制,也可以单个方法受控制。

也可以得知,这个是最小粒度的cors控制办法了,精确到单个请求级别。

12、Spring中bean的作用域

Spring IOC容器创建一个Bean实例时,可以为Bean指定实例的作用域,作用域包含singleton(单例模式)、prototype(原型模式)、request(http请求)、session(会话)、global-session(全局会话)

image.png

13、Spring事务传播机制详解

所谓事务传播机制,也就是在事务在多个方法的调用中是如何传递的,是重新创建事务还是使用父方法的事务?父方法的回滚对子方法的事务是否有影响?这些都是可以通过事务传播机制来决定的。

1、REQUIRED:如果有事务则加入事务,如果没有事务,则创建一个新的(默认值)
2、NOT_SUPPORTED:Spring不为当前方法开启事务,相当于没有事务
3、REQUIRES_NEW:不管是否存在事务,都创建一个新的事务,原来的方法挂起,新的方法执行完毕后,继续执行老的事务
4、MANDATORY:必须在一个已有的事务中执行,否则报错
5、NEVER:必须在一个没有的事务中执行,否则报错
6、SUPPORTS:如果其他bean调用这个方法时,其他bean声明了事务,则就用这个事务,如果没有声明事务,那就不用事务

14、spring有哪些主要模块?

Spring框架的七大模块

  1. Spring Core
    框架的最基础部分,提供 IoC 容器,对 bean 进行管理。

2.Spring Context
基于 bean,提供上下文信息,扩展出JNDI、EJB、电子邮件、国际化、校验和调度等功能。

3.Spring DAO
提供了JDBC的抽象层,它可消除冗长的JDBC编码和解析数据库厂商特有的错误代码,还提供了声明性事务管理方法。

4.Spring ORM
提供了常用的“对象/关系”映射APIs的集成层。 其中包括JPA、JDO、Hibernate、MyBatis 等。

5.Spring AOP
提供了符合AOP Alliance规范的面向方面的编程实现。

6.Spring Web
提供了基础的 Web 开发的上下文信息,可与其他 web 进行集成。

7.Spring Web MVC
提供了 Web 应用的 Model-View-Controller 全功能实现。

15、BeanFactory 和 ApplicationContext有什么区别?

BeanFactory和ApplicationContext是Spring的两大核心接口,都可以当做Spring的容器。其中ApplicationContext是BeanFactory的子接口。

可以从依赖关系、加载方式、创建方式、注册方式这四方面去讲。

依赖关系

BeanFactory:是Spring里面最底层的接口,包含了各种Bean的定义,读取bean配置文档,管理bean的加载、实例化,控制bean的生命周期,维护bean之间的依赖关系。

ApplicationContext接口作为BeanFactory的派生,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能:

  • 继承MessageSource,因此支持国际化。
  • 统一的资源文件访问方式。
  • 提供在监听器中注册bean的事件。
  • 同时加载多个配置文件。

载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层。

加载方式

BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。这样,我们就不能发现一些存在的Spring的配置问题。如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常。

ApplicationContext,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误,这样有利于检查所依赖属性是否注入。 ApplicationContext启动后预载入所有的单实例Bean,通过预载入单实例bean ,确保当你需要的时候,你就不用等待,因为它们已经创建好了。

相对于基本的BeanFactory,ApplicationContext 唯一的不足是占用内存空间。当应用程序配置Bean较多时,程序启动较慢。

创建方式

BeanFactory通常以编程的方式被创建,ApplicationContext还能以声明的方式创建,如使用ContextLoader。

注册方式

BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。

BeanFactory 简单粗暴,可以理解为就是个 HashMap,Key 是 BeanName,Value 是 Bean 实例。通常只提供注册(put),获取(get)这两个功能。我们可以称之为 “低级容器”。

ApplicationContext 可以称之为 “高级容器”。因为他比 BeanFactory 多了更多的功能。他继承了多个接口。因此具备了更多的功能。例如资源的获取,支持多种消息(例如 JSP tag 的支持),对 BeanFactory 多了工具级别的支持等待。所以你看他的名字,已经不是 BeanFactory 之类的工厂了,而是 “应用上下文”, 代表着整个大容器的所有功能。该接口定义了一个 refresh 方法,此方法是所有阅读 Spring 源码的人的最熟悉的方法,用于刷新整个容器,即重新加载/刷新所有的 bean。


image.png

16、构造器依赖注入和 Setter方法注入的区别

构造函数注入 setter 注入
没有部分注入 有部分注入
不会覆盖 setter 属性 会覆盖 setter 属性
任意修改都会创建一个新实例 任意修改不会创建一个新实例
适用于设置很多属性 适用于设置少量属性

两种依赖方式都可以使用,构造器注入和Setter方法注入。最好的解决方案是用构造器参数实现强制依赖,setter方法实现可选依赖。

17、前后端分离跨域解决

我们知道一个http请求,先走filter,到达servlet后才进行拦截器的处理,如果我们把cors放在filter里,就可以优先于权限拦截器执行。

@Configuration
public class CorsConfig {

    @Bean
    public CorsFilter corsFilter() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedOrigin("*");
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedMethod("*");
        corsConfiguration.setAllowCredentials(true);
        UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
        return new CorsFilter(urlBasedCorsConfigurationSource);
    }
}

现在可以通过实现WebMvcConfigurer接口然后重写addCorsMappings方法解决跨域问题。

 @Configuration
public class CorsConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowCredentials(true)
                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
                .maxAge(3600);
    }

}

18、Spring Boot 中的 starter 到底是什么 ?

首先,这个 Starter 并非什么新的技术点,基本上还是基于 Spring 已有功能来实现的。首先它提供了一个自动化配置类,一般命名为 XXXAutoConfiguration ,在这个配置类中通过条件注解来决定一个配置是否生效(条件注解就是 Spring 中原本就有的),然后它还会提供一系列的默认配置,也允许开发者根据实际情况自定义相关配置,然后通过类型安全的属性注入将这些配置属性注入进来,新注入的属性会代替掉默认属性。正因为如此,很多第三方框架,我们只需要引入依赖就可以直接使用了。当然,开发者也可以自定义 Starter

19、Cglib能代理final方法吗?

不能,由于cglib是基于继承的方式实现类的动态代理,因此无法实现对final方法的代理。

20、@Conditional的使用

作用:根据条件,决定类是否加载到Spring Ioc容器中,在SpringBoot中有大量的运用

应用场景:在一些需要条件满足才是实例化的类中,使用此注解,我曾经在项目中需要根据不同的场景使用不同的mq中间件的时候使用过,在mq的实例化bean上,加上此注解,根据配置文件的不同,来决定这个bean是否加载至ioc容器中。

21、Spring与SpringMVC父子容器的区别和联系

1、 Spring 与SpringMVC 两个都是容器,存在父子关系(包含和被包含的关系)

2 、 Spring容器中存放着mapper代理对象,service对象,SpringMVC存放着Controller对象。子容器SpringMVC中可以访问父容器中的对象。但父容器Spring不能访问子容器SpringMVC的对象(存在领域作用域的原因,子容器可以访问父容器中的成员,而子容器的成员则只能被自己使用)。如:Service对象可以在Controller层中注入,反之则不行。

3、Spring容器导入的properties配置文件,只能在Spring容器中用而在SpringMVC容器中不能读取到。 需要在SpringMVC 的配置文件中重新进行导入properties文件,并且同样在父容器Spring中不能被使用,导入后使用@Value("${key}")在java类中进行读取。

22、注解service和component的区别

@Component

  • Component 用于将所标注的类加载到 Spring 环境中,需要搭配 component-scan 使用

  • 泛指各种组件,就是说当我们的类不属于各种归类的时候(不属于@Controller、@Services等的时候),我们就可以使用@Component来标注这个类。

  • @service
    @service和@controller引用来了@component注解,而@service是对@component进一步拓展,也就是component注解实现的功能@service都能实现,被@service注解标注的百类会被spring认定是业务逻辑层

23、Spring事务什么情况下回滚?

** Spring事务回滚机制是这样的:当所拦截的方法有指定异常抛出,事务才会自动进行回滚!**

  • 默认配置下,事务只会对Error与RuntimeException及其子类这些UNChecked异常,做出回滚。一般的Exception这些Checked异常不会发生回滚(如果一般Exception想回滚要做出配置);

24、Spring事务trycatch会回滚吗?

依赖spring事物时,当service层进行try catch异常捕获时,事物不会产生回滚,代码如下

    public void insertMsg(ConversationBean conversationBean){
        try{
            for(int i=0;i<100;i++){
                if(i!=10){
                    testDao.insert2(i);
                }else{
                    testDao.insert1(i);
                }
            }
        }catch(Exception e){
        }  
    }

此时异常被捕获,这种业务方法也就等于脱离了spring事务的管理,因为没有任何异常会从业务方法中抛出,全被捕获,导致spring异常抛出触发事务回滚策略失效。

解决此类问题时,需要在try catch中显示的抛出异常RuntimeException 然后在Controller层捕获异常并编写返回值,代码如下:

   public void insertMsg(ConversationBean conversationBean){
        try{
            for(int i=0;i<100;i++){
                if(i!=10){
                    testDao.insert2(i);
                }else{
                    testDao.insert1(i);
                }
            }
        }catch(Exception e){
            throw new RuntimeException();
        }

25、过滤器filter、拦截器interceptor、和AOP的区别与联系

filter过滤器

  • 过滤器拦截web访问url地址。 严格意义上讲,filter只是适用于web中,依赖于Servlet容器,利用Java的回调机制进行实现。
  • Filter过滤器:和框架无关,可以控制最初的http请求,但是更细一点的类和方法控制不了。
  • 过滤器可以拦截到方法的请求和响应(ServletRequest request, ServletResponse response),并对请求响应做出像响应的过滤操作,
  • 比如设置字符编码,鉴权操作

Interceptor拦截器

  • 拦截器拦截以 .action结尾的url,拦截Action的访问。 Interfactor是基于Java的反射机制(APO思想)进行实现,不依赖Servlet容器。
  • 拦截器可以在方法执行之前(preHandle)和方法执行之后(afterCompletion)进行操作,回调操作(postHandle)可以获取执行的方法的名称,请求(HttpServletRequest)
  • Interceptor:可以控制请求的控制器和方法,但控制不了请求方法里的参数(只能获取参数的名称,不能获取到参数的值)
  • 用于处理页面提交的请求响应并进行处理,例如做国际化,做主题更换,过滤等)。

Spring AOP拦截器

  • 只能拦截Spring管理Bean的访问(业务层Service)。 具体AOP详情参照 Spring AOP:原理、 通知、连接点、切点、切面、表达式
  • 实际开发中,AOP常和事务结合:Spring的事务管理:声明式事务管理(切面)
  • AOP操作可以对操作进行横向的拦截,最大的优势在于他可以获取执行方法的参数( ProceedingJoinPoint.getArgs() ),对方法进行统一的处理。
  • Aspect : 可以自定义切入的点,有方法的参数,但是拿不到http请求,可以通过其他方式如RequestContextHolder获得(
    ServletRequestAttributes servletRequestAttributes= (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    )。
  • 常见**使用日志,事务,请求参数安全验证

26、Spring中使用哪些设计模式?

  • 简单工厂模式:spring中的BeanFactory就是简单工厂模式的体现,根据传入一个唯一的标识来获得bean对象
  • 单例模式:Spring下默认的bean均为singleton,可以通过singleton=“true|false” 或者 scope="?"来指定。
  • 代理模式:AOP
  • 适配器模式:AOP的处理中有Adapter模式,由于Advisor链需要的是MethodInterceptor对象,所以每一个Advisor中的Advice都要适配成对应的MethodInterceptor对象。
  • 包装器模式:
  • 观察者模式:listener的实现。如ApplicationListener
  • 策略模式:spring中在实例化对象的时候用到Strategy模式
  • 模板方法模式:spring中的JdbcTemplate

27、@Transactional的参数配置

1、propagation参数,Propagation类型(枚举),默认值为Propogation.REQUIRED,支持的值有REQUIRED、MANDATORY、NESTED、NEVER、NOT_SUPPORTED、REQUIRE_NEW、SUPPORTS。关于这个问题的详细说明将在以后的文章中展开。
2、isolation参数,Isolation类型(枚举),默认值为Isolation.DEFAULT,支持的值有DEFAULT、READ_COMMITTED、READ_UNCOMMITTED、REPEATABLE_READ、SERIALIZABLE。关于这个问题的详细说明将在以后的文章中展开。
3、timeout参数,int类型,事务的超时时间,默认值为-1,即不会超时。
4、readOnly参数,boolean类型,true表示事务为只读,默认值为false。
5、rollbackFor参数,Class<? extends Throwable>[]类型,默认为空数组。
6、rollbackForClassName参数,String[]类型,默认为空数组。
7、noRollbackFor参数,Class<? extends Throwable>[]类型,默认为空数组。
8、noRollbackForClassName参数,String[]类型,默认为空数组。

最后四个参数都与回滚有关,首先,一般不推荐使用rollbackForClassName和noRollbackForClassName两个参数,而用另外两个参数来代替,从参数的类型上就可以看出区别,使用字符串的缺点在于:如果不是用类的完整路径,就可能导致回滚设置对位于不同包中的同名类都生效;且如果类名写错,也无法得到IDE的动态提示。

但是,如果不配置任何与回滚有关的参数,不代表事务不会进行回滚,如果没有配置这四个选项,那么DefaultTransactionAttribute配置将会生效,具体的行为是,抛掷任何unchecked Exception都会触发回滚,当然包括所有的RuntimeException。

相关文章

网友评论

      本文标题:Spring

      本文链接:https://www.haomeiwen.com/subject/klvmmhtx.html