Spring框架是大家学习后续其他框架的基础吧,建议大家好好学习,有时间和精力的可以结合视频去看看源码,对自己的提升还是很不错的。在阅读源码的过程中可以自己画一些流程图之类的,加深自己的理解。下图就是我当时在看源码视频跟着画的图,画完后感觉印象很深,在面试的是就可以跟面试官说你看过Spring某一块的源码,这绝对是一个加分项!
1 Spring框架了解吗?说说它的优缺点
回答:Spring 是一种轻量级开发框架,旨在提高开发人员的开发效率以及系统的可维护性。
一般说 Spring 框架指的都是 Spring Framework,它是很多模块的集合,使用这些模块可以很方便地协助我们进行开发。这些模块是:核心容器、数据访问/集成,、Web、AOP(面向切面编程)、工具、消息和测试模块。比如:Core Container 中的 Core 组件是Spring 所有组件的核心,Beans 组件和 Context 组件是实现IOC和依赖注入的基础,AOP组件用来实现面向切面编程。
Spring 官网列出的 Spring 的 6 个特征:
- 核心技术 :依赖注入(DI),AOP,事件(events),资源,i18n,验证,数据绑定,类型转换,SpEL。
- 测试 :模拟对象,TestContext框架,Spring MVC 测试,WebTestClient。
- 数据访问 :事务,DAO支持,JDBC,ORM,编组XML。
- Web支持 : Spring MVC和Spring WebFlux Web框架。
- 集成 :远程处理,JMS,JCA,JMX,电子邮件,任务,调度,缓存。
- 语言 :Kotlin,Groovy,动态语言。
Spring框架的好处?
- 轻量级:Spring框架是轻量级的,最基础的版本大约只有2MB。
- 控制反转(IOC):通过控制反转技术,实现了解耦合。对象给出它们的依赖,而不是创建或查找依赖的对象。
- 面向切面(AOP):Spring支持面向切面的编程,并将应用程序业务逻辑与系统服务分离。
- MVC框架:Spring的WEB框架是一个设计良好的web MVC框架,它为web框架提供了一个很棒的替代方案。
- 容器:Spring包含并管理对象的生命周期和配置。
- 事务管理:Spring提供了一个一致性的事务管理接口,可以收缩到本地事务,也可以扩展到全局事务(JTA)。
- 异常处理:Spring提供了方便的API来将具体技术的异常(由JDBC、Hibernate或JDO抛出)转换为一致的unchecked 异常。
Spring框架的缺点?
Spring 能够给我们带来很多方便之处,但是同样也存在很多的问题:
使用了大量的反射机制,反射机制非常占用内存。
(一)重量级框架
我们看到 Spring 架构图时会发现 Spring 里面包含有很多其他组件,比如数据访问、MVC、事务管理、面向切点、WebSocket 功能等,因此这么复杂的组件集中到一起就会提高初学者的学习成本。还有一方面随着你的服务越多,那么 Spring 的启动就会变得越慢。
(二)集成复杂
比如我们想要使用 MyBatis 或者 MongoDB的时候,我们要做很多工作不管使用配置方式也好还是使用注解方式。
(三)配置复杂
在使用 Spring 的时候,我们更多可能是选择 XML 进行配置,但目前这种配置方式已不在流行。
(四)构建和部署复杂
启动 Spring 的 IOC 容器,是完全要依赖于第三方的 web 服务器。自身不能启动的。
2、Spring中有两个重要特性是什么?
回答:Spring中有两个非常重要的特性IOC和AOP,其中AOP是对IOC功能的拓展,
IOC:IOC是一种设计思想,就是 **将原本在程序中手动创建对象的控制权,交由Spring框架来管理。**负责创建对象,使用依赖注入(dependency injection,DI)管理它们,将对象集中起来,配置对象,管理对象的整个生命周期。
AOP:AOP模块用于为支持Spring应用程序面向切面的开发。AOP联盟提供了很多支持,这样就确保了Spring和其他AOP框架的共通性。面向切面编程能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。
追问:IOC的好处有哪些?
- IOC或依赖注入最小化应用程序代码量。
- 它使测试应用程序变得容易,因为单元测试中不需要单例或JNDI查找机制。
- 以最小的代价和最少的干扰来促进松耦合。
- IOC容器支持快速实例化和懒加载。
追问:AOP是怎么实现的?
回答:AOP就是基于动态代理的,如果要代理的对象,实现了某个接口,那么Spring AOP会使用JDK Proxy,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候Spring AOP会使用Cglib ,这时候Spring AOP会使用 Cglib 生成一个被代理对象的子类来作为代理
追问:JDK代理和Cglib代理的区别?
回答:
1、CGLib所创建的动态代理对象在实际运行时候的性能要比JDK动态代理高(1.6和1.7的时候,CGLib更快;1.8的时候,jdk更快)
2、CGLib在创建对象的时候所花费的时间却比JDK动态代理多
3、singleton的代理对象或者具有实例池的代理,因为无需频繁的创建代理对象,所以比较适合采用CGLib动态代理,反之,则适合用JDK动态代理
4、JDK生成的代理类类型是Proxy(因为继承的是Proxy),CGLIB生成的代理类类型是Enhancer类型
5、JDK动态代理是面向接口的,CGLib动态代理是通过字节码底层继承代理类来实现(如果被代理类被final关键字所修饰,那么会失败)
6、如果要被代理的对象是个实现类,那么Spring会使用JDK动态代理来完成操作(Spirng默认采用JDK动态代理实现机制);
如果要被代理的对象不是实现类,那么Spring会强制使用CGLib来实现动态代理。
3、Spring中bean有哪些作用域?
- singleton:Spring将bean定义的范围限定为每个Spring IOC容器只有一个单实例。
- prototype:单个bean定义有任意数量的对象实例。
- request:作用域为一次http请求,该作用域仅在基于web的Spring ApplicationContext情形下有效。
- session:作用域为HTTP Session,该作用域仅在基于web的Spring ApplicationContext情形下有效。
- global-session:作用域为全局的HTTP session。该作用域也是仅在基于web的Spring ApplicationContext情形下有效。
默认的作用域是singleton。
追问:单例模式是线程安全的吗?
(这个问题阿里二面的时候被问到过)
若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则就可能影响线程安全。(这个大家可以自行去网上搜一下,顺便学习一下。)
4、Spring中有哪些常见的注解?
回答:这个问题可以选择几个常见的说一下就行,下面给大家列出比较全面的,主要说几个自己项目里用到的即可。
- @Component:用于指示类是组件。这些类用于自动注入,并在使用基于注解的配置时配置为bean。
- @Controller:是一种特定类型的组件,用于MVC应用程序,主要与@RequestMapping注解一起使用。
- @Repository:用于表示组件用作存储库和存储/检索/搜索数据的操作。我们可以将此注解应用于DAO实现类。
- @Service:用于指示类是服务层。
- @Required:此注解简单地说明作用的bean属性必须在配置时通过bean定义中的显式属性值或通过自动注入填充。如果作用的bean属性未填充,容器将抛出BeanInitializationException。
- @ResponseBody:用于将对象作为response,通常用于将XML或JSON数据作为response发送。
- @PathVariable:用于将动态值从URI映射到处理方法参数。
- @Autowired:对自动注入的位置和方式提供了更细粒度的控制。它可以用于在setter方法上自动注入bean。就像@Required 注解一样,修饰setter方法、构造器、属性或者具有任意名称和/或多个参数的PN方法。
- @Qualifier:当有多个相同类型的bean并且只需要将一个bean自动注入时,@Qualifier注解与@Autowired注释一起使用,通过指定将连接哪个bean来消除歧义。
- @Scope:配置Spring bean的作用域。
- @Configuration:表示Spring IOC容器可以将该类用作bean定义的源。
- @ComponentScan:应用此注解时,将扫描包下的所有可用类。
- @Bean:对于基于java的配置,用@Bean注解修饰的方法将返回一个在Spring应用程序上下文中注册为Bean的对象。
- 用于配置切面和通知、@Aspect、@Before、@After、@Around、@Pointcut等的AspectJ注解。
5、XML配置和注解之间有什么区别?
注解的优点:
- 所有信息都在一个文件中
- 当类更改了,不用修改xml配置文件
xml配置的优点:
- POJO及其行为之间更清晰地分离
- 当你不知道哪个POJO负责该行为时,更容易找到该POJO
6、 Spring支持的事务管理类型?
回答:
- 编程式事务,在代码中硬编码。(不推荐使用)
- 声明式事务,在配置文件中配置(推荐使用)
声明式事务又分为两种:
- 基于XML的声明式事务
- 基于注解的声明式事务
追问:Spring 事务中的隔离级别有哪几种?
TransactionDefinition 接口中定义了五个表示隔离级别的常量:
- TransactionDefinition.ISOLATION_DEFAULT: 使用后端数据库默认的隔离级别,Mysql 默认采用的 REPEATABLE_READ隔离级别 Oracle 默认采用的 READ_COMMITTED隔离级别.
- TransactionDefinition.ISOLATION_READ_UNCOMMITTED: 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
- TransactionDefinition.ISOLATION_READ_COMMITTED: 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
- TransactionDefinition.ISOLATION_REPEATABLE_READ: 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
- TransactionDefinition.ISOLATION_SERIALIZABLE: 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
SpringMVC的基本原理(浅析)
原理:
客户端发送请求到DispacherServlet(分发器),
.由DispacherServlet控制器查询HanderMapping,找到处理请求的Controller
Controller调用业务逻辑处理后,返回ModelAndView
DispacherSerclet查询视图解析器,找到ModelAndView指定的视图
视图图负责将结果显示到客户端
跟踪SpringMVC的请求:
每当用户在 Web 浏览器中点击链接或者提交表单的时候,请求就开始工作了,像是邮递员一样,从离开浏览器开始到获取响应返回,它会经历很多站点,在每一个站点都会留下一些信息同时也会带上其他信息:
第一站:DispatcherServlet
从请求离开浏览器以后,第一站到达的就是 DispatcherServlet,看名字这是一个 Servlet,通过 J2EE 的学习,我们知道 Servlet 可以拦截并处理 HTTP 请求,DispatcherServlet 会拦截所有的请求,并且将这些请求发送给 Spring MVC 控制器。
<!--核心的servlet -->
<servlet>
<servlet-name>SpringMvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>0</load-on-startup>
</servlet>
- DispatcherServlet 的任务就是拦截请求发送给 Spring MVC 控制器。
第二站:处理器映射(HandlerMapping)
- 问题: 典型的应用程序中可能会有多个控制器,这些请求到底应该发给哪一个控制器呢?
所以 DispatcherServlet 会查询一个或多个处理器映射来确定请求的下一站在哪里,处理器映射会根据请求
所在的Controller包去寻找Controller
<!--查找控制器所在的包 -->
<context:component-scan base-package="com.ydq.controller"/>
第三站:控制器
一旦选择了合适的控制器, DispatcherServlet 会将请求发送给选中的控制器,到了控制器,请求会卸下其负载(用户提交的请求)等待控制器处理完这些信息:
@Controller
@RequestMapping("/serviceType")
public class ServiceTypeController {
@Resource
private ServiceTypeDao serviceTypeDao;
@RequestMapping("/add")
public ModelAndView add(){
ModelAndView modelAndView = new ModelAndView("serviceType/input");
modelAndView.addObject("act","serviceType/addSave.do");
return modelAndView;
}
第四站:返回 DispatcherServlet
当控制器在完成逻辑处理后,通常会产生一些信息,这些信息就是需要返回给用户并在浏览器上显示的信息,它们被称为模型(Model)。仅仅返回原始的信息时不够的——这些信息需要以用户友好的方式进行格式化,一般会是 HTML,所以,信息需要发送给一个视图(view),通常会是 JSP。
控制器所做的最后一件事就是将模型数据打包,并且表示出用于渲染输出的视图名**(逻辑视图名)。它接下来会将请求连同模型和视图名发送回 DispatcherServlet。**
第五站:视图解析器
这样以来,控制器就不会和特定的视图相耦合,传递给 DispatcherServlet 的视图名并不直接表示某个特定的 JSP。(实际上,它甚至不能确定视图就是 JSP)相反,它传递的仅仅是一个逻辑名称,这个名称将会用来查找产生结果的真正视图。
DispatcherServlet 将会使用视图解析器(view resolver)来将逻辑视图名匹配为一个特定的视图实现,它可能是也可能不是 JSP
第六站:视图
既然 DispatcherServlet 已经知道由哪个视图渲染结果了,那请求的任务基本上也就完成了。
它的最后一站是视图的实现,在这里它交付模型数据,请求的任务也就完成了。视图使用模型数据渲染出结果,这个输出结果会通过响应对象传递给客户端。
总结:
原理其实也不是太难,总之就是SpringMVC还是建立在Servlet基础上的,而这个servlet就在web.xml中进行配置,这时就会有一个
<load-on-startup>0</load-on-startup>
容器在应用启动时就加载并初始化这个servlet;并且加载springmvc的配置文件,在springmvc的配置文件中,就可以指明控制器的包在哪,通过反射,找到该包下的所有类,通过反射的class对象,就可以知道有没有配置成@Controller,如果有,就可以得到所有的方法,并且这些方法都是配置了@RequestMapping的方法,然后记录所有的Methon对象,记录的时候是一个Map集合<String,Methon>,写一个通用的方法,完成,收参,转换数据类型,填充Model,通过Spring调用业务逻辑,根据业务逻辑,返回ModelAndView,然后返回分发器,分发器经处理得到ViewResolw和model,model就是Attrbutes,view resolver就是视图解析器,
6.Spring中AOP是怎么实现的?
AOP就是基于动态代理的,如果要代理的对象,实现了某个接口,那么Spring AOP会使用JDK Proxy,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候Spring AOP会使用Cglib ,这时候Spring AOP会使用 Cglib 生成一个被代理对象的子类来作为代理
追问 Spring中Bean的初始化过程?
这块建议看一下Spring的源码就比较清楚了。
7.Spring中IOC?
IOC:IOC是一种设计思想,就是 **将原本在程序中手动创建对象的控制权,交由Spring框架来管理。**负责创建对象,使用依赖注入(dependency injection,DI)管理它们,将对象集中起来,配置对象,管理对象的整个生命周期。
IOC的好处有哪些?
- IOC或依赖注入最小化应用程序代码量。
- 它使测试应用程序变得容易,因为单元测试中不需要单例或JNDI查找机制。
- 以最小的代价和最少的干扰来促进松耦合。
- IOC容器支持快速实例化和懒加载。
7.JVM的内存模型?
Java的运行时区主要包含堆、方法区、虚拟机栈、程序计数器和本地方法栈,其中堆和方法区是所有线程所共有的。而且虚拟机栈、程序计数器和本地方法栈是线程所私有的。
堆:存放对象实例
方法区:用来存储已经被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。
虚拟机栈:(生命周期与线程相同)Java中每个方法执行的时候,Java虚拟机都会同步创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
程序计数器:保存下一条需要执行的字节码指令,是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都是依赖程序计数器。
本地方法栈:与虚拟机栈类似
8.sleep和wait有什么区别?
(1)sleep方法属于Thread类,wait方法属于Object类
(2)sleep方法暂停执行指定的时间,让出CPU给其他线程,但其监控状态依然保持在指定的时间过后又会自动恢复运行状态。
(3)在调用sleep方法的过程中,线程不会释放对象锁,而wait会释放对象锁。
wait为什么是数Object类下面的方法?
所谓的释放锁资源实际是通知对象内置的monitor对象进行释放,而只有所有对象都有内置的monitor对象才能实现任何对象的锁资源都可以释放。又因为所有类都继承自Object,所以wait()就成了Object方法,也就是通过wait()来通知对象内置的monitor对象释放,而且事实上因为这涉及对硬件底层的操作,所以wait()方法是native方法,底层是用C写的。【来自网络】
9.创建线程的方式有哪些?
Java中创建线程的方式有4种,分别是
(1)写一个类继承子Thread类,重写run方法
(2)写一个类重写Runable接口,重写run方法
(3)写一个类重写Callable接口,重写call方法
(4)使用线程池
追问:使用Callable()创建线程比另外两种方式有什么优势吗?
10.怎么调整堆的大小?要修改哪个参数?
初始值-Xms和最大值-Xmx
11.用Linux命令查看当前有哪些进程在活跃呢?
Linux查看进程命令:PS命令
ps命令是一个相当强大地Linux进程查看命令.运用该命令可以确定有哪些进程正在运行和运行地状态、 进程是否结束、进程有没有僵死、哪些进程占用了过多地资源等等.总之大部分信息均为可以通过执行该命令得到地。
PS命令语法:
ps [选项]
-e 显示所有进程,环境变量
-f 全格式
-h 不显示标题
-l 长格式
-w 宽输出
-a 显示终端上地所有进程,包括其他用户地进程
-r 只显示正在运行地进程
-x 显示没有控制终端地进程
PS命令使用:
ps命令用于查看当前正在运行的进程,常用的方法是ps aux,然后再通过管道使用grep命令过滤查找特定的进程,再对特定的进程进行操作,其中grep起到搜索作用。
例如:
ps -ef | grep java
表示查看所有进程里 CMD 是 java 的进程信息
ps -aux | grep java
-aux 显示所有状态
通常用 ps 查看进程 PID ,用 kill 命令终止进程,如:
例如: kill -9 [PID]
-9 表示强迫进程立即停止
Linux查看进程命令:Top命令
top命令可以实时显示各个线程情况。要在top输出中开启线程查看,请调用top命令的“-H”选项,该选项会列出所有Linux线程。在top运行时,你也可以通过按“H”键将线程查看模式切换为开或关。
12.用Linux查看文件有哪些命令?
(1)目录管理命令
——ls:列出指定目录下的内容
格式:ls [OPTION]… [FILE]…
-a:显示所有文件包括隐藏文件
-A:显示除.和…之外的所有文件
-l,–long:显示文件的详细属性信息
-h:对文件大小进行单位换算,可能影响精度
-d:查看目录本身而非其内部的文件
-r:逆序显示文件
-R:递归显示文件
示例:ls -lah / --详细显示/目录下的所有文件(包括隐藏文件)
ls -ldh /etc --详细显示/etc目录本身
ls -lhv / --倒序显示/目录下所有文件(包括隐藏文件)
ls -R /etc --递归显示/etc下所有文件
——mkdir:创建目录
格式:mkdir [OPTION]… DIRECTORY…
-p:自动按需创建父目录
-m:创建目录时给定权限
示例:mkdir -p /data/test/A/B --在/data目录下递归创建/test/A/B三个目录
mkdir -m 711 -p /data/MODE/A --在/data目录下递归创建MODE/A两个目录同时指定目录A的权限为711
——rmdir:删除目录
格式:rmdir [OPTION]… DIRECTORY…
-p:删除目录后如果其父目录为空,则一并删除
示例:rmdir -p /data/test/A --删除A目录后,test目录为空,一并删除
——cd:切换目录
示例:cd …:切换到上级目录
cd ~:切换回自己的家目录
cd -:在上一次目录与当前目录直接来回切换
——pwd:显示当前目录
(2)文件管理命令
——cp:复制
格式:单源复制:cp [OPTION]… [-T] SOURCE DEST(如果DEST不存在则创建,存在则覆盖)
多源复制:cp [OPTION]… SOURCE… DIRECTORY(DEST必须为directory)
-i:交互式复制,即覆盖前提醒用户确认
-f:强制覆盖目标文件
-r,-R:递归复制目录
示例:cp -if /data/[1-3].txt /data/test --test必须为目录,把三个文件一起复制到test中
cp -r /data /practice --把data目录及目录下的内容一起复制到practice中
——mv:剪切
格式:单源复制:mv [OPTION]… [-T] SOURCE DEST(如果DEST不存在则创建,存在则覆盖)
多源复制:mv [OPTION]… SOURCE… DIRECTORY(DEST必须为directory)
-i:交互式复制,即覆盖前提醒用户确认
-f:强制覆盖目标文件
示例:mv -i /data/[1-3].txt /practice --把/data目录下三个txt文件剪切到/practice下
——rm:删除
格式:rm [OPTION]… FILE…
-i:交互式复制,即覆盖前提醒用户确认
-f:强制覆盖目标文件
-r,-R:递归处理,将制定目录下的所有文件包括目录一并删除
示例:rm -rf /practice --递归删除/practice目录
(3)文本内容管理命令
——cat:正向查看文本内容
格式:cat [OPTION]… [FILE]…
-n:给显示的文本行编号
-E:显示行结束符号$
示例:cat -n /etc/fstab --查看/etc/fatab内容并显示行号
——tac:倒叙查看文本内容
格式:tac [OPTION]… [FILE]…
示例:tac /etc/passwd --倒叙查看文本内容
——head:显示文本内容,默认显示头10行
格式:head [OPTION]… [FILE]…
-n #:显示文本头#行内容
示例:head -5 /etc/passwd --显示/etc/passwd文件头5行内容
——tail:显示文本内容,默认显示后10行
格式:tail [OPTION]… [FILE]…
-n #:显示文本后#行内容
-f:查看文件尾部内容结束后不退出,跟随显示新增的行
示例:tail -8 /etc/passwd --显示/etc/passwd文件后8行内容
——more:分屏显示文本内容,每次显示一屏显示完停止
格式:more [options] file […]
Space键:显示文本下一屏内容
Enter键:只显示文本下一行内容
b键:显示文本上一屏内容
q键:退出
——less:分屏显示文本内容,不主动退出
格式:less [options] file […]
Space键:显示文本下一屏内容
Enter键:只显示文本下一行内容
b键:显示文本上一屏内容
q键:退出
13.Redis有哪些应用场景?
Redis是基于C语言编写的,而且是内存中的数据库,读写速度很快。在项目中也经常会使用Redis,一般会用来做缓存、或者分布式锁,也可以来设计消息队列,同时还支持事务 、持久化、Lua 脚本、多种集群方案。
14.你用Redis有哪些场景?使用的是哪些数据结构?
常见的有五种基本数据类型和三种特殊数据类型,
基本数据结构:String、 list、set、zset和hash,三种特殊数据类型:位图(bitmaps) 、计数器(hyperloglogs)和地理空间(geospatial indexes)。
String:一般常用在需要计数的场景,比如用户的访问次数、热点文章的点赞转发数量等等。
list:发布与订阅或者说消息队列、慢查询。
hash:系统中对象数据的存储。
set:需要存放的数据不能重复以及需要获取多个数据源交集和并集等场景
zset:需要对数据根据某个权重进行排序的场景。比如在直播系统中,实时排行信息包含直播间在线用户列表,各种礼物排行榜,弹幕消息(可以理解为按消息维度的消息排行榜)等信息。
SpringMVC的注解
@Controller
用于标记在一个类上,使用它的标记就是一个SpringMVCController对象,分发器DispacherServlet会扫描使用了该注解的类的方法,并且检测该方法是否使用**@RequestMapping**,@Controller只是定义了一个控制器类,而使用@RequestMapping是注解的方法才是真正处理请求的处理器,单单使用**@Controller**标记一个类还不能正真意义上的说他就是SpringMVC的一个控制器类,,这个是时候Spring还不认识它,怎么做才能让它认识,
(1)在SpringMVC 的配置文件中定义MyController 的bean 对象。
(2)在SpringMVC 的配置文件中告诉Spring 该到哪里去找标记为@Controller 的Controller 控制器。
@RequestMapping
RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。
RequestMapping注解有六个属性,我们把它分成三类进行说明
1、 value, method;
value: 指定请求的实际地址,指定的地址可以是URI Template 模式(后面将会说明);
method: 指定请求的method类型, GET、POST、PUT、DELETE等;
2、consumes,produces
consumes: 指定处理请求的提交内容类型(Content-Type),例如application/json, text/html;
produces: 指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回;
3、params,headers
params: 指定request中必须包含某些参数值是,才让该方法处理。
headers: 指定request中必须包含某些指定的header值,才能让该方法处理请求。
@Resource和@Autowired
@Resource和@Autowired都是做bean的注入时使用,其实@Resource并不是Spring的注解,它的包是javax.annotation.Resource,需要导入,但是Spring支持该注解的注入。
1、共同点
两者都可以写在字段和setter方法上。两者如果都写在字段上,那么就不需要再写setter方法。
2、不同点
(1)@Autowired
@Autowired为Spring提供的注解,需要导入包org.springframework.beans.factory.annotation.Autowired;只按照byType注入。
@Autowired注解是按照类型(byType)装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它的required属性为false。如果我们想使用按照名称(byName)来装配,可以结合@Qualifier注解一起使用。如下:
public class TestServiceImpl {
@Autowired
@Qualifier(“userDao”)
private UserDao userDao;
}
(2)@Resource
@Resource默认按照ByName自动注入,由J2EE提供,需要导入包javax.annotation.Resource。@Resource有两个重要的属性:name和type,而Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以,如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不制定name也不制定type属性,这时将通过反射机制使用byName自动注入策略。
注:最好是将@Resource放在setter方法上,因为这样更符合面向对象的思想,通过set、get去操作属性,而不是直接去操作属性。
@Resource的作用相当于@Autowired,只不过@Autowired按照byType自动注入。
@ModelAttribute和 @SessionAttributes
这两个代表的是该Controller的所有方法在调用前,先执行**@ModelAttribute**,可用于注解和方法的参数中,,这个注解可以利用在BaseController当中,所有的Controller继承BaseController,即可实现在调用Controller时,先执行@ModelAttribute方法。
@SessionAttributes即将值放到session作用域中,写在class上面。
@PathVariable
用于将请求URL中的模板变量映射到功能处理方法的参数上,即取出uri模板中的变量作为参数。
@requestParam
@requestParam主要用于在SpringMVC后台控制层获取参数,类似一种是request.getParameter(“name”),它有三个常用参数:defaultValue = “0”, required = false, value = "isApp";defaultValue 表示设置默认值,required 通过boolean设置是否是必须要传入的参数,value 值表示接受的传入的参数类型。
@ResponseBody
作用: 该注解用于将Controller的方法返回的对象,通过适当的HttpMessageConverter转换为指定格式后,写入到Response对象的body数据区。
使用时机:返回的数据不是html标签的页面,而是其他某种格式的数据时(如json、xml等)使用;
@Component
会被spring容器识别,并转化为bean
@Repository
用于dao的实现类,目前没有涉及。
mybatis $ #的区别
相同点:
都能取到变量的值。都能绑定参数。
不同点:
#可以实现预编译,会先把#{变量}编译成?,在执行时再取值,可以防止sql注入。#使用的是prepareStament
$是直接进行字符串替换。默认 使用Stament,
1.1 #符号解析:
上面说了#可以实现预编译,那你知道预编译过程中mybatis都做了啥吗,其实主要表现在数据类型检查和安全检查两部分:
数据类型检查表现在:若检测到为数值类型,就不加引号,即?;若检测到位字符串类型,就加上引号,即'?'。
安全检查表现在:若变量的值带有引号,会对引号进行转义处理,这样可以防止sql注入。
1.2 #符号的应用场景:
需要在sql映射文件中动态拼接sql时的开发场景,比如传入多个变量进行条件查询、传入一个POJO进行数据插入等。
2.1 $符号解析:
符
号
主
要
用
于
传
入
的
参
数
是
s
q
l
片
段
的
场
景
下
,
会
直
接
进
行
字
符
串
替
换
,
完
成
了
s
q
l
的
拼
接
。
比
如
我
们
不
在
s
q
l
映
射
文
件
中
利
用
m
y
b
a
t
i
s
的
动
态
s
q
l
来
拼
接
s
q
l
,
而
是
在
j
a
v
a
代
码
中
去
动
态
的
拼
接
s
q
l
,
然
后
将
拼
接
的
s
q
l
片
段
作
为
变
量
传
入
s
q
l
映
射
文
件
。
上
面
也
说
了
符号主要用于传入的参数是sql片段的场景下,会直接进行字符串替换,完成了sql的拼接。比如我们不在sql映射文件中利用mybatis的动态sql来拼接sql,而是在java代码中去动态的拼接sql,然后将拼接的sql片段作为变量传入sql映射文件。上面也说了
符号主要用于传入的参数是sql片段的场景下,会直接进行字符串替换,完成了sql的拼接。比如我们不在sql映射文件中利用mybatis的动态sql来拼接sql,而是在java代码中去动态的拼接sql,然后将拼接的sql片段作为变量传入sql映射文件。上面也说了符号只是进行字符串的替换,如果我们传入一个sql片段的话,相当于直接进行了sql拼接,就不用在sql映射文件中利用mybatis提供的动态sql标签来拼接sql了。甚至可以这样:
对于这样外部传入的sql,就不能使用#{},上面也说了,#{}会进行预编译,检测到该sql片段是个字符串,就会加上引号,即’sql片段’,这样就是字符串了而不是sql,执行会报错。
可以说,${}让以前在java代码中动态拼接sql的麻烦时代,在mybatis中成为了可能,mybatis想的真周到啊,嘿嘿。
依据$适用于传入sql片段的情况,我们可以将数据表字段名、表名、数据库名等传入,来进行sql拼接,如:
2.2 $符号的应用场景:
比如:select ${columns} from ${tableName},假设columns变量的值是userName,age 。tableName变量的值是user,那么该sql语句就会被解析成:select userName,age from user
比如有这样一个需求:对于后台管理系统的table列,想让用户选择展示哪些列,不展示哪些列,或者根据权限来分配不同的用户可以看到哪些列?
这个需求正好用到了 符 号 , 我 们 可 以 将 每 个 用 户 对 于 一 个 t a b l e 能 看 到 哪 些 列 , 存 储 到 数 据 表 中 , 对 该 数 据 表 进 行 c r u d , 每 次 展 示 列 表 的 时 候 , 先 根 据 用 户 I D 和 t a b l e 类 型 , 到 数 据 表 中 查 询 该 用 户 对 该 t a b l e 能 看 到 的 列 名 ( 字 段 名 ) 列 表 , 然 后 将 该 字 段 名 列 表 作 为 s q l 片 段 传 入 m y b a t i s 进 行 查 询 , 此 时 就 必 须 得 用 到 符号,我们可以将每个用户对于一个table能看到哪些列,存储到数据表中,对该数据表进行crud,每次展示列表的时候,先根据用户ID和table类型,到数据表中查询该用户对该table能看到的列名(字段名)列表,然后将该字段名列表作为sql片段传入mybatis进行查询,此时就必须得用到 符号,我们可以将每个用户对于一个table能看到哪些列,存储到数据表中,对该数据表进行crud,每次展示列表的时候,先根据用户ID和table类型,到数据表中查询该用户对该table能看到的列名(字段名)列表,然后将该字段名列表作为sql片段传入mybatis进行查询,此时就必须得用到符号,而不能用#符号了。
2.3 符 号 的 s q l 注 入 问 题 : 用 符号的sql注入问题: 用 符号的sql注入问题:用{}时要特别注意sql注入的风险,如果该sql片段是根据用户的输入拼接的,要注意检查sql注入的问题,防止数据的泄露与丢失!
反面案例:
假如这个sql:select * from ${tableName} where name = ${name}
ontroller的所有方法在调用前,先执行**@ModelAttribute**,可用于注解和方法的参数中,,这个注解可以利用在BaseController当中,所有的Controller继承BaseController,即可实现在调用Controller时,先执行@ModelAttribute方法。
@SessionAttributes即将值放到session作用域中,写在class上面。
@PathVariable
用于将请求URL中的模板变量映射到功能处理方法的参数上,即取出uri模板中的变量作为参数。
@requestParam
@requestParam主要用于在SpringMVC后台控制层获取参数,类似一种是request.getParameter(“name”),它有三个常用参数:defaultValue = “0”, required = false, value = "isApp";defaultValue 表示设置默认值,required 通过boolean设置是否是必须要传入的参数,value 值表示接受的传入的参数类型。
@ResponseBody
作用: 该注解用于将Controller的方法返回的对象,通过适当的HttpMessageConverter转换为指定格式后,写入到Response对象的body数据区。
使用时机:返回的数据不是html标签的页面,而是其他某种格式的数据时(如json、xml等)使用;
@Component
会被spring容器识别,并转化为bean
@Repository
用于dao的实现类,目前没有涉及。
mybatis $ #的区别
相同点:
都能取到变量的值。都能绑定参数。
不同点:
#可以实现预编译,会先把#{变量}编译成?,在执行时再取值,可以防止sql注入。#使用的是prepareStament
$是直接进行字符串替换。默认 使用Stament,
1.1 #符号解析:
上面说了#可以实现预编译,那你知道预编译过程中mybatis都做了啥吗,其实主要表现在数据类型检查和安全检查两部分:
数据类型检查表现在:若检测到为数值类型,就不加引号,即?;若检测到位字符串类型,就加上引号,即'?'。
安全检查表现在:若变量的值带有引号,会对引号进行转义处理,这样可以防止sql注入。
1.2 #符号的应用场景:
需要在sql映射文件中动态拼接sql时的开发场景,比如传入多个变量进行条件查询、传入一个POJO进行数据插入等。
2.1 $符号解析:
符
号
主
要
用
于
传
入
的
参
数
是
s
q
l
片
段
的
场
景
下
,
会
直
接
进
行
字
符
串
替
换
,
完
成
了
s
q
l
的
拼
接
。
比
如
我
们
不
在
s
q
l
映
射
文
件
中
利
用
m
y
b
a
t
i
s
的
动
态
s
q
l
来
拼
接
s
q
l
,
而
是
在
j
a
v
a
代
码
中
去
动
态
的
拼
接
s
q
l
,
然
后
将
拼
接
的
s
q
l
片
段
作
为
变
量
传
入
s
q
l
映
射
文
件
。
上
面
也
说
了
符号主要用于传入的参数是sql片段的场景下,会直接进行字符串替换,完成了sql的拼接。比如我们不在sql映射文件中利用mybatis的动态sql来拼接sql,而是在java代码中去动态的拼接sql,然后将拼接的sql片段作为变量传入sql映射文件。上面也说了
符号主要用于传入的参数是sql片段的场景下,会直接进行字符串替换,完成了sql的拼接。比如我们不在sql映射文件中利用mybatis的动态sql来拼接sql,而是在java代码中去动态的拼接sql,然后将拼接的sql片段作为变量传入sql映射文件。上面也说了符号只是进行字符串的替换,如果我们传入一个sql片段的话,相当于直接进行了sql拼接,就不用在sql映射文件中利用mybatis提供的动态sql标签来拼接sql了。甚至可以这样:
对于这样外部传入的sql,就不能使用#{},上面也说了,#{}会进行预编译,检测到该sql片段是个字符串,就会加上引号,即’sql片段’,这样就是字符串了而不是sql,执行会报错。
可以说,${}让以前在java代码中动态拼接sql的麻烦时代,在mybatis中成为了可能,mybatis想的真周到啊,嘿嘿。
依据$适用于传入sql片段的情况,我们可以将数据表字段名、表名、数据库名等传入,来进行sql拼接,如:
2.2 $符号的应用场景:
比如:select ${columns} from ${tableName},假设columns变量的值是userName,age 。tableName变量的值是user,那么该sql语句就会被解析成:select userName,age from user
比如有这样一个需求:对于后台管理系统的table列,想让用户选择展示哪些列,不展示哪些列,或者根据权限来分配不同的用户可以看到哪些列?
这个需求正好用到了 符 号 , 我 们 可 以 将 每 个 用 户 对 于 一 个 t a b l e 能 看 到 哪 些 列 , 存 储 到 数 据 表 中 , 对 该 数 据 表 进 行 c r u d , 每 次 展 示 列 表 的 时 候 , 先 根 据 用 户 I D 和 t a b l e 类 型 , 到 数 据 表 中 查 询 该 用 户 对 该 t a b l e 能 看 到 的 列 名 ( 字 段 名 ) 列 表 , 然 后 将 该 字 段 名 列 表 作 为 s q l 片 段 传 入 m y b a t i s 进 行 查 询 , 此 时 就 必 须 得 用 到 符号,我们可以将每个用户对于一个table能看到哪些列,存储到数据表中,对该数据表进行crud,每次展示列表的时候,先根据用户ID和table类型,到数据表中查询该用户对该table能看到的列名(字段名)列表,然后将该字段名列表作为sql片段传入mybatis进行查询,此时就必须得用到 符号,我们可以将每个用户对于一个table能看到哪些列,存储到数据表中,对该数据表进行crud,每次展示列表的时候,先根据用户ID和table类型,到数据表中查询该用户对该table能看到的列名(字段名)列表,然后将该字段名列表作为sql片段传入mybatis进行查询,此时就必须得用到符号,而不能用#符号了。
2.3 符 号 的 s q l 注 入 问 题 : 用 符号的sql注入问题: 用 符号的sql注入问题:用{}时要特别注意sql注入的风险,如果该sql片段是根据用户的输入拼接的,要注意检查sql注入的问题,防止数据的泄露与丢失!
反面案例:
假如这个sql:select * from ${tableName} where name = ${name}
如果tableName的值为 user; delete user; --,该sql最终解析成select * from user; delete user; – where name = xxx,结果查询了整张表,然后把user表给删了…