文档章节

Spring事务处理详解

村东邹师傅
 村东邹师傅
发布于 2015/04/23 10:58
字数 3356
阅读 40
收藏 1
点赞 0
评论 0

1.spring与事务处理

事务管理对于企业应用而言至关重要。它保证了用户的每一次操作都是可靠的,即便出现了异常的访问情况,也不至于破坏后台数据的完整性。在很多情况下,不会只有一个单独的数据库操作,而是一组数据库操作。在这个处理过程中,涉及事务处理单元划分的问题,Spring借助IoC容器的强大配置能力,为应用提供了声明式的事务划分方式。有了spring的支持,只需要一些简单的配置,应用就能完成复杂的事务处理工作,提供很大的方便。

2.Spring声明式事务处理

Spring事务处理模块是通过AOP功能来实现声明式事务处理的,比如事务的配置等。我们从TransactionProxyFactoryBean入手,通过代码来了解spring是如何通过AOP来完成事务配置的。

public class TransactionProxyFactoryBean extends AbstractSingletonProxyFactoryBean implements BeanFactoryAware {
 //设置TransactionInterceptor,发挥aop的作用
 private final TransactionInterceptor transactionInterceptor = new TransactionInterceptor();
 private Pointcut pointcut;
 
 public TransactionProxyFactoryBean() {
    }
    //注入PlatformTransactionManager
    public void setTransactionManager(PlatformTransactionManager transactionManager) {
        this.transactionInterceptor.setTransactionManager(transactionManager);
 }
    //注入事务属性
    //把属性注入到TransactionInterceptor中
    public void setTransactionAttributes(Properties transactionAttributes) {
        this.transactionInterceptor.setTransactionAttributes(transactionAttributes);
 }
 
    public void setTransactionAttributeSource(TransactionAttributeSource transactionAttributeSource) {
        this.transactionInterceptor.setTransactionAttributeSource(transactionAttributeSource);
 }
 
    public void setPointcut(Pointcut pointcut) {
        this.pointcut = pointcut;
 }
 
    public void setBeanFactory(BeanFactory beanFactory) {
        this.transactionInterceptor.setBeanFactory(beanFactory);
 }
 
    protected Object createMainInterceptor() {
//afterPropertiesSet是完成aop配置的地方
        this.transactionInterceptor.afterPropertiesSet();
//通知DefaultPointcutAdvisor或者TransactionAttributeSourceAdvisor
 return this.pointcut != null?new DefaultPointcutAdvisor(this.pointcut, this.transactionInterceptor):new TransactionAttributeSourceAdvisor(this.transactionInterceptor);
 }
}

然后进行事务的配置读入,首先读到TransactionAttributeSource中。NameMatchTransactionAttributeSource作为TransactionAttributeSource的具体实现,是实际完成属性读入和匹配的地方,会读取事务的方法名和属性,把他们作为键值对放入到nameMap中。在调用目标方法的时候,因为这个目标方法已经被TransactionProxyFactoryBean代理,先会判断这个方法是不是事务方法(以方法名为索引在nameMap中查找,使用PatternMatchUtils.simpleMatch进行模式匹配)进行模式匹配的原因是,可以使用通配符*配置事务。所以,如果方法名没能匹配上,而通过模式匹配能匹配上,也要进行事务处理。对应的事务属性也从nameMap中读取出来,触发事务处理拦截器进行拦截。

public TransactionAttribute getTransactionAttribute(Method method, Class<?> targetClass) {
//对方法进行判断,看是不是事务方法,如果是,就取出相应的属性
String methodName = method.getName(); //直接匹配
TransactionAttribute attr = (TransactionAttribute)this.nameMap.get(methodName);
//模式匹配
if(attr == null) {
String bestNameMatch = null;
Iterator i$ = this.nameMap.keySet().iterator();

while(i$.hasNext()) {
String mappedName = (String)i$.next();
if(this.isMatch(methodName, mappedName) && (bestNameMatch == null || bestNameMatch.length() <= mappedName.length())) {
attr = (TransactionAttribute)this.nameMap.get(mappedName);
bestNameMatch = mappedName;
}
}
}

return attr;
}

之后,在事务处理拦截器TransactionInterceptor中,通过invoke方法对aop的代理对象进行回调。首先的到调用方法的事务配置,然后会取得PlatformTransactionManager,通过这个事务处理器来完成事务的创建、提交、回滚操作。

 2.1事务的创建

//TransactionInterceptor的invoke方法中调用invokeWithinTransaction中使用createTransactionIfNecessary方法。
protected TransactionAspectSupport.TransactionInfo createTransactionIfNecessary(PlatformTransactionManager tm, final TransactionAttribute txAttr, final String joinpointIdentification) {
//获取事务的配置信息,如果没有指定名字,使用方法作为事务名
    if(txAttr != null && ((TransactionAttribute)txAttr).getName() == null) {
        txAttr = new DelegatingTransactionAttribute((TransactionAttribute)txAttr) {
            public String getName() {
                return joinpointIdentification;
            }
        };
    }
//status封装了事务的状态信息
    TransactionStatus status = null;
    if(txAttr != null) {
        if(tm != null) {
//事务的创建使用处理器完成的,返回事务的状态
            status = tm.getTransaction((TransactionDefinition)txAttr);
        } else if(this.logger.isDebugEnabled()) {
            this.logger.debug("Skipping transactional joinpoint [" + joinpointIdentification + "] because no transaction manager has been configured");
        }
    }
//返回TransactionInfo,包含了事务配置以及status
    return this.prepareTransactionInfo(tm, (TransactionAttribute)txAttr, joinpointIdentification, status);
}
 
protected TransactionAspectSupport.TransactionInfo prepareTransactionInfo(PlatformTransactionManager tm, TransactionAttribute txAttr, String joinpointIdentification, TransactionStatus status) {
    TransactionAspectSupport.TransactionInfo txInfo = new TransactionAspectSupport.TransactionInfo(tm, txAttr, joinpointIdentification);
    if(txAttr != null) {
        if(this.logger.isTraceEnabled()) {
            this.logger.trace("Getting transaction for [" + txInfo.getJoinpointIdentification() + "]");
        }
//为txInfo设置status,status包含事务transtation对象
        txInfo.newTransactionStatus(status);
    } else if(this.logger.isTraceEnabled()) {
        this.logger.trace("Don\'t need to create transaction for [" + joinpointIdentification + "]: This method isn\'t transactional.");
    }
//把当前的txInfo与线程绑定,同时保存以前的txInfo。这样就有了一连串的txInfo,虽然不一定需要创建事务,但是总会创建txInfo
    txInfo.bindToThread();
    return txInfo;
}

由tm.getTransaction((TransactionDefinition)txAttr)调用触发,创建了一个TransactionStatus对象,在AbstractPlatformTransactionManager中提供了创建事务的模板。

public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
//doGetTransaction是个抽象函数,具体实现在具体的事务处理器如datasourcetransactionmanager中
    Object transaction = this.doGetTransaction();
    boolean debugEnabled = this.logger.isDebugEnabled();
//如果没有任何数据定义,就用默认的定义
    if(definition == null) {
        definition = new DefaultTransactionDefinition();
    }
//当前存在事务,需要考虑事务的传播属性
    if(this.isExistingTransaction(transaction)) {
        //这个方法对于理解事务的传播属性很有帮助(后边讲)
        return this.handleExistingTransaction((TransactionDefinition)definition, transaction, debugEnabled);
    } else if(((TransactionDefinition)definition).getTimeout() < -1) {   //检查超时时间合理性
        throw new InvalidTimeoutException("Invalid transaction timeout", ((TransactionDefinition)definition).getTimeout());
    } 
//当前没有事务,需要根据属性来创建事务,这些数字定义代表传播属性,定义在TransactionDefinition接口中
   else if(((TransactionDefinition)definition).getPropagationBehavior() == 2) {  
        throw new IllegalTransactionStateException("No existing transaction found for transaction marked with propagation \'mandatory\'");
    } else if(((TransactionDefinition)definition).getPropagationBehavior() != 0 && ((TransactionDefinition)definition).getPropagationBehavior() != 3 && ((TransactionDefinition)definition).getPropagationBehavior() != 6) {
        boolean newSynchronization1 = this.getTransactionSynchronization() == 0;
        return this.prepareTransactionStatus((TransactionDefinition)definition, (Object)null, true, newSynchronization1, debugEnabled, (Object)null);
    } else {
        AbstractPlatformTransactionManager.SuspendedResourcesHolder newSynchronization = this.suspend((Object)null);
        if(debugEnabled) {
            this.logger.debug("Creating new transaction with name [" + ((TransactionDefinition)definition).getName() + "]: " + definition);
        }
 
        try {
            boolean err = this.getTransactionSynchronization() != 2;
            DefaultTransactionStatus status = this.newTransactionStatus((TransactionDefinition)definition, transaction, true, err, debugEnabled, newSynchronization);
 
//创建事务,具体由事务处理器实现
            this.doBegin(transaction, (TransactionDefinition)definition);
            this.prepareSynchronization(status, (TransactionDefinition)definition);
            return status;
        } catch (RuntimeException var7) {
            this.resume((Object)null, newSynchronization);
            throw var7;
        } catch (Error var8) {
            this.resume((Object)null, newSynchronization);
            throw var8;
        }
    }
}

在这个模板的基础上,具体的事务的创建需要事务处理器来完成。要实现doBegin方法。事务的创建会生成一个TransactionStatus 对象,是TransactionInfo的一个属性,TransactionInfo保存在ThreadLocl中,这样当前线程就能从Threadlocal中获取当前的事务。

在 Spring 中,事务是通过 TransactionDefinition 接口来定义的。该接口包含与事务属性有关的方法。具体如下所示:
TransactionDefinition 接口中定义的主要方法
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 = 1;
int ISOLATION_READ_COMMITTED = 2;
int ISOLATION_REPEATABLE_READ = 4;
int ISOLATION_SERIALIZABLE = 8;
int TIMEOUT_DEFAULT = -1;

int getPropagationBehavior();

int getIsolationLevel();

int getTimeout();

boolean isReadOnly();

String getName();

} 

可以看到,接口只提供了获取属性的方法,而没有提供相关设置属性的方法。其实道理很简单,事务属性的设置完全是由我们控制的,因此可以自定义任何设置属性的方法,而且保存属性的字段也没有任何要求。唯一的要求的是,Spring 进行事务操作的时候,通过调用以上接口提供的方法必须能够返回事务相关的属性取值。

 

当创建事务的时候,已经存在事务了。这个处理在handleExistingTransaction中:

private TransactionStatus handleExistingTransaction(TransactionDefinition definition, Object transaction, boolean debugEnabled) throws TransactionException {
    if(definition.getPropagationBehavior() == 5) {
        throw new IllegalTransactionStateException("Existing transaction found for transaction marked with propagation \'never\'");
    } else {
        AbstractPlatformTransactionManager.SuspendedResourcesHolder newSynchronization2;
        boolean isoConstants2;
        if(definition.getPropagationBehavior() == 4) {
            if(debugEnabled) {
                this.logger.debug("Suspending current transaction");
            }
 
            newSynchronization2 = this.suspend(transaction);
            isoConstants2 = this.getTransactionSynchronization() == 0;
            return this.prepareTransactionStatus(definition, (Object)null, false, isoConstants2, debugEnabled, newSynchronization2);
        } else if(definition.getPropagationBehavior() == 3) {
            if(debugEnabled) {
                this.logger.debug("Suspending current transaction, creating new transaction with name [" + definition.getName() + "]");
            }
 
            newSynchronization2 = this.suspend(transaction);
 
            try {
                isoConstants2 = this.getTransactionSynchronization() != 2;
                DefaultTransactionStatus status = this.newTransactionStatus(definition, transaction, true, isoConstants2, debugEnabled, newSynchronization2);
                this.doBegin(transaction, definition);
                this.prepareSynchronization(status, definition);
                return status;
            } catch (RuntimeException var7) {
                this.resumeAfterBeginException(transaction, newSynchronization2, var7);
                throw var7;
            } catch (Error var8) {
                this.resumeAfterBeginException(transaction, newSynchronization2, var8);
                throw var8;
            }
        } else {
            boolean newSynchronization1;
            if(definition.getPropagationBehavior() == 6) {
                if(!this.isNestedTransactionAllowed()) {
                    throw new NestedTransactionNotSupportedException("Transaction manager does not allow nested transactions by default - specify \'nestedTransactionAllowed\' property with value \'true\'");
                } else {
                    if(debugEnabled) {
                        this.logger.debug("Creating nested transaction with name [" + definition.getName() + "]");
                    }
 
                    if(this.useSavepointForNestedTransaction()) {
                        DefaultTransactionStatus newSynchronization3 = this.prepareTransactionStatus(definition, transaction, false, false, debugEnabled, (Object)null);
                        newSynchronization3.createAndHoldSavepoint();
                        return newSynchronization3;
                    } else {
                        newSynchronization1 = this.getTransactionSynchronization() != 2;
                        DefaultTransactionStatus isoConstants1 = this.newTransactionStatus(definition, transaction, true, newSynchronization1, debugEnabled, (Object)null);
                        this.doBegin(transaction, definition);
                        this.prepareSynchronization(isoConstants1, definition);
                        return isoConstants1;
                    }
                }
            } else {
                if(debugEnabled) {
                    this.logger.debug("Participating in existing transaction");
                }
 
                if(this.isValidateExistingTransaction()) {
                    if(definition.getIsolationLevel() != -1) {
                        Integer newSynchronization = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
                        if(newSynchronization == null || newSynchronization.intValue() != definition.getIsolationLevel()) {
                            Constants isoConstants = DefaultTransactionDefinition.constants;
                            throw new IllegalTransactionStateException("Participating transaction with definition [" + definition + "] specifies isolation level which is incompatible with existing transaction: " + (newSynchronization != null?isoConstants.toCode(newSynchronization, "ISOLATION_"):"(unknown)"));
                        }
                    }
 
                    if(!definition.isReadOnly() && TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
                        throw new IllegalTransactionStateException("Participating transaction with definition [" + definition + "] is not marked as read-only but existing transaction is");
                    }
                }
 
                newSynchronization1 = this.getTransactionSynchronization() != 2;
                return this.prepareTransactionStatus(definition, transaction, false, newSynchronization1, debugEnabled, (Object)null);
            }
        }
    }
}

事务隔离级别
隔离级别是指若干个并发的事务之间的隔离程度。TransactionDefinition 接口中定义了五个表示隔离级别的常量:
* TransactionDefinition.ISOLATION_DEFAULT:这是默认值,表示使用底层数据库的默认隔离级别。对大部分数据库而言,通常这值就是TransactionDefinition.ISOLATION_READ_COMMITTED。
* TransactionDefinition.ISOLATION_READ_UNCOMMITTED:该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读和不可重复读,因此很少使用该隔离级别。
* TransactionDefinition.ISOLATION_READ_COMMITTED:该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。
* TransactionDefinition.ISOLATION_REPEATABLE_READ:该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。即使在多次查询之间有新增的数据满足该查询,这些新增的记录也会被忽略。该级别可以防止脏读和不可重复读。
* TransactionDefinition.ISOLATION_SERIALIZABLE:所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
事务传播行为
所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。在TransactionDefinition定义中包括了如下几个表示传播行为的常量:
* TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
* TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
* TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
* TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
* TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
* TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
* TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。
以 PROPAGATION_NESTED 启动的事务内嵌于外部事务中(如果存在外部事务的话),此时,内嵌事务并不是一个独立的事务,它依赖于外部事务的存在,只有通过外部的事务提交,才能引起内部事务的提交,嵌套的子事务不能单独提交。如果熟悉 JDBC 中的保存点(SavePoint)的概念,那嵌套事务就很容易理解了,其实嵌套的子事务就是保存点的一个应用,一个事务中可以包括多个保存点,每一个嵌套子事务。另外,外部事务的回滚也会导致嵌套子事务的回滚。

 2.2事务的提交

事务的提交在TransactionInterceptor中的invokeWithinTransaction中,同样在AbstractPlatformTransactionManager中提供了模板。

public final void commit(TransactionStatus status) throws TransactionException {
//在TransactionStatus中表示事务已经结束了
    if(status.isCompleted()) {
        throw new IllegalTransactionStateException("Transaction is already completed - do not call commit or rollback more than once per transaction");
    } else {
        DefaultTransactionStatus defStatus = (DefaultTransactionStatus)status;
//出现异常
        if(defStatus.isLocalRollbackOnly()) {
            if(defStatus.isDebug()) {
                this.logger.debug("Transactional code has requested rollback");
            }
//回滚
            this.processRollback(defStatus);
        } else if(!this.shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
            if(defStatus.isDebug()) {
                this.logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
            }
 
            this.processRollback(defStatus);
//其实因为事务嵌套,serviceA#methodA中有异常标记并直接回滚,serviceB#methodB中有异常只是标记回滚状态,在调用回serviceA#methodA中回滚) 这个方法中serviceB#methodB有异常事务被标记为
//回滚,继续执行到serviceA.methodA() 可是被methodA捕获了,也就不回滚了,一直执行到最后commit。在commit时spring会判断回滚标志,若有回滚标记,回滚并抛出UnexpectedRollbackException
     if(status.isNewTransaction() || this.isFailEarlyOnGlobalRollbackOnly()) {
                throw new UnexpectedRollbackException("Transaction rolled back because it has been marked as rollback-only");
            }
        } else {
            this.processCommit(defStatus);
        }
    }
}
//提交事务的入口
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
    try {
        boolean beforeCompletionInvoked = false;
 
        try {
            this.prepareForCommit(status);
            this.triggerBeforeCommit(status);
            this.triggerBeforeCompletion(status);
            beforeCompletionInvoked = true;
            boolean err = false;
            if(status.isNewTransaction() || this.isFailEarlyOnGlobalRollbackOnly()) {
                err = status.isGlobalRollbackOnly();
            }
//嵌套事务的处理
            if(status.hasSavepoint()) {
                if(status.isDebug()) {
                    this.logger.debug("Releasing transaction savepoint");
                }
 
                status.releaseHeldSavepoint();
            }
//判断当前事务的状态,如果新事务,直接提交。如果不是,则不提交,交由已经存在的事务提交
 else if(status.isNewTransaction()) {
                if(status.isDebug()) {
                    this.logger.debug("Initiating transaction commit");
                }
 
                this.doCommit(status);
            }
 
            if(err) {
                throw new UnexpectedRollbackException("Transaction silently rolled back because it has been marked as rollback-only");
            }
        } catch (UnexpectedRollbackException var19) {
            this.triggerAfterCompletion(status, 1);
            throw var19;
        } catch (TransactionException var20) {
            if(this.isRollbackOnCommitFailure()) {
                this.doRollbackOnCommitException(status, var20);
            } else {
                this.triggerAfterCompletion(status, 2);
            }
 
            throw var20;
        } catch (RuntimeException var21) {
            if(!beforeCompletionInvoked) {
                this.triggerBeforeCompletion(status);
            }
 
            this.doRollbackOnCommitException(status, var21);
            throw var21;
        } catch (Error var22) {
            if(!beforeCompletionInvoked) {
                this.triggerBeforeCompletion(status);
            }
 
            this.doRollbackOnCommitException(status, var22);
            throw var22;
        }
 
        try {
            this.triggerAfterCommit(status);
        } finally {
            this.triggerAfterCompletion(status, 0);
        }
    } finally {
        this.cleanupAfterCompletion(status);
    }
 
}

2.3事务的回滚

事务的回滚和事务的提交非常的相似,AbstractPlatformTransactionManager中的processRollback方法 提供了回滚的模板。

private void processRollback(DefaultTransactionStatus status) {
    try {
        try {
            this.triggerBeforeCompletion(status);
//嵌套事务的回滚
            if(status.hasSavepoint()) {
                if(status.isDebug()) {
                    this.logger.debug("Rolling back transaction to savepoint");
                }
 
                status.rollbackToHeldSavepoint();
            } 
//新建事务的回滚
            else if(status.isNewTransaction()) {
                if(status.isDebug()) {
                    this.logger.debug("Initiating transaction rollback");
                }
 
                this.doRollback(status);
            } 
//没有新创建的事务的回滚
else if(status.hasTransaction()) {
                if(!status.isLocalRollbackOnly() && !this.isGlobalRollbackOnParticipationFailure()) {
                    if(status.isDebug()) {
                        this.logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
                    }
                } else {
                    if(status.isDebug()) {
                        this.logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
                    }
 
                    this.doSetRollbackOnly(status);
                }
            } else {
                this.logger.debug("Should roll back transaction but cannot - no transaction available");
            }
        } catch (RuntimeException var7) {
            this.triggerAfterCompletion(status, 2);
            throw var7;
        } catch (Error var8) {
            this.triggerAfterCompletion(status, 2);
            throw var8;
        }
 
        this.triggerAfterCompletion(status, 1);
    } finally {
        this.cleanupAfterCompletion(status);
    }
 
}

3.我们应该注意什么

1.事务的传播属性

2.事务的回滚

3.超时时间设置

4.隔离级别设置

5.两种配置方式的优缺点

 


© 著作权归作者所有

共有 人打赏支持
村东邹师傅
粉丝 8
博文 15
码字总数 25501
作品 0
朝阳
程序员
关于SpringBoot父子容器加载Bean的问题

Spring容器优先加载由ServletContextListener(对应applicationContext.xml)产生的父容器,而SpringMVC(对应mvcdispatcherservlet.xml)产生的是子容器。子容器Controller进行扫描装配时装...

孟德宣 ⋅ 05/19 ⋅ 0

spring注意事项

1.spring容器与springMVC容器冲突(spring version 4.0.2.RELEASE) SpringMVC容器是Spring容器的一个子容器,它同样能够初始化实体类。由于SpringMVC容器的初始化是在Spring容器初始化之后,...

图样图森破 ⋅ 2016/12/11 ⋅ 0

spring mvc 注解详解

1. 声明Bean的注解: 1.1 @Component : 组件,没有明确的角色 1.2 @Service : 在业务逻辑层(service层)使用 1.3 @Repository : 在数据访问层(dao层)使用 1.4 @Controller : 在展现层(MVC--Spr...

坚持30天 ⋅ 2017/10/19 ⋅ 0

Java Web学习计划

--- 本月为入门阶段,从零开始,一步一步的做出一个实用的网站。 深入学习Java语言,初步掌握前端技术,使用JSP和MySQL完成一个简单的网站 第1周 Java高级编程学习目标:
1.深入了解JDK环境...

SVD ⋅ 2016/12/01 ⋅ 0

推荐几个自己写的Java后端相关的范例项目(转载)

http://wosyingjun.iteye.com/blog/2312553 这里推荐几个自己写的范例项目,主要采用SSM(Spring+SpringMVC+Mybatis)框架,分布式架构采用的是(dubbo+zookeeper)。范例项目的好处是简单易...

指尖的舞者 ⋅ 2016/09/27 ⋅ 0

SpringMVC+Spring事物失效问题

1、spring扫描配置 springmvc扫描配置导致事物失效 一个项目中既有SpringMVC又有Spring 的时候,会发生事物失效问题 原因:使用的spring注解+springMVC注解,默认情况下spring应该先加载appli...

宿小帅 ⋅ 2016/10/12 ⋅ 0

Spring AOP解释及在项目中使用举例

一.AOP是什么 AOP - Aspect Oriented Programing,面向切面编程。将封装好的对象切开,找出其中对多个对象产生影响的公共行为,并将其封装为一个可重用的模块,这个模块被命名为“切面”,切...

Jacktanger ⋅ 06/08 ⋅ 0

spring-data-elasticsearch 基本案例详解(三)

『 风云说:能分享自己职位的知识的领导是个好领导。 』 运行环境:JDK 7 或 8,Maven 3.0+ 技术栈:SpringBoot 1.5+, Spring Data Elasticsearch 1.5+ ,ElasticSearch 2.3.2 本文提纲 一、...

夜黑人模糊灬 ⋅ 05/13 ⋅ 0

开源俱乐部博文目录

MyEclipse设置 《MyEclipse Blue Edition版安装【图文】》 《MyEclipse设置编码格式》 Struts 《Struts2环境搭建》 《Struts2中Namespace的使用》 《Struts2中的动态方法调用》 《Struts2中结...

开源俱乐部 ⋅ 2014/06/30 ⋅ 0

详解Java的Spring框架中的注解的用法

使用Spring注解来注入属性 1.1. 使用注解以前我们是怎样注入属性的 类的实现: 配置文件: 1.2. 引入@Autowired注解(不推荐使用,建议使用@Resource) 类的实现(对成员变量进行标注) 或者...

Airship ⋅ 04/22 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

个人博客的运营模式能否学习TMALL天猫质量为上?

心情随笔|个人博客的运营模式能否学习TMALL天猫质量为上? 中国的互联网已经发展了很多年了,记得在十年前,个人博客十分流行,大量的人都在写博客,而且质量还不错,很多高质量的文章都是在...

原创小博客 ⋅ 今天 ⋅ 0

JavaScript零基础入门——(十一)JavaScript的DOM操作

JavaScript零基础入门——(十一)JavaScript的DOM操作 大家好,欢迎回到我们的JavaScript零基础入门。最近有些同学问我说,我讲的的比书上的精简不少。其实呢,我主要讲的是我在开发中经常会...

JandenMa ⋅ 今天 ⋅ 0

volatile和synchronized的区别

volatile和synchronized的区别 在讲这个之前需要先了解下JMM(Java memory Model :java内存模型):并发过程中如何处理可见性、原子性、有序性的问题--建立JMM模型 详情请看:https://baike.b...

MarinJ_Shao ⋅ 今天 ⋅ 0

深入分析Kubernetes Critical Pod(一)

Author: xidianwangtao@gmail.com 摘要:大家在部署Kubernetes集群AddOn组件的时候,经常会看到Annotation scheduler.alpha.kubernetes.io/critical-pod"="",以表示这是一个关键服务,那你知...

WaltonWang ⋅ 今天 ⋅ 0

原子性 - synchronized关键词

原子性概念 原子性提供了程序的互斥操作,同一时刻只能有一个线程能对某块代码进行操作。 原子性的实现方式 在jdk中,原子性的实现方式主要分为: synchronized:关键词,它依赖于JVM,保证了同...

dotleo ⋅ 今天 ⋅ 0

【2018.06.22学习笔记】【linux高级知识 14.4-15.3】

14.4 exportfs命令 14.5 NFS客户端问题 15.1 FTP介绍 15.2/15.3 使用vsftpd搭建ftp

lgsxp ⋅ 今天 ⋅ 0

JeeSite 4.0 功能权限管理基础(Shiro)

Shiro是Apache的一个开源框架,是一个权限管理的框架,实现用户认证、用户授权等。 只要有用户参与一般都要有权限管理,权限管理实现对用户访问系统的控制,按照安全规则或者安全策略控制用户...

ThinkGem ⋅ 昨天 ⋅ 0

python f-string 字符串格式化

主要内容 从Python 3.6开始,f-string是格式化字符串的一种很好的新方法。与其他格式化方式相比,它们不仅更易读,更简洁,不易出错,而且速度更快! 在本文的最后,您将了解如何以及为什么今...

阿豪boy ⋅ 昨天 ⋅ 0

Python实现自动登录站点

如果我们想要实现自动登录,那么我们就需要能够驱动浏览器(比如谷歌浏览器)来实现操作,ChromeDriver 刚好能够帮助我们这一点(非谷歌浏览器的驱动有所不同)。 一、确认软件版本 首先我们...

blackfoxya ⋅ 昨天 ⋅ 0

线性回归原理和实现基本认识

一:介绍 定义:线性回归在假设特证满足线性关系,根据给定的训练数据训练一个模型,并用此模型进行预测。为了了解这个定义,我们先举个简单的例子;我们假设一个线性方程 Y=2x+1, x变量为商...

wangxuwei ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部