SSM阶段面试题
1.mybatis
1.1. #{}和${}的区别是什么?
- #{}是预编译处理,$ {}是字符串替换。
- MyBatis在处理#{}时,会将SQL中的#{}替换为?号,使用PreparedStatement的set方法来赋值;MyBatis在处理 $ { } 时,就是把 ${ } 替换成变量的值。
- 使用 #{} 可以有效的防止SQL注入,提高系统安全性。
1.2. 当实体类中的属性名和表中的字段名不一样 ,怎么办 ?
-
如果符合驼峰命名规则,可以使用@Column(name = “表字段名”)转驼峰
-
如果符合驼峰命名规则,也可以在xml配置文件中开启全局配置
<setting name="mapUnderscoreToCamelCase" value="true"/>
-
如果不符合驼峰命名,可以通过@result自定义映射规则或者
<result column="字段名" property="实体属性名"/>
1.3. Mybatis是如何进行分页的?分页插件的原理是什么?
实现原理:使用Mybatis里面的第三方插件pagehelper(也称为拦截器)进行分页,通过PageHelper.startPage(拦截规则)进行拦截数据库数据,也就是重写sql语句,根据不同的数据生产不同的分页语句。
-
导入pagehelper导入依赖
<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.1.11</version> </dependency>
-
在setPlugins()设置添加pageInterceptor插件
PageInterceptor pageInterceptor = new PageInterceptor();
pageInterceptor.setProperties(new Properties());
factoryBean.setPlugins(new PageInterceptor[]{pageInterceptor});
-
或者在xml配置文件中
<configuration>
中添加plugin
插件配置和设置分页规则<plugin interceptor="com.autumn.interceptor.MyPageInterceptor">
1.4. 在mapper中如何传递多个参数?
- 可以将多个参数封装成一个对象或map集合传递
- 可以通过参数池传递,@param设置存储在参数池的名字
- 也可以通过#{}表达式传值,前提是参数命名和#{}的参数保持一致
1.5. Mybatis是否支持延迟加载?如果支持,它的实现原理是什么?
Mybatis支持延迟加载,实现原理主要通过使用CGLIB为目标对象建立代理对象,当调用目标对象的方法时进入拦截器方法,当调用一个方法时,拦截器方法invoke()会根据这个方法所需要的东西发送给方法。
1.6. 详细解释一下Mybatis的一级、二级缓存
一级缓存是sqlsession级别:首先从sqlsession的一级缓存中cache查找数据,如果没有数据,则从数据库中查询返回给一级缓存,再返回数据信息。
/**
* 一级缓存是sqlSession级别
* 在一个sqlSession的声明周期中有效
* 在一次的连接过程中有效(无法跨sqlsession有效)
* 默认开启无法关闭
* 当前的sqlSession调用了增删改会清空一级缓存中的数据
* 如果提交也会清空一级缓存中的数据
*/
二级缓存是mapper级别:首先通过cacheExecuter缓存执行器中,在二级缓存mapper查找,如果没有记录,再从一级缓存sqlsession中查找,如果再没有记录,则从数据库查找返回给一级缓存返回给执行器,再返回数据信息,但关闭session时,一级缓存会清空,一级缓存的会将数据序列号放入二级缓存中,下次再查相同的数据时,可以直接从二级缓存中查找,可以跨sqlsession传递。
/**
* 二级缓存:
* mapper级别的缓存,所有sqlSession都可以获取到二级缓存中的数据
* 当一级缓存关闭的时候,会将数据写入二级缓存
* 当数据发生更改,会导致缓存清空
*
* 二级缓存操作:
* 1.开启全局二级缓存支持,默认开启
* 2.在需要使用二级缓存的mapper添加 <cache></cache> 或 @CacheNameSpace
* 3.需要被缓存的方法查询数据上添加 useCache=true 默认是true
* 4.需要被缓存的数据对象的类型上,需要添加序列化
*/
缓存的执行顺序:二级缓存==》一级缓存==》数据库
1.7. Mapper编写有哪几种方式?
-
可以在.xml中配置mapper.xml文件即可(使用此种方法需要编写mapper 接口,mapper 接口实现类、mapper.xml 文件)
<mappers> <mapper resource="mapper.xml 文件的地址" /> <mapper resource="mapper.xml 文件的地址" /> </mappers>
-
通过mapper扫描器扫描dao包,注解@MapperScan,或者xml配置:
<!--映射配置 namespace:设置命名空间 mapper的唯一id mybatis会创建一个配置文件对象,并根据namespace设置其id值 零配置操作需要与dao接口的全限定名一致 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="mapper 接口包地址"></property> <property name="sqlSessionFactoryBeanName"value="sqlSessionFactory"/> </bean>
-
使用org.mybatis.spring.mapper.MapperFactoryBean,此方法即mapper接口开发方法,只需定义mapper接口,不用编写mapper接口实现类。每个mapper接口都需要在spring配置文件中定义。
2. SpringMVC
2.1. SpringMVC的流程?
- 发送请求
- DispatcherServlet核心控制器(配置映射规则,配置读取spring配置文件,创建spring容器)
- handlermapping处理映射器,根据请求的URL匹配对应的处理器(方法)
- Controller控制器处理请求,返回给核心控制器一个handler处理对象
- 核心控制器传递给HandAdapter处理适配器执行handler,返回ModelAndView给核心控制器
- ViewResolver视图解析器解析ModelAndView查找视图(view)资源,并传送Model
- 返回页面
2.2. SpringMvc的控制器是不是单例模式,如果是,有什么问题,怎么解决?
默认是单例模式,这样每一次访问时就不需要new一个新的控制器,减少内存,但是当多线程访问时,会出现线程安全问题,尽量不要在控制器中定义一个成员变量就可以解决,也可以通过同步代码块解决(会影响性能,不介意使用)
2.3. SpringMVC常用的注解有哪些?
@Controller:标志为控制层
@RestController:相当于@Controller+@ResponseBody两个注解的结合,返回json数据不需要在方法前面加@ResponseBody注解了,不能直接返回一个页面,只能通过ModelAndView封装返回一个页面
@RequestMapping:处理请求地址映射的,支持上下文路径
@ResponseBody:用于将Controller的方法返回的是对象,不是页面
@Autowired:spring容器元素注入
@RequestBody:表示请求体,请求的数据类型是一个对象类型
@RequestParam:根据指定请求数据的key值,获取请求数据
@PathVariable:用于将请求URL中的模板变量映射到功能处理方法的参数上,即取出uri模板中的变量作为参数。
2.4. 如何自定义一个视图解析器?
- 可以在xml配置文件配置:
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
- 自定义一个MVC配置类,设置视图解析器规则:
@Bean
public InternalResourceViewResolver getViewResolver(){
return new InternalResourceViewResolver("/WEB-INF/html", ".html");
}
2.5. 在SpringMVC如何添加适配器?
2.6. SpringMVC如何实现多文件上传?
-
导入依赖
<dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.3</version> </dependency>
-
xml配置上传文件解析器
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> //命名必须为multipartResolver <property name="defaultEncoding" value="utf-8"></property> //文件字符集为utf-8 <property name="maxUploadSize" value="104857600"></property> //上传文件大小限制 </bean>
-
Java配置类配置上传文件解析器
@Bean("multipartResolver") //bean名必须是multipartResolver
public CommonsMultipartResolver getMultipartResolver(){
return new CommonsMultipartResolver();
}
- 前端上传文件是,用MultiFileDomain multiFileDomain接收多个文件,MultiFile接收单个文件
2.7. 如何定义一个拦截器,并让其生效
3. Spring
3.1. Spring的AOP理解
AOP:面向切面编程,可在不改变程序源码的情况下为程序添加额外的功能,从而降低代码之间的耦合度。
3.2. 说说JDK动态代理和CGLIB动态代理
-
利用cglib工具包
目标对象没有实现接口使用CGLIB代理。
-
利用 JDK Proxy API
如果目标对象实现接口,使用JDK代理。
3.3. Spring的IoC理解
IOC:控制反转,就是将代码的调用权从调用方转移给第三方容器
3.4. BeanFactory和ApplicationContext有什么区别?
BeanFactory:是Spring里面最低层的接口,提供了最简单的容器的功能,只提供了实例化对象和拿对象的功能;
ApplicationContext:应用上下文,继承BeanFactory接口,它是Spring的一各更高级的容器,提供了更多的有用的功能(国际化,访问资源,载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,消息发送和响应,AOP拦截器)
两者装载bean的区别:
BeanFactory:BeanFactory在启动的时候不会去实例化Bean,中有从容器中拿Bean的时候才会去实例化;
ApplicationContext:ApplicationContext在启动的时候就把所有的Bean全部实例化了。
3.5. 请解释Spring Bean的生命周期?
- bean的实例化
- 封装属性
- 获取bean的名字或实现上下文
- 前置处理
- 全局初始化
- 本类初始化
- 后置处理
- 业务执行
- 销毁
3.6. 解释Spring支持的几种bean的作用域。
- singleton,单例模式(可实现懒加载)。
- prototype,一个bean的定义可以有多个实例。
- request,每次http请求都会创建一个bean,该作用域仅在基于web的Spring ApplicationContext情形下有效。
- session,在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。
- global-session,在一个全局的HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。
3.7. Spring框架中的单例Beans是线程安全的吗?
大部分的Spring bean并没有可变的状态(比如Serview类和DAO类),所以在某种程度上说Spring的单例bean是线程安全的。如果你的bean有多种状态的话(比如 View Model 对象),就需要自行保证线程安全。
3.8. Spring如何处理线程并发问题?
在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域,因为Spring对一些Bean中非线程安全状态采用ThreadLocal进行处理,解决线程安全问题。
3.9. spring的事务传播行为
- PROPAGATION_REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
- PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
- PROPAGATION_MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常。
- PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
- PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
- PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
3.10. Spring中的隔离级别
-
读未提交(READ_UNCOMMITTED),也称为脏读,就是一个事务可以读取另一个事务未提交的内容
-
读已提交(READ_COMMITTED),也称为不可重复读,就是不同的事务重复读了一条记录会出问题。一个事务只可以读取另一个事务提交的内容
-
可重复读(REPEATABLE_READ),一个事务在读取了记录之后,这条记录不能再被修改,也就是把这一行记录给锁住,不在允许其它的事物染指此行记录
-
串行化(SERIALIZABLE),一个事务必须等待另一个事务执行完成再可以执行
3.11. Spring框架中有哪些不同类型的事件?
-
上下文更新事件(ContextRefreshedEvent):该事件会在 ApplicationContext 被初始化或者更新时发布。也可以在调ConfigurableApplicationContext 接口中的 refresh()方法时被触发。
-
上下文开始事件(ContextStartedEvent):当容器调用 ConfigurableApplicationContext 的Start()方法开始/重新开始容器时触发该事件。
-
上下文停止事件(ContextStoppedEvent):当容器调用 ConfigurableApplicationContext 的Stop()方法停止容器时触发该事件。
-
上下文关闭事件(ContextClosedEvent):当 ApplicationContext 被关闭时触发该事件。容器被关闭时,其管理的所有单例 Bean 都被销毁。
-
请求处理事件(RequestHandledEvent):在 Web 应用中,当一个 http 请求(request)结束触发该事件。
3.12. 介绍一下AOP相关术语?
切面,通知,连接点,切点,织入
连接点:符合进行切面条件的点或位置
切点:进行切面编程的连接点,和连接点的主要区别在于,连接点只是符合条件的点或位置,而切点是实际上真正进行切面的点。
通知:通知切面什么时候,什么位置进行切面,又分为前置通知,环绕通知,异常通知,后置通知,返回通知五种通知,
前置通知:在方法被调用前执行
环绕通知:在方法被调用前后执行
异常通知:在方法被调用抛出异常时执行
后置通知:在方法被调用后执行
返回通知:目标方法成功执行后调用通知,如果出现异常则不执行
切面:进行切面的内容或插入的逻辑内容
织入:进行切面切入的过程
4.shiro认证器
4.1.说说Shiro授权过程
调用了SecurityManager进行鉴权,使用了认证器进行鉴权,调用Realm自身的getAuthorizationInfo进行授权,再根据自定义的realm鉴权,最后才是鉴权操作。
4.2.Shiro 如何自实现认证
在认证、授权内部实现机制中,最终处理都将交给Real进行处理。因为在Shiro中,最终是通过Realm来获取应用程序中的用户、角色及权限信息的。在应用程序中要做的是自定义一个Realm类,继承AuthorizingRealm抽象类,重载doGetAuthenticationInfo (),重写获取用户信息的方法。而授权实现则与认证实现非常相似,在我们自定义的Realm中,重载doGetAuthorizationInfo()方法,重写获取用户权限的方法。
4.3.如何在spring配置中使用shiro
Subject | 主体 |
---|---|
SecurityManager | 安全管理器 |
Authenticator | 认证器 |
Authrizer | 授权器 |
Realm | 域 |
SessionManager | Session管理器 |
CacheManager | 缓存控制器 |
Cryptography | 密码模块 |