使用Spring框架的好处
-
解耦合:
在不使用Spring开发的项目当中,在使用相同接口的不同实现类时,每次在进行不同的实现时都要创建不同的实现类对象,而这也大大提高了对象与对象之间的耦合性,而在使用了Spring框架开发项目后,我们可以把项目当中对象的创建交给容器来管理,Spring管理实例与实例/类与类之间的依赖关系以及实例的创建,当我们要使用到不同的实现类时,只需要修改依赖注入时注入的实例bean即可
-
方便集成:
Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如Struts、Hibernate、MyBatis、Quartz等)的直接支持。简化了在使用其他框架所需的冗余性代码。
-
轻量级:
Spring是一个轻量级的IOC和AOP容器框架
-
低侵入性:
Spring是一种非侵入式(non-invasive)框架,它可以使应用程序代码对框架的依赖最小化。
Spring和SpringBoot的区别
SpringBoot是基于Spring衍生出来的一个框架体系,是Spring本身的扩展。
使用SpringBoot完成的项目在进行线上部署时,我们只需要将打好的jar包放在类路径classpath下,然后使用java -jar运行即可,它会自动配置数据源
在创建项目时,SpringBoot会为我们提供一组预先配置好的启动项目,通过starter依赖添加到我们的项目当中
Spring IOC
Spring的两大核心思想之一
IOC(Inverse of Control:控制反转)是一种设计思想,就是 将原本在程序中手动创建对象的控制权,交由Spring框架来管理。 IoC 在其他语言中也有应用,并非 Spring 特有。 IOC 容器是 Spring 用来实现 IoC 的载体, IoC 容器实际上就是个Map(key,value),Map 中存放的是各种对象。
将对象之间的相互依赖关系交给 IoC 容器来管理,并由 IoC 容器完成对象的注入。这样可以很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出来。 IoC 容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的。 在实际项目中一个 Service 类可能有几百甚至上千个类作为它的底层,假如我们需要实例化这个 Service,你可能要每次都要搞清这个 Service 所有底层类的构造函数,这可能会把人逼疯。如果利用 IoC 的话,你只需要配置好,然后在需要的地方引用就行了,这大大增加了项目的可维护性且降低了开发难度。
Spring AOP,动态代理
AOP(Aspect-Oriented Programming:面向切面编程),和OOP一样,它也是一种编程思想,能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任,或者说为实现业务模块代码所必不可少的系统代码部分(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。
Spring AOP就是基于动态代理的,如果要代理的对象,实现了某个接口,那么Spring AOP会使用JDK Proxy,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候Spring AOP会使用Cglib ,这时候Spring AOP会使用 Cglib 生成一个被代理对象的子类来作为代理。
Bean生命周期
加载配置 每个bean的配置信息,会封装为一个BeanDefinition对象
创建实例 使用Java反射机制,调用构造器,完成实例创建
-
执行注入
属性
-
资源 Aware接口 ,分别有:
BeanFactoryAware
BeanNameAware
ResourceLoaderAware
ApplicationContextAware
ServletContextAware
ServletConfigAware
-
初始化
初始化前执行:BeanPostProcessor接口的postProcessBeforeInitialization()方法
InitializingBean接口的afterPropertiesSet()方法 (一般不会使用这种初始化,因为要实现接口,增强了类的侵入性,不符合Spring的设计思想)
自定义初始化方法
初始化后执行:BeanPostProcessor接口的postProcessAfterInitialization()方法
-
销毁
DisposableBean接口的destroy()方法(一般不使用)
自定义销毁方法
Bean作用域?默认什么级别?是否线程安全?Spring如何保障线程安全的?
作用域
singleton:默认作用域,单例,每次获取都是同一个实例
prototype:每次请求都会创建一个新的-bean-实例
request:每一次http请求都会产生一个新的bean,该bean仅在当前http-request内有效
session:该bean仅在当前 http-session-内有效
global session:该bean在全局session会话范围内有效
是否线程安全
Spring中的Bean是否线程安全 和Spring无关。
因为Spring只承担了创建和管理Bean的职责,并没有对Bean进行任何修改。
因为Ioc并不具备线程安全的特性,但是要看@Scope,原型模式一定是线程安全的,因为每次都需要创建新的对象;如果单例模式的Bean是无状态的,那就是线程安全的,如果不是,则需要通过ThreadLocal来解决线程安全问题,ThreadLocal为每个线程保存了线程私有的数据,用空间来换取了时间。
解释:bean有状态就是有数据存储功能 无状态就是不会保存数据
Spring如何保障线程安全
-
Spring 的常见业务组件采取单例如何保证线程安全
Spring 作为 IOC 框架,一般来说,Spring 管理的 controller、service、dao 都是单例存在,节省内存和 cpu、提高单机资源利用率(默认单例,配置多例模式使用 scope=prototype),既然是单例,那么如何控制单例被多个线程同时访问线程安全呢?
首先要理解每个 http 请求到后台都是一个单独的线程,线程之间共享同一个进程的内存、io、cpu 等资源,但线程栈是线程独有,线程之间不共享栈资源
其次,bean 分为有状态 bean 和无状态 bean,有状态 bean 即类定义了成员变量,可能被多个线程同时访问,则会出现线程安全问题;无状态 bean 每个线程访问不会产生线程安全问题,因为各个线程栈及方法栈资源都是独立的,不共享。即是,无状态 bean 可以在多线程环境下共享,有状态 bean 不能
-
Spring 的 dao、service 层使用的有状态 bean 如何保证线程安全
Spring 应用中 dao、service 一般以单例形式存在,dao、service 中使用的数据库 connection 以及 RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder 等都是有状态 bean,而 dao、service 又是单例,如何保证线程安全呢?
答案是使用 threadLocal 进行处理,ThreadLocal 是线程本地变量,每个线程拥有变量的一个独立副本,所以各个线程之间互不影响,保证了线程安全
Spring事务隔离级别和事务传播属性
事务传播属性(事务的传播机制)
REQUIRED(默认属性)(required) 如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。 被设置成这个级别时,会为每一个被调用的方法创建一个逻辑事务域。如果前面的方法已经创建了事务,那么后面的方法支持当前的事务,如果当前没有事务会重新建立事务。
MANDATORY (mandatory) 支持当前事务,如果当前没有事务,就抛出异常。
NEVER (never) 以非事务方式执行,如果当前存在事务,则抛出异常。
NOT_SUPPORTED (not_supported) 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
REQUIRES_NEW (requires_new) 新建事务,如果当前存在事务,把当前事务挂起。
SUPPORTS (supports) 支持当前事务,如果当前没有事务,就以非事务方式执行。
NESTED (nested) 支持当前事务,新增Savepoint点,与当前事务同步提交或回滚。 嵌套事务一个非常重要的概念就是内层事务依赖于外层事务。外层事务失败时,会回滚内层事务所做的动作。而内层事务操作失败并不会引起外层事务的回滚。
Spring事务的隔离级别
DEFAULT (默认) 这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别。另外四个与JDBC的隔离级别相对应。
READ_UNCOMMITTED (读未提交) 这是事务最低的隔离级别,它允许另外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读。
READ_COMMITTED (读已提交) 保证一个事务修改的数据提交后才能被另外一个事务读取,另外一个事务不能读取该事务未提交的数据。这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻像读。
REPEATABLE_READ (可重复读) 这种事务隔离级别可以防止脏读、不可重复读,但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了不可重复读。
SERIALIZABLE(串行化) 这是花费最高代价但是最可靠的事务隔离级别,事务被处理为顺序执行。除了防止脏读、不可重复读外,还避免了幻像读。
Spring以及Spring MVC常见注解
Spring常见注解
@Component:组件(普通的bean,三层架构中每一层都可以使用,但是不能标识该组件具体属于哪一层)
@Controller:控制器组件(表示该组件属于WEB层的bean)
@Service:业务层组件(表示该组件属于业务层的bean)
@Repository:数据访问层组件(表示该组件属于数据访问层的bean)
@Autowired:自动装配(默认按照类型自动装配),按照当前声明的接口类型自动,在容器中查找该接口的实现类对象bean,进行自动注入(装配)
@Qualifier:按照bean名称自动装配,与@Autowired注解配合使用。按照当前指定的bean名称,在容器中查找该名称对应的bean,进行自动注入(装配)
@Resource:javax扩展包提供的注解,完成自动注入,默认按照名称自动注入,也可以设置name属性按名称进行自动注入
@Primary:当前bean为主要bean,当发现多个相同类型的bean时,以主要bean为首选。
@Scope("prototype"):设置bean的作用域为每次获取时创建新实例
其中Spring MVC常见注解有:
组件型注解:
@Component 在类定义之前添加@Component注解,他会被spring容器识别,并转为bean。
@Repository 对Dao实现类进行注解 (特殊的@Component)
@Service 用于对业务逻辑层进行注解 (特殊的@Component)
@Controller 用于控制层注解 (特殊的@Component)
请求和参数型注解:
@RequestMapping:用于处理请求地址映射,可以作用于类和方法上。
@RequestParam:用于获取传入参数的值
@PathViriable:用于定义路径参数值
@ResponseBody:作用于方法上,可以将整个返回结果以某种格式返回,如json或xml格式。
@autowired和@resource的区别,当UserDao存在不止一个bean或没有存在时,会怎样?怎么解决?
提供方:@Autowired是由org.springframework.beans.factory.annotation.Autowired提供,换句话说就是由Spring提供;@Resource是由javax.annotation.Resource提供,即J2EE提供,需要JDK1.6及以上;
注入方式:@Autowired只按照类型注入;@Resource默认按名称自动注入,也提供按照类型注入;
属性:@Autowired按类型装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它required属性为false(即@Autowired(required=false))。如果我们想使用按名称装配,可以结合@Qualifier注解一起使用。@Resource有两个中重要的属性:name和type。name属性指定byName,如果没有指定name属性,当注解标注在字段上,即默认取字段的名称作为bean名称寻找依赖对象,当注解标注在属性的setter方法上,即默认取属性名作为bean名称寻找依赖对象。需要注意的是,@Resource如果没有指定name属性,并且按照默认的名称仍然找不到依赖对象时, @Resource注解会回退到按类型装配。但一旦指定了name属性,就只能按名称装配了。
mybatis如何防止sql注入?$和#的区别是什么?传入表明用哪个?
SQL注入是一种攻击手段,它指的是使用构造恶意的SQL语句,欺骗服务器执行SQL命令,让后台的数据库去解析,从而达到入侵目标网络,获取敏感信息的攻击手段。
mybatis中的#和$的区别
将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。如:order by #user_id#,如果传入的值是111,那么解析成sql时的值为order by "111", 如果传入的值是id,则解析成的sql为order by "id".
将传入的数据直接显示生成在sql中。如:order by ,如果传入的值是111,那么解析成sql时的值为order by user_id, 如果传入的值是id,则解析成的sql为order by id.
方式能够很大程度防止sql注入。利用JDBC的PreparedStatement类进行预编译。
$方式无法防止Sql注入。
$方式一般用于传入数据库对象,例如传入表名.
一般能用#的就别用$.
防止Sql注入
注意:SQL语句不要写成select * from t_stu where s_name like '%%',这样极易受到注入攻击。
“{xxx}”这样格式的参数会直接参与sql编译,从而不能避免注入攻击。但涉及到动态表名和列名时,只能使用“{xxx}”这样的参数,要手工地做好过滤工作,来防止sql注入攻击。
简单说,“#{}”是经过预编译的,是安全的;“${}”是未经过预编译的,仅仅是取变量的值,是非安全的,存在SQL注入。
Spring MVC工作原理
Spring框架介绍:
- Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。
Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。使用 Spring 可插入的 MVC 架构,可以选择是使用内置的 Spring Web 框架还是 Struts 这样的 Web 框架。通过策略接口,Spring 框架是高度可配置的,而且包含多种视图技术,例如 JavaServer Pages(JSP)技术、Velocity、Tiles、iText 和 POI。Spring MVC 框架并不知道使用的视图,所以不会强迫您只使用 JSP 技术。
Spring MVC 分离了控制器、模型对象、分派器以及处理程序对象的角色,这种分离让它们更容易进行定制。
- Spring的MVC框架主要由DispatcherServlet、处理器映射、处理器(控制器)、视图解析器、视图组成。
工作原理:
客户端请求提交到DispatcherServlet
由DispatcherServlet控制器查询一个或多个HandlerMapping,找到处理请求的Controller
DispatcherServlet将请求提交到Controller
Controller调用业务逻辑处理后,返回ModelAndView
DispatcherServlet查询一个或多个ViewResoler视图解析器,找到ModelAndView指定的视图
视图负责将结果显示到客户端
SpringBoot自动配置的原理是什么?介绍SpringBootApplication注解.
自动配置原理:
Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。它使用“习惯优于配置”的理念可以让你的项目快速运行部署。使用Spring Boot可以不用或者只需要很少的Spring配置。
Spring Boot核心的功能就是自动配置。它会根据在类路径中的jar、类自动配置Bean,当我们需要配置的Bean没有被Spring Boot提供支持时,也可以自定义自动配置。
Spring Boot将所有的功能场景都抽取出来,做成一个个的starters(启动器),只需要在项目里面引入这些starter相关场景的所有依赖都会导入进来。要用什么功能就导入什么场景的启动器
SpringBootApplication注解:
@SpringBootApplication: Spring Boot应用标注在某个类上说明这个类是SpringBoot的主配置类,SpringBoot就应该运行这个类的main方法来启动SpringBoot应用;被标注的类为应用的主程序类,主入口类
Mybatis和Hibernate的区别
-
两者最大的区别:
Mybatis是持久层的一个“半自动化”的技术框架,而Hibernate是基于JPA实现的一个“全自动化”的持久层框架
针对简单逻辑,Hibernate和MyBatis都有相应的代码生成工具,可以生成简单基本的DAO层方法。
针对高级查询,Mybatis需要手动编写SQL语句,以及ResultMap。而Hibernate有良好的映射机制,开发者无需关心SQL的生成与结果映射,可以更专注于业务流程。
-
开发难度对比:
Hibernate的开发难度要大于Mybatis。主要由于Hibernate比较复杂、庞大,学习周期较长。
而Mybatis则相对简单一些,并且Mybatis主要依赖于sql的书写,让开发者感觉更熟悉。
-
sql书写比较
Mybatis的SQL是手动编写的,所以可以按需求指定查询的字段。不过没有自己的日志统计,所以要借助log4j来记录日志。
Hibernate也可以自己写SQL来指定需要查询的字段,但这样就破坏了Hibernate开发的简洁性。不过Hibernate具有自己的日志统计。
-
数据库扩展性比较
Mybatis由于所有SQL都是依赖数据库书写的,所以扩展性,迁移性比较差。
Hibernate与数据库具体的关联都在XML中,所以HQL对具体是用什么数据库并不是很关心。
-
缓存机制比较
相同点:Hibernate和Mybatis的二级缓存除了采用系统默认的缓存机制外,都可以通过实现你自己的缓存或为其他第三方缓存方案,创建适配器来完全覆盖缓存行为。
不同点:Hibernate的二级缓存配置在SessionFactory生成的配置文件中进行详细配置,然后再在具体的表-对象映射中配置是那种缓存。
MyBatis的二级缓存配置都是在每个具体的表-对象映射中进行详细配置,这样针对不同的表可以自定义不同的缓存机制。并且Mybatis可以在命名空间中共享相同的缓存配置和实例,通过Cache-ref来实现。
两者比较:因为Hibernate对查询对象有着良好的管理机制,用户无需关心SQL。所以在使用二级缓存时如果出现脏数据,系统会报出错误并提示。
而MyBatis在这一方面,使用二级缓存时需要特别小心。如果不能完全确定数据更新操作的波及范围,避免Cache的盲目使用。否则,脏数据的出现会给系统的正常运行带来很大的隐患。
总结:
mybatis:小巧、方便、高效、简单、直接、半自动 hibernate:强大、方便、高效、复杂、绕弯子、全自动
spring中的注解原理?例如事务注解,spring如何根据注解实现事务功能的
事务功能的实现:
Spring事务管理主要包括3个接口,Spring的事务主要是由他们三个共同完成的。
1)PlatformTransactionManager:事务管理器--主要用于平台相关事务的管理
主要有三个方法:commit 事务提交;
rollback 事务回滚;
getTransaction 获取事务状态。
2)TransactionDefinition:事务定义信息--用来定义事务相关的属性,给事务管理器PlatformTransactionManager使用
这个接口有下面四个主要方法:
getIsolationLevel:获取隔离级别;
getPropagationBehavior:获取传播行为;
getTimeout:获取超时时间;
isReadOnly:是否只读(保存、更新、删除时属性变为false--可读写,查询时为true--只读)
事务管理器能够根据这个返回值进行优化,这些事务的配置信息,都可以通过配置文件进行配置。
3)TransactionStatus:事务具体运行状态--事务管理过程中,每个时间点事务的状态信息。
例如它的几个方法:
hasSavepoint():返回这个事务内部是否包含一个保存点,
isCompleted():返回该事务是否已完成,也就是说,是否已经提交或回滚
isNewTransaction():判断当前事务是否是一个新事务
Spring是依赖于传播机制来保证事务的一致性的
声明式事务的优缺点:
优点:不需要在业务逻辑代码中编写事务相关代码,只需要在配置文件配置或使用注解(@Transaction),这种方式没有侵入性。
缺点:声明式事务的最细粒度作用于方法上,如果像代码块也有事务需求,只能变通下,将代码块变为方法。
Spring中用到了哪些设计模式?单例、*、工厂、适配、观察者之类的说一说就行
工厂模式 : Spring使用工厂模式通过
BeanFactory
、ApplicationContext
创建 bean 对象。代理模式 : Spring AOP 功能的实现。
单例模式 : Spring 中的 Bean 默认都是单例的。
模板方法模式 : Spring 中
jdbcTemplate
、hibernateTemplate
等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。包装器设计模式 : 我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。
观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用。
适配器模式 :Spring AOP 的增强或通知(Advice)使用到了适配器模式、spring MVC 中也是用到了适配器模式适配
Controller
。……