zoukankan      html  css  js  c++  java
  • 面试题----spring

    Spring是什么?

      spring 可以看成一个大的集合,他由许多开源框架和组件组成,是一个统称。而我们平常学习的是springframework,springframework 是sping 里面的一个开源框架。

      springframework主要由以下几个模块组成:  

        Spring Core:核心类库,提供IOC服务,Spring AOP:AOP服务;Spring Context:提供框架式的Bean访问方式,以及企业级功能(JNDI、定时任务等);数据访问(jdbc,对现有的orm框架支持),Spring Web:提供了基本的面向Web的综合特性,例如多方文件上传;Spring MVC:提供面向Web应用的Model-View-Controller实现。

    Spring 的优点?

      (1)spring属于低侵入式设计,代码的污染极低;

      (2)spring的DI机制将对象之间的依赖关系交由框架处理,减低组件的耦合性;

      (3)Spring提供了AOP技术,支持将一些通用任务,如安全、事务、日志、权限等进行集中式管理,从而提供更好的复用。

      (4)spring对于主流的应用框架提供了集成支持。

    Spring的AOP理解

      传统的OOP开发的代码逻辑是自上而下,而这自上而下的过程中可能会产生横切性的问题,比如事务管理,日志等,这些是与主逻辑代码关联不是特别大,如果将这些代码放到主逻辑中,会时代吗臃肿和冗余,不利于维护,AOP编程就可以将这些横切性的问题和主业务之间进行分离,从而启动解耦的目的。

      AOP实现的关键在于 代理模式,AOP代理主要分为静态代理和动态代理。静态代理的代表为AspectJ;动态代理则以Spring AOP为代表。

      (1)AspectJ是静态代理的增强,所谓静态代理,就是AOP框架会在编译阶段生成AOP代理类,因此也称为编译时增强,他会在编译阶段将AspectJ(切面)织入到Java字节码中,运行的时候就是增强之后的AOP对象。

      (2)Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。

      ①JDK动态代理只提供接口的代理,不支持类的代理。核心InvocationHandler接口和Proxy类,InvocationHandler 通过invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起;接着,Proxy利用 InvocationHandler动态创建一个符合某一接口的的实例,  生成目标类的代理对象。

      ②如果代理类没有实现 InvocationHandler 接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现AOP。CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。

    (3)静态代理与动态代理区别在于生成AOP代理对象的时机不同,相对来说AspectJ的静态代理方式具有更好的性能,但是AspectJ需要特定的编译器进行处理,而Spring AOP则无需特定的编译器处理。

    BeanFactory和ApplicationContext有什么区别?

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

    (1)BeanFactory:

      是Spring里面最底层的接口,包含了各种Bean的定义,读取bean配置文档,管理bean的加载、实例化,控制bean的生命周期,维护bean之间的依赖关系。ApplicationContext接口作为BeanFactory的派生,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能:

        ①继承MessageSource,因此支持国际化。

        ②统一的资源文件访问方式。

        ③提供在监听器中注册bean的事件。

        ④同时加载多个配置文件。

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

    (2)

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

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

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

    (3)

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

    (4)

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

     6、请解释Spring Bean的生命周期?

    实例化和初始化的区别:实例化是在jvm的堆中创建了这个对象实例,此时它只是一个空的对象,所有的属性为null。而初始化的过程就是讲对象依赖的一些属性进行赋值之后,调用某些方法来开启一些默认加载。比如spring中配置的数据库属性Bean,在初始化的时候就会将这些属性填充,比如driver、jdbcurl等,然后初始化连接

     (1)实例化Bean  

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

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

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

    (3)处理Aware接口

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

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

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

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

    (4)BeanPostProcessor

      如果想对Bean进行一些自定义的处理,那么可以让Bean实现了BeanPostProcessor接口,那将会调用postProcessBeforeInitialization(Object obj, String s)方法。

    (5)InitializingBean 与 init-method:

       如果Bean在Spring配置文件中配置了 init-method 属性,则会自动调用其配置的初始化方法。如果Bean实现了InitializingBean接口并且调用afterPropertiesSet()方法。

    (6)BeanPostProcessor:

       如果想对Bean进行一些实例化后的处理,那么可以让Bean实现了BeanPostProcessor接口,那将会调用postProcessAfterInitialization(Object obj, String s)方法。

    (7)DisposableBean

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

    (8)destroy-method

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

    对于postProcessBeforeInitialization和postProcessAfterInitialization方法的调用存在一些自己不了解的问题

    1、xml配置

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
        <bean id="test" class="com.zy.Test"></bean>
    </beans>
    

    2、Test类实现BeanPostProcessor接口

    public class Test implements  BeanPostProcessor{
        public String a;
        public String getA() {
            return a;
        }
        public void setA(String a) {
            this.a = a;
        }
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            System.out.println(((Test1) bean));
            return bean;
        }
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            //传入的却是Test1这个对象?
            System.out.println(((Test1) bean));
            return bean;
        }
    }

     3、测试发现并没有调用这两个方法。

    public class Demo {
        public static void main(String[] args) {
            ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
            applicationContext.getBean("test");
        }
    }
    

    4、继续测试

      添加类(注意这个类不要实现)

    package com.zy;
    public class Test1{
    }
    

      修改xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
        <bean id="test" class="com.zy.Test"></bean>
        <bean class="com.zy.Test1"></bean>
    </beans>
    

      测试方法不变,发现postProcessAfterInitialization()方法传入的bean竟然是Test1

    5、继续测试

      将Test1类实现方法BeanPostProcessor,测试发现 postProcessAfterInitialization()方法有没有被调用.......

    6、继续测试,将Test修改为原型就可以触发BeanPostProcessor接口方法了

    7、spring事件机制(订阅发布模式 == 观察者模式)

      ApplicationContext事件机制是观察者设计模式的 实现,通过ApplicationEvent类和ApplicationListener接口,可以实现ApplicationContext事件处理。 如果容器中有一个ApplicationListener Bean,每当ApplicationContext发布ApplicationEvent时,ApplicationListener Bean将自动被触发。

      果我们的监听器继承ApplicationListener<ApplicationEvent>;相当于我们这个监听器监听的是Spring里面所有的消息

      两个重要成员

        ApplicationEvent:容器事件,必须由ApplicationContext发布;

        ApplicationListener:监听器,可由容器中的任何监听器Bean担任。

      1. 定义容器事件

    package com.cxg.test.springPlatfrom;  
      
    import org.springframework.context.ApplicationEvent;  
    public class EmailEvent extends ApplicationEvent{  
        private static final long serialVersionUID = 1L;  
        //属性  
        private String address;  
        private String text;  
        //构造方法  
        public EmailEvent(Object source) {  
            super(source);  
        }  
        public EmailEvent(Object source, String address, String text) {  
            super(source);  
            this.address = address;  
            this.text = text;  
        }  
        //getter和setter设置  
        public String getAddress() {  
            return address;  
        }  
        public void setAddress(String address) {  
            this.address = address;  
        }  
        public String getText() {  
            return text;  
        }  
        public void setText(String text) {  
            this.text = text;  
        }  
    }  
    

      2. 定义监听器

    package com.cxg.test.springPlatfrom;  
      
    import org.springframework.context.ApplicationEvent;  
    import org.springframework.context.ApplicationListener;  
    /** 
     * Title: email之监听类 
     * 容器事件的监听器类必须实现ApplicationListener接口,实现该接口就必须实现 
     */  
    public class EmailNotifier implements ApplicationListener<ApplicationEvent>{ //可以直接写成ApplicationListener<EmailEvent>:表示只监听EmailEvent事件
      
        @Override  
        public void onApplicationEvent(ApplicationEvent event) {  
            if(event instanceof EmailEvent){  
                EmailEvent emailEvent = (EmailEvent) event;  
                System.out.println("email's address:"+emailEvent.getAddress());  
                System.out.println("email's text:"+emailEvent.getText());  
            } else {  
                System.out.println("the Spring's event:"+event);  
            }  
        }  
      
    }  
    

      3. 将监听器注入到spring容器

    <bean class="com.cxg.test.springPlatfrom.EmailNotifier" />
    

      4. 测试

    package com.cxg.test.springPlatfrom;  
      
    import org.springframework.context.ApplicationContext;  
    import org.springframework.context.support.ClassPathXmlApplicationContext;  
    public class SpringTest {  
        public static void main(String arg[]){  
            //读取Spring容器的配置文件  
            @SuppressWarnings("resource")  
            ApplicationContext applicationContext=new ClassPathXmlApplicationContext("application.xml");  
            //创建一个事件对象  
            EmailEvent emailEvent = new EmailEvent("hello Spring!", "cxg@126.com", "This is SpringApplicatoinContext test!");  
            //主动触发事件监视机制  
            applicationContext.publishEvent(emailEvent);  
        }  
    }  
    

      应用,监听spring启动完成之后的事件

    @Component //applicaiton.xml注意扫描这个包
    public class InitAdminListener implements
    		ApplicationListener<ContextRefreshedEvent> {
    
    	@Autowired
    	private LogininfoService logininfoService;
    
    	/**
    	 * 这个方法就是监听到指定的事件之后,要做的事情; ApplicationEvent就是这次监听到的事件对象
    	 */
    	@Override
    	public void onApplicationEvent(ContextRefreshedEvent event) {
    		logininfoService.initAdmin();
    	}
    }

    8、 解释Spring支持的几种bean的作用域。

    (1)singleton:默认,每个容器中只有一个bean的实例,单例的模式由BeanFactory自身来维护。

    (2)prototype:为每一个bean请求提供一个实例。

    (3)request:为每一个网络请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回收。

    (4)session:与request范围类似,确保每个session中有一个bean的实例,在session过期后,bean会随之失效。

    (5)global-session:全局作用域,global-session和Portlet应用相关。当你的应用部署在Portlet容器中工作时,它包含很多portlet。如果你想要声明让所有的portlet共用全局的存储变量的话,那么这全局变量需要存储在global-session中。全局作用域与Servlet中的session作用域效果相同。

    9、Spring框架中的单例Beans是线程安全的么?

      Spring框架并没有对单例bean进行任何多线程的封装处理。关于单例bean的线程安全和并发问题需要开发者自行去搞定。但实际上,大部分的Spring bean并没有可变的状态(比如Serview类和DAO类),所以在某种程度上说Spring的单例bean是线程安全的。如果你的bean有多种状态的话(比如 View Model 对象),就需要自行保证线程安全。最浅显的解决办法就是将多态bean的作用域由“singleton”变更为“prototype”。

    10、Spring如何处理线程并发问题?

      在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域,因为Spring对一些Bean中非线程安全状态采用ThreadLocal进行处理,解决线程安全问题。

      ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。同步机制采用了“时间换空间”的方式,仅提供一份变量,不同的线程在访问前需要获取锁,没获得锁的线程则需要排队。而ThreadLocal采用了“空间换时间”的方式。

      ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。

    public class Test {
        static ThreadLocal<String> stringThreadLocal = new ThreadLocal<>();
        public static void main(String[] args) {
            //当前线程给ThreadLocal设置值,只能当前线程可以访问
            stringThreadLocal.set("1");
            System.out.println(stringThreadLocal.get());
            new Thread(()->{
                System.out.println(stringThreadLocal.get());
            }).start();
        }
    }
    

      

    11、Spring基于xml注入bean的几种方式:

    (1)Set方法注入;

    (2)构造器注入:①通过index设置参数的位置;②通过type设置参数类型;

    (3)静态工厂注入;

    (4)实例工厂;

    详细:https://blog.csdn.net/a745233700/article/details/89307518

      https://blog.csdn.net/lyt_7cs1dn9/article/details/65631479

    12、spring注解有哪些?

    参考:https://blog.csdn.net/weixin_39805338/article/details/80770472

    13、Spring事务的实现方式和实现原理:

       Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的。真正的数据库层的事务提交和回滚是通过binlog或者redo log实现的。

      

  • 相关阅读:
    远程桌面连接(mstsc)
    google浏览器网页截取全屏
    Guava缓存工具类封装和使用
    The project description file (.project) for XXX is missing
    国际化常用时间格式并进行格式转换
    MySQL 查询索引失效及如何进行索引优化
    The requested URL could not be retrieved
    java.util.zip.ZipException: invalid LOC header (bad signature)
    熊猫破解 --- 软件分享站
    「在线宣传利器」上传文档转为翻页电子书,生成链接二维码
  • 原文地址:https://www.cnblogs.com/yanxiaoge/p/11628945.html
Copyright © 2011-2022 走看看