介绍spring框架
Spring 为简化企业级应用开发而生的一个开源框架. 使用 Spring 可以使简单的 JavaBean 实现以前只有 EJB 才能实现的功能.
Spring 是一个 IOC(DI) 和 AOP 容器框架。
IOC(Inversion of Control,控制反转)
DI(Dependency Injection ,依赖注入)
AOP(Aspect-Oriented Programming, 面向切面编程)
具体描述 Spring:
关于IOC/DI更通俗的解释 https://www.cnblogs.com/liubin1988/p/8909610.html
Spring 中的核心模块
https://blog.csdn.net/saizzzzzz/article/details/51494458
Spring的优点
spring中的核心类有那些,各有什么作用?
省略
Spring中用到的设计模式
参考:https://www.cnblogs.com/kyoner/p/10949246.html
- 工厂设计模式
Spring使用工厂模式可以通过 BeanFactory
或 ApplicationContext
创建 bean 对象。
BeanFactory
仅提供了最基本的依赖注入支持,ApplicationContext
扩展了BeanFactory
,除了有BeanFactory
的功能之外还有额外更多功能
ApplicationContext的三个实现类:
ClassPathXmlApplication
:把上下文文件当成类路径资源。
FileSystemXmlApplication
:从文件系统中的 XML 文件载入上下文定义信息。
XmlWebApplicationContext
:从Web系统中的XML文件载入上下文定义信息。
- 单例设计模式
singleton
: 单例的,容易只创建一次prototype
: 每次请求都会创建一个新的 bean 实例。request
: 每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP request内有效。session
: 每一次HTTP请求都会产生一个新的 bean,该bean仅在当前 HTTP session 内有效。-
@Scope(value = “singleton”)
- 代理设计模式
- 模板方法
jdbcTemplate
、hibernateTemplate
等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。一般情况下,我们都是使用继承的方式来实现模板模式,但是 Spring 并没有使用这种方式,而是使用Callback 模式与模板方法模式配合,既达到了代码复用的效果,同时增加了灵活性。
- 观察者模式
观察者模式是一种对象行为型模式。它表示的是一种对象与对象之间具有依赖关系,当一个对象发生改变的时候,这个对象所依赖的对象也会做出反应。Spring 事件驱动模型就是观察者模式很经典的一个应用。
Spring 事件驱动模型中的三种角色
事件角色
ApplicationEvent
(org.springframework.context包下)充当事件的角色,这是一个抽象类,它继承了java.util.EventObject
并实现了 java.io.Serializable
接口。
Spring 中默认存在以下事件,他们都是对 ApplicationContextEvent
的实现(继承自ApplicationContextEvent
):
ContextStartedEvent
:ApplicationContext 启动后触发的事件;
ContextStoppedEvent
:ApplicationContext 停止后触发的事件;
ContextRefreshedEvent
:ApplicationContext 初始化或刷新完成后触发的事件;
ContextClosedEvent
:ApplicationContext 关闭后触发的事件。
事件监听者角色
ApplicationListener
充当了事件监听者角色,它是一个接口,里面只定义了一个 onApplicationEvent()
方法来处理ApplicationEvent
,在 Spring中我们只要实现 ApplicationListener
接口实现 onApplicationEvent()
方法即可完成监听事件
什么是Spring beans?
事件发布者角色
ApplicationEventPublisher
充当了事件的发布者,它也是一个接口。
ApplicationEventPublisher
接口的publishEvent()
这个方法在AbstractApplicationContext
类中被实现,阅读这个方法的实现,你会发现实际上事件真正是通过ApplicationEventMulticaster来广播出去的。
- 适配器模式
AdvisorAdapter
。Advice 常用的类型有:BeforeAdvice
(目标方法调用前,前置通知)、AfterAdvice
(目标方法调用后,后置通知)、AfterReturningAdvice
(目标方法执行结束后,return之前)等等。每个类型Advice(通知)都有对应的拦截器:MethodBeforeAdviceInterceptor
、AfterReturningAdviceAdapter
、AfterReturningAdviceInterceptor
。Spring预定义的通知要通过对应的适配器,适配成 MethodInterceptor
接口(方法拦截器)类型的对象(如:MethodBeforeAdviceInterceptor
负责适配 MethodBeforeAdvice
)。DispatcherServlet
根据请求信息调用 HandlerMapping
,解析请求对应的 Handler。解析到对应的 Handler
(也就是我们平常说的 Controller
控制器)后,开始由HandlerAdapter
适配器处理。HandlerAdapter
作为期望接口,具体的适配器实现类用于对目标类进行适配,Controller
作为需要适配的类。
- 装饰者模式
Decorator
套在原有代码外面。其实在 JDK 中就有很多地方用到了装饰者模式,比如 InputStream
家族,InputStream
类下有 FileInputStream
(读取文件)BufferedInputStream
(增加缓存,使读取文件速度大大提升)等子类都在不修改InputStream
代码的情况下扩展了它的功能。 DataSource
的时候,DataSource
可能是不同的数据库和数据源。我们能否根据客户的需求在少修改原有类的代码下动态切换不同的数据源?这个时候就要用到装饰者模式。Spring 中用到的包装器模式在类名上含有 Wrapper
或者 Decorator
。这些类基本上都是动态地给一个对象添加一些额外的职责如何给Spring 容器提供配置元数据
什么是基于注解的配置元数据呢?
@Controller,@Service
,@Repository
,@Component
的注解。
@ComponentScan
来使用,如果使用的是SpringBoot,扫描组件的默认的路径为与Application.class同级的包什么是基于Java的配置元数据呢?
@Configuration
注解需要作为配置的类,表示该类将定义Bean的元数据@Bean
注解相应的方法,该方法名默认就是Bean的名称,该方法返回值就是Bean的对象。AnnotationConfigApplicationContext
或子类进行加载基于java类的配置。 @Configuration
class ApplicationContextConfig {
@Bean
public String message() {
return "hello";
}
}
//然后还需要一个测试类,来查看配置是否成功
public class ConfigurationTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx =new AnnotationConfigApplicationContext(ApplicationContextConfig.class);
System.out.println(ctx.getBean("message"));
}
}
bean的装配
- 什么是bean装配?
- 什么是bean的自动装配?
- 自动装配的方式
no
:(默认)无自动装配。Bean引用必须由ref元素定义。不建议对较大的部署更改默认设置,因为明确指定协作者可以提供更好的控制和清晰度。在某种程度上,它记录了系统的结构。byName
:按属性名称自动装配。Spring查找与需要自动装配的属性同名的bean。例如,如果bean定义按名称设置为autowire并且它包含一个master属性(即,它有一个 setMaster(…)方法),则Spring会查找名为bean的定义master并使用它来设置属性。 byType
::如果容器中只存在一个属性类型的bean,则允许属性自动装配。如果存在多个,则抛出致命异常,这表示您可能不会byType对该bean 使用自动装配。如果没有匹配的bean,则不会发生任何事情(该属性未设置)。constructor
:类似byType但适用于构造函数参数。如果容器中没有构造函数参数类型的一个bean,则会引发致命错误。
- 自动装配的局限性是:
property
和constructor-arg
设置中的显式依赖项始终覆盖自动装配。不能自动连接简单属性,如基元、字符串和类(比如简单属性的数组)。
- 构造方法注入和设值注入有什么区别?
Spring Bean的作用域
Scope | Description |
---|---|
singleton | (默认)将单个bean定义范围限定为每个Spring IoC容器的单个对象实例。 |
prototype | 将单个bean定义范围限定为任意数量的对象实例。 |
request | 将单个bean定义范围限定为单个HTTP请求的生命周期。也就是说,每个HTTP请求都有自己的bean实例,它是在单个bean定义的后面创建的。仅在具有Web感知的Spring环境中有效。 |
session | 将单个bean定义范围限定为HTTP的生命周期Session。仅在具有Web感知的Spring环境中有效。 |
application | 将单个bean定义范围限定生命周期为ServletContext。仅在具有Web感知的Spring环境中有效。 |
websocket | 将单个bean定义范围限定为生命周期为WebSocket。仅在具有Web感知的Spring环境中有效。 |
Spring框架中Bean的生命周期
参考:https://blog.csdn.net/w_linux/article/details/80086950
BeanNameAware
接口,工厂调用Bean的setBeanName()
方法传递Bean的ID。(和下面的一条均属于检查Aware接口)。BeanFactoryAware
接口,工厂调用setBeanFactory()
方法传入工厂自身。postProcessBeforeInitialization(Object var1, String var2)
方法。postProcessAfterInitialization(Object var1, String var2)
方法。初始化方法和销毁方法的指定:2种方式
init-method
destroy-method
InitializingBean
接口,重写afterPropertiesSet()
DisposableBean
接口,重写destroy()
实现bean的前置处理器和后置处理器
往容器中添加一个实现BeanPostProcessor
接口的对象,并重写postProcessBeforeInitialization(Object var1, String var2)
,postProcessAfterInitialization(Object var1, String var2)
获得bean的方式
ApplicationContext
(可以自己实例ApplicationContext
对象或 通过Spring提供的工具类WebApplicationContextUtils
获取ApplicationContext
对象)ApplicationContextAware
ApplicationObjectSupport
Spring 的内部 bean
Spring的延迟加载
默认情况下,ApplicationContext实现会急切地创建和配置所有单例 bean,作为初始化过程的一部分。通常,这种预先实例化是可取的,因为配置或周围环境中的错误是立即发现的,而不是几小时甚至几天后。当不希望出现这种情况时,可以通过将bean定义标记为延迟初始化来阻止单例bean的预实例化。延迟初始化的bean告诉IoC容器在第一次请求时创建bean实例,而不是在启动时。
示例:
<bean id="lazy" class="com.something.ExpensiveToCreateBean" lazy-init="true"/>
<bean name="not.lazy" class="com.something.AnotherBean"/>
Spring AOP
AOP术语
术语 | 解释 |
---|---|
切面(Aspect) | 就是切入点和通知的组合。 |
通知(Advice) | 切面必须要完成的工作,指拦截后要做的事情 |
目标(Target) | 被通知的对象 |
代理(Proxy) | 被应用了增强后,产生了一个代理对象,AOP代理是JDK动态代理或CGLIB代理。 |
连接点(Joinpoint) | 指的是可以被拦截到的点 |
切点(pointcut) | 真正被拦截到的点 |
通知类型
基于XML 方式的切面实现。
public class Service {
public void doService(){
System.out.println("service.............");
}
}
public class LoggingAspect {
public void beforeAdvice() {
System.out.println("beforeAdvice..........");
}
public void afterAdvice() {
System.out.println("afterAdvice..........");
}
}
<bean id="service" class="com.spring.test.Service">bean>
<bean id="loggingAspect" class="com.spring.test.LoggingAspect">bean>
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* com.spring.test.Service.doService())">aop:pointcut>
<aop:aspect ref="loggingAspect" order="2">
<aop:before method="beforeAdvice" pointcut-ref="pointcut">aop:before>
<aop:after method="afterAdvice" pointcut-ref="pointcut">aop:after>
aop:aspect>
aop:config>
基于注解的切面实现
@Component
public class Service {
public void doService() {
System.out.println("service.............");
}
}
//将这个类声明为一个切面,需要放入到IOC容器中,再声明为一个切面
@Component
@Aspect
public class LoggingAspect {
@Before("execution(* com.spring.test.Service.doService())")
public void beforeAdvice() {
System.out.println("beforeAdvice..........");
}
@After("execution(* com.spring.test.Service.doService())")
public void afterAdvice() {
System.out.println("afterAdvice..........");
}
}
<context:component-scan base-package="com.spring.test">context:component-scan>
<aop:aspectj-autoproxy>aop:aspectj-autoproxy>
ApplicationContext通常的实现
BeanFactory和 Application contexts 有什么区别
ApplicationContext包还提供了以下的功能:
Spring中IOC 对象创建的过程。
Spring 中的 IoC 的实现原理,就是工厂模式加反射机制。
可以把IOC容器看作是一个工厂,这个工厂里要生产的对象都在配置文件中给出定义,首先系统解析xml文件,将xml文件的信息保存在一个Map中,然后利用编程语言的的反射编程,根据配置文件中给出的类的信息生成相应的对象。存放在容器中,提高灵活性和可维护性
Bean 的配置方式
factory-method
属性里指定工厂方法的名称. 最后, 使用
元素为该方法传递方法参数,在实例工厂情况下在 bean 的>code> factory-bean 属性里指定拥有该工厂方法的 BeanFactoryBean
接口,什么是IOC,什么又是DI,他们有什么区别
IOC是控制反转:创建对象实例的控制权从代码控制剥离到IOC容器控制(之前的写法,由程序代码直接操控使用new关键字),实际就是你在xml文件控制,控制权的转移是所谓反转,侧重于原理。
DI是依赖注入:创建对象实例时,为这个对象注入属性值或其它对象实例,侧重于实现。
区别
它们是spring核心思想的不同方面的描述。
依赖注入和控制反转是对同一件事情的不同描述,从某个方面讲,就是它们描述的角度不同。依赖注入是从应用程序的角度在描述,可以把依赖注入描述完整点:应用程序依赖容器创建并注入它所需要的外部资源;而控制反转是从容器的角度在描述,描述完整点:容器控制应用程序,由容器反向的向应用程序注入应用程序所需要的外部资源。
Spring注解
什么是基于Java的Spring注解配置? 给一些注解的例子
基于Java的配置,允许你在少量的Java注解的帮助下,进行你的大部分Spring配置而非通过XML文件。
以@Configuration 注解为例,它用来标记类可以当做一个bean的定义,被Spring IOC容器使用。另一个例子是@Bean注解,它表示此方法将要返回一个对象,作为一个bean注册进Spring应用上下文。
什么是基于注解的容器配置?
相对于XML文件,注解型的配置依赖于通过字节码元数据装配组件,而非尖括号的声明。
开发者通过在相应的类,方法或属性上使用注解的方式,直接组件类中进行配置,而不是使用xml表述bean的装配关系。
怎样开启注解装配?
元素。常用注解
@Autowired
@Autowired 注解自动装配具有兼容类型的单个 Bean属性
构造器, 普通字段(即使是非 public), 一切具有参数的方法都可以应用@Authwired 注解
默认情况下, 所有使用 @Authwired 注解的属性都需要被设置. 当 Spring 找不到匹配的 Bean 装配属性时, 会抛出异常, 若某一属性允许不被设置, 可以设置 @Authwired 注解的 required 属性为 false
@Qualifier
当 IOC 容器里存在多个类型兼容的 Bean 时, 通过类型的自动装配将无法工作. 此时可以在 @Qualifier 注解里提供 Bean 的名称. Spring 允许对方法的入参标注 @Qualifiter 已指定注入 Bean 的名称
@Resource
要求提供一个 Bean 名称的属性,若该属性为空,则自动采用标注处的变量或方法名作为 Bean 的名称
@Inject
@Autowired 注解一样也是按类型匹配注入的 Bean, 但没有 reqired 属性
@Component
@Component
,用于标注一个普通的类
@Controller
用于标注一个控制器类
@Service
用于标注业务逻辑类
@Repository
用于标注DAO数据访问类
这几个注解没有什么区别,都是将bean加入到容器,主要用于区别不同的组件类,提高代码的可读性,
@DepondsOn
该注解也是配合@Component这类注解使用,用于强制初始化其他bean
@Scope()
该注解和@Component这一类注解联合使用,用于标记该类的作用域,默认singleton。也可以和@Bean一起使用,此时@Scope修饰一个方法。
spring事务
事物的ACID特性
特性 | 描述 |
---|---|
原子性 Atomicity |
表示事务内操作不可分割,要么都成功,要么都失败 |
一致性 Consistency |
要么都成功,要么都失败,失败了要对事务内前面的操作要回滚,保证关系数据的完整性以及业务逻辑上的一致性。 |
隔离性 Isolation |
一个事务开始后,不能被其他事物干扰,这里就涉及到隔离级别。 |
持久性 Durabuility |
意味着在事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。 |
Spring管理事务有几种方式?具体执行流程以及原理
答:有两种方式:
1、编程式事务,在代码中硬编码。(不推荐使用)
2、声明式事务,在配置文件中配置(推荐使用)
声明式事务又分为两种:
- 基于XML的声明式事务
<bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///spring"/>
<property name="username" value="root"/>
<property name="password" value="tiger"/>
bean>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
bean>
<tx:advice id="TestAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" />
<tx:method name="del*" />
<tx:method name="update*" />
<tx:method name="add*"/>
<tx:method name="*" />
tx:attributes>
tx:advice>
<aop:config>
<aop:pointcut id="services"
expression="execution(* com.website.service.*.*(..))" />
<aop:advisor pointcut-ref="services" advice-ref="TestAdvice" />
aop:config>
tx:method
的属性:
name
是必须的,表示与事务属性关联的方法名(业务方法名),对切入点进行细化。通配符()可以用来指定一批关联到相同的事务属性的方法。 如:'get’、‘handle*’、'on*Event’等等.
propagation
不是必须的 ,默认值是REQUIRED ,表示事务传播行为, 包括REQUIRED,SUPPORTS,MANDATORY,REQUIRES_NEW,NOT_SUPPORTED,NEVER,NESTED
isolation
不是必须的 默认值DEFAULT ,表示事务隔离级别(数据库的隔离级别)
timeout
不是必须的 默认值-1(永不超时)表示事务超时的时间(以秒为单位)
read-only
不是必须的 默认值false不是只读的 ,表示事务是否只读?
no-rollback-for
不是必须的 ,表示不被触发进行回滚的 Exception(s);
任何 RuntimeException
将触发事务回滚,但是任何 checked Exception
将不触发事务回滚
- 基于注解的声明式事务
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource">property>
bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
@Transactional
注解方法@Transactional(propagation=Propagation.REQUIRES_NEW,
isolation=Isolation.READ_COMMITTED,
noRollbackFor={UserAccountException.class},
rollbackFor = IOException.class,
readOnly=false,
timeout=3)
public void purchase(String username, String isbn) {}
Spring事务隔离级别:
事物并发的相互影响:脏读,不可重复读,幻读。
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交:Read Uncommited | √ | √ | √ |
读已提交:Read commited | × | √ | √ |
可重复读:Repeatable Read | × | × | √ |
可串行化:Serializable | × | × | × |
spring的事务传播行为:
传播属性 | 描述 |
---|---|
PROPAGATION_REQUIRED | 如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。 |
PROPAGATION_REQUIRES_NEW | 创建新事务,无论当前存不存在事务,都创建新事务。 |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。‘ |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
PROPAGATION_MANDATORY | 支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。 |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。 |
Spring框架中的单例Beans是线程安全的么?
Spring如何处理线程并发问题?
Spring使用ThreadLocal
解决线程安全问题。
我们知道在一般情况下,只有有状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域。就是因为Spring对一些Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非线程安全状态采用ThreadLocal进行处理,让它们也成为线程安全的状态,因为有状态的Bean就可以在多线程中共享了。
ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。