1 Maven 项目管理工具有什么优缺点?

优点:

  1. 简化了项目依赖管理:
  2. 易于上手,对于新手可能一个”mvn clean package”命令就可能满足他的工作
  3. 便于与持续集成工具(jenkins)整合
  4. 便于项目升级,无论是项目本身升级还是项目使用的依赖升级。
  5. 有助于多模块项目的开发,一个模块开发好后,发布到仓库,依赖该模块时可以直接从仓库更新,而不用自己去编译。
  6. maven有很多插件,便于功能扩展,比如生产站点,自动发布版本等

缺点:

  1. maven是一个庞大的构建系统,学习难度大
  2. maven采用约定优于配置的策略(convention over configuration),虽然上手容易,但是一旦出了问题,难于调试。
  3. 当依赖很多时,m2eclipse 老是搞得Eclipse很卡。
  4. 中国的网络环境差,很多repository无法访问,比如google code, jboss 仓库无法访问等

2 在使用 Maven 管理项目时,常见的标签有什么?

父项目的坐标。
构建标识符
全球唯一标识符
版本
插件列表
插件元素
所有依赖
依赖元素

3 请列举 Maven 的私服仓库?

http://maven.aliyun.com/nexus/content/groups/public


4 Spring 的主要有哪些模块,各个模块主要提供了什么服务支持?

image.png

  1. Spring Core: Core封装包是框架的最基础部分,提供IOC和依赖注入特性。这里的基础概念是BeanFactory,它提供对Factory模式的经典实现来消除对程序性单例模式的需要,并真正地允许你从程序逻辑中分离出依赖关系和配置。

2.Spring Context: 构建于Core封装包基础上的 Context封装包,提供了一种框架式的对象访问方法,有些象JNDI注册器。Context封装包的特性得自于Beans封装包,并添加了对国际化(I18N)的支持(例如资源绑定),事件传播,资源装载的方式和Context的透明创建,比如说通过Servlet容器。

3.Spring DAO: DAO (Data Access Object)提供了JDBC的抽象层,它可消除冗长的JDBC编码和解析数据库厂商特有的错误代码。 并且,JDBC封装包还提供了一种比编程性更好的声明性事务管理方法,不仅仅是实现了特定接口,而且对所有的POJOs(plain old Java objects)都适用。

4.Spring ORM: ORM 封装包提供了常用的“对象/关系”映射APIs的集成层。 其中包括JPA、JDO、Hibernate 和 iBatis 。利用ORM封装包,可以混合使用所有Spring提供的特性进行“对象/关系”映射,如前边提到的简单声明性事务管理。

5.Spring AOP: Spring的 AOP 封装包提供了符合AOP Alliance规范的面向方面的编程实现,让你可以定义,例如方法拦截器(method-interceptors)和切点(pointcuts),从逻辑上讲,从而减弱代码的功能耦合,清晰的被分离开。而且,利用source-level的元数据功能,还可以将各种行为信息合并到你的代码中。

6.Spring Web: Spring中的 Web 包提供了基础的针对Web开发的集成特性,例如多方文件上传,利用Servlet listeners进行IOC容器初始化和针对Web的ApplicationContext。当与WebWork或Struts一起使用Spring时,这个包使Spring可与其他框架结合。

7.Spring Web MVC: Spring中的MVC封装包提供了Web应用的Model-View-Controller(MVC)实现。Spring的MVC框架并不是仅仅提供一种传统的实现,它提供了一种清晰的分离模型,在领域模型代码和Web Form之间。并且,还可以借助Spring框架的其他特性。


5 请简单叙述你对 Spring AOP 面向切面编程的理解?

  1. 横向编程思想。
  2. 通过Aspect表明的注解类,给由spring管理的bean中方法提供操作。
  3. 用@Aspect注解方式来实现前置通知、返回通知、后置通知、异常通知、环绕通知

6 BeanFactory 和 ApplicationContext 有什么区别?

BeanFactory 是spring的原始接口,针对原始结构的实现类功能比较单一,BeanFactory接口实现的容器,特点是在每次获取对象时才会创建对象。


ApplicationContext 继承了BeanFactory接口,拥有BeanFactory的全部功能,并且扩展了很多高级特性,每次容器启动时就会创建所有的对象。


7 Spring 是如何管理 bean 的生命周期的?

image.png
Bean的生命周期可以表达为:Bean的实例化(定义)- Bean的初始化 - Bean的使用 - Bean的销毁
1、实例化一个Bean--也就是我们常说的new;
2、按照Spring上下文对实例化的Bean进行配置(属性注入)--也就是IOC注入;
3、如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String)方法,此处传递的就是Spring配置文件中Bean的id值;
4、如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory(setBeanFactory(BeanFactory)传递的是Spring工厂自身(可以用这个方式来获取其它Bean,只需在Spring配置文件中配置一个普通的Bean就可以);
5、如果这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文(同样这个方式也可以实现步骤4的内容,但比4更好,因为ApplicationContext是BeanFactory的子接口,有更多的实现方法);
6、如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor经常被用作是Bean内容的更改,并且由于这个是在Bean初始化结束时调用那个的方法,也可以被应用于内存或缓存技术;
7、如果Bean在Spring配置文件中配置了init-method属性会自动调用其配置的初始化方法;
8、如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法、;
注:以上工作完成以后就可以应用这个Bean了,那这个Bean是一个Singleton的,所以一般情况下我们调用同一个id的Bean会是在内容地址相同的实例,当然在Spring配置文件中也可以配置非Singleton;
9、当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用那个其实现的destroy()方法;
10、最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。


Bean是一个被实例化,组装,并通过Spring IOC容器(BeanFactory和ApplicationContext容器)所管理的对象。


Spring支持五个作用域,分别是singleton、prototype、request、session、global session
image.png


[

](https://blog.csdn.net/qq_37109975/article/details/88718925)

8 请说明目前常用的 Sring Boot 相比 Spring MVC 有什么优缺点?

SpringMVC
(1)Spring MVC是Spring提供的一个强大而灵活的模块式web框架。通过Dispatcher Servlet, ModelAndView 和 View Resolver,开发web应用变得很容易。
(2)SpringMVC是一种基于Java的以请求为驱动类型的轻量级Web框架,其目的是将Web层进行解耦,即使用“请求-响应”模型,从工程结构上实现良好的分层,区分职责,简化Web开发。借助于注解,Spring MVC提供了几乎是POJO的开发模式,使得控制器的开发和测试更加简单。这些控制器一般不直接处理请求,而是将其委托给Spring上下文中的其他bean,通过Spring的依赖注入功能,这些bean被注入到控制器中。
(3)Spring框架最核心的就是所谓的依赖注射和控制反转。完全解耦类之间的依赖关系,一个类如果要依赖什么,那就是一个接口。至于如何实现这个接口,这都不重要了。只要拿到一个实现了这个接口的类,就可以轻松的通过xml配置文件把实现类注射到调用接口的那个类里。所有类之间的这种依赖关系就完全通过配置文件的方式替代了。

SpringMVC的优点:
(1)使用简单,学习成本低。
(2)很容易就可以写出性能优秀的程序.
(3)灵活性强,Spring MVC的框架易扩展
SpringMVC的缺点:
(1)Spring与MVC 的Servlet API 耦合,难以脱离容器独立运行
(2)太过于细分,开发效率低
(3)过度追求完美,有过度设计的危险解决的问题领域是:网站应用程序或者服务开发——URL路由、Session、模板引擎、静态Web资源等等。


Spring Boot引入自动配置的概念,让项目设置变得很容易。Spring Boot本身并不提供Spring框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于Spring框架的应用程序。也就是说,它并不是用来替代Spring的解决方案,而是和Spring框架紧密结合用于提升Spring开发者体验的工具。同时它集成了大量常用的第三方库配置(例如Jackson, JDBC, Mongo, Redis, Mail等等),Spring Boot应用中这些第三方库几乎可以零配置的开箱即用(out-of-the-box),大部分的Spring Boot应用都只需要非常少量的配置代码,开发者能够更加专注于业务逻辑。Spring Boot只是承载者,辅助开发者简化项目搭建过程的。如果承载的是WEB项目,使用Spring MVC作为MVC框架,那么工作流程和SpringMVC的是完全一样的,因为这部分工作是Spring MVC做的而不是Spring Boot。


9 MyBatis 的存在主要是为了解决什么问题,有什么优点?

Mybatis是一个半自动的ORM持久层框架,内部封装了JDBC。作为开发者只需要关注sql语句本身。Mybatis是通过xml或注解的方式将需要执行的各种statement配置起来。通过Java对象和statement中的sql动态参数映射生成最终执行的sql语句,最终由Mabtais框架执行sql并将结果映射为Java对象并返回。MyBatis 支持定制化 SQL、存储过程以及高级映射。MyBatis 是可以双向映射的,可以将数据集映射为Java对象,也可以将Java对象映射为数据库中的记录。


优点:
简单易学,容易上手(相比于Hibernate) 基于SQL编程;
JDBC相比,减少了50%以上的代码量,消除了JDBC大量冗余的代码,不需要手动开关连接
能够与Spring很好的集成
提供XML标签,支持编写动态SQL语句
提供映射标签,支持对象与数据库的ORM字段关系映射
提供对象关系映射标签,支持对象关系组建维护。

10 MyBatis与Hibernate有哪些不同?

1)sql 优化方面
Hibernate 不需要编写大量的 SQL,就可以完全映射,提供了日志、缓存、级联(级联比 MyBatis 强大)等特性,此外还提供HQL(Hibernate Query Language)对 POJO 进行操作。但会多消耗性能。
MyBatis 手动编写 SQL,支持动态 SQL、处理列表、动态生成表名、支持存储过程。工作量相对大些。(优化工作比较方便
2)开发方面
MyBatis 是一个半自动映射的框架(半自动ROM),因为 MyBatis 需要手动匹配 POJO、SQL 和映射关系。(Mybatis 需要写sql,并配置对象之间的关系)
Hibernate 是一个全表映射的框架(全自动ROM),只需提供 POJO 和映射关系即可。(Hibernate 不需要写sql, 就可以完成数据库的操作,虽然支持写sql的方式,但是一般都不写。通过java代码操作对象,框架自动封装成sql语句之后在执行,并且是跨数据库的)
3) Hibernate 优势
Hibernate 的 DAO 层开发比 MyBatis 简单,Mybatis 需要维护 SQL 和结果映射。
Hibernate 对对象的维护和缓存要比 MyBatis 好,对增删改查的对象的维护要方便。
Hibernate 数据库移植性很好,MyBatis 的数据库移植性不好,不同的数据库需要写不同 SQL。
Hibernate 有更好的二级缓存机制,可以使用第三方缓存。MyBatis 本身提供的缓存机制不佳。
4)Mybatis优势
MyBatis 可以进行更为细致的 SQL 优化,可以减少查询字段。
MyBatis 容易掌握,而 Hibernate 门槛较高。


总的来说,MyBatis 是一个小巧、方便、高效、简单、直接、半自动化的持久层框架。Hibernate 是一个强大、方便、高效、复杂、间接、全自动化的持久层框架。所以对于性能要求不太苛刻的系统,比如管理系统、ERP 等推荐使用 Hibernate,而对于性能要求高、响应快、灵活的系统则推荐使用 MyBatis。


11 在使用 Mybatis 插件写 SQL 时,#{}和${}的区别是什么?

1.#{}是预编译处理,是占位符,${}是字符串替换,是拼接符
2.Mybatis在处理#{}的时候会将sql中的#{}替换成?号,调用PreparedStatement来赋值

  1. 如:select * from user where name = #{userName};设userName=yuze
  2. 看日志我们可以看到解析时将#{userName}替换成了
  3. select * from user where name = ?;
  4. 然后再把yuze放进去,外面加上单引号

3.Mybatis在处理${}的时候就是把${}替换成变量的值,调用Statement来赋值

  1. 如:select * from user where name = #{userName};设userName=yuze
  2. 看日志可以发现就是直接把值拼接上去了
  3. select * from user where name = yuze;
  4. 这极有可能发生sql注入,下面举了一个简单的sql注入案例

4.#{}的变量替换是在DBMS中、变量替换后,#{}对应的变量自动加上单引号
5.${}的变量替换是在DBMS外、变量替换后,${}对应的变量不会加上单引号
6.使用#{}可以有效的防止sql注入,提高系统的安全性


下面举一个简单的sql注入问题:

这是一条用户的账号、密码数据
image.png
当用户登录,我们验证账号密码是否正确时用这个sql:
username=yyy;password=123

  1. select * from user where username=${username} and password=${password}

显然这条sql没问题可以查出来,但是如果有人不知道密码但是想登录账号怎么办
我们不需要填写正确的密码:
密码输入1 or 1=1,sql执行的其实是

  1. select * from user where username='yyy' and password=1 or 1 =1

注意:这里的yyy外面的单引号不是${}符号提供的。${}没有这个功能,可以是sql手动拼接的,这里前后逻辑可能并不严密,但是sql注入最简单的例子就是这样。


12 MyBatis 动态 SQL 的执行原理是什么?

mybatis可以通过两种方式实现动态SQL,通过XML配置SQL,也可以使用注解的方式配置。两种不同的方式,执行的逻辑都不一样。


  1. 通过XML配置实现动态SQL
    构建SqlSessionFactory对象时,解析mapper.xml文件,XMLStatementBuilder.parseStatementNode()根据不同的SQL标签()生成对应的MappedStatement对象,存放在Configuration.mappedStatements集合中。
    然后遍历主标签里所有节点生成对应的SQL子节点对象(SqlNode)。XMLScriptBuilder.parseScriptNode()解析每个标签生成SqlNode对象。

    1. public SqlSource parseScriptNode() {
    2. List<SqlNode> contents = parseDynamicTags(context); //context 当前遍历到的sql标签
    3. MixedSqlNode rootSqlNode = new MixedSqlNode(contents);
    4. SqlSource sqlSource = null;
    5. if (isDynamic) {
    6. sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
    7. } else {
    8. sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
    9. }
    10. return sqlSource;
    11. }

    执行Executor.query()方法,根据keyStatementId获取到对应的MappedStatement对象,获取BoundSql对象时。遍历当前SqlSource.rootSqlNode对象。执行SqlNode.apply()方法。把动态sql拼接到DynamicContext.sqlBuilder字段中。

    1. @Override
    2. public BoundSql getBoundSql(Object parameterObject) {
    3. DynamicContext context = new DynamicContext(configuration, parameterObject);
    4. rootSqlNode.apply(context);
    5. SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
    6. Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
    7. SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
    8. BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    9. for (Map.Entry<String, Object> entry : context.getBindings().entrySet()) {
    10. boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());
    11. }
    12. return boundSql;
    13. }

    使用sqlSourceParser.parse()方法替换sql中 ${}, #{} 为 ?,并且把大括号中的值存放在
    ParameterMappingTokenHandler.parameterMappings集合中。

    1. //SqlSourceParser.parse()
    2. public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
    3. ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
    4. GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
    5. String sql = parser.parse(originalSql);
    6. return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
    7. }
    8. //TokenHandler.handleToken(content)
    9. @Override
    10. public String handleToken(String content) {
    11. parameterMappings.add(buildParameterMapping(content));
    12. return "?";
    13. }

  1. 通过注解实现动态SQL
    注解实现动态SQL不同的地方在于或许主标签的方式不一样。XML动态SQL可以直接解析等标签的节点。注解需要通过MapperAnnotationBuilder.getSqlSourceFromAnnotations(Method method, Class<?> parameterType, LanguageDriver languageDriver)方法获取SQL语句。然后在执行XMLScriptBuilder.parseScriptNode()解析每个标签生成SqlNode对象。
    1. private SqlSource getSqlSourceFromAnnotations(Method method, Class<?> parameterType, LanguageDriver languageDriver) {
    2. try {
    3. Class<? extends Annotation> sqlAnnotationType = getSqlAnnotationType(method);
    4. Class<? extends Annotation> sqlProviderAnnotationType = getSqlProviderAnnotationType(method);
    5. if (sqlAnnotationType != null) {
    6. if (sqlProviderAnnotationType != null) {
    7. throw new BindingException("You cannot supply both a static SQL and SqlProvider to method named " + method.getName());
    8. }
    9. Annotation sqlAnnotation = method.getAnnotation(sqlAnnotationType);
    10. final String[] strings = (String[]) sqlAnnotation.getClass().getMethod("value").invoke(sqlAnnotation);
    11. return buildSqlSourceFromStrings(strings, parameterType, languageDriver);
    12. } else if (sqlProviderAnnotationType != null) {
    13. Annotation sqlProviderAnnotation = method.getAnnotation(sqlProviderAnnotationType);
    14. return new ProviderSqlSource(assistant.getConfiguration(), sqlProviderAnnotation, type, method);
    15. }
    16. return null;
    17. } catch (Exception e) {
    18. throw new BuilderException("Could not find value method on SQL annotation. Cause: " + e, e);
    19. }
    20. }

13 怎样配置 MyBatis 的二级缓存?

mybatis 的缓存机制有两级:
(1)一级缓存:一级缓存mybatsi已近为我们自动开启,不用我们手动操作,而且我们是关闭不了的!!但是我们可以手动清除缓存。(SqlSession级别)
(2)二级缓存:二级缓存需要我们手动开启。(全局级别)
注:缓存说白了就是查询时可以去历史记录找一找,不用去数据库中查询(不用执行查询语句)。


1.配置文件配置二级缓存(两步):

  1. <settings>
  2. <!--
  3. (1):开启二级缓存,这个全局的配置二级缓存
  4. 默认是开启的,但是还是需要写上,防止版本的更新
  5. -->
  6. <setting name="cacheEnabled" value="true"/>
  7. </settings>

2.在具体需要二级缓存的mapeer映射文件中开启二级缓存,值需要在相应的映射文件中添加一个cache标签即可

  1. (2):在相应的映射文件中开启二级缓存
  2. <!-- 开启二级缓存 -->
  3. <cache></cache>

2.注解开启二级缓存(两步):
1.在setting.xml(mybaits配置文件)中

  1. <settings>
  2. <!--默认是开启的,防止版本更新-->
  3. <setting name="cacheEnabled" value="true"/>
  4. </settings>

2.在dao中开启二级缓存
在类名上配置 注解:

  1. @CacheNamespace(blocking = true)