Spring框架面试总结

Spring章节中本篇中,IOC/DI 的概念是基础,主要面试点在于,spring的工作原理、AOP和事物方面知识点。一般还会问spring中的设计模式等。

文章目录

  • Spring
  • 1、Spring 的优势
  • 2、Spring Bean
    • 1> SpringBean 3种创建方式
    • 2> Spring Bean 3种配置方式
    • 3> Spring Bean 5种作用域
    • 4> Spring Bean的生命周期
  • 3、IOC 和DI
    • 1> IOC:控制反转
    • 2> DI:依赖注入
    • 3> 循环注入
  • 4、切面编程(AOP)
  • 5、Spring 声明式事物
    • 1> 事务隔离级别
    • 2> 事物的传播途径
    • 3> 事务实现原理
    • 4> 事务属性
  • 6、Spring 注解
  • 7、Spring 的设计模式
  • 8、spring常见jar包

Spring

​ Spring是一个轻量级的IoC和AOP容器框架。是为Java应用程序提供基础性服务的一套框架,目的是用于简化企业应用程序的开发,它使得开发者只需要关心业务需求。


1、Spring 的优势

​ 轻量级的 DI / IoC 和 AOP 容器的开源框架

  • 低侵入 / 低耦合 (降低组件之间的耦合度,实现软件各层之间的解耦)
  • 支持声明式事务管理(基于切面和惯例)
  • 方便集成其他框架(如MyBatis、Hibernate)
  • 降低 Java 开发难度

2、Spring Bean

1> SpringBean 3种创建方式

BeanFactory 创建bean

BeanFactory factory = new XmlBeanFactory( new ClassPathResource ( "com/hsp/beans.xml" ) ) ;
factory.getBean("beanId");

优点:节约内存。缺点:速度慢。

ApplicationContext 创建bean

优点:预先加载,使用的时候速度快;缺点:耗内存,所有的bean都被实例化了

1:调用构造器创建Bean

package com.itheima.spring.helloword;
public class Helloword {
	public Helloword() {
		System.out.println("new instance");
	}
	public void hello() {
		System.out.println("hello word");
	}
}
<bean id="helloWorld" class="com.itheima.spring.helloword.Helloword">bean>
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Helloword helloword = (Helloword) context.getBean("helloWorld");

2:调用静态工厂方法创建Bean

<bean id="helloWorld2" class="com.itheima.spring.helloword.HelloWordFactory"  factory-method="getInstance">bean>
public class HelloWordFactory {
	public static Helloword getInstance() {
		return new Helloword();
	}
}
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Helloword helloword = (Helloword) context.getBean("helloWorld2");
helloword.hello();

3:调用实例工厂方法创建Bean

<bean id="helloWorldFactory" class="com.itheima.spring.helloword.HelloWordFactory2">bean>	
<bean id="helloWorld3" factory-bean="helloWorldFactory" factory-method="getInstance" >bean>

public class HelloWordFactory2 {
	public Helloword getInstance() {
		return new Helloword();
	}
}

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Helloword helloword = (Helloword) context.getBean("helloWorld3");
helloword.hello();

Spring的beanFactory和factoryBean的区别

  • BeanFactory是接口类,表示它是一个工厂类,用于管理Bean的一个工厂

  • FactoryBean也是接口;

    • 不是简单的Bean,是一个能生产或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂模式和修饰器模式类似

    • 3个方法:getObject()、isSingleton()、getObjectType()

    • 如果希望获取CarFactoryBean的实例,则需要在使用getBean(beanName)方法时在beanName前显示的加上"&“前缀:如getBean(”&car");


2> Spring Bean 3种配置方式

  • 基于XML配置

    • 当Bean的实现类来源于第三方类库,比如DataSource、HibernateTemplate等,无法在类中标注注解信息,只能通过XML进行配置;而且命名空间的配置,比如aop、context等,也只能采用基于XML的配置。
  • 基于注解配置

    • @Component:标注一个普通的Spring Bean类(可以指定Bean名称,未指定时默认为小写字母开头的类名)
    • @Controller:标注一个控制器类
    • @Service:标注一个业务逻辑类
    • @Repository:标注一个DAO类
  • 基于java反射机制

    • 注解:@Configuration、@Bean、@DependsOn、@Primary、@Lazy、@Import、@ImportResource、@Value

    • 
      @Configuration
      public class Config {
        @Bean
        public MyService myService() {
            return new MyServiceImpl();
        }
      }
      //测试类
      ApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
      MyService myService = ctx.getBean(MyService.class);
      myService.sayHello();
      

3> Spring Bean 5种作用域

singleton、prototype、request、session和globalSession。

  • singleton:定义bean的范围为每个spring容器一个实例(默认值);
  • prototype:定义bean可以被多次实例化(使用一次就创建一次);
  • request:定义bean的范围是http请求(SpringMVC中有效);
  • session:定义 bean的范围是http会话(SpringMVC)中有效;
  • global-session:定义bean的范围是全局http会话(portlet)中有效。

4> Spring Bean的生命周期

  • 实例化Bean:

    对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注入另一个尚未初始化的依赖时,容器就会调用createBean进行实例化。对于ApplicationContext容器,当容器启动结束后,通过获取BeanDefinition对象中的信息,实例化所有的bean。

  • 设置对象属性(依赖注入):

    实例化后的对象被封装在BeanWrapper对象中,紧接着,Spring根据BeanDefinition中的信息 以及 通过BeanWrapper提供的设置属性的接口完成依赖注入。

  • 处理Aware接口:

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

    ​ ①如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String beanId)方法,此处传递的就是Spring配置文件中Bean的id值;

    ​ ②如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory()方法,传递的是Spring工厂自身。

    ​ ③如果这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文;

  • BeanPostProcessor:

    如果想对Bean进行一些自定义的处理,那么可以让Bean实现了BeanPostProcessor接口,那将会调用postProcessBeforeInitialization(Object obj, String s)方法。由于这个方法是在Bean初始化结束时调用的,所以可以被应用于内存或缓存技术;

  • InitializingBean 与 init-method:

    如果Bean在Spring配置文件中配置了 init-method 属性,则会自动调用其配置的初始化方法。

    如果这个Bean实现了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法;

  • 以上几个步骤完成后,Bean就已经被正确创建了,之后就可以使用这个Bean了。
  • DisposableBean:

    当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用其实现的destroy()方法;

  • **destroy-method:**最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。


3、IOC 和DI

1> IOC:控制反转

  • 对象的创建交给外部容器完成,这个就做控制反转

实现原理

  • IOC 就是通过反射技术,根据给出的类名(字符串方式)来动态地生成对象

  • IOC容器相当于一个工厂模式

2> DI:依赖注入

  • 由容器动态的将某个依赖关系注入到组件之中,(3种注入方式:构造器注入、setter方法注入、注解注入。
//使用构造函数
public Teacher(Course course,int age){
    this.course = course;
    this.age = age;
}
<bean id="teacher" class="com.niit.DI2.Teacher">
    
    <constructor-arg name="course" ref="course">constructor-arg>
    <constructor-arg name="age" value="20">constructor-arg>
bean>
//setter方法注入
public void setCourse(Course course) {
	this.course = course;
}
<bean id="student" class="com.niit.DI.Student">
    
    
    <property name="course" ref="course" >property>
    <property name="age" value="20">property>
bean>

3> 循环注入

  • 循环依赖就是循环引用,就是两个或多个Bean相互之间的持有对方
  • 构造器循环依赖 :只能抛出BeanCurrentlyInCreationException异常表示循环依赖。
  • setter方法循环注入:分单例(scope=“singleton”)和非单例模式(scope=“prototype”)的情况
    • 多例模式: 因为prototype作用域的Bean,Spring容器不进行缓存,因此无法提前暴露一个创建中的Bean。会抛出异常
    • 单例模式:通过Spring容器提前暴露刚完成构造器注入但未完成其他步骤(如setter注入)的Bean来完成的,而且只能解决单例作用域的Bean循环依赖。

4、切面编程(AOP)

AOP

  • 通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术
  • 利用AOP可以对业务逻辑的各个部分进行隔离,降低耦合度,提高程序的可重用性,同时提高了开发的效率;
  • 主要功能:日志记录,性能统计,安全控制,事务处理,异常处理等等。
  • Struts2的拦截器 设计就是基于AOP的思想,是个比较经典的例子。

**AOP实现的3种方式:**XML、注解、动态代理

  • 基于XML

    • //目标对象
      public class BraveKnight {
          public void saying(){
              System.out.println("我是骑士");
          }
      }
      //切面类
      public class Minstrel {
          public void beforSay(){
              System.out.println("前置通知");
          }
          public void afterSay(){
              System.out.println("后置通知");
          }
      }
      
      
      <bean id="knight" class="com.cjh.aop2.BraveKnight"/>
      
      <bean id="mistrel" class="com.cjh.aop2.Minstrel"/>
      
      <aop:config>
          <aop:aspect ref="mistrel">
              
              <aop:pointcut expression="execution(* *.saying(..))" id="embark"/>
              
              <aop:before method="beforSay" pointcut-ref="embark"/>
              
              <aop:after method="afterSay" pointcut-ref="embark"/>
          aop:aspect>
      aop:config>
      
      
  • 基于注解

    • 
      <context:component-scan base-package="com.linjie.aop">context:component-scan>  
      
      <aop:aspectj-autoproxy>aop:aspectj-autoproxy>
      
      //目标对象
      @Component("arithmetic")
      public class Arithmetic  {
          public void add(int d,int e) {
              System.out.println("add method ! a+b="+(d+e));
          }
      }
      //切面类
      @Component("logginAspectJ")
      @Aspect
      public class LogginAspectJ  {
          /*
           *定义一个方法,用于声明切点表达式,该方法一般没有方法体
           *@Pointcut用来声明切点表达式
           *通知直接使用定义的方法名即可引入当前的切点表达式 
           */
          @Pointcut("execution(* com.hsy.aop.Arithmetic.*(..))")
          public void PointcutDeclaration() {}
          //前置通知,方法执行之前执行
          @Before("PointcutDeclaration()")
          public void beforAdd(){
              System.out.println("前置通知");
          }
          //后置通知,方法执行之后执行(不管是否发生异常)
          @After("PointcutDeclaration()")
          public void afterAdd(){
              System.out.println("后置通知");
          }
      }
      
      
  • 基于代理:(静态代理、cglib 代理、JDK动态代理:代码实现见设计模式—代理模式)

    • 静态代理:被代理对象与代理对象一起实现相同的接口或者是继承相同父类.
    • jdk动态代理:java.lang.reflect.Proxy.newProxyInstance() 和InvocationHandler对象
    • cglib动态代理:实现MethodInterceptor 接口,重写intercept 方法

AOP编程中:如果加入容器的目标对象有实现接口,用JDK代理;如果目标对象没有实现接口,用Cglib代理

5种通知类型: 前置、后置、环绕、异常、最后

  • 前置通知(before advice) 在某个连接点(jion point)之前执行的通知,但不能阻止连接点前的执行(除非抛出一个异常)
  • 后置通知(after returning advice)在某个连接点(jion point)正常执行完后执行通知
  • 异常通知(after throwing advice) 在方法异常退出时执行的通知
  • 最终通知(after(finally) advice)在方法退出时候的执行通知(不管正常返回还是异常退出)
  • 环绕通知(around advice) 包围一个连接点(jion point)的通知

AOP核心概念

  • 切面(Aspect): 一个关注点的模块化,这个关注点可能会横切多个对象
  • 连接点(Joinpoint) :程序执行过程中某个特定的连接点
  • 通知(Advice) :在切面的某个特的连接点上执行的动作
  • 切入点(Pointcut) :匹配连接的断言,在Aop中通知和一个切入点表达式关联
  • 引入(Intruduction) : 在不修改类代码的前提下,为类添加新的方法和属性
  • 目标对象(Target Object) : 被一个或者多个切面所通知的对象
  • Aop代理(AOP Proxy) : AOP框架创建的对象,用来实现切面契约(aspect contract)(包括方法执行等)
  • 织入(Weaving) :把切面连接到其他的应用程序类型或者对象上,并创建一个被通知的对象,氛围:编译时织入,类加载时织入。执行时织入

5、Spring 声明式事物

事务的两种声明方式: xml、注解(@Transactional )

1> 事务隔离级别

Spring中支持五种事务隔离级别:

默认隔离级别、未提交读、提交读(Oracle、sqlserver 默认)可重复读(Mysql 默认)串行化(级别最高)

  • ISOLATION_DEFAULT:数据库默认隔离级别,(Mysql 默认:可重复读,Oracle 默认:读已提交)
  • ISOLATION_READ_UNCOMMITTED:未提交读;读取到没有被提交的数据
  • ISOLATION_READ_COMMITTED:提交读(Oracle\sqlserver 默认),读到那些已经提交的数据
  • ISOLATION_REPEATABLE_READ(repeatable):可重复读(Mysql 默认);这个事务不结束,别的事务就不可以改这条记录,加锁
  • SERIALIZABLE(serializable):串行化的 ,运行完一个事务的所有子事务之后才可以执行另外一个事务

2> 事物的传播途径

Spring共有7种传播途径:propagation_

  • 支持当前事务

    • required:支持当前事务,如果不存在 就新建一个(默认)
    • supports:支持当前事务,如果不存在,就不使用事务
    • mandatory(强制):支持当前事务,如果不存在,抛出异常
  • 不支持当前事务

    • requires_new:如果有事务存在,挂起当前事务,创建一个新的事务
    • not_supported:如果有事务存在,挂起当前事务 ,如果不存在以非事务方式运行
    • never:如果有事务存在,抛出异常 ,如果不存在以非事务方式运行
    • nested:如果当前事务存在,则嵌套事务执行(他的提交是要等和他的父事务一块提交的,父回滚他回滚)

3> 事务实现原理

  • 事务声明默认使用 AOP 代理;
  • 如果使用 this.xxx 的方式,事物会失效。this 此时不是代理类的对象。
  • 解决方法:1.注入自身,使用代理对象调用;2.使用AopContext,获取当前代理对象;3.使用BeanFactory获取代理对象。

4> 事务属性

Transactional 的事务属性 (如级别、回滚、传播途径、时长等)

  • @isolation:用于指定事务的隔离级别。默认为底层事务的隔离级别
  • @noRollbackFor:指定遇到特定异常时不回滚事务
  • @noRollbackForClassName:指定遇到特定的多个异常时不回滚事务
  • @propagation:指定事务传播行为
  • @readOnly:指定事务是否可读
  • @rollbackFor:指定遇到特定异常时回滚事务
  • @rollbackForClassName:指定遇到特定的多个异常时回滚事务
  • @timeout:指定事务的超长时长。

6、Spring 注解

@Controller, @Service, @Repository,@Component

@RequestMapping("/test")

bean的注解

  • @Controller 在展现层使用,控制层的声明
  • @Service 在业务逻辑层使用(service层)
  • @Repository 在数据访问层使用(dao层)
  • @Component 组件:没有明确的角色

属性注入注解

  • @Autowired:按照类型(byType)装配依赖对象;和Qualifier(name=" ")一起用;

  • @Resource:默认按 byName自动注入

异步注解

  • EnableAsync :开启对异步任务的支持,作用在类级别

  • Async :申明其是一个异步任务,作用于方法

配置类相关注解

  • @Configuration :声明当前类为配置类,相当于xml形式的Spring配置(类上)
  • @Bean :注解在方法上,声明当前方法的返回值为一个bean,替代xml中的方式(方法上)
  • @ComponentScan : 用于对Component进行扫描,相当于xml中的(类上)
  • @WishlyConfiguration :为@Configuration与@ComponentScan的组合注解,可以替代这两个注解

AOP相关注解

  • @Aspect :声明一个切面(类上) ;使用@After、@Before、@Around定义建言(advice),可直接将拦截规则(切点)作为参数。
  • @After 在方法执行之后执行(方法上)
  • @Before 在方法执行之前执行(方法上)
  • @Around 在方法执行之前与之后执行(方法上)
  • @PointCut 声明切点 在java配置类中使用@EnableAspectJAutoProxy注解开启Spring对AspectJ代理的支持(类上)

7、Spring 的设计模式

(1)工厂模式:BeanFactory就是简单工厂模式的体现,用来创建对象的实例;

(2)单例模式:Bean默认为单例模式。

(3)代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术;

(4)模板方法:用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。

(5)观察者模式:定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,如Spring中listener的实现–ApplicationListener。


8、spring常见jar包

  • spring-core.jar:包含spring框架基本的核心工具类,其他组件都要使用这个包里面的类,是其他组件的核心;
  • spring-bean.jar:是所有的应用都要用到的,包含访问配置文件、创建和管理bean以及进行IoC和DI操作所需的相关类;
  • spring-web.jar:包含web开发时,用到spring框架时所需的核心类;
  • spring-webmvc.jar:包含 Spring MVC框架相关的所有类;
  • spring-aop.jar:包含使用AOP特性时所需的类;
  • spring-dao.jar:包含spring DAO、spring Transaction进行数据访问的所有类;
  • spring-hibernate.jar:包含spring对hibernate 2以及hibernate 3进行封装的所有类;
  • spring-jdbc.jar:包含spring对JDBC数据库访问进行封装的所有类;

你可能感兴趣的:(Spring系列)