23.spring面试

1.spring是什么?
    轻量级的开源j2EE框架,是一个容器框架,用来封装javabean,中间层框架,可以起到一个连接作用,比如说是把srtus和hibernate粘合一起使用
    可以使企业开发更快、更简洁
    spring是一个轻量级的控制反转(IOC)和面向切面(AOP)的容器框架
        -从大小与开销上而言Spring都是轻量级的
        -通过控制反转(IOC)技术达到松耦合的目的
        -提供了面向切面编程的丰富支持,允许通过分离应用的业务和逻辑和系统服务进行内聚性开发(例如在方法执行前后加日志打印,可以利用切面编程,这样
         就不用在代码里面写凌乱的日志打印了)
        -包含并管理对象(Bean) 的配置和生命周期,其意义就是一个容器,不需要我们手动创建对象和销毁对象
       -将简单的组件配置、组合成复杂的应用,其意义就是一个框架 
       
2.谈谈你对AOP的理解:
    系统是由不同的组件所组成的,每一个组件负责一块特定的功能,除了实现自身核心功能外,这些组还经常承担着额外的职责。
    例如日志、事务管理、安全这样的核心服务经常融入到自身具有核心业务逻辑的组件中。
    这些服务经常被称为横切关注点,因为他们会跨越系统的多个组件。
    
    当我们需要为分散的对象引入公共行为的时候,OOP(面向对象)则显得无能为力。也就是说,OOP允许你定义从上到下的关系,但并不适合从左到右的关系。比如日志功能。
    日志代码往往水平的散布在所有对象层次中,而与他散步到的对象核心功能无关
    
    在OOP设计中,会导致大量代码的重复,而不利于模块的重用
    AOP:将程序中的交叉业务逻辑(例如安全、日志、事务等),封装成一个切面,然后注入到目标对象中(具体业务逻辑中)去。
       AOP可以对某个对象或者某些对象的功能进行加强,比如对象中的方法进行加强,可以在执行某个方法之前、之后等额外做一些事情,
       
3.谈谈你对IOC的理解:
    3.1容器概念
    3.2控制反转
    3.3依赖注入
    
    3.1 IOC容器:
        实际上就是个map(key,value),里面存的是各种对象(在xml里配置bean的节点、@repository、@service、@controller、@component),在项目启动
        时会读取配置文件的bena节点,根据权限制类名使用反射创建对象并放入到map里,扫描到打上上述注解的类还是通过反射创建对象放入到map中
        这时,map中就有各种对象了,接下来我们在代码中需要用到容器中的对象时,再通过id注入(@autowired\@resource等注解,xml里bean节点内的ref属性
        项目启动的时候会读取xml节点ref属性根据id注入,也会扫描这些注解,根据类型或者id注入;id就是对象名)
        ---->谈及person和book,大范围和小范围在xml中配置顺序不同,导致的方法执行顺序不同(构造方法/set方法)
    
    3.2 控制反转
        没有引入IOC容器之前,对象A依赖对象B,那么对象A在初始化或者运行到某一点的时候,自己必须主动去创建对象B或者使用已经创建的对象B
        无论是创建还是使用对象B,控制权都在自己手上
        
        引入IOC容器之后,对象A和对象B之间失去了直接联系,当对象A运行到需要对象B的时候,IOC容器会主动创建一个对象B到A需要的地方
        
        通过前后对比,不难看出,对象A获得对象B的过程,由主动行为变为了被动行为,控制权颠倒过来了,这就是控制反转名称的由来
        全部对象的控制权全部上缴给"第三方"IOC容器,所以IOC容器成了整个系统的关键核心,把系统的所有对象粘合在一起发挥作用
    
    3.3 依赖注入
        "获取依赖对象的过程反转了"。控制反转之后,获取依赖对象的额过程由自身的管理变为了由IOC容器自动注入,依赖注入是实现IOC的方法,就是由
        IOC容器运行期间,动态的将某种依赖关系注入到对象中
    
 
4.BeanFactory和ApplicationContext有什么区别?
    ApplicationContext是BeanFactory的子接口
    ApplicaitonContext提供了更完整的功能:
        1.继承MessageSource,因此支持国际化
        2.统一的资源访问方式
        3.提供在监听器中注册bean的事件
        4.同时加载多个配置文件
        5.载入多个有继承关系的上下文,是每一个上下文都专注一个特定的层次,比如应用的web层   
    1.BeanFactory采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean),才对该Bean进行加载实例化。
      这样,我们就不能发现一些存在的Spring配置问题。如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常。
    
    2.ApplicationContext,它时在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误,
      这样有利于检查所依赖属性是否注入。ApplicationContext启动后预载入所有的单例Bean,通过预载入单例Bean,确保当你需要的时候,
      你就不用等待,因为它们已经创建好了。
    
    相对于基本的BeanFactory,ApplicationContext唯一的不足是占用内存空间。当应用程序配置Bean较多时,程序启动较慢。
    BeanFactory通常以编程的方式被创建,ApplicationContext还能以声明的方式创建,如使用ContextLoader.
    BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryProcessor的使用,
    但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。

5.描述一下springbean的生命周期
    1.解析类得到BeanDefinition
    2.如果有多个构造方法,则需要推断构造方法
    3.确定好构造方法后,进行实例化得到一个对象
    4.对对象中加了@Autowired注解的属性进行属性填充
    5.回调Aware方法,比如BeanNameAware、BeanFactoryAware
    6.调用BeanPostProcessor的初始化前的方法
    7.调用初始化方法
    8.调用BeanPostProcessor的初始化后的方法,在这里进行AOP
    9.如果当前创建的bean是单例的则会把bean放入到单例池中
    10.使用bean
    11.Spring容器关闭是调用DisposableBean中的destroy()方法

6.解释bean的作用域
    https://blog.csdn.net/weidaoyouwen/article/details/80503575
    Spring Bean 中所说的作用域,在配置文件中即是“scope”
    在面向对象程序设计中作用域一般指对象或变量之间的可见范围。
    而在Spring容器中是指其创建的Bean对象相对于其他Bean对象的请求可见范围。
    
    在Spring 容器当中,一共提供了5种作用域类型,在配置文件中,通过属性scope来设置bean的作用域范围。
    1.singleton:当Bean的作用域为singleton的时候,Spring容器中只会存在一个共享的Bean实例,
      所有对Bean的请求只要id与bean的定义相匹配,则只会返回bean的同一实例。单一实例会被存储在单例缓存中,为Spring的缺省作用域。
        <bean id="userInfo" class="cn.lovepi.UserInfo" scope="singleton"></bean>
   2.prototype
       每次对该Bean请求的时候,Spring IoC都会创建一个新的作用域。
       对于有状态的Bean应该使用prototype,对于无状态的Bean则使用singleton
           <bean id="userInfo" class="cn.lovepi.UserInfo" scope=" prototype "></bean>  
   3.request
       Request作用域针对的是每次的Http请求,Spring容器会根据相关的Bean的
        定义来创建一个全新的Bean实例。而且该Bean只在当前request内是有效的。
            <bean id="userInfo" class="cn.lovepi.UserInfo" scope=" request "></bean> 
    
    4.session
        针对http session起作用,Spring容器会根据该Bean的定义来创建一个全新的Bean的实例。
        而且该Bean只在当前http session内是有效的。
        <bean id="userInfo" class="cn.lovepi.UserInfo" scope=" session "></bean>
        
    5.global session:类似标准的http session作用域,不过仅仅在基于portlet的web应用当中才有意义。Portlet规范定义了全局的Session的概念。
    他被所有构成某个portlet外部应用中的各种不同的portlet所共享。在global session作用域中所定义的bean被限定于全局的portlet session的生命周期范围之内
        <bean id="userInfo" class="cn.lovepi.UserInfo"scope=“globalSession"></bean>  

1)singleton作用域 

是指在Spring IoC容器中仅存在一个Bean的示例,Bean以单实例的方式存在,单实例模式是重要的设计模式之一,在Spring中对此实现了超越,可以对那些非线程安全的对象采用单实例模式。
接下来看一个示例:

1. <bean id="car" class="cn.lovepi.Car" scope="singleton"></bean>  
2. <bean id="boss1" class="cn.lovepi .Boss” p:car-ref=“car"></bean>  
3. <bean id="boss2" class="cn.lovepi .Boss” p:car-ref=“car"></bean>  
4. <bean id="boss3" class="cn.lovepi .Boss” p:car-ref=“car"></bean> 
在1中car这个Bean生命周期声明为了singleton模式,其他的bean如2,3,4引用了这个Bean。在容器中boss1、boss2、boss3的属性都指向同一个bean car。如下图所示:

不仅在容器中通过对象引入配置注入引用相同的car bean,通过容器的getBean()方法返回的实例也指向同一个bean。
在默认的情况下Spring的ApplicationContext容器在启动的时候,自动实例化所有的singleton的bean并缓存于容器当中。
 
虽然启动时会花费一定的时间,但是他却带来了两个好处:
  1. 对bean提前的实例化操作,会及早发现一些潜在的配置的问题。
  2. Bean以缓存的方式运行,当运行到需要使用该bean的时候,就不需要再去实例化了。加快了运行效率。

 

2)prototype作用域
是指每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行new Bean()的操作。在默认情况下,Spring容器在启动时不实例化prototype的Bean
接下来看一个示例:
1. <bean id=“car" class="cn.lovepi.Car" scope=“prototype"></bean>  
2. <bean id=“boss1" class="cn.lovepi.Boss” p:car-ref=“car"></bean>  
3. <bean id=“boss2" class="cn.lovepi.Boss” p:car-ref=“car"></bean>  
4. <bean id=“boss3" class="cn.lovepi.Boss” p:car-ref=“car"></bean>  
通过以上的配置,Boss1、boss2、boss3所引用的都是一个新的car的实例,每次通过容器getBean()方法返回的也是一个新的car实例。如下图所示:

在默认情况下,Spring容器在启动时不实例化prototype这种bean。此外,Spring容器将prototype的实例交给调用者之后便不在管理他的生命周期了。
 
3)当用户使用Spring的WebApplicationContext时,还可以使用另外3种Bean的作用域,即request,session和globalSession。
在使用Web应用环境相关的Bean作用域时,必须在Web容器中进行一些额外的配置:
 
对于低版本Web容器配置:
用户可以使用http过滤器来进行配置并在url-pattern中对所有的页面进行过滤。
<filter>  
    <filter-name>requestContextFilter</filter-name>  
    <filter-class>org.springframework.web.filter.RequestContextFilter</filterclass>  
</filter>  
<filter-mapping>  
    <filter-name>requestContextFilter</filter-name>  
    <url-pattern>/*</url-pattern>  
</filter-mapping>  
对于高版本Web容器配置:
使用http请求监听器来进行配置。
<listener> <listener-class>  
    org.springframework.web.context.request.RequestContextListener  
</listener-class></listener>  
ServletContextListener只负责监听web容器启动和关闭的事件,
而RequestContextListener实现了ServletRequestListener监听器接口,该监听器监听http请求事件。
Web服务器接收到的每一次请求都会通知该监听器。
 
Spring容器的启动和关闭事件由web容器的启动和关闭事件来触发。
但如果Spring容器中的bean需要request、session和globalSession作用域的支持,Spring容器本身就必须获得web容器的http请求事件。以http请求事件来驱动bean的作用域的控制逻辑,也就是说通过配置RequestContextListener接口,Spring和web容器的结合就更紧密了。

 

那么接下来简单的介绍下这三种作用域。
1.request作用域
       对应一个http请求和生命周期,当http请求调用作用域为request的bean的时候,Spring便会创建一个新的bean,在请求处理完成之后便及时销毁这个bean。
2.session作用域
       Session中所有http请求共享同一个请求的bean实例。Session结束后就销毁bean。
3.globalSession作用域
       与session大体相同,但仅在portlet应用中使用。
Portlet规范定义了全局session的概念。请求的bean被组成所有portlet的自portlet所共享。
如果不是在portlet这种应用下,globalSession则等价于session作用域。
 
接下来使用一个实例来演示下bean作用域的概念:
首先我们创建scope包,然后在包中创建两个Java Bean:老板(Boss.java)以及轿车(Car.java)

public class Boss {  
    private String name;  
    private Car car;  
  
    public Boss() {  
    }  
  
    public Car getCar() {  
        return car;  
    }  
  
    public void setCar(Car car) {  
        this.car = car;  
    }  
  
    @Override  
    public String toString() {  
        return "Boss{" +  
                "name='" + name + '\'' +  
                ", car=" + car +  
                '}';  
    }  
}  
  
public class Car {  
    private String color;  
    private String brand;  
    private double price;  
  
    public String getColor() {  
        return color;  
    }  
  
    public void setColor(String color) {  
        this.color = color;  
    }  
  
    public String getBrand() {  
        return brand;  
    }  
  
    public void setBrand(String brand) {  
        this.brand = brand;  
    }  
  
    public double getPrice() {  
        return price;  
    }  
  
    public void setPrice(double price) {  
        this.price = price;  
    }  
  
}  
接着我们创建对应的创建配置文件,在conf包中创建一个conf-scope.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"  
  xmlns:p="http://www.springframework.org/schema/p"  
  xsi:schemaLocation="http://www.springframework.org/schema/beans  
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">  
  
  <bean id="car" class="cn.lovepi.chapter03.scope.Car" scope="singleton"/>  
  <bean id="boss1" class="cn.lovepi.chapter03.scope.Boss" p:car-ref="car" />  
  <bean id="boss2" class="cn.lovepi.chapter03.scope.Boss" p:car-ref="car" />  
  <bean id="boss3" class="cn.lovepi.chapter03.scope.Boss" p:car-ref="car" />  
</beans>  
最后我们创建测试类进行测试
[java]  view plain  copy
public class Main {  
    public static void main(String[] args){  
        //加载配置文件,启动IoC容器  
        BeanFactory factory=  
                new FileSystemXmlApplicationContext("src/conf/conf-scope.xml");  
        //获得bean实例  
        Boss boss1=factory.getBean("boss1",Boss.class);  
        Boss boss2=factory.getBean("boss2",Boss.class);  
        Boss boss3=factory.getBean("boss3",Boss.class);  
        System.out.println(boss1.getCar());  
        System.out.println(boss2.getCar());  
        System.out.println(boss3.getCar());  
    }  
}  
然后将作用域设置成singleton和 prototype分别执行程序观察结果。
 
接下来再来简单的学习下在Spring当中如何自定义作用域:
在Spring 2.0中,Spring的Bean作用域机制是可以扩展的,这意味着,你不仅可以使用Spring提供的预定义Bean作用域,还可以定义自己的作用域,甚至重新定义现有的作用域(不提倡这么做,而且你不能覆盖内置的singleton和prototype作用域)
 
1.首先需要实现自定义Scope类:
      org.springframework.beans.factory.config.Scope
      首先我们得实现config.scope这个接口,要将自定义scope集成到Spring容器当中就必须要实现这个接口。接口中只有两个方法,分别用于底层存储机制获取和删除这个对象。
 
2.在实现一个或多个自定义Scope并测试通过之后,接下来便是如何让Spring容器来识别新的作用域。我们需要注册自定义Scope类,使用registerScope方法
     ConfigurableBeanFactory.registerScope(String scopeName, Scope scope)
其中:第一个参数是与作用域相关的全局唯一的名称,第二个参数是准备实现的作用域的实例。
 
3.在实现和注册自定义的scope类之后我们便来使用自定义的Scope
Scope customScope = new ThreadScope();  
beanFactory.registerScope(“thread”, customeScope);  
<bean id=“***” class=“***”  scope=“scopeName” /> 
1.spring框架的单例bean是线程安全的么?
    结论:spring中bean默认是单例模式的,框架并没有对bean进行多线程的封装处理,所以单例bean不是线程安全的。
    如果Bean是有状态的(类中有属性如count记数作为存储,并多个线程做并发操作),那就需要开发人员自己进行线程安全的保证
    最简单的办法就是改变bean的作用域,把Singleton该为protopyte这样每次请求Bean就相当于new Bean()这样就可以保证线程的安全
    
    1.有状态就是有数据存储的功能(类中有属性保存数据,多个线程共同操作这个属性,如count(记数))
    2.无状态就是不会保存数据:controller\service和dao层本身并不是线程安全的,只是如果只是调用里面的方法,而且多线程调用同一实例的方法
      会在内存中复制变量(这里涉及到jvm的线程栈),这是线程的工作内存,是线程安全的
      
   做法:不要在bean中声明任何有状态的示例变量或者类变量,如果非要如此,name就使用ThreadLocal把变量变为线程私有的,如果bean的示例变量和类变量
   需要在多个线程之间共享,那么久只能使用synchronized、lock、cas等这些实现线程同步的方法了
   
    
2.spring框架中用到了哪些设计模式?
    参考地址:
        1.https://www.cnblogs.com/kyoner/p/10949246.html
        
   
    1.工厂方法:
        Spring使用工厂模式可以通过 BeanFactory 或 ApplicationContext创建bean象
        两者对比:
        BeanFactory :延迟注入(使用到某个 bean 的时候才会注入),相比于BeanFactory来说会占用更少的内存,程序启动速度更快。
        ApplicationContext :容器启动的时候,不管你用没用到,一次性创建所有 bean 。BeanFactory 仅提供了最基本的依赖注入支持,ApplicationContext 扩展了 BeanFactory ,除了有BeanFactory的功能之外还有额外更多功能,所以一般开发人员使用ApplicationContext会更多。
        ApplicationContext的三个实现类:
            1.ClassPathXmlApplication:把上下文文件当成类路径资源。
            2.FileSystemXmlApplication:从文件系统中的 XML 文件载入上下文定义信息。
            3.XmlWebApplicationContext:从Web系统中的XML文件载入上下文定义信息。
        import org.springframework.context.ApplicationContext;
        import org.springframework.context.support.FileSystemXmlApplicationContext;
        public class App {
            public static void main(String[] args) {
                ApplicationContext context = new FileSystemXmlApplicationContext(
                        "C:/work/IOC Containers/springframework.applicationcontext/src/main/resources/bean-factory-config.xml");
                HelloApplicationContext obj = (HelloApplicationContext) context.getBean("helloApplicationContext");
                obj.getMsg();
            }
        }
        
    2.单例设计模式
        在我们的系统中,有一些对象其实我们只需要一个,比如说:线程池、缓存、对话框、注册表、日志对象、充当打印机、显卡等设备驱动程序的对象。
        事实上,这一类对象只能有一个实例,如果制造出多个实例就可能会导致一些问题的产生,比如:程序的行为异常、资源使用过量、或者不一致性的结果
        使用单例模式的好处:
            1.对于频繁使用的对象,可以省略创建对象所花费的时间,这对于那些重量级对象而言,是非常可观的一笔系统开销;
            2.由于new操作的次数减少,因而对系统内存的使用频率也会降低,这将减轻GC压力,缩短GC停顿时间

        Spring中bean的默认作用域就是singleton(单例)的,除了singleton作用域,Spring中bean还有下面几种作用域:
            1.prototype : 每次请求都会创建一个新的bean实例
            2.request : 每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP request内有效
            3.session : 每一次HTTP请求都会产生一个新的 bean,该bean仅在当前 HTTP session 内有效。
            4.global-session: 全局session作用域,仅仅在基于portlet的web应用中才有意义,Spring5已经没有了。
              Portlet是能够生成语义代码(例如:HTML)片段的小型Java Web插件。它们基于portlet容器,可以像servlet一样处理HTTP请求。
              但是,与 servlet 不同,每个 portlet 都有不同的会话
      Spring实现单例的方式:
        1.xml:<bean id="userService" class="top.snailclimb.UserService" scope="singleton"/>
        2.注解:@Scope(value = "singleton")
        Spring通过ConcurrentHashMap实现单例注册表的特殊方式实现单例模式。
    Spring实现单例的核心代码如下:
        // 通过 ConcurrentHashMap(线程安全) 实现单例注册表
        private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64);
            public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
                    Assert.notNull(beanName, "'beanName' must not be null");
                    synchronized (this.singletonObjects) {
                        // 检查缓存中是否存在实例  
                        Object singletonObject = this.singletonObjects.get(beanName);
                        if (singletonObject == null) {
                            //...省略了很多代码
                            try {
                                singletonObject = singletonFactory.getObject();
                            }
                            //...省略了很多代码
                            // 如果实例对象在不存在,我们注册到单例注册表中。
                            addSingleton(beanName, singletonObject);
                        }
                        return (singletonObject != NULL_OBJECT ? singletonObject : null);
                    }
                }
                //将对象添加到单例注册表
                protected void addSingleton(String beanName, Object singletonObject) {
                        synchronized (this.singletonObjects) {
                            this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
                        }
                    }
            }
    
    3.代理设计模式
        AOP(Aspect-Oriented Programming:面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,
        便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性
        
        Spring AOP就是基于动态代理的,如果要代理的对象实现了某个接口,那么Spring AOP会使用JDK Proxy,去创建代理对象,
        而对于没有实现接口的对象,Spring AOP会使用Cglib,这时候Spring AOP会使用Cglib生成一个被代理对象的子类来作为代理,如下图所示

当然你也可以使用AspectJ,Spring AOP以及集成了AspectJ,AspectJ应该算得上是Java生态系统中最完整的AOP框架了。
使用AOP之后我们可以把一些通用的功能抽象出来,在在需要用到的地方直接使用即可,这样大大简化了代码量。我们需要增加新功能时也方便,
这样也提高了系统扩展性。日志功能、事务管理等等场景都用到了 AOP 。
    
    Spring AOP 和 AspectJ AOP 有什么区别?
        Spring AOP 属于运行时增强,而 AspectJ 是编译时增强。 Spring AOP 基于代理(Proxying),而 AspectJ 基于字节码操作(Bytecode Manipulation)。
        Spring AOP 已经集成了 AspectJ ,AspectJ 应该算的上是 Java 生态系统中最完整的 AOP 框架了。AspectJ 相比于 Spring AOP 功能更加强大,
        但是 Spring AOP 相对来说更简单,功能更弱。
        如果我们的切面比较少,那么两者性能差异不大。但是,当切面太多的话,最好选择 AspectJ ,它比Spring AOP 快很多
        

    4.模板方法
        模板方法模式是一种行为设计模式,它定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。 
        模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤的实现方式。
         示例:
             public abstract class Template {
                //这是我们的模板方法
                public final void TemplateMethod(){
                    PrimitiveOperation1();  
                    PrimitiveOperation2();
                    PrimitiveOperation3();
                }
            
                protected void  PrimitiveOperation1(){
                    //当前类实现
                }
            
                //被子类实现的方法
                protected abstract void PrimitiveOperation2();
                protected abstract void PrimitiveOperation3();
            
            }
            public class TemplateImpl extends Template {
            
                @Override
                public void PrimitiveOperation2() {
                    //当前类实现
                }
            
                @Override
                public void PrimitiveOperation3() {
                    //当前类实现
                }
            }
    Spring 中 jdbcTemplate、hibernateTemplate 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。
    一般情况下,我们都是使用继承的方式来实现模板模式,但是 Spring 并没有使用这种方式,而是使用Callback 模式与模板方法模式配合,
    既达到了代码复用的效果,同时增加了灵活性

    4.适配器模式
        spring MVC中的适配器模式:
            在Spring MVC中,DispatcherServlet 根据请求信息调用 HandlerMapping,解析请求对应的 Handler。
            解析到对应的 Handler(也就是我们平常说的 Controller 控制器)后,开始由HandlerAdapter 适配器处理。
            HandlerAdapter 作为期望接口,具体的适配器实现类用于对目标类进行适配,Controller 作为需要适配的类。
        为什么要在 Spring MVC 中使用适配器模式? 
            Spring MVC 中的 Controller 种类众多,不同类型的 Controller 通过不同的方法来对请求进行处理。
            如果不利用适配器模式的话,DispatcherServlet 直接获取对应类型的 Controller,需要的自行来判断,像下面这段代码一样:
            if(mappedHandler.getHandler() instanceof MultiActionController){  
               ((MultiActionController)mappedHandler.getHandler()).xxx  
            }else if(mappedHandler.getHandler() instanceof XXX){  
                ...  
            }else if(...){  
               ...  
            }

 

posted @ 2022-05-08 22:26  努力的达子  阅读(47)  评论(0编辑  收藏  举报