天问的私人面试宝典03 框架

编程入门 行业动态 更新时间:2024-10-25 08:20:31

天问的私人面试宝典03 <a href=https://www.elefans.com/category/jswz/34/1770644.html style=框架"/>

天问的私人面试宝典03 框架

一、SpringMVC 框架

        Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。使用 Spring 可插入的 MVC 架构,从而在使用Spring进行WEB开发时,可以选择使用Spring的Spring MVC框架或集成其他MVC开发框架,如Struts1(现在一般不用),Struts 2(一般老项目使用)等等。

        从springmvc2.5开始引入注解方式,特别到了3.0就全面引入注解方式,号称xml零配置。spring3.0配置注解引入后也就是这个点成为了它和struts2的分水岭。随着springmvc的成熟,struts2开始落幕,趋于被市场淘汰。

注解整理

        @RequestMapping注解拦截用户请求 实现业务调用

        @Controller 将该类交给spring容器管理,同时开启SpringMVC机制。对应前后端不分离的情况

        @RequestParam  参数说明    1.name/value 接收参数的名称

                                                        2.required 默认值为true,该数据为必填项

                                                        3.defaultValue 设定数据默认值,如果参数为null则设定默认值

                                       功能:为参数设定默认值;当提交参数名称与接收参数不一致时使用

        @Accessors(chain = true)

        @Date

        @PathVariable   参数说明:1.name/value动态接收形参的数据,如果参数相同则省略不写                                                           2.必填项 required  默认为 true

        @ResponseBody  返回一个JSON串 (必须添加 get/set方法)

        返回对象之后,SpringMVC通过内部API (ObjectMapper)调用对象的getXXX()方法动态获          取属性和属性值     演化规则:getAge()~~~去掉get~~~Age()~~~首字母小写~~~age()~~~获          取属性age~~~通过getAge()动态获取属性值

        @RestController 该类中所有方法的返回值都是JSON串  =@Controller+@Responsebody

        @CrossOrigin(origins = "*")  允许跨域的注解

         将一个类声明为Spring的bean的注解4个

        @Controller 用来标识Controller层的代码 相当于将对象交给Spring管理
        @Service 用来标识Service层代码
        @Repository 用来标识持久层
        @Component 万用注解

        @Configuration 标识我是一个配置类,相当于 application.xml

        @ComponentScan("com.cy")设定包扫描路径,如果注解中只有value属性,可以省略不写

        @Bean通过该注解,可以将业务数据实例化之后,交给Spring容器管理. 但是@Bean注解应该写到配置类中.

SpringMVC注解

@RequestMapping

是一个用来处理请求地址映射的注解,可用在方法上也可用在类上,用在类上会将一个特定请求映射到控制器之上,
表示类中的所有响应请求的方法都是以该地址为父路径;方法的级别上该注解表示进一步指定到处理方法的映射关系。有两个常用属性
value:指定请求的实际地址。method:指定请求的method类型,GET、POST等

@ResponseBody

一般在异步获取数据时使用,在使用@RequestMapping后,返回值通常解析为跳转路径,加上@ResponseBody后返回结果不会被解析为跳转路径,而是直接写入HTTP response body中。即将返回的数据格式转换为JSON格式。

@RequestParam

是从 Request 里获取参数值  

参数说明                                          1.name/value 接收参数的名称

                                                        2.required 默认值为true,该数据为必填项

                                                        3.defaultValue 设定数据默认值,如果参数为null则设定默认值

                                       功能:为参数设定默认值;当提交参数名称与接收参数不一致时使用

@RequestBody

用于接收前端传来的实体,接收参数也是对应的实体,比如前端通过 JSON 提交传来两个参数 username 和 password,此时我们需要在后端封装一个实体来接收。在传递的参数比较多的情况下,使用。@RequestBody 注解用于 POST 请求上,接收 JSON 实体参数。

@RestController
相当于@Controller+@ResponseBody两个注解的结合,返回json数据不需要在方法前面加@ResponseBody注解了,但使用@RestController这个注解,就不能返回jsp,html页面,视图解析器无法解析jsp,html页面了。

@PathVariable

主要用来获取 URL 参数

Spring MVC调用原理

Servlet作用

说明: servlet是浏览器与服务器(tomcat) 进行交互的一种机制.
核心对象: 1.Request 包含了用户的所有的请求相关信息(参数…协议…地址…)
                2.Response 包含了服务器相关的信息(服务器地址,返回的数据)

缺点:接收的所有参数都是String类型

SpringMVC调用步骤

1.当用户发起请求时,被SpringMVC框架中的前端控制器拦截DispatcherServlet.
2.由于前端控制器,并不清楚哪个方法与请求对应,所以查询处理器映射器HandlerMapping.
3.当tomcat服务器启动,则处理器映射器会加载所有的@RequestMapping注解,将其中的路径与方法进行绑定. Map</请求路径,包名.类名.方法名(参数)>,将查找到的方法信息回传给前端控制器 进行后续调用.
4.秉承着松耦合的思想,前端控制器将查询得到的方法, 请求处理器适配器(mvc针对不同的配置文件有专门的处理器(运行程序的机制))挑选合适的处理器去执行(程序内置的规则 无需人为干预)
5.当挑选合适的处理器之后,程序开始真正的执行业务方法. Controller-Service-Mapper(Dao),执行业务. 当业务执行成功之后.返回统一的ModelAndView对象.
其中包含2部分数据 1-Model(服务器数据) 2.View(页面逻辑名称)
6.当前端控制器获取ModelAndView对象之后,交给视图解析器 解析View对象的逻辑名称. 动态的拼接前缀 + 页面逻辑名称 + 后缀. 最终形成了用户展现页面的全路径.
7.将Model数据填充到页面中的过程,叫做视图渲染. 渲染之后,将数据交给前端控制器处理.
8.将得到的完整页面 响应给用户进行展现
 

SpringMVC 用法

1.@RequestParam

2.同名提交问题

SpringMVC中对于页面要求应该保证name属性尽可能唯一,但是如果遇到复选框操作时 重名问题将不能避免

3.对象的方式接参

        大量的页面的提交数据,,如果采用单独的参数接收,必然导致Controller方法结构混乱,不便于理解.所以采用对象的方式封装.

 /**
     * 请求路径: http://localhost:8090/addUser
     * 请求参数: id: 100  name: 张三
     * request/response对象说明  只要用户调用就会自动的赋值
     * servlet缺点:  接收的参数都是String类型
     * @param model
     * @return
     */
    @RequestMapping("/addUser")
    public String addUser(HttpServletRequest request){
        //利用工具API进行类型转化
        Integer id = Integer.parseInt(request.getParameter("id"));
        String name = request.getParameter("name");
        System.out.println("参数:"+id+":"+name);
        return "success";
    }
 

        对象赋值的原理:

        当用户提交数据之后,利用对象的set方法为属性赋值。要求:POJO对象中必须有set方法

        内部根据 request.getParameter(“id”)方式获取数据

4.为对象的引用赋值

        说明: 有时可能会遇到 name属性重复的问题. 由于业务需要不得不写一个重复的名称.那么这时采用对象的引入赋值.

        Dog     属性    id   name        User    private String  name;          private  Dog dog;

5.简化ModelAndView(简单参数传递)

 @RequestMapping("/hello")
    public String hello(Model model){

        model.addAttribute("name", "简化mvc结构");
        return "hello";
    }
 

SpringMVC 转发 和 重定向

转发

概念: 由服务器内部进行页面的跳转.
说明: 一般情况下 SpringMVC内部 以转化为主.

关键字 :forward   转发的是一个请求  return “forward:/findDog”;

重定向

当用户发起请求时,由服务器返回有效的网址信息.之后由用户再次发起请求的结构

关键字: redirect    多次请求多次响应  return “redirect:/findDog”;

重定向/转发特点

1.转发时 会携带用户提交的数据.
2.转发时 用户浏览器的地址不会发生改变.
3.重定向时 由于是多次请求,所以不会携带用户的数据
4.重定向时 由于是多次请求,所以用户的浏览器的地址会发生变化

重定向/转发意义

意义: 实现了方法内部的松耦合
什么时候使用转发/重定向:
1.如果需要携带参数 使用转发
2.如果一个业务已经完成需要一个新的开始 则使用重定向

RestFul

请求类型

        get      查询    弊端:如果参数有多个,则key/value的结构需要多份

        post     新增

        put       更新

        delete   删除

RestFul结构: 

        1.参数之间用   /  分割

        2.参数位置一旦确定,不可更改

        3.参数使用 { } 号的形式进行包裹,并且设定形参

        4.在接收参数时,使用特定的注解取值@PathVariable  @PathVariable   参数说明:                                                          1.name/value动态接收形参的数据,如果参数相同则省略不写                                                           2.必填项 required  默认为 true

SpringMVC JSON返回

        Object格式:对象(object) 是一个无序的“‘名称/值’对”集合。一个对象以“{”(左括号)开始,“}”(右括号)结束。每个“名称”后跟一个“:”(冒号);“‘名称/值’ 对”之间使用“,”(逗号)分隔。

JSON练习: {“key1”:“value1”,“key2”:“value2”}

        Array格式:数组(array) 是值(value)的有序集合。一个数组以“[”(左中括号)开始,“]”(右中括号)结束。值之间使用“,”(逗号)分隔。

JSON: [“value1”,“value2”,“value3”]

        现阶段一般的请求都是前后端分离的方式.ajax (jQuery/axios),一般向服务器请求的数据 通常详情下 都是采用JSON串的方式返回.使用

@ResponseBody  返回一个JSON串 (必须添加 get/set方法)

        返回对象之后,SpringMVC通过内部API (ObjectMapper)调用对象的getXXX()方法动态获          取属性和属性值     演化规则:getAge()~~~去掉get~~~Age()~~~首字母小写~~~age()~~~获          取属性age~~~通过getAge()动态获取属性值

过滤器、拦截器、监听器的区别?

过滤器 Filter、拦截器 Interceptor、监听器Listener

  1. 范围不同:过滤器是Servlet规范规定的,只能用于Web程序中,而拦截器既可以用于Web程序,也可以用于Application、Swing程序中
  2. 规范不同:过滤器是在Servlet规范中定义的,是Servlet容器支持的。而拦截器是在Spring容器内的,是Spring框架支持的
  3. 深度不同:过滤器只能在Servlet前后起作用,而拦截器能够深入到方法前后、异常抛出前后等,因为拦截器的使用具有更大的弹性,在Spring框架中优先使用拦截器
  4. 机制不同:过滤器是基于函数回调的,而拦截器是基于Java的反射机制
  5. 拦截器只能对controller请求起作用,而过滤器则可以对几乎所有的请求起作用
  6. 拦截器可以访问controller的上下文、值栈里的对象,而过滤器不能访问
  7. 在controller的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次
  8. 拦截器可以获取IOC容器中的各个bean,而过滤器就不行,这点很重要,在拦截器里注入一个service,可以调用业务逻辑

二、Spring框架

        Spring是一种轻量级开发框架,旨在提高开发人员的开发效率和代码的可维护性。

         Spring解决了开发者在J2EE开发中遇到的许多常见的问题,提供了功能强大IOC、AOP及Web MVC等功能。Spring可以单独应用于构筑应用程序,也可以和Struts、Webwork、Tapestry等众多Web框架组合使用,并且可以与 Swing等桌面应用程序AP组合。因此, Spring不仅仅能应用于JEE应用程序之中,也可以应用于桌面应用程序以及小应用程序之中。Spring框架主要由七部分组成,分别是 Spring Core、 Spring AOP、 Spring ORM、 Spring DAO、Spring Context、 Spring Web和 Spring Web MVC。

核心容器Spring Core

核心容器提供Spring框架的基本功能。核心容器的主要组件是BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转(IOC)模式,将应用程序的配置和依赖性规范与实际的应用程序代码分开。

     2.

Spring上下文

Spring Context

Spring上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。

     3.

Spring AOP

通过配置管理特性,Spring AOP 模块直接将面向方面的编程功能集成到了 Spring 框架中。可以很容易地使 Spring框架管理的任何对象支持AOP。Spring AOP模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中。

     4.

Spring DAO

JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。

     5.

Spring ORM

Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括JDO、Hibernate和iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。

      6.

Spring Web

Web上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以Spring 框架支持与 Jakarta Struts的集成。Web模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。

      7.

Spring MVC框架

MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。

        总结: Spring的主要的作用将其他框架进行整合,以一种统一的通用的方法进行管理(“角色:框架的大管家”)

核心概念

模块

说明

BeanFactory

Spring内部使用,创建bean的工厂

ApplicationContext

外部应用程序调用,也成为spring容器

IoC控制反转

Inversion of Control

开发者在无需自己new对象,无需关心对象的创建过程

User user = new User();             手动创建对象

User user = context.getBean(user); 容器创建对象

DI依赖注入

Dependency Injection

松耦合方式实现对象直接的依赖

AOP面向切面编程

补充java面向对象的不足

Spring常用注解

@Controller

标注一个控制器组件类

@Service

标注一个业务逻辑组件类

@Repository

标注一个DAO组件类

@Component

标注一个普通uu的Spring Bean类

@Autowired和@Resource 两者的区别

相同点 :
两者都是用在属性上,spring可以自动帮你把bean里面引用的对象的getter和setter方法省略,自动帮你get/set。
不同点:
a. @Autowired默认按照byType方式进行bean匹配 ,@Resource默认按照byName方式进行bean匹配
b. @Autowired是Spring的注解。@Resource是J2EE的注解,这个看下导入注解的时候这两个注解的包名就一清二楚了,
Spring属于第三方的,J2EE是java自己的东西,因此,建议使用@Resource注解,以减少代码和Spring之间的耦合。
 

MVC模型

说明: 在大型项目中由于模块众多,如果将所有的项目都写到一起则代码特别的混乱.不便于后期的维护. 所以通过MVC设计模型,将代码进行分级.
实现步骤: 分为3级
1.M(Model) 持久层 代码与数据库进行交互的代码(Mybatis-dao层)
2.C(Control) 控制层 完成某项业务的具体操作过程(Controller层----Service层)
3.V(View) 视图层 一般指用户看到的内容(页面)

设计思想:解耦
 

IOC

        控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。

        概念: 将对象创建的权利交给Spring容器管理,由容器控制对象的生命周期.        

        作用: 降低代码之间的耦合性

IOC原理:

1).当Spring程序执行时,首先会根据配置文件的内容进行解析   

        <bean id="user" class="com.jt.pojo.User">
2).当程序解析到bean标签时,则会根据反射的机制实例化对象

         Class userClass = Class.forName("com.jt.pojo.User");

         User user = (User) userClass.newInstance();
         user.say();

3).将实例化好的对象保存到超大的Map集合中<K,V> bean中的Id当做map的Key,实例化好的对象当做Value
Map<id,对象>
4). 从容器中获取对象. 则从Map集合中通过id获取对象即可.
       User user = (User) context.getBean("user");

工厂模式

        通过spring容器创建的对象一般是通过反射机制调用.但是有的时候由于业务需要需要实例化抽象类的对象/复杂的接口对象。工厂模式用于实例化复杂对象。

1.Spring源码中创建对象都是采用工厂模式 接口:BeanFactory(顶级接口)
2.Spring开发中需要手动的创建对象时,一般采用 FactoryBean(业务接口)

BeanFactory 和 ApplicationContext的区别

ApplicationContext 继承了Environmentcapable, ListableBeanFactory,HierarchicalBeanFactory,MessageSource,ApplicationEventpublisher,ResourcePatternResolver

1.BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。 而ApplicationContext则相反,它是在容器启动时,一次性创建了所有的Bean。
2.BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。
3. ApplicationContext除了提供上述BeanFactory所能提供的功能之外,还提供了更完整的框架功能: a. 国际化支持, b. 资源访问, 如URL和文件 c. 事件传递

静态工厂模式

特点: 1.静态方法调用可以通过类名直接调用. static
         2.静态属性 内存当中独一份.

   <!--静态工厂实例化对象的写法 方法必须是static-->
   <bean id="calendar1" class="com.jt.factory.StaticFactory" factory-method="getCalendar"/>

实例化工厂

   <!--2.实例化工厂   步骤1:将工厂交给spring容器管理   步骤2: 通过对象调用方法 -->
   <bean id="instanceFactory" class="com.jt.factory.InstanceFactory"></bean>
   <bean id="calendar2" factory-bean="instanceFactory" factory-method="getCalendar"></bean>
调用: 对象.方法()

关于单例多例

1.Spring容器中默认的对象都是单例对象(通过构造方法实例化对象)
2.有时需要通过多例对象为用户提供服务(数据源链接)

    <!--默认条件下都是单例模式 可以通过scope属性修改singleton-->
    <!--<bean id="user" class="com.jt.pojo.User" scope="singleton"></bean>-->

    <!--当对象为多例时,初始化容器不会创建对象 什么时候 用,什么时候创建-->
    <bean id="user" class="com.jt.pojo.User" scope="prototype"></bean>
</beans>

        scope="prototype" 多例设置
        scope="singleton" 缺省值  单例

懒加载: 当用户需要获取对象时,容器才创建对象,称之为懒加载
说明: Spring容器中默认的规则是:容器创建则对象创建.
如果需要配置懒加载 则需要添加额外的属性       lazy-init="true" 开启懒加载

原则: 只要是多例对象 都是懒加载. 懒加载只对单例对象有效
      关于懒加载说明: 一般服务器对象应该先行创建,用户直接使用即可.
      多例对象: 用户使用时创建,同时将对象的生命周期交给使用者管理,
               Spring不负责维护对象的生命周期
 

Spring容器的Bean是否线程安全?

容器本身并没有提供Bean的线程安全策略,因此可以说Spring容器中的Bean本身不具备线程安全的特性,但是具体还是要结合具体scope的Bean去研究。
1、singleton:单例,默认作用域。

2、prototype:原型,每次创建一个新对象。
对于原型Bean,每次创建一个新对象,也就是线程之间并不存在Bean共享,自然是不会有线程安全的问题。

 对于单例Bean,所有线程都共享一个单例实例Bean,因此是存在资源的竞争。

如果单例Bean,是一个无状态Bean,也就是线程中的操作不会对Bean的成员执行查询以外的操作,那么这个单例Bean是线程安全的。比如Spring mvc 的 Controller、Service、Dao等,这些Bean大多是无状态的,只关注于方法本身。

对于有状态的bean,Spring官方提供的bean,一般提供了通过ThreadLocal去解决线程安全的方法,比如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等。
注: Spring容器本身并没有提供线程安全的策略,因此是否线程安全完全取决于Bean本身的特性。

作用域?

Spring生命周期

1.实例化对象

2.初始化操作 (一般对对象的属性赋值)

3.用户使用对象(调用其中的方法)

4.对象销毁 (一般都是释放资源)

依赖注入DI

        当某个角色(可能是一个Java实例,调用者)需要另一个角色(另一个Java实例,被调用者)的协助时,在 传统的程序设计过程中,通常由调用者来创建被调用者的实例。但在Spring里,创建被调用者的工作不再由调用者来完成,因此称为控制反转;创建被调用者 实例的工作通常由Spring容器来完成,然后注入调用者,因此也称为依赖注入。
        对象中的属性**,应该由spring容器动态赋值。

两种方式

        1.set注入

        2.构造注入(必须有无参构造)

自动装配

Spring基于配置文件 为了让属性(对象的引用)注入更加的简单.则推出了自动装配模式.
1).根据名称自动装配
2).根据类型自动装配

autowire通过byName进行注入 会根据属性名称查找Bean的ID进行注入

                     byType 根据属性类型,查找Bean的Class进行 注入. 但是Class类型必须唯一

 自动装配: 程序无需手动的编辑property属性
      autowire="byName" 根据属性的名称进行注入
         1.找到对象的所有的set方法 setUserDao()
         2.setUserDao~~~~set去掉~~~UserDao~~~~首字母小写~~~userDao属性
         3.Spring会根据对象的属性查询自己维护的Map集合,根据userDao名称,查找Map
         中的Key与之对应,如果匹配成功.则能自动调用set方法实现注入(必需有set方法)
      autowire="byType"
         1.找到对象的所有的set方法 setUserDao()
         2.根据set方法找到方法中参数的类型UserDao.class
         3.Spring根据自己维护对象的Class进行匹配.如果匹配成功则实现注入(set方法)
 

特殊字符

说明: 由于由于业务需要xml配置文件中 可能会有特殊字符,但是该特殊字符与xml关键字(标签)形成冲突.
解决方案: 实现字符串转义

万能转义字符: <![CDATA[XXX任意字符]]>

属性注入/属性注解

1.@Autowired 首先按照类型进行注入如果类型注入失败,则根据属性名称注入
2.@Qualifier(“userDao”) 根据属性的名称进行注入;必须配合Autowired一起使用
3.@Resource (type = "xxx.class",name="属性名称")  根据属性和类型进行注入; 不是Spring原生提供的,所以兼容性上有待考量 ,spring建议使用原生注解

上述的属性的注入在调用时 自动的封装了Set方法,所以Set方法可以省略不写

4.@Value 可以直接为属性赋值;  @Value("XXBB")    private  String  name;

                动态获取数据进行注入@Value("${user.id}")    private  Integer  id;

  @Value(123) 将123值赋值给Id @Value("${user.id}") 在Spring容器中查找key=user.id的数据.通过${}语法获取

注解模式

注解作用: 一些复杂的程序 以一种低耦合度的形式进行调用
元注解:
@Target({ElementType.TYPE}) 标识注解对谁有效 type:类 method:方法有效
@Retention(RetentionPolicy.RUNTIME) 运行期有效(大)
@Documented 该注解注释编译到API文档中.

   <!--1.构建user对象-->
   <bean id="user" class="com.jt.pojo.User">
      <property name="id" value="100"></property>
      <property name="name">
         <value><![CDATA[<范冰冰>]]></value>
      </property>
   </bean>

   <!--2.
      让注解生效,开启包扫描
      包路径特点: 给定包路径,则自动扫描同包及子孙包中的类
      base-package: 根据指定的包路径 查找注解
      写方式: 多个包路径 使用,号分隔
   -->
   <!--<context:component-scan base-package="com.jt.controller,com.jt.service,com.jt.dao"></context:component-scan>-->
   <context:component-scan base-package="com.jt"/>
   
   <!--业务需求1: 只想扫描@controller注解
       属性说明: use-default-filters="true"
                默认规则 :true  表示可以扫描其他注解
                        :false  按照用户指定的注解进行加载,默认规则不生效
   -->
   <context:component-scan base-package="com.jt" use-default-filters="false">
      <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
   </context:component-scan>
    
    <!--业务需求2: 不想扫描@controller注解-->
   <context:component-scan base-package="com.jt">
      <!--通过包扫描 可以加载其他的注解 排除Controller注解-->
      <context:exclude-filter type="annotation"
            expression="org.springframework.stereotype.Controller"/>
   </context:component-scan>
</beans>
 

注解模式的执行流程

1).当程序启动Spring容器时 AnnotationConfigApplicationContext 利用beanFactory实例化对象

2).根据配置类中的包扫描开始加载指定的注解(4个). 根据配置文件的顺序依次进行加载

3).当程序实例化Controller时,由于缺少Service对象,所以挂起线程 继续执行后续逻辑.
当构建Service时,由于缺少Dao对象,所以挂起线程 继续执行后续逻辑.
当实例化Dao成功时,保存到Spring所维护的Map集合中. 执行之前挂起的线程.
所以以此类推 所有对象实现封装.最终容器启动成功

4). 根据指定的注解/注入指定的对象.之后统一交给Spring容器进行管理.最终程序启动成功

接口多实现类情况

原则: Spring中规定 一个接口最好只有一个实现类.
业务需求: 要求给UserService接口提供2个实现类.

4.23   1.5

SpringAOP

        在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

        总结:AOP主要是利用动态代理的模式 降低程序的耦合度,扩展业务功能方法。

AOP名词介绍

1).连接点: 用户可以被扩展的方法   joinPoint
2).切入点: 用户实际扩展的方法    (判断方法能否进入切面) pointcut 切入点表达式
3).通知: 扩展方法的具体实现
4).切面: 将通知应用到切入点的过程,方法功能得到扩展全部配置(切面=切入点表达式+通知方法)

pointcut

说明:判断方法能否进入切面 IF判断
注解说明: @Pointcut 内部编辑切入点表达式

joinPoint

说明: 当用户执行方法时,如果方法满足pointcut表达式,则该方法就是连接点.
如果通知中需要获取方法相关参数 则Spring会将joinPoint对象进行封装.为通知参数进行赋值

通知类型

before: 在目标方法执行之前执行  用途: 如果需要记录程序在方法执行前的状态,则使用前置通知.
afterReturning: 在目标方法执行之后返回时执行  ,用途: 用来监控方法的返回值,进行日志的记录
afterThrowing: 在目标方法执行之后,抛出异常时执行 用途: 用来监控方法的返回值,进行日志的记录
after: 无论程序是否执行成功,都要最后执行的通知   实际作用: 可以控制目标方法是否执行.
around: 在目标方法执行前后 都要执行的通知(完美体现了动态代理模式)
功能最为强大 只有环绕通知可以控制目标方法的执行                                                                    参数: ProceedingJoinPoint 通过proceed方法控制目标方法执行.
关于通知方法总结:
1.环绕通知是处理业务的首选. 可以修改程序的执行轨迹
2.另外的四大通知一般用来做程序的监控.(监控系统) 只做记

通知方法的执行顺序

1.执行around开始
2.执行before
3.执行目标方法
4.执行afterReturning
5.执行afterThrowing
6.执行after
7,执行around通知结束

切入点表达式

概念:当程序满足切入点表达式,才能进入切面,执行通知方法.

1.bean(“bean的ID”) 根据beanId进行拦截 只能匹配一个
2.within(“包名.类名”) 可以使用通配符*? 能匹配多个.
粒度: 上述的切入点表达式 粒度是类级别的. 粗粒度.
3.execution(返回值类型 包名.类名.方法名(参数列表…))
粒度: 控制的是方法参数级别. 所以粒度较细. 最常用的.
4.@annotation(包名.注解名) 只拦截注解.
粒度: 注解是一种标记 根据规则标识某个方法/属性/类 细粒度

切面 = 切入点表达式 + 通知方法

@Aspect 标识该类为AOP切面;Spring容器默认不能识别切面注解,需要手动配置

配置类            @EnableAspectJAutoProxy(proxyTargetClass=false) //启动AOP注解 创建代理对象
                        //默认启用JDK动态代理,
                        //目标对象没有实现接口时,则采用CGLIB
                        //强制使用cglib proxyTargetClass=true
                        //JDK代理创建速度快.运行时稍慢
                        //CGLIB创建时速度较慢,运行时更快

     * 切入点表达式练习
     * within:
     *  1.within(com.jt.*.DeptServiceImpl)   一级包下的类
     *  2.within(com.jt..*.DeptServiceImpl)  ..代表多级包下的类
     *  3.within(com.jt..*)  包下的所有的类
     *
     * execution(返回值类型 包名.类名.方法名(参数列表))
     *  1.execution(* com.jt..*.DeptServiceImpl.add*())
     *  注释: 返回值类型任意的, com.jt下的所有包中的DeptServiceImpl的类
     *        的add开头的方法 ,并且没有参数.
     *
     *  2.execution(* com.jt..*.*(..))
     *  注释: 返回值类型任意,com.jt包下的所有包的所有类的所有方法 任意参数.
     *
     *  3.execution(int com.jt..*.*(int))
     *  4.execution(Integer com.jt..*.*(Integer))
     *  强调: 在Spring表达式中没有自动拆装箱功能! 注意参数类型
     *
     * @annotation(包名.注解名)
     *     @Before("@annotation(com.jt.anno.Cache)")
     *    只拦截特定注解的内容.
@Pointcut 定义切入点表达式

Spring为了AOP动态获取目标对象及方法中的数据,则通过joinPoint对象

getSignature : 方法签名 获取方法的参数

System.out.println("获取目标对象类名:"+joinPoint.getSignature().getDeclaringTypeName());

关于Order注解

@Order(1)

控制切面的执行的顺序.通过数组进行控制,值越小,越先执行

声明式事务和编程式事务

声明式事务:通过AOP(面向切面)方式在方法前使用编程式事务的方法开启事务,在方法后提交或回滚。用配置文件的方法或注解方法(如:@Transactional)控制事务。

编程式事务:手动开启、提交、回滚事务。

通俗地去理解两者的区别,即声明式事务只需要“声明”就可以达到事务的效果;编程式事务需要“编程”才可以达到事务效果。
 

三、Mybatis

        MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

        Mybatis:操作数据库,封装了JDBC,自动ORM

内部组件结构图

$ 和 #

$只取值本身,没有拼串的功能 where name=jack ,底层使用Statement工具类不安全,可能发生SQL注入攻击问题,低效
#可以自动拼接双引号,where name=“jack”,底层使用PreparedStatement工具类安全,安全,高效

resultType和resultMap的区别:

ORM:是指对象关系映射。是指从表里获取字段的值 自动 交给类里的属性 保存
resultType:只能完成简单的ORM,如果字段名字 和 属性的名字一致
resultMap:只能完成复杂的ORM,如果字段名字 和 属性的名字不一致,需要单独处理

集合:list

在MyBatis中,大多默认采用List集合,声明集合参数或者返回值时,都有个奇怪的写法,本应写List<String>,但习惯只写集合元素的类型:String,大家切记。

当SQL中有特殊字符,mybatis不能正常解析时,用CDATA括起来就解决了

           <![CDATA[ and age<=#{age} ]]>

XML和接口方式的区别

        MyBatis提供了两种操作数据库的方式,一种是通过xml映射文件,一种是通过java的接口类。按面向对象方式更加推荐接口方式,但如果复杂的多表映射,仍然需要使用xml映射文件的ResultMap方式实现。

        接口只是假象,其底层仍然是通过xml实现。

接口方式怎么找到XML执行的?

        SqlSession的getMapper方法找到类,通过反射可以获取到类的全路径(包名.类名),相加后就定位到某个xml的命名空间namespace,在根据调用的方法去找到xml中某个标签的id属性。从而实现价值接口,调用接口的方法而间接找到xml中的标签,通过解析xml获取这个标签的内容,从而获取到sql语句。

从 XML 中构建 SqlSessionFactory

        每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例。

        从 XML 文件中构建 SqlSessionFactory 的实例非常简单,建议使用类路径下的资源文件进行配置。 但也可以使用任意的输入流(InputStream)实例,比如用文件路径字符串或 file:// URL 构造的输入流。MyBatis 包含一个名叫 Resources 的工具类,它包含一些实用方法,使得从类路径或其它位置加载资源文件更加容易。

不使用 XML 构建 SqlSessionFactory

DataSource dataSource = BlogDataSourceFactory.getBlogDataSource();
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
configuration.addMapper(BlogMapper.class);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);

        configuration 添加了一个映射器类(mapper class)。映射器类是 Java 类,它们包含 SQL 映射注解从而避免依赖 XML 文件。不过,由于 Java 注解的一些限制以及某些 MyBatis 映射的复杂性,要使用大多数高级映射(比如:嵌套联合映射),仍然需要使用 XML 配置。有鉴于此,如果存在一个同名 XML 配置文件,MyBatis 会自动查找并加载它(在这个例子中,基于类路径和 BlogMapper.class 的类名,会加载 BlogMapper.xml)。

从 SqlSessionFactory 中获取 SqlSession

        在上面提到的例子中,一个语句既可以通过 XML 定义,也可以通过注解定义。我们先看看 XML 定义语句的方式,事实上 MyBatis 提供的所有特性都可以利用基于 XML 的映射语言来实现,这使得 MyBatis 在过去的数年间得以流行。如果你用过旧版本的 MyBatis,你应该对这个概念比较熟悉。 但相比于之前的版本,新版本改进了许多 XML 的配置,后面我们会提到这些改进。这里给出一个基于 XML 映射语句的示例,它应该可以满足上个示例中 SqlSession 的调用。

作用域(Scope)和生命周期

SqlSessionFactoryBuilder

这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。(所以一般在局部变量) 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。 你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但最好还是不要一直保留着它,以保证所有的 XML 解析资源可以被释放给更重要的事情。

SqlSessionFactory(可以想象为数据库连接池)

SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。

SqlSession

每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。(用完之后赶紧关闭) 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。 换句话说,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中。 下面的示例就是一个确保 SqlSession 关闭的标准模式:

try (SqlSession session = sqlSessionFactory.openSession()) {// 你的应用逻辑代码
}

在所有代码中都遵循这种使用模式,可以保证所有数据库资源都能被正确地关闭。

ORM映射:ResultMap

        Mybatis最强大就来使用ResultMap把多表查询的结果映射到多个关联的java对象中。(其实我们使用了resultType,其本质还是使用resultMap,Mybatis自动的帮我们建立了。)表现关联关系比hibernate简单,没有分那么细致one-to-many、many-to-one、one-to-one、many-to-many。而是只有两种association(对一)、collection(对多),表现很赞,特简洁,把复杂问题简单化。

        实现步骤:

先进行多表联查,形成一个大结果集,最终所需要的字段,都放在这个结果集中

构建<resultMap type="Item" id="itemRM">,先写主表字段,再写普通字段

        主键字段:<id property="id" column="id"/>

        普通字段:<result property="title" column="title"/>

        property代表 java对象的属性名,column代表结果集的字段名

在配置关系

        对一:<association property="itemDesc" >

        对多:<collection property="items"  ofType="Item">

        对多为集合collection,集合元素类型使用ofType进行标识,固定搭配

引用ResultMap定义

        <select id="findItem" resultMap="itemRM">

注意:形成的结果集中不能有同名字段,同名字段不报错,但结果不对,坑啊

          关联表的主键都出现

为什么说 Mybatis 是半自动 ORM 映射工具?它与全自动 的区别在哪里?

Hibernate 属于全自动 ORM 映射工具,使用 Hibernate 查询关联对象或者关联 集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。而 Mybatis 在查询关联对象或关联集合对象时,需要手动编写 sql 来完成,所以,称之为半自 动 ORM 映射工具。

Mybatis 的 Xml 映射文件中,不同的 Xml 映射文件,id 是否可以重复?

不同的 Xml 映射文件,如果配置了 namespace,那么 id 可以重复;如果没有配 置 namespace,那么 id 不能重复; 原因就是 namespace+id 是作为 Map的 key 使用的,如果没有 namespace,就剩下 id,那么,id 重复会导致数据互相覆盖。 有了 namespace,自然 id 就可以重复,namespace 不同,namespace+id 自然 也就不同。

简述 Mybatis 的插件运行原理。

答:Mybatis 仅可以编写针对 ParameterHandler、ResultSetHandler、 StatementHandler、Executor 这 4 种接口的插件,Mybatis 使用 JDK 的动态代 理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这 4 种 接口对象的方法时,就会进入拦截方法,具体就是 InvocationHandler 的 invoke() 方法,当然,只会拦截那些你指定需要拦截的方法。

Mybatis的缓存机制:一级缓存,二级缓存,使用LRU算法

        Mybatis 中有一级缓存(本地缓存)和二级缓存(全局缓存),默认情况下一级缓存是开启的,而且是不能关闭的。一级缓存 是指 SqlSession 级别的缓存,当在同一个 SqlSession 中进行相同的 SQL 语句查询时,第二次以 后的查询不会从数据库查询,而是直接从缓存中获取,一级缓存最多缓存 1024 条 SQL。二级缓存 是指可以跨 SqlSession 的缓存。是 mapper 级别的缓存,对于 mapper 级别的缓存不同的 sqlsession 是可以共享的。

Mybatis 的一级缓存原理(sqlsession 级别)

Mybatis 的一级缓存原理(sqlsession 级别) 第一次发出一个查询 sql,sql 查询结果写入 sqlsession 的一级缓存中,缓存使用的数据结构是一 个 map。 key:MapperID+offset+limit+Sql+所有的入参 value:用户信息 同一个 sqlsession 再次发出相同的 sql,就从缓存中取出数据。如果两次中间出现 commit 操作 (修改、添加、删除),本 sqlsession 中的一级缓存区域全部清空,下次再去缓存中查询不到所 以要从数据库查询,从数据库查询到再写入缓存。

        缓存失效:

        1.查询不同的东西        

        2.增删改操作,可能会改变原来的数据,所以必定会刷新缓存

        3.查询不同的Mapper.xml

        4.手动清理缓存

小结:

        1.一级缓存默认开启,只在一次SQLSession中有效,也就是拿到连接到关闭连接这个区间段

        2.一级缓存就是一个map

二级缓存原理(mapper 基本)

        二级缓存的范围是 mapper 级别(mapper 同一个命名空间),mapper 以命名空间为单位创建缓 存数据结构,结构是 map。mybatis 的二级缓存是通过 CacheExecutor 实现的。CacheExecutor 13/04/2018 Page 139 of 283 其实是 Executor 的代理对象。所有的查询操作,在 CacheExecutor 中都会先匹配缓存中是否存 在,不存在则查询数据库。
        key:MapperID+offset+limit+Sql+所有的入参 具体使用需要配置:
        1. Mybatis 全局配置中启用二级缓存配置
        2. 在对应的 Mapper.xml 中配置 cache 节点
        3. 在对应的 select 查询节点中添加 useCache=true

要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:

<cache/>   或者显示开启,CacheEnabled   true

这个简单语句的效果如下:

  • 映射语句文件中的所有 select 语句的结果将会被缓存。
  • 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
  • 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
  • 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
  • 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
  • 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。

可用的清除策略有:

LRU – 最近最少使用:移除最长时间不被使用的对象。  默认的清除策略是 LRU。
FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。(系统内存不足是,可能会被销毁 生命力稍强于软引用)
WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。(系统触发GC是直接销毁引用的对象,生命力最弱)

工作机制:

        1.一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;

        2.如果会话关闭了,这个会话对应的一级缓存就没了;但我们想要的是,会话关闭了,一级缓存中数据被保存到二级缓存中;

        3.新的会话查询信息,就可以从二级缓存中获取内容;

        4.不同的mapper查询出的数据会放在自己对应的缓存(map)中。

小结:

        只要开启了二级缓存,在同一个Mapper下就有效

        所有的数据都会先放在一级缓存中

        只有当会话提交,或者关闭的时候才会提交到二级缓存中

缓存顺序:

        先看二级缓存中有没有,再看一级缓存中有没有,再查数据库

脏读、幻读、不可重复读

分页

limit实现分页

语法:SELECT * FROM user limit startIndex,pageSiz;起始位置,每页显示容量。

pageSize为 -1,代表从startIndex位置开始到最后一个结束,目前会报错

如果只给定了一个参数,limit n;相当于limit 0,n;

当要查询的数据只有一条数据的时候,要加limit 1,可以实现优化,速度更快。

interceptor plugin实现:

需要定义一个类实现Interceptor接口

PageHelper实现;

这种方式需要引入maven依赖;

Mybatis 是如何进行分页的?分页插件的原理是什么?

Mybatis 使用 RowBounds 对象进行分页,它是针对 ResultSet 结果集执行的内 存分页,而非物理分页。可以在 sql 内直接书写带有物理分页的参数来完成物理分 页功能,也可以使用分页插件来完成物理分页。

分页插件的基本原理是使用 Mybatis 提供的插件接口,实现自定义插件,在插件 的拦截方法内拦截待执行的 sql,然后重写 sql,根据 dialect 方言,添加对应的物 理分页语句和物理分页参数。


sum(1) 相当于count(1) 求的就是行数统计,count(0),count(1),count(*),
sum(0)就是不统计   为0

Springboot

Spring Boot 的核心注解是哪个?它主要由哪几个注解组成的?
启动类上面的注解是@SpringBootApplication,它也是 Spring Boot 的核心注解,主要组合包含了以下 3 个注解:

@SpringBootConfiguration:组合了 @Configuration 注解,实现配置文件的功能。

@EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项,如关闭数据源自动配置功能: @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })。组合了@AutoConfigurationPackage:自动配置包

@ComponentScan:Spring组件扫描。

@SpringBootApplication

作用:标注在某个类上说明这个类是SpringBoot的主配置类 , SpringBoot就应该运行这个类的main方法来启动SpringBoot应用;

进入这个注解:可以看到上面还有很多其他注解!

@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan(    excludeFilters = {@Filter(    type = FilterType.CUSTOM,    classes = {TypeExcludeFilter.class}), @Filter(    type = FilterType.CUSTOM,    classes = {AutoConfigurationExcludeFilter.class})})public @interface SpringBootApplication {    // ......}

@ComponentScan

这个注解在Spring中很重要 ,它对应XML配置中的元素。

作用:自动扫描并加载符合条件的组件或者bean , 将这个bean定义加载到IOC容器中

@SpringBootConfiguration

作用:SpringBoot的配置类 ,标注在某个类上 , 表示这是一个SpringBoot的配置类;

我们继续进去这个注解查看

// 点进去得到下面的 @Component@Configurationpublic @interface SpringBootConfiguration {}
@Componentpublic @interface Configuration {}

这里的 @Configuration,说明这是一个配置类 ,配置类就是对应Spring的xml 配置文件;

里面的 @Component 这就说明,启动类本身也是Spring中的一个组件而已,负责启动应用!

我们回到 SpringBootApplication 注解中继续看。

@EnableAutoConfiguration

@EnableAutoConfiguration :开启自动配置功能

以前我们需要自己配置的东西,而现在SpringBoot可以自动帮我们配置 ;@EnableAutoConfiguration告诉SpringBoot开启自动配置功能,这样自动配置才能生效;

点进注解接续查看:

@AutoConfigurationPackage :自动配置包

@Import({Registrar.class})public @interface AutoConfigurationPackage {}

@import :Spring底层注解@import , 给容器中导入一个组件

Registrar.class 作用:将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器 ;

这个分析完了,退到上一步,继续看

@Import({AutoConfigurationImportSelector.class}) :给容器导入组件 ;

AutoConfigurationImportSelector :自动配置导入选择器,那么它会导入哪些组件的选择器呢?我们点击去这个类看源码:

1、这个类中有一个这样的方法

// 获得候选的配置protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {    //这里的getSpringFactoriesLoaderFactoryClass()方法    //返回的就是我们最开始看的启动自动导入配置文件的注解类;EnableAutoConfiguration    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());    Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");    return configurations;}

2、这个方法又调用了  SpringFactoriesLoader 类的静态方法!我们进入SpringFactoriesLoader类loadFactoryNames() 方法

public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {    String factoryClassName = factoryClass.getName();    //这里它又调用了 loadSpringFactories 方法    return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());}

3、我们继续点击查看 loadSpringFactories 方法

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {    //获得classLoader , 我们返回可以看到这里得到的就是EnableAutoConfiguration标注的类本身    MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);    if (result != null) {        return result;    } else {        try {            //去获取一个资源 "META-INF/spring.factories"            Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");            LinkedMultiValueMap result = new LinkedMultiValueMap();
            //将读取到的资源遍历,封装成为一个Properties            while(urls.hasMoreElements()) {                URL url = (URL)urls.nextElement();                UrlResource resource = new UrlResource(url);                Properties properties = PropertiesLoaderUtils.loadProperties(resource);                Iterator var6 = properties.entrySet().iterator();
                while(var6.hasNext()) {                    Entry<?, ?> entry = (Entry)var6.next();                    String factoryClassName = ((String)entry.getKey()).trim();                    String[] var9 = StringUtilsmaDelimitedListToStringArray((String)entry.getValue());                    int var10 = var9.length;
                    for(int var11 = 0; var11 < var10; ++var11) {                        String factoryName = var9[var11];                        result.add(factoryClassName, factoryName.trim());                    }                }            }
            cache.put(classLoader, result);            return result;        } catch (IOException var13) {            throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);        }    }}

4、发现一个多次出现的文件:spring.factories,全局搜索它

spring.factories

我们根据源头打开spring.factories , 看到了很多自动配置的文件;这就是自动配置根源所在!

WebMvcAutoConfiguration

我们在上面的自动配置类随便找一个打开看看,比如 :WebMvcAutoConfiguration

可以看到这些一个个的都是JavaConfig配置类,而且都注入了一些Bean,可以找一些自己认识的类,看着熟悉一下!

所以,自动配置真正实现是从classpath中搜寻所有的META-INF/spring.factories配置文件 ,并将其中对应的 org.springframework.boot.autoconfigure. 包下的配置项,通过反射实例化为对应标注了 @Configuration的JavaConfig形式的IOC容器配置类 , 然后将这些都汇总成为一个实例并加载到IOC容器中。

结论:

  1. SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值

  2. 将这些值作为自动配置类导入容器 , 自动配置类就生效 , 帮我们进行自动配置工作;

  3. 整个J2EE的整体解决方案和自动配置都在springboot-autoconfigure的jar包中;

  4. 它会给容器中导入非常多的自动配置类 (xxxAutoConfiguration), 就是给容器中导入这个场景需要的所有组件 , 并配置好这些组件 ;

  5. 有了自动配置类 , 免去了我们手动编写配置注入功能组件等的工作;

自动装配原理

小结:springboot所有的自动装配都是在启动的时候扫描并加载:spring.factories所有的自动装配类都在这里,但是不一定生效,要判断条件是否成立,只要导入了对应的start,就有对应的启动器了,有了启动器,自动装配机会生效,然后配置成功。

1.springboot在启动的时候,从类路径下/META -INF /spring.factories获取指定的值;

2.将这些自动配置的类导入容器,自动配置类就会生效,帮我们进行自动配置;

3.以前需要我们自动配置的东西,现在springboot帮我们做了;

4.整个javaEE,解决方案和自动配置的东西都在spring-boot-autoconfigure-2.2.0.RELEASE.jar这个包下;

5.它会把所有需要导入的组件以类名方式返回,这些组件就会被添加到容器;

6.融其中也会存在很多XXXAutoConfigure的文件,就是这些类给容器中导入了这个场景需要的所有组件,并自动配置@Configuration,JavaConfig;

7.有了自动配置类就免去了手动编写配置文件的操作。

更多推荐

天问的私人面试宝典03 框架

本文发布于:2024-02-05 10:30:39,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1744807.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:框架   宝典   天问   私人

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!