Spring面试题(上)

Spring面试题(上)

1.简述Spring框架

Spring致力于JavaEE应用的各种解决方案,是一款轻量级框架,大大简化了Java企业级开发,提供了强大、稳定的功能。

Spring有两个目标:一是让现有技术更易于使用,二是促进良好的编程习惯(或者称为最佳实践)。

2.为什么使用Spring(优缺点)

  1. 方便解耦,简化开发

    通过Spring提供的IOC容器,我们可以将对象之间的依赖关系交由Spring进行控制,避免因编码所照成的过度程序耦合。 用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层应用。

  2. AOP编程的支持

    通过Spring的AOP功能,方便进行面向切面编程。许多不容易用传统OOP实现的功能可以通过AOP轻松应付。

  3. 声明式事务的支持

    我们可以从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务管理,提高开发效率和质量。

  4. 方便程序的测试

    Spring对Junit的支持,可以通过注解方便的测试Spring程序。

  5. 方便集成各种优秀框架

    Spring可以降低各框架的使用难度,提供对各种优秀框架的直接支持。

  6. 降低JavaEE API的使用难度

    Spring对Java API(如JDBC、JavaMain、远程调用等)进行了薄薄的封装,使这些API的使用难度大大降低。

3.Spring七大模块

  • Spring Core

    Core封装包是框架的最基础部分,停供IOC和依赖注入特性。这里的基础概念是BeanFactory,它提供对Factory模式的经典实现来消除对程序性单例模式的需要,并真正的允许你从程序逻辑中分离出依赖关系和配置。

  • Spring Context

    构建于Core封装包基础上的Context封装包,提供了一种框架式的对象访问方法,有些像JNDI注册器。Context封装包的特性得自于Beans封装包,并添加了对国际化(I18N)的支持(例如资源绑定),事件传播,资源装载的方式和Context的透明创建,比如说通过Servlet容器。

  • Spring DAO

    DAO(Data Access Object)提供了JDBC得抽象层,它可消除冗长得JDBC编码和解析数据库厂商特有的错误代码。并且,JDBC封装包还提供了一种比编程性更好的声明性事务管理方法,不仅仅是实现了特定接口,而且对所有的POJOs(plain old Java objects)都适用。

  • Spring ORM

    ORM封装包提供了常用的“对象/关系”映射APIs的集成层。 其中包括JPA、JDO、Hibernate 和 iBatis 。利用ORM封装包,可以混合使用所有Spring提供的特性进行“对象/关系”映射,如前边提到的简单声明性事务管理。

  • Spring AOP

    Spring AOP封装包提供了符合AOP Alliance规范的面向方面的编程实现,让你可以定义,例如方法拦截器(method-interceptors)和切点(pointcuts),从逻辑上讲,从而减弱代码的功能耦合,清晰的被分离开。而且,利用source-level的元数据功能,还可以将各种行为信息合并到你的代码中。

  • Spring Web

    Spring中的Web包提供了基础的针对Web开发的集成特性,例如多方文件上传,利用Servlet listeners进行IOC容器初始化和针对Web的ApplicationContext。当与WebWork或Struts一起使用Spring时,这个包使Spring可与其他框架结合。

  • Spring Web MVC

    Spring中的MVC封装包提供了Web应用的Model-View-Controller(MVC)实现。Spring的MVC框架并不是仅仅提供一种传统的实现,它提供了一种清晰的分离模型,在领域模型代码和Web Form之间。并且,还可以借助Spring框架的其他特性。
    在这里插入图片描述

4.Spring中的设计模式

  • 第一种:简单工厂

    有叫做静态工厂方法(StaticFactory Method)模式,但不属于23种GOF设计模式之一。简单工厂模式的实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类。Spring中的BeanFactory就是简单工厂模式的体现,根据传入一个唯一标识来获得bean对象,但是否是在传入参数前还是后创建这个对象要根据具体情况来定。

  • 第二种:工厂方法(Factory Method)

    通常由应用程序直接使用new创建新的对象,为了将对象的创建和使用相分离,采用工厂模式,即应用程序将对象的创建及初始化职责交给工厂对象。一般情况下,应用程序有自己的工厂来创建bean,如果将应用程序自己的工厂对象交给Spring管理,那么Spring管理的就不是普通的bean,而是工厂bean。

  • 第三种:单例模式(Singleton)

    保证一个类仅有一个实例,并提供一个访问它的全局访问点。

    Spring中的单例模式完成了后半句话,即提供了全局的访问点BeanFactory。但没有从构造器级别去控制单例,这是因为spring管理的是是任意的java对象。

  • 第四种:适配器(Adapter)

    在Spring的Aop中,使用的Advice(通知)来增强被代理类的功能。Spring实现这一AOP功能的原理就使用代理模式(1、JDK动态代理。2、CGLib字节码生成技术代理。)对类进行方法级别的切面增强,即生成被代理类的代理类, 并在代理类的方法前,设置拦截器,通过执行拦截器中的内容增强了代理方法的功能,实现的面向切面编程。

  • 第五种:包装器(Decorator)

    在我们的项目中遇到这样一个问题:我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。我们以往在spring和hibernate框架中总是配置一个数据源,因而sessionFactory的dataSource属性总是指向这个数据源并且恒定不变,所有DAO在使用sessionFactory的时候都是通过这个数据源访问数据库。但是现在,由于项目的需要,我们的DAO在访问sessionFactory的时候都不得不在多个数据源中不断切换,问题就出现了:如何让sessionFactory在执行数据持久化的时候,根据客户的需求能够动态切换不同的数据源?我们能不能在spring的框架下通过少量修改得到解决?是否有什么设计模式可以利用呢? 首先想到在spring的applicationContext中配置所有的dataSource。这些dataSource可能是各种不同类型的,比如不同的数据库:Oracle、SQL Server、MySQL等,也可能是不同的数据源:比如apache 提供的org.apache.commons.dbcp.BasicDataSource、spring提供的org.springframework.jndi.JndiObjectFactoryBean等。然后sessionFactory根据客户的每次请求,将dataSource属性设置成不同的数据源,以到达切换数据源的目的。
    spring中用到的包装器模式在类名上有两种表现:一种是类名中含有Wrapper,另一种是类名中含有Decorator。基本上都是动态地给一个对象添加一些额外的职责。

  • 第六种:代理(Proxy)

    为其他对象提供一种代理以控制对这个对象的访问。 从结构上来看和Decorator模式类似,但Proxy是控制,更像是一种对功能的限制,而Decorator是增加职责。
    spring的Proxy模式在aop中有体现,比如JdkDynamicAopProxy和Cglib2AopProxy。

  • 第七种:观察者(Observer)

    定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
    spring中Observer模式常用的地方是listener的实现。如ApplicationListener。

  • 第八种:策略(Strategy)

    定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。

  • 第九种:模板方法(Template Method)

    定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
    Template Method模式一般是需要继承的。这里想要探讨另一种对Template Method的理解。spring中的JdbcTemplate,在用这个类时并不想去继承这个类,因为这个类的方法太多,但是我们还是想用到JdbcTemplate已有的稳定的、公用的数据库连接,那么我们怎么办呢?我们可以把变化的东西抽出来作为一个参数传入JdbcTemplate的方法中。但是变化的东西是一段代码,而且这段代码会用到JdbcTemplate中的变量。怎么办?那我们就用回调对象吧。在这个回调对象中定义一个操纵JdbcTemplate中变量的方法,我们去实现这个方法,就把变化的东西集中到这里了。然后我们再传入这个回调对象到JdbcTemplate,从而完成了调用。这可能是Template Method不需要继承的另一种实现方式吧。

5.什么是控制反转

控制反转(Inversion of Control 英文缩写为IOC)把创建对象的权利交给框架,是框架的重要特征,并非面向对象编程的专用术语。它包括依赖注入(Dependency Injection,简称DI)和依赖查找(Dependency Lookup)。

6.控制反转的作用

原来我们在获取对象时,都是采用new的方式。是主动的。

在这里插入图片描述

现在我们我们获取对象时,只需要跟工厂要,工厂会为我们查找和创建对象。时被动的。

在这里插入图片描述

这种被动接收的方式获取对象的思想就是控制反转,它时Spring框架的核心思想之一。

7.如何使用控制反转进行解耦

  1. 导入spring的依赖

  2. 在类的根路径下创建一个xml配置文件

  3. 在配置文件中配置service和dao

    <!-- bean 标签:用于配置让 spring 创建对象,并且存入 ioc 容器之中
     id 属性:对象的唯一标识。
     class 属性:指定要创建对象的全限定类名
    -->
    <!-- 配置 service --> 
    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"></bean>
    <!-- 配置 dao --> 
    <bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl"></bean>
    

    8.BeanFactory和ApplicationContext的区别

    BeanFactory是Spring容器的顶层接口

    ApplicationContext是BeanFactory的子接口

    区别:创建对象的时间点不一样

    ​ ApplicationContext:只要一读取配置文件,默认情况下就会创建对象。

    ​ BeanFactory:什么时候使用,什么时候创建对象。

9.ApplicationContext接口的实现类

  • ClassPathXmlApplicationContext:从类的根路径下加载配置文件(推荐)

  • FileSystemXmlApplicationContex:从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置

  • AnnotationConfigApplicationContext:使用注解配置容器对象时,需要使用此类来创建Spring容器,用它来读取注解。

10.怎么把对象给Spring容器管理

在配置文件中使用bean标签配置对象。

bean标签

作用:

​ 用于配置对象让 spring 来创建的。

​ 默认情况下它调用的是类中的无参构造函数。如果没有无参构造函数则不能创建成功。

属性:

  • id:给对象在容器中提供一个唯一标识。用于获取对象。
  • class:指定类的全限定类名。用于反射创建对象。默认情况下调用无参构造函数。
  • scope:指定对象的作用范围。
  • singleton :默认值,单例的.
  • prototype :多例的.
  • request :WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中.
  • session :WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中.
  • global session :WEB 项目中,应用在 Portlet 环境.如果没有 Portlet 环境那么globalSession 相当于 session.
  • init-method:指定类中的初始化方法名称。
  • destroy-method:指定类中销毁方法名称。

11.bean的作用范围和生命周期

  • 单例对象:scope=“singleton”

    一个应用只有一个对象的实例,它的作用范围就是整个引用。

    生命周期:

    • 对象出生:当应用加载,创建容器时,对象就被创建了。
    • 对象活着:只要容器在,对象就一直活着。
    • 对象死亡:当应用卸载时,销毁容器时,对象就被销毁了。
  • 多例对象:scope=“prototype”

    每次访问对象时,都会重新创建对象实例。

    生命周期:

    • 对象出生:使用对象时,就会创建一个新的对象实例。

    • 对象活着:只要对象在使用中,就一直活着。

    • 对象死亡:当对象长时间不用时,被java的垃圾回收器回收了。

12.实例化Bean的三种方式

  • 第一种:使用无参构造函数

    <!--在默认情况下:
    它会根据默认无参构造函数来创建类对象。如果 bean 中没有默认无参构造函数,将会创建失败。
    --> 
    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"/>
    
  • 第二种:spring管理静态工厂-使用静态工厂的方法创建对象

    /**
    * 模拟一个静态工厂,创建业务层实现类
    */
    public class StaticFactory {
          
          
        public static IAccountService createAccountService(){
          
          
            return new AccountServiceImpl();
        } 
    }
    
<!-- 此种方式是:
使用 StaticFactory 类中的静态方法 createAccountService 创建对象,并存入 spring 容器
id 属性:指定 bean 的 id,用于从容器中获取
class 属性:指定静态工厂的全限定类名
factory-method 属性:指定生产对象的静态方法
-->
<bean id="accountService"
 class="com.itheima.factory.StaticFactory"
 factory-method="createAccountService"></bean>
  • 第三种:spring管理实例工厂-使用实例工厂的方法创建对象

    /**
    * 模拟一个实例工厂,创建业务层实现类
    * 此工厂创建对象,必须现有工厂实例对象,再调用方法
    */
    public class InstanceFactory {
          
          
    	public IAccountService createAccountService(){
          
          
    		return new AccountServiceImpl();
    	}	
    }
    
    <!-- 此种方式是:
    先把工厂的创建交给 spring 来管理。
    然后在使用工厂的 bean 来调用里面的方法
    factory-bean 属性:用于指定实例工厂 bean 的 id。
    factory-method 属性:用于指定实例工厂中创建对象的方法。
    --> 
    <bean id="instancFactory" class="com.itheima.factory.InstanceFactory"></bean> 
    <bean id="accountService"
     factory-bean="instancFactory"
     factory-method="createAccountService"></bean>
    

13.Spring的依赖注入

构造函数注入

  • 通过配置的方式让spring框架为我们注入构造函数的值。
public class AccountServiceImpl implements IAccountService {
    
    
	private String name;
	private Integer age;
	private Date birthday;
	public AccountServiceImpl(String name, Integer age, Date birthday) {
    
    
		this.name = name;
		this.age = age;
		this.birthday = birthday; 
	}
	@Override
	public void saveAccount() {
    
    
		System.out.println(name+","+age+","+birthday);
	} 
}
<!-- 使用构造函数的方式,给 service 中的属性传值
要求:
类中需要提供一个对应参数列表的构造函数。
涉及的标签:
constructor-arg
属性:
index:指定参数在构造函数参数列表的索引位置
type:指定参数在构造函数中的数据类型
name:指定参数在构造函数中的名称 用这个找给谁赋值
=======上面三个都是找给谁赋值,下面两个指的是赋什么值的==============
value:它能赋的值是基本数据类型和 String 类型
ref:它能赋的值是其他 bean 类型,也就是说,必须得是在配置文件中配置过的 bean
-->
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
    <constructor-arg name="name" value="张三"></constructor-arg>
    <constructor-arg name="age" value="18"></constructor-arg>
    <constructor-arg name="birthday" ref="now"></constructor-arg>
</bean>
<bean id="now" class="java.util.Date"></bean>

set方法注入

  • ​ 顾名思义,就是在类中提供需要注入成员的set方法。

  • public class AccountServiceImpl implements IAccountService {
          
          
    	private String name;
    	private Integer age;
    	private Date birthday;
    	public void setName(String name) {
          
          
    		this.name = name; 
        }
    	public void setAge(Integer age) {
          
          
    	this.age = age; 
        }
    	public void setBirthday(Date birthday) {
          
          
    		this.birthday = birthday; 
        }
    	@Override
    	public void saveAccount() {
          
          
    		System.out.println(name+","+age+","+birthday);
    	} 
    }
    
    <!-- 通过配置文件给 bean 中的属性传值:使用 set 方法的方式
    涉及的标签:
    property
    属性:
    name:找的是类中 set 方法后面的部分
    ref:给属性赋值是其他 bean 类型的
    value:给属性赋值是基本数据类型和 string 类型的
    实际开发中,此种方式用的较多。
    --> 
    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"> 
        <property name="name" value="test"></property> 
        <property name="age" value="21"></property> 
        <property name="birthday" ref="now"></property>
    </bean> 
    <bean id="now" class="java.util.Date"></bean>
    

    注入集合属性

    • 给类中的集合成员传值,也是用set方法注入,只不过变量的数据类型都是集合(数组、List、Set、Map、Properties)。

    • public class AccountServiceImpl implements IAccountService {
              
              
          private String[] myStrs;
      private List<String> myList;
          private Set<String> mySet;
        private Map<String, String> myMap;
          private Properties myProps;
      
          public void setMyStrs(String[] myStrs) {
              
              
              this.myStrs = myStrs;
          }
      
          public void setMyList(List<String> myList) {
              
              
              this.myList = myList;
          }
      
          public void setMySet(Set<String> mySet) {
              
              
              this.mySet = mySet;
          }
      
          public void setMyMap(Map<String, String> myMap) {
              
              
              this.myMap = myMap;
          }
      
          public void setMyProps(Properties myProps) {
              
              
              this.myProps = myProps;
          }
      
          @Override
          public void saveAccount() {
              
              
              System.out.println(Arrays.toString(myStrs));
              System.out.println(myList);
              System.out.println(mySet);
              System.out.println(myMap);
              System.out.println(myProps);
          }
      }
      
      <!-- 注入集合数据
      List 结构的:
      array,list,set
      Map 结构的
      map,entry,props,prop
      --> 
      <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
      <!-- 在注入集合数据时,只要结构相同,标签可以互换 -->
      <!-- 给数组注入数据 --> 
          <property name="myStrs"> 
              <set>
                  <value>AAA</value> 
                  <value>BBB</value> 
                  <value>CCC</value>
      		</set>
      	</property>
      <!-- 注入 list 集合数据 --> 
          <property name="myList"> 
              <array> 
                  <value>AAA</value> 
                  <value>BBB</value> 
                  <value>CCC</value>
      		</array>
      	</property>
      <!-- 注入 set 集合数据 --> 
          <property name="mySet"> 
              <list>
                  <value>AAA</value> 
                  <value>BBB</value> 
                  <value>CCC</value>
      	</list>
      	</property>
      <!-- 注入 Map 数据 --> 
          <property name="myMap"> 
              <props> 
                  <prop key="testA">aaa</prop> 
                  <prop key="testB">bbb</prop>
      		</props>
      	</property>
      <!-- 注入 properties 数据 -->
          <property name="myProps"> 
              <map>
                  <entry key="testA" value="aaa"></entry> 
                  <entry key="testB"> 
                      <value>bbb</value>
                  </entry>
              </map>
          </property>
      </bean>
      

    14.Spring中的常用注解

    用于创建对象的:相当于:

    • @Controller用来注入控制层组件
    • @Service用来注入服务层组件
    • @Repository用来注入持久层组件
    • @Component用来注入一般的组件

    用于注入数据的:相当于:

    • @Autowired:自动按类型注入
    • @Qualifier:在自动按类型注入基础上,按照Bean的id进行注入。(在给字段注入时不能单独使用,必须和@Autowired一起使用)
    • @Resource:直接按照Bean的id注入
    • @Value:注入基本数据类型和String类型

    用于改变作用范围的:相当于:<bean id="" class="“scope=”">

    • @Scope:指定bean的作用范围 value取值[singleton prototype request session globalsession]

    和生命周期有关的:相当于:

    • @PostConstruct:用于指定初始方法
    • @PreDestroy:用于指定销毁方法

在这里插入图片描述

用注解配置Spring的配置类:

  • @Configuration:用来指定该类是一个spring配置类,当创建容器时会从该类上加载注解
  • @ComponentScan:用于指定sprign在初始化容器时要扫描的包(<context:component-scan base-package=“com.itheima”/>)
  • @Bean:该注解只能写在方法上,表明用该方法创建一个对象,并放入spring的容器中
  • @PropertySource:用于加载properties文件中的配置
  • @Import:用于导入其他配置类

猜你喜欢

转载自blog.csdn.net/u013456390/article/details/114450221