如想了解更多更全面的Java必备内容可以阅读:所有JAVA必备知识点面试题文章目录:
Spring是为解决企业级应用开发的复杂性而设计,它可以做很多事。但归根到底支撑Spring的仅仅是少许的基本理念,而所有的这些基本理念都能可以追溯到一个最根本的使命:简化开发。
它主要采取了4个关键策略:
而它主要是通过:面向Bean(BOP)、依赖注入(DI) 以及面向切面(AOP) 这三种方式来达成的。
控制反转(Inversion of Control, IOC):当某个Java对象(调用者)需要调用另一个Java对象(被调用者,即被依赖对象)时,在传统模式下,调用者通常会采用“new 被调用者”的代码方式来创建对象,如图1所示。这种方式会导致调用者与被调用者之间的耦合性增加,不利于后期项目的升级和维护。
在使用Spring框架之后,对象的实例不再由调用者来创建,而是由Spring容器来创建,Spring容器会负责控制程序之间的关系,而不是由调用者的程序代码直接控制。这样,控制权由应用代码转移到了Spring容器,控制权发生了反转。即就是Spring的控制反转。
依赖注入(Dependency Injection, DI):从Spring容器的角度来看,Spring容器负责将被依赖对象赋值给调用者的成员变量,这相当于为调用者注入了它依赖的实例,这就是Spring的依赖注入。
面向切面(Aspect Oriented Programming,AOP):通过动态代理实现程序功能的统一维护的一种技术,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
它允许程序员对横切关注点或横切典型的职责分界线的行为(例如日志和事务管理)进行模块化。
AOP 的功能完全集成到了 Spring 事务管理、日志和其他各种特性的上下文中。
AOP 编程的常用场景有:Authentication(权限认证)、Auto Caching(自动缓存处理)、Error Handling(统一错误处理)、Debugging(调试信息输出)、Logging(日志记录)、Transactions(事务处理)等。
BeanFactory接口:是 Spring 框架中的核心接口,它是工厂模式的具体实现。允许通过名称创建和检索对象。BeanFactory 也可以管理对象之间的关系。
BeanFactory 使用控制反转对应用程序的配置和依赖性规范与实际的应用程序代码进行了分离。但 BeanFactory 容器实例化后并不会自动实例化 Bean,只有当 Bean 被使用时,BeanFactory 容器才会对该 Bean 进行实例化与依赖关系的装配。
BeanFactory 最底层支持两个对象模型:
其中BeanFactory作为最顶层的一个接口类,它定义了IOC容器的基本功能规范,BeanFactory有三个重要的子类:
ListableBeanFactory(示这些Bean是可列表化的)、HierarchicalBeanFactory(这些Bean是有继承关系的,也就是每个Bean有可能有父Bean)和AutowireCapableBeanFactory(定义Bean的自动装配规则)。这三个接口共同定义了Bean的集合、Bean之间的关系、以及Bean行为。
//spring5.2 BeanFactory 部分源码如下:
public interface BeanFactory {
//对 FactoryBean 的转义定义,因为如果使用 bean 的名字检索 FactoryBean 得到的对象是工厂生成的对象,
//如果需要得到工厂本身,需要转义
String FACTORY_BEAN_PREFIX = "&";
//根据 bean 的名字,获取在 IOC 容器中得到 bean 实例
Object getBean(String name) throws BeansException;
//根据 bean 的名字和 Class 类型来得到 bean 实例,增加了类型安全验证机制。
<T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException;
Object getBean(String name, Object... args) throws BeansException;
<T> T getBean(Class<T> requiredType) throws BeansException;
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
//提供对 bean 的检索,看看是否在 IOC 容器有这个名字的 bean
boolean containsBean(String name);
//根据 bean 名字得到 bean 实例,并同时判断这个 bean 是不是单例
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
//得到 bean 实例的 Class 类型
@Nullable
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
//得到 bean 的别名,如果根据别名检索,那么其原名也会被检索出来
String[] getAliases(String name);
.............
}
核心容器:
AOP和设备支持:
数据访问与集成:
Web组件:
通信报文:
集成测试:
ApplicationContext app = new ClassPathXmlApplicationContext("application.xml");
//AnnotationConfigApplicationContext、FileSystemXmlApplicationContext、XmlWebApplicationContext等都继承自父容器
//AbstractApplicationContext,主要用到了装饰器模式和策略模式,最终都是调用refresh()方法。
super(parent);
this.setConfigLocations(configLocations);
if(refresh){
this.refresh();
}
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
//1、调用容器准备刷新的方法,获取容器的当时时间,同时给容器设置同步标识
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
//2、告诉子类启动refreshBeanFactory()方法,Bean定义资源文件的载入从
//子类的refreshBeanFactory()方法启动
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
//3、为BeanFactory配置容器特性,例如类加载器、事件处理器等
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
//4、为容器的某些子类指定特殊的BeanPost事件处理器
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
//5、调用所有注册的BeanFactoryPostProcessor的Bean
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
//6、为BeanFactory注册BeanPost事件处理器.
//BeanPostProcessor是Bean后置处理器,用于监听容器触发的事件
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
//7、初始化信息源,和国际化相关.
initMessageSource();
// Initialize event multicaster for this context.
//8、初始化容器事件传播器.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
//9、调用子类的某些特殊Bean初始化方法
onRefresh();
// Check for listener beans and register them.
//10、为事件传播器注册事件监听器.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
//11、初始化所有剩余的单例Bean
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
//12、初始化容器的生命周期事件处理器,并发布容器的生命周期事件
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
//13、销毁已创建的Bean
destroyBeans();
// Reset 'active' flag.
//14、取消refresh操作,重置容器的同步标识。
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
//15、重设公共缓存
resetCommonCaches();
}
}
}
//AbstractRefreshableApplicationContext类
@Override
protected final void refreshBeanFactory() throws BeansException {
//如果已经有容器,销毁容器中的bean,关闭容器
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
//创建IOC容器
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
//对IOC容器进行定制化,如设置启动参数,开启注解的自动装配等
customizeBeanFactory(beanFactory);
//调用载入Bean定义的方法,主要这里又使用了一个委派模式,在当前类中只定义了抽象的loadBeanDefinitions方法,具体的实现调用子类容器
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
//将解析的 BeanDefinitionHold 注册到容器中
public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)throws BeanDefinitionStoreException {
//获取解析的 BeanDefinition 的名称
String beanName = definitionHolder.getBeanName();
//向 IOC 容器注册 BeanDefinition
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
//如果解析的 BeanDefinition 有别名,向容器为其注册别名
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
注解(Annotation)是JDK1.5中引入的一个新特性,用于简化Bean的配置,可以取代XML配置文件。
随着SpringBoot的主流,基于注解的开发甚至实现了零配置。
当SpringIOC容器完成了Bean定义资源的定位、载入和解析注册以后,IOC容器中已经管理类Bean定义的相关数据,但是此时IOC容器还没有对所管理的Bean进行依赖注入,依赖注入在以下两种情况发生:
#####AbstractBeanFactory 的 getBean()
//获取 IOC 容器中指定名称的 Bean
@Override
public Object getBean(String name) throws BeansException {
//doGetBean 才是真正向 IOC 容器获取被管理 Bean 的过程
return doGetBean(name, null, null, false);
}
//获取 IOC 容器中指定名称和类型的 Bean
@Override
public <T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException {
//doGetBean 才是真正向 IOC 容器获取被管理 Bean 的过程
return doGetBean(name, requiredType, null, false);
}
//获取 IOC 容器中指定名称和参数的 Bean
@Override
public Object getBean(String name, Object... args) throws BeansException {
//doGetBean 才是真正向 IOC 容器获取被管理 Bean 的过程
return doGetBean(name, null, args, false);
}
//获取 IOC 容器中指定名称、类型和参数的 Bean
public <T> T getBean(String name, @Nullable Class<T> requiredType, @Nullable Object... args)throws BeansException {
//doGetBean 才是真正向 IOC 容器获取被管理 Bean 的过程
return doGetBean(name, requiredType, args, false);
}
我们知道IOC容器的初始化过程就是对Bean定义资源的定位、载入和注册,此时容器对Bean的依赖注入并没有发生,依赖注入主要是在应用程序第一次向容器索取Bean时,通过getBean()方法的调用完成。
当Bean定义资源的< Bean>元素中配置了lazy-init=false属性时,容器将会在初始化的时候对所配置的Bean进行预实例化,Bean的依赖注入在容器初始化的时候就已经完成。这样,当应用程序第一次向容器索取被管理的Bean时,就不用再初始化和对Bean进行依赖注入了,直接从容器中获取已经完成依赖注入的现成Bean,可以提高应用第一次向容器获取Bean的性能。
BeanFactory:Bean工厂,是一个工厂(Factory),是SpringIOC容器的最顶层接口,它的作用是管理Bean,即实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。
FactoryBean:工厂Bean,是一个Bean,作用是产生其他bean实例。通常情况下,这种Bean没有什么特别的要求,仅需要提供一个工厂方法,该方法用来返回其他Bean实例。通常情况下,Bean无须自己实现工厂模式,Spring容器担任工厂角色;但少数情况下,容器中的Bean本身就是工厂,其作用是产生其它Bean实例。
当用户使用容器本身时,可以使用转义字符”&”来得到FactoryBean本身,以区别通过FactoryBean产生的实例对象和FactoryBean对象本身。在BeanFactory中通过如下代码定义了该转义字符:
StringFACTORY_BEAN_PREFIX="&";
如果myJndiObject是一个FactoryBean,则使用&myJndiObject得到的是myJndiObject对象,而不是myJndiObject产生出来的对象。
AOP是OOP的延续,是Aspect Oriented Programming的缩写,意思是面向切面编程。可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。
AOP设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,AOP可以说也是这种目标的一种实现。我们现在做的一些非业务,如:日志、事务、安全等都会写在业务代码中(也即是说,这些非业务类横切于业务类),但这些代码往往是重复,复制——粘贴式的代码会给程序的维护带来不便,AOP就实现了把这些业务需求与系统需求分开来做。这种解决的方式也称代理机制。
注意:可以将多个通知应用到一个目标对象上,即可以将多个切面织入到同一目标对象。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-2.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scan base-package="com.springStudy"/>
<context:annotation-config />
</beans>
//声明这是一个组件
@Component
//声明这是一个切面 Bean
@Aspect
@Slf4j
public class AnnotaionAspect {
//配置切入点,该方法无方法体,主要为方便同类中其他方法使用此处配置的切入点
@Pointcut("execution(* com.pattern.spring.aop.service..*(..))")
public void aspect(){ }
/*
* 配置前置通知,使用在方法 aspect()上注册的切入点
* 同时接受 JoinPoint 切入点对象,可以没有该参数
*/
@Before("aspect()")
public void before(JoinPoint joinPoint){
log.info("before 通知 " + joinPoint);
}
//配置后置通知,使用在方法 aspect()上注册的切入点
@After("aspect()")
public void after(JoinPoint joinPoint){
log.info("after 通知 " + joinPoint);
}
//配置环绕通知,使用在方法 aspect()上注册的切入点
@Around("aspect()")
public void around(JoinPoint joinPoint){
long start = System.currentTimeMillis();
try {
((ProceedingJoinPoint) joinPoint).proceed();
long end = System.currentTimeMillis();
log.info("around 通知 " + joinPoint + "\tUse time : " + (end - start) + " ms!");
} catch (Throwable e) {
long end = System.currentTimeMillis();
log.info("around 通知 " + joinPoint + "\tUse time : " + (end - start) + " ms with exception :" + e.getMessage());
}
}
//配置后置返回通知,使用在方法 aspect()上注册的切入点
@AfterReturning("aspect()")
public void afterReturn(JoinPoint joinPoint){
log.info("afterReturn 通知 " + joinPoint);
}
//配置抛出异常后通知,使用在方法 aspect()上注册的切入点
@AfterThrowing(pointcut="aspect()", throwing="ex")
public void afterThrow(JoinPoint joinPoint, Exception ex){
log.info("afterThrow 通知 " + joinPoint + "\t" + ex.getMessage());
}
}
<bean id="xmlAspect" class="com.pattern.spring.aop.aspect.XmlAspect"></bean>
<!-- AOP 配置 -->
<aop:config>
<!-- 声明一个切面,并注入切面 Bean,相当于@Aspect -->
<aop:aspect ref="xmlAspect">
<!-- 配置一个切入点,相当于@Pointcut -->
<aop:pointcut expression="execution(* com.pattern.spring.aop.service..*(..))"
id="simplePointcut"/>
<!-- 配置通知,相当于@Before、@After、@AfterReturn、@Around、@AfterThrowing -->
<aop:before pointcut-ref="simplePointcut" method="before"/>
<aop:after pointcut-ref="simplePointcut" method="after"/>
<aop:after-returning pointcut-ref="simplePointcut" method="afterReturn"/>
<aop:after-throwing pointcut-ref="simplePointcut" method="afterThrow" throwing="ex"/>
</aop:aspect>
</aop:config>
/**
* Wrap the given bean if necessary, i.e. if it is eligible for being proxied.
* @param bean the raw bean instance
* @param beanName the name of the bean
* @param cacheKey the cache key for metadata access
* @return a proxy wrapping the bean, or the raw bean instance as-is
*/
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
// 判断是否不应该代理这个 bean
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
/*
* 判断是否是一些 InfrastructureClass 或者是否应该跳过这个 bean。
* 所谓 InfrastructureClass 就是指 Advice/PointCut/Advisor 等接口的实现类。
* shouldSkip 默认实现为返回 false,由于是 protected 方法,子类可以覆盖。
*/
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// 获取这个 bean 的 advice
// Create proxy if we have advice.
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName,null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 创建代理
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
Spring 提供了两种方式来生成代理方式,分别是JdkDynamicAopProxy 和 ObjenesisCglibAopProxy。
#### Spring5.2源码如下:
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " + "Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
//如果实现了接口,使用jdk动态代理
return new JdkDynamicAopProxy(config);
}
//使用CGLib动态代理
return new ObjenesisCglibAopProxy(config);
}else {
//默认使用jdk动态代理
return new JdkDynamicAopProxy(config);
}
}
/**
* Determine whether the supplied {@link AdvisedSupport} has only the
* {@link org.springframework.aop.SpringProxy} interface specified
* (or no proxy interfaces specified at all).
*/
private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
Class<?>[] ifcs = config.getProxiedInterfaces();
return (ifcs.length == 0 || (ifcs.length == 1 &&
SpringProxy.class.isAssignableFrom(ifcs[0])));
}
}
容器初始化时会建立所有url和Controller中的Method的对应关系,保存到HandlerMapping中,用户请求是根据Request请求的url快速定位到Controller中的某个方法。在Spring中先将url和Controller的对应关系,保存到Map
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
/**
* Initialize the strategy objects that this servlet uses.
* May be overridden in subclasses in order to initialize further strategy objects.
*/
//初始化策略
protected void initStrategies(ApplicationContext context) {
//多文件上传的组件
initMultipartResolver(context);
//初始化本地语言环境
initLocaleResolver(context);
//初始化模板处理器
initThemeResolver(context);
//handlerMapping
initHandlerMappings(context);
//初始化参数适配器
initHandlerAdapters(context);
//初始化异常拦截器
initHandlerExceptionResolvers(context);
//初始化视图预处理器
initRequestToViewNameTranslator(context);
//初始化视图转换器
initViewResolvers(context);
//FlashMap 管理器
initFlashMapManager(context);
}
@Override
public void initApplicationContext() throws ApplicationContextException {
super.initApplicationContext();
detectHandlers();
}
/**
* 建立当前 ApplicationContext 中的所有 Controller 和 url 的对应关系
*/
protected void detectHandlers() throws BeansException {
ApplicationContext applicationContext = obtainApplicationContext();
if (logger.isDebugEnabled()) {
logger.debug("Looking for URL mappings in application context: " + applicationContext);
}
// 获取 ApplicationContext 容器中所有 bean 的 Name
String[] beanNames = (this.detectHandlersInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) :
applicationContext.getBeanNamesForType(Object.class));
// 遍历 beanNames,并找到这些 bean 对应的 url
for (String beanName : beanNames) {
// 找 bean 上的所有 url(Controller 上的 url+方法上的 url),该方法由对应的子类实现
String[] urls = determineUrlsForHandler(beanName);
if (!ObjectUtils.isEmpty(urls)) {
// 保存 urls 和 beanName 的对应关系,put it to Map,
// 该方法在父类 AbstractUrlHandlerMapping 中实现
registerHandler(urls, beanName);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Rejected bean name '" + beanName + "': no URL paths identified");
}
}
}
}
/** 获取 Controller 中所有方法的 url,由子类实现,典型的模板模式 **/
protected abstract String[] determineUrlsForHandler(String beanName);
Spring的事务机制包括声明式事务和编程式事务。
Spring事务管理主要包括3个接口:
ackage org.springframework.transaction;
public interface PlatformTransactionManager {
TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;
}
package org.springframework.transaction;
public interface TransactionDefinition {
int PROPAGATION_REQUIRED = 0;
int PROPAGATION_SUPPORTS = 1;
int PROPAGATION_MANDATORY = 2;
int PROPAGATION_REQUIRES_NEW = 3;
int PROPAGATION_NOT_SUPPORTED = 4;
int PROPAGATION_NEVER = 5;
int PROPAGATION_NESTED = 6;
int ISOLATION_DEFAULT = -1;
int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED;
int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED;
int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ;
int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE;
int TIMEOUT_DEFAULT = -1;
int getPropagationBehavior();
int getIsolationLevel();
int getTimeout();
boolean isReadOnly();
String getName();
}
package org.springframework.transaction;
public interface TransactionStatus extends SavepointManager, Flushable {
boolean isNewTransaction();
boolean hasSavepoint();
void setRollbackOnly();
boolean isRollbackOnly();
@Override
void flush();
boolean isCompleted();
}
所谓spring事务的传播属性,就是定义存在多个事务同时存在的时候,spring应该如何处理这些事务的行为。
常量名称 | 常量解释 |
---|---|
PROPAGATION_REQUIRED | 如果当前存在事务,则加入该事务,如果当前没有事务,就新建一个事务。这是最常见的选择,也是Spring默认的事务的传播。 |
PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。新建的事务将和被挂起的事务没有任何关系,是两个独立的事务,外层事务失败回滚之后,不能回滚内层事务执行的结果,内层事务失败抛出异常,外层事务捕获,也可以不处理回滚操作。 |
PROPAGATION_SUPPORTS | 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行 |
PROPAGATION_MANDATORY | 如果当前存在事务,则加入该事务,如果当前没有事务,就抛出异常。 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED | 如果一个活动的事务存在,则运行在一个嵌套的事务中。如果没有活动事务,则按REQUIRED属性执行。它使用了一个单独的事务,这个事务拥有多个可以回滚的保存点。内部事务的回滚不会对外部事务造成影响。它只对DataSourceTransactionManager事务管理器起效。 |
隔离级别 | 隔离级别的值 | 导致的问题 |
---|---|---|
Read-Uncommitted | 0 | 导致脏读 |
Read-Committed | 1 | 避免脏读,允许不可重复读和幻读 |
Repeatable-Read | 2 | 避免脏读,不可重复读,允许幻读 |
Serializable | 3 | 串行化读,事务只能一个一个执行,避免了脏读、不可重复读、幻读。执行效率慢,使用时慎重 |
常量 | 解释 |
---|---|
ISOLATION_DEFAULT | 这是个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别。另外四个与JDBC的隔离级别相对应。 |
ISOLATION_READ_UNCOMMITTED | 这是事务最低的隔离级别,它允许另外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读。 |
ISOLATION_READ_COMMITTED | 保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。 |
ISOLATION_REPEATABLE_READ | 这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。 |
ISOLATION_SERIALIZABLE | 这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。 |
下面列举了一些使用 Spring 框架带来的主要好处:
构造器注入、Setter 方法注入、接口注入
首先ApplicationContext是BeanFactory的子接口。
BeanFactory可以理解为含有bean集合的工厂类。BeanFactory包含了种bean的定义,以便在接收到客户端请求时将对应的bean实例化。
BeanFactory还能在实例化对象时生成协作类之间的关系。此举将bean自身与bean客户端的配置中解放出来。BeanFactory还包含了bean生命周期的控制,调用客户端的初始化方法(initialization Methods)和销毁方法(destruction Methods)。
从表面上看,ApplicationContext如同beanfactory一样具有bean定义、bean关联关系的设置,
根据请求分发bean的功能。但ApplicationContext在此基础上还提供了其他的功能。
以下是三种较常见的ApplicationContext实现方式:
将 Spring 配置到应用开发中有以下三种方式:
Spring 的 XML 配置方式是使用被 Spring 命名空间的所支持的一系列的 XML 标签来实现的。Spring
有以下主要的命名空间:context、beans、jdbc、tx、aop、mvc 和 aso。
<beans>
<!-- JSON Support -->
<bean name="viewResolver"
class="org.springframework.web.servlet.view.BeanNameViewResolver"/>
<bean name="jsonTemplate"
class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"/>
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate"/>
</beans>
注解装配在Spring 中是默认关闭的。所以需要在Spring文件中配置一下才能使用基于注解的装配模式。
<!--在标签配置完成以后,就可以用注解的方式在 Spring 中向属性、方法和构造方法中自动装配变量。-->
<beans>
<context:annotation-config/>
</beans>
Spring对Java 配置的支持是由**@Configuration 注解和@Bean 注解**来实现的。
@Configuration
public class AppConfig{
@Bean
public MyService myService() {
return new MyServiceImpl();
}
}
Spring Bean 的生命周期简单易懂。在一个bean实例被初始化时,需要执行一系列的初始化操作以达到可用的状态。同样的,当一个bean不在被调用时需要进行相关的析构操作,并从 bean 容器中移除。
bean factory负责管理在spring容器中被创建的bean的生命周期。Bean的生命周期由两组回调(call back)方法组成。
Spring 框架提供了以下四种方式来管理 bean 的生命周期事件:
Spring容器中的bean可以分为5个范围如下:
Spring框架并没有对单例bean进行任何多线程的封装处理。关于单例bean的线程安全和并发问题需要开发者结合业务而定。
但实际上,大部分的bean并没有可变的状态(比如 Serview 类和 DAO类),所以在某种程度上说Spring的单例bean是线程安全的。如果你的bean有多种状态的话(比如View Model 对象),就需要自行保证线程安全。最浅显的解决办法就是将多态 bean 的作用域由“singleton”变更为“prototype”。
Spring 提供了以下四种集合类的配置元素:
在Spring框架中,在配置文件中设定bean的依赖关系是一个很好的机制,Spring容器还可以自动装配合作关系 bean 之间的关联关系。
这意味着Spring可以通过向Bean Factory中注入的方式自动完成bean之间的依赖关系。自动装配可以设置在每个bean上,也可以设定在特定的 bean 上。
使用XML 配置根据名称将一个bean设置为自动装配:
<bean id="employeeDAO" class="com.gupaoedu.EmployeeDAOImpl" autowire="byName" />
使用@Autowired 注解来自动装配指定的 bean。
@Autowired
public EmployeeDAOImpl ( EmployeeManager manager ) {
this.manager = manager;
}
//在使用@Autowired 注解之前需要在按照如下的配置方式在 Spring 配置文件进行配置才可以使用。
<context:annotation-config />
在Spring框架中共有 5 种自动装配:
@Required | @Autowired | |
---|---|---|
区别 | 1.@Required作用在Setter方法上(用于检查一个Bean的属性的值在配置期间是否被赋予或设置(populated))。 2.@Required作用在Setter方法上就必须赋值,否则容器就会抛出一个BeanInitializationException异常。 | 1.@Autowired 可以作用在Setter 方法中,属性,构造函数中。 2.可以使用 @Autowired 的(required=false)选项关闭默认行为。也就是被标注的属性不会被赋值。 |
联系 | .@Required作用在Setter方法上需要生成Setter方法。 | 1.@Autowired 作用在Setter 方法也需要生成Setter方法。2.@Autowired 作用在属性上,则可以省略Setter方法,根据Bean类型来注入。 |
Spring 的 ApplicationContext 提供了支持事件和代码中监听器的功能。
Spring 提供了以下 5 中标准的事件:
还可以通过扩展 ApplicationEvent 类来开发自定义的事件。
FileSystemResource在配置文件中读取配置文件。
ClassPathResource在环境变量中读取配置文件。
Spring 框架中通过使用模板类能更有效的使用 JDBC,也就是所谓的 JdbcTemplate。
完全可以。
Spring 框架中使用到了大量的设计模式,下面列举了比较有代表性的:
什么是循环依赖:假设A依赖B,B依赖A这样就构成了循环依赖。
循环依赖的场景:构造器循环依赖 和 属性循环依赖 或者 前两者组合。
现象:在实例化A时调用getBean() - - -> doGetBean,发现A依赖的B的实例,此时调用doGetBean去实例B,实例化的B的时候发现又依赖A,如果不解决这个循环依赖的话此时的doGetBean将会无限循环下去,导致内存溢出,程序奔溃。
属性循环依赖解决方案:Spring提供的三级缓存。
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
//一级缓存:存放完全实例化属性赋值完成的Bean,直接可以使用。
private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
//三级缓存:存放实例化完成的Bean工厂。
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
//二级缓存:存放早期Bean的引用,尚未属性装配的Bean【将对象Bean提前曝光出来让大家认识,让大家使用】
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16);
···
}
假设A依赖B,B依赖A(注意:这里是set属性依赖)主要执行步骤:
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//从singletonObjects(一级缓存)中尝试获取
Object singletonObject = this.singletonObjects.get(beanName);
//如果获取不到并且对象在创建中
if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
//从earlySingletonObjects(二级缓存)中获取
singletonObject = this.earlySingletonObjects.get(beanName);
//如果二级缓存也为空,并且允许从三级缓存中获取
if (singletonObject == null && allowEarlyReference) {
synchronized(this.singletonObjects) {
//再从一级缓存获取
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {//一级缓存为空
//再从二级缓存获取
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {//二级缓存为空
//从三级缓存获取
ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
if (singletonFactory != null) {//三级缓存不为空
//获取对象
singletonObject = singletonFactory.getObject();
//将对象放到二级缓存
this.earlySingletonObjects.put(beanName, singletonObject);
//将对象从三级缓存中移除
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
在Bean调用构造器实例化之前,一二三级缓存并没有Bean的任何相关信息,在实例化之后才放入三级缓存中,因此当getBean的时候缓存并没有命中,这样就抛出了循环依赖的异常了。
#### 构造器的循环依赖---简单例子:
@Component
public class A{
private B b;
public A(B b) {
this.b = b;
}
}
@Component
public class B{
private A a;
public B(A a) {
this.a= a;
}
}
解决方案:将其中一个的构造函数中使用 @Lazy 注解。比如以下方式:
//A依赖的B由于标识了@Lazy注解,因此注入的是一个代理对象,顺利完成了A实例的构造;
//而B依赖的A是直注入完整的A对象本身。
public A(@Lazy B b) {
this.b = b;
}
@Lazy注解的原理:通过在构造器参数中标识@Lazy注解,Spring 生成并返回了一个代理对象。
多实例Bean是每次创建都会调用doGetBean方法,根本没有使用一二三级缓存,肯定不能解决循环依赖。
·····
内容后续不断更新中 ~~~
====================================================================
······
帮助他人,快乐自己,最后,感谢您的阅读!
所以如有纰漏或者建议,还请读者朋友们在评论区不吝指出!
个人网站…知识是一种宝贵的资源和财富,益发掘,更益分享…