Lecture:波哥
JavaWeb中的框架是一种开发工具或者平台,它提供了一系列的功能和组件,用于简化和加速Web应用的开发过程。框架可以提供一些基础设施,如数据库访问、用户认证和授权、日志记录等,以及一些高级功能,如MVC模式、依赖注入、AOP等。
对于我来说,JavaWEB中的框架是一个可重用的代码库,它提供了一些现成的解决方案,可以帮助开发人员更快地构建稳定、高效的应用程序。框架可以帮助我们遵循最佳实践,减少重复劳动,提高代码的可维护性和可扩展性。同时,框架还可以提供一些标准化的API和约定,使得不同开发人员之间更容易合作和交流。通过使用框架,我们可以更专注于业务逻辑的实现,而不需要过多地关注底层的技术细节。
列举常用的开源框架及作用:
Spring框架之所以被称为轻量级的框架,体现在以下几个方面:
总的来说,Spring框架的轻量级体现在其简单的配置、非侵入式的设计、松耦合的架构、模块化的功能和高度可扩展的特性上,使得开发者可以更加轻松地开发和维护应用程序。
Spring中通过IoC来管理Bean对象。然后通过DI来管理Bean之间的依赖关系。
Spring中的IoC(Inversion of Control)是一种设计原则,通过该原则,对象的创建和依赖关系的管理被转移到了容器中,从而降低了对象之间的耦合性。
从比较粗的角度介绍写IoC中的核心对象的作用:
BeanDefinition
:BeanDefinition是Spring中定义Bean的元数据信息的接口。它包含了Bean的类名、属性、构造函数参数等信息,通过BeanDefinition可以告诉容器如何创建和配置Bean。BeanDefinitionRegistry
:BeanDefinitionRegistry提供了对BeanDefinition的管理和操作功能,是Spring IoC容器中用于注册和管理Bean的核心接口。BeanFactory
:BeanFactory是Spring IoC容器的核心接口,负责实例化、配置和管理应用中的对象(Bean)。它是IoC容器的基础,提供了一种获取Bean的机制,可以通过Bean的名称或类型来获取Bean的实例。ApplicationContext
:ApplicationContext是BeanFactory的子接口,它是Spring中更高级的IoC容器。除了提供BeanFactory的功能外,它还提供了更多的企业级功能,例如国际化支持、事件发布、资源加载等。BeanPostProcessor
:BeanPostProcessor是Spring中的一个扩展接口,用于在Bean实例化和依赖注入的过程中对Bean进行增强处理。通过实现BeanPostProcessor接口,可以在Bean的初始化前后对Bean进行自定义操作。BeanWrapper
:BeanWrapper是Spring中对Bean对象的一种封装,提供了对Bean属性的访问和设置的方法。BeanWrapper可以对Bean的属性进行类型转换和验证等操作。更细节的初始化的过程:https://www.processon.com/view/link/64b8ac2c10ce753b813fc371
通过上面的IoC的原理分析我们也可以看到DI的实现过程。其实就是在Bean的实例化过程中会处理对象的依赖关系。
循环依赖问题不只是在Spring中有。平常的代码环境中也会存在。
public class CircularTest {
public static void main(String[] args) {
new CircularTest1();
}
}
class CircularTest1{
private CircularTest2 circularTest2 ;
public CircularTest1(){
this.circularTest2 = new CircularTest2();
}
}
class CircularTest2{
private CircularTest1 circularTest1 = new CircularTest1();
public CircularTest2(CircularTest1 circularTest1){
this.circularTest1=new CircularTest1();
}
}
解决方案就是:
解决的本质为:
Spring的核心是IoC和DI也就是对Bean的管理。这里肯定会涉及到对象之间关联关系的维护。那么就有可能会产生循环依赖的问题。在Spring中针对循环依赖的支持是:
Spring中解决循环依赖问题的关键是:
Spring中为了提供更加灵活的扩展和提高耦合性。在Bean对象的生命周期中提供了各种的后置处理器以及代理模式的应用。所以在处理循环依赖问题的时候也会比上面单纯的循环依赖问题的解决要更加的复杂些。这里需要弄清楚三级缓存中的每一级缓存的作用
AOP
(面向切面编程)是一种软件设计思想,旨在通过将横切关注点与主要业务逻辑分离,提高代码的可维护性和可重用性。是 OOP
(面向对象)的有效补充。
在传统的面向对象编程中,代码的功能通常被分散在多个类中,这种分散导致了代码的重复和散乱。AOP的目标是通过将这些横切关注点(如日志记录、权限验证、事务管理等)从核心业务逻辑中分离出来,并将其封装成可复用的模块,从而实现代码的解耦和重用。
AOP的关键概念是切面(Aspect),切面是横切关注点的模块化,它定义了在何处(连接点)以及何时(切点)应用特定的横切关注点。切面可以通过通知(Advice)来实现具体的横切功能,通常包括前置通知(Before)、后置通知(After)、异常通知(AfterThrowing)和返回通知(AfterReturning)等。
https://blog.csdn.net/qq_38526573/article/details/86441916
https://dpb-bobokaoya-sm.blog.csdn.net/article/details/127263992
总之,Spring的AOP在实际工作中可以应用于各种场景,通过将通用的横切关注点集中处理,提高了系统的可维护性、可扩展性和代码的复用性。
数据库事务(Database Transaction) ,是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。
事务处理可以确保除非事务性单元内的所有操作都成功完成,否则不会永久更新面向数据的资源。通过将一组相关操作组合为一个要么全部成功要么全部失败的单元,可以简化错误恢复并使应用程序更加可靠。
一个逻辑工作单元要成为事务,必须满足所谓的 ACID
(原子性、一致性、隔离性和持久性)属性。事务是数据库运行中的逻辑工作单位,由DBMS中的事务管理子系统负责事务的处理。
Spring中的事务实现有两种方式:
事务管理器:
事务管理器(PlatformTransactionManager).
事务的定义:TransactionDefinition
事务的开启 TransactionStatus
核心方法演示:
getTransaction()
方法
/**
* This implementation handles propagation behavior. Delegates to
* {@code doGetTransaction}, {@code isExistingTransaction}
* and {@code doBegin}.
* @see #doGetTransaction
* @see #isExistingTransaction
* @see #doBegin
*/
@Override
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
throws TransactionException {
// Use defaults if no transaction definition given.
// 如果没有事务定义信息则使用默认的事务管理器定义信息
TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());
// 获取事务
Object transaction = doGetTransaction();
boolean debugEnabled = logger.isDebugEnabled();
// 判断当前线程是否存在事务,判断依据为当前线程记录的连接不为空且连接中的transactionActive属性不为空
if (isExistingTransaction(transaction)) {
// Existing transaction found -> check propagation behavior to find out how to behave.
// 当前线程已经存在事务
return handleExistingTransaction(def, transaction, debugEnabled);
}
// Check definition settings for new transaction.
// 事务超时设置验证
if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
throw new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout());
}
// No existing transaction found -> check propagation behavior to find out how to proceed.
// 如果当前线程不存在事务,但是PropagationBehavior却被声明为PROPAGATION_MANDATORY抛出异常
if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw new IllegalTransactionStateException(
"No existing transaction found for transaction marked with propagation 'mandatory'");
}
// PROPAGATION_REQUIRED,PROPAGATION_REQUIRES_NEW,PROPAGATION_NESTED都需要新建事务
else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
//没有当前事务的话,REQUIRED,REQUIRES_NEW,NESTED挂起的是空事务,然后创建一个新事务
SuspendedResourcesHolder suspendedResources = suspend(null);
if (debugEnabled) {
logger.debug("Creating new transaction with name [" + def.getName() + "]: " + def);
}
try {
return startTransaction(def, transaction, debugEnabled, suspendedResources);
}
catch (RuntimeException | Error ex) {
// 恢复挂起的事务
resume(null, suspendedResources);
throw ex;
}
}
else {
// Create "empty" transaction: no actual transaction, but potentially synchronization.
// 创建一个空的事务
if (def.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
logger.warn("Custom isolation level specified but no actual transaction initiated; " +
"isolation level will effectively be ignored: " + def);
}
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null);
}
}
关键的方法:doGetTransaction()方法
/**
* 创建一个DataSourceTransactionObject当作事务,设置是否允许保存点,然后获取连接持有器ConnectionHolder
* 里面会存放JDBC的连接,设置给DataSourceTransactionObject,当然第一次是空的
*
* @return
*/
@Override
protected Object doGetTransaction() {
// 创建一个数据源事务对象
DataSourceTransactionObject txObject = new DataSourceTransactionObject();
// 是否允许当前事务设置保持点
txObject.setSavepointAllowed(isNestedTransactionAllowed());
/**
* TransactionSynchronizationManager 事务同步管理器对象(该类中都是局部线程变量)
* 用来保存当前事务的信息,我们第一次从这里去线程变量中获取 事务连接持有器对象 通过数据源为key去获取
* 由于第一次进来开始事务 我们的事务同步管理器中没有被存放.所以此时获取出来的conHolder为null
*/
ConnectionHolder conHolder =
(ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
// 非新创建连接则写false
txObject.setConnectionHolder(conHolder, false);
// 返回事务对象
return txObject;
}
然后事务管理的代码
/**
* Create a TransactionStatus for an existing transaction.
*/
private TransactionStatus handleExistingTransaction(
TransactionDefinition definition, Object transaction, boolean debugEnabled)
throws TransactionException {
/**
* 判断当前的事务行为是不是PROPAGATION_NEVER的
* 表示为不支持事务,但是当前又存在一个事务,所以抛出异常
*/
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
throw new IllegalTransactionStateException(
"Existing transaction found for transaction marked with propagation 'never'");
}
/**
* 判断当前的事务属性不支持事务,PROPAGATION_NOT_SUPPORTED,所以需要先挂起已经存在的事务
*/
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
if (debugEnabled) {
logger.debug("Suspending current transaction");
}
// 挂起当前事务
Object suspendedResources = suspend(transaction);
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
// 创建一个新的非事务状态(保存了上一个存在事务状态的属性)
return prepareTransactionStatus(
definition, null, false, newSynchronization, debugEnabled, suspendedResources);
}
/**
* 当前的事务属性状态是PROPAGATION_REQUIRES_NEW表示需要新开启一个事务状态
*/
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
if (debugEnabled) {
logger.debug("Suspending current transaction, creating new transaction with name [" +
definition.getName() + "]");
}
// 挂起当前事务并返回挂起的资源持有器
SuspendedResourcesHolder suspendedResources = suspend(transaction);
try {
// 创建一个新的非事务状态(保存了上一个存在事务状态的属性)
return startTransaction(definition, transaction, debugEnabled, suspendedResources);
}
catch (RuntimeException | Error beginEx) {
resumeAfterBeginException(transaction, suspendedResources, beginEx);
throw beginEx;
}
}
// 嵌套事务
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
// 不允许就报异常
if (!isNestedTransactionAllowed()) {
throw new NestedTransactionNotSupportedException(
"Transaction manager does not allow nested transactions by default - " +
"specify 'nestedTransactionAllowed' property with value 'true'");
}
if (debugEnabled) {
logger.debug("Creating nested transaction with name [" + definition.getName() + "]");
}
// 嵌套事务的处理
if (useSavepointForNestedTransaction()) {
// Create savepoint within existing Spring-managed transaction,
// through the SavepointManager API implemented by TransactionStatus.
// Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.
// 如果没有可以使用保存点的方式控制事务回滚,那么在嵌入式事务的建立初始简历保存点
DefaultTransactionStatus status =
prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
// 为事务设置一个回退点
status.createAndHoldSavepoint();
return status;
}
else {
// Nested transaction through nested begin and commit/rollback calls.
// Usually only for JTA: Spring synchronization might get activated here
// in case of a pre-existing JTA transaction.
// 有些情况是不能使用保存点操作
return startTransaction(definition, transaction, debugEnabled, null);
}
}
// Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED.
if (debugEnabled) {
logger.debug("Participating in existing transaction");
}
if (isValidateExistingTransaction()) {
if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
Integer currentIsolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
if (currentIsolationLevel == null || currentIsolationLevel != definition.getIsolationLevel()) {
Constants isoConstants = DefaultTransactionDefinition.constants;
throw new IllegalTransactionStateException("Participating transaction with definition [" +
definition + "] specifies isolation level which is incompatible with existing transaction: " +
(currentIsolationLevel != null ?
isoConstants.toCode(currentIsolationLevel, DefaultTransactionDefinition.PREFIX_ISOLATION) :
"(unknown)"));
}
}
if (!definition.isReadOnly()) {
if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
throw new IllegalTransactionStateException("Participating transaction with definition [" +
definition + "] is not marked as read-only but existing transaction is");
}
}
}
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
}
最后来看看 startTransaction() 方法
/**
* Start a new transaction.
*/
private TransactionStatus startTransaction(TransactionDefinition definition, Object transaction,
boolean debugEnabled, @Nullable SuspendedResourcesHolder suspendedResources) {
// 是否需要新同步
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
// 创建新的事务
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
// 开启事务和连接
doBegin(transaction, definition);
// 新同步事务的设置,针对于当前线程的设置
prepareSynchronization(status, definition);
return status;
}
doBegin方法开启和连接事务
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
// 强制转化事务对象
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;
try {
// 判断事务对象没有数据库连接持有器
if (!txObject.hasConnectionHolder() ||
txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
// 通过数据源获取一个数据库连接对象
Connection newCon = obtainDataSource().getConnection();
if (logger.isDebugEnabled()) {
logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
}
// 把我们的数据库连接包装成一个ConnectionHolder对象 然后设置到我们的txObject对象中去
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}
// 标记当前的连接是一个同步事务
txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
con = txObject.getConnectionHolder().getConnection();
// 为当前的事务设置隔离级别
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
// 设置先前隔离级别
txObject.setPreviousIsolationLevel(previousIsolationLevel);
// 设置是否只读
txObject.setReadOnly(definition.isReadOnly());
// Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
// so we don't want to do it unnecessarily (for example if we've explicitly
// configured the connection pool to set it already).
// 关闭自动提交
if (con.getAutoCommit()) {
//设置需要恢复自动提交
txObject.setMustRestoreAutoCommit(true);
if (logger.isDebugEnabled()) {
logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
}
// 关闭自动提交
con.setAutoCommit(false);
}
// 判断事务是否需要设置为只读事务
prepareTransactionalConnection(con, definition);
// 标记激活事务
txObject.getConnectionHolder().setTransactionActive(true);
// 设置事务超时时间
int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
}
// Bind the connection holder to the thread.
// 绑定我们的数据源和连接到我们的同步管理器上,把数据源作为key,数据库连接作为value 设置到线程变量中
if (txObject.isNewConnectionHolder()) {
// 将当前获取到的连接绑定到当前线程
TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
}
}
catch (Throwable ex) {
if (txObject.isNewConnectionHolder()) {
// 释放数据库连接
DataSourceUtils.releaseConnection(con, obtainDataSource());
txObject.setConnectionHolder(null, false);
}
throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
}
}
在doBegin方法中核心的关闭了自动提交
同时把连接绑定到本地线程中bindResource方法
编程式事务:
@Autowired
private UserDao userDao;
@Autowired
private PlatformTransactionManager txManager;
@Autowired
private LogService logService;
@Transactional
public void insertUser(User u) {
// 1、创建事务定义
DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
// 2、根据定义开启事务
TransactionStatus status = txManager.getTransaction(definition);
try {
this.userDao.insert(u);
Log log = new Log(System.currentTimeMillis() + "", System.currentTimeMillis() + "-" + u.getUserName());
// this.doAddUser(u);
this.logService.insertLog(log);
// 3、提交事务
txManager.commit(status);
} catch (Exception e) {
// 4、异常了,回滚事务
txManager.rollback(status);
throw e;
}
}
AOP事务
上面的案例代码我们可以看到在Service中我们通过事务处理的代码实现了事务管理,同时结合我们前面学习的AOP的内容,我们发现我们完全可以把事务的代码抽取出来,然后我们来看看Spring中这块是如何处理的。
我们可以通过Debug的方式看到处理的关键流程 TransactionInterceptor
就是事务处理的 advice
@Override
@Nullable
public Object invoke(MethodInvocation invocation) throws Throwable {
// Work out the target class: may be {@code null}.
// The TransactionAttributeSource should be passed the target class
// as well as the method, which may be from an interface.
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
// Adapt to TransactionAspectSupport's invokeWithinTransaction...
return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}
进入到invokeWithinTransaction方法中
@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {
// If the transaction attribute is null, the method is non-transactional.
// 获取我们的事务属性源对象
TransactionAttributeSource tas = getTransactionAttributeSource();
// 通过事务属性源对象获取到当前方法的事务属性信息
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
// 获取我们配置的事务管理器对象
final TransactionManager tm = determineTransactionManager(txAttr);
if (this.reactiveAdapterRegistry != null && tm instanceof ReactiveTransactionManager) {
ReactiveTransactionSupport txSupport = this.transactionSupportCache.computeIfAbsent(method, key -> {
if (KotlinDetector.isKotlinType(method.getDeclaringClass()) && KotlinDelegate.isSuspend(method)) {
throw new TransactionUsageException(
"Unsupported annotated transaction on suspending function detected: " + method +
". Use TransactionalOperator.transactional extensions instead.");
}
ReactiveAdapter adapter = this.reactiveAdapterRegistry.getAdapter(method.getReturnType());
if (adapter == null) {
throw new IllegalStateException("Cannot apply reactive transaction to non-reactive return type: " +
method.getReturnType());
}
return new ReactiveTransactionSupport(adapter);
});
return txSupport.invokeWithinTransaction(
method, targetClass, invocation, txAttr, (ReactiveTransactionManager) tm);
}
PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
// 获取连接点的唯一标识 类名+方法名
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
// 声明式事务处理
if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
// 创建TransactionInfo
TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
Object retVal;
try {
// This is an around advice: Invoke the next interceptor in the chain.
// This will normally result in a target object being invoked.
// 执行被增强方法,调用具体的处理逻辑
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// target invocation exception
// 异常回滚
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
//清除事务信息,恢复线程私有的老的事务信息
cleanupTransactionInfo(txInfo);
}
if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {
// Set rollback-only in case of Vavr failure matching our rollback rules...
TransactionStatus status = txInfo.getTransactionStatus();
if (status != null && txAttr != null) {
retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
}
}
//成功后提交,会进行资源储量,连接释放,恢复挂起事务等操作
commitTransactionAfterReturning(txInfo);
return retVal;
}
else {
// 编程式事务处理
Object result;
final ThrowableHolder throwableHolder = new ThrowableHolder();
// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
try {
result = ((CallbackPreferringPlatformTransactionManager) ptm).execute(txAttr, status -> {
TransactionInfo txInfo = prepareTransactionInfo(ptm, txAttr, joinpointIdentification, status);
try {
Object retVal = invocation.proceedWithInvocation();
if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {
// Set rollback-only in case of Vavr failure matching our rollback rules...
retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
}
return retVal;
}
catch (Throwable ex) {
if (txAttr.rollbackOn(ex)) {
// A RuntimeException: will lead to a rollback.
if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
}
else {
throw new ThrowableHolderException(ex);
}
}
else {
// A normal return value: will lead to a commit.
throwableHolder.throwable = ex;
return null;
}
}
finally {
cleanupTransactionInfo(txInfo);
}
});
}
catch (ThrowableHolderException ex) {
throw ex.getCause();
}
catch (TransactionSystemException ex2) {
if (throwableHolder.throwable != null) {
logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
ex2.initApplicationException(throwableHolder.throwable);
}
throw ex2;
}
catch (Throwable ex2) {
if (throwableHolder.throwable != null) {
logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
}
throw ex2;
}
// Check result state: It might indicate a Throwable to rethrow.
if (throwableHolder.throwable != null) {
throw throwableHolder.throwable;
}
return result;
}
}
然后进入到createTransactionIfNecessary方法中
核心的是doBegin方法。完成 自动提交的关闭和 本地线程 对象的存储
TransactionInterceptor
接下来看看TransactionInterceptor是如何注入到容器中的,首先来看看事务的开启
一步步进入
可以看到对应的拦截器的注入
然后可以看到拦截器关联到了Advisor中了
列举几种讲解:
这是个相对比较简单的问题。我们可以从这几个角度来回答
SpringMVC是基于Java的开源框架,用于构建Web应用程序。它是Spring框架的一部分,采用了MVC(模型-视图-控制器)架构模式。
核心对象:
这个问题针对的是SpringMVC控制器处理完业务请求后给前端服务做相关的响应。这块我们可以对 HandlerMethodReturnValueHandler
做相关的真强。
/**
* 对Controller响应请求的数据做装饰增强
*/
@Configuration
public class InitializingAdviceDecorator implements InitializingBean {
private final RequestMappingHandlerAdapter adapter;
public InitializingAdviceDecorator(RequestMappingHandlerAdapter adapter) {
this.adapter = adapter;
}
@Override
public void afterPropertiesSet() {
//获取所有的handler对象
List<HandlerMethodReturnValueHandler> returnValueHandlers = adapter.getReturnValueHandlers();
//因为上面返回的是unmodifiableList,所以需要新建list处理
assert returnValueHandlers != null;
List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>(returnValueHandlers);
this.decorateHandlers(handlers); // 对 对应的 handler 做装饰增强
//将增强的返回值回写回去
adapter.setReturnValueHandlers(handlers);
}
/**
* 使用自定义的返回值控制类
*
* @param handlers
*/
private void decorateHandlers(List<HandlerMethodReturnValueHandler> handlers) {
for (HandlerMethodReturnValueHandler handler : handlers) {
if (handler instanceof RequestResponseBodyMethodProcessor) {
//找到返回值的handler并将起包装成自定义的handler
ControllerReturnValueHandler decorator = new ControllerReturnValueHandler((RequestResponseBodyMethodProcessor) handler);
int index = handlers.indexOf(handler);
handlers.set(index, decorator);
break;
}
}
}
/**
* 自定义返回值的Handler
* 采用装饰者模式
*/
private static class ControllerReturnValueHandler implements HandlerMethodReturnValueHandler {
//持有一个被装饰者对象
private final HandlerMethodReturnValueHandler handler;
ControllerReturnValueHandler(RequestResponseBodyMethodProcessor handler) {
this.handler = handler;
}
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return true;
}
/**
* 增强被装饰者的功能
*
* @param returnValue 返回值
* @param returnType 返回类型
* @param mavContainer view
* @param webRequest 请求对象
* @throws Exception 抛出异常
*/
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
//如果是下载文件跳过包装
IgnoredResultWrapper ignoredResultWrapper = returnType.getMethodAnnotation(IgnoredResultWrapper.class);
if (ignoredResultWrapper != null) {
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
return;
}
if (returnValue == null) {
Optional<String> contentType = Optional.of(webRequest)
.map(nativeWebRequest -> ((ServletWebRequest) webRequest))
.map(ServletRequestAttributes::getResponse)
.map(ServletResponse::getContentType);
if (contentType.isPresent() && contentType.get().contains("application/vnd.openxmlformats-officedocument")) {
return;
}
}
//如果已经封装了结构体就直接放行
if (returnValue instanceof ResultWrapper) {
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
return;
}
//正常返回success
ResultWrapper<Object> success = ResultWrapper.success(returnValue);
handler.handleReturnValue(success, returnType, mavContainer, webRequest);
}
}
}
源码梳理从四个方面介绍
https://www.processon.com/view/link/64c06b8938c1420369f6958a
Spring Security是一个基于Spring框架的开源安全框架,它提供了一套全面的安全解决方案,用于保护Java应用程序的安全性。
Spring Security的主要功能包括 身份验证
、授权
、密码加密
、会话管理
和 安全事件处理
等。它可以集成到各种类型的应用程序中,包括Web应用程序、RESTful服务和微服务等。
https://www.processon.com/view/link/5f708e967d9c08039fbe2110
https://jwt.io/
JWT(JSON Web Token)是一种用于身份验证和授权的开放标准(RFC 7519)。它是一种轻量级的安全凭证,以JSON格式存储信息,并使用数字签名或加密进行验证。
Token是一种在身份验证和授权中用于识别用户的凭证。它可以是任何形式的字符串,通常由服务器生成并返回给客户端。Token可以用来验证用户身份、授权用户访问特定资源或服务,并在一定时间内保持有效。
JWT是一种特定类型的Token,它使用JSON格式存储信息,并使用数字签名或加密来验证其完整性和真实性。JWT由三部分组成:头部(Header)、负载(Payload)和签名(Signature)。头部和负载以Base64编码的形式存储,并用点号连接起来,形成JWT的第一部分。签名是通过对头部、负载和密钥进行加密生成的,用于验证JWT的真实性。
Token可以是任何形式的字符串,包括JWT。JWT是一种更安全和可扩展的Token,它可以包含更多的信息,并且通过数字签名或加密来验证其真实性。与传统的Token相比,JWT具有更好的灵活性和安全性,并且可以在不同的应用程序中共享和验证。
Shiro的认证和授权的实现本质上是通过各种过滤器来实现的。具体如下图:
MyBatis是一种持久层框架的 ORM
框架,用于在Java应用程序中与关系型数据库进行交互。它提供了一种简单且灵活的方式来处理数据库操作,同时也允许开发人员使用原生的SQL语句。
具有如下的相关的特点:
MyBatis和JDBC哪个性能效率高?
MyBatis的整体架构非常简洁,分别是:接口层,核心处理层和基础模块:
MyBatisPlus是当下持久层中使用频率非常高的一个开源框架。和MyBatis的对比就非常多,这块也可以介绍下:
总的来说,MyBatis是一个轻量级的持久层框架,而MyBatis Plus是在MyBatis基础上进行了扩展,提供了更多的便捷功能和增强特性.
这个题目的主要目的是考察大家对MyBatis的理解深度,如果单纯的是做 CRUD
的开发,可能就忽略了。首先我们需要介绍清楚MyBatis执行启动的操作代码
@Test
public void start() throws Exception{
// 1. 加载全局配置文件
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
// 2.获取SqlSessionFactory
// DefaultSqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
// 3.获取SqlSession对象 DefaultSqlSession Executor--> SimpleExecutor --> CachingExecutor --> 插件的逻辑植入
SqlSession sqlSession = factory.openSession();
// 4.通过sqlSession的API接口实现数据库操作
List<User> list = sqlSession.selectList("com.bobo.vip.mapper.UserMapper.selectUserById",2);
for (User user : list) {
System.out.println(user);
}
// 关闭会话
sqlSession.close();
}
具体的操作过程如下:
当然这块的过程相对还是比较简单的。面试官可能会在这个基础上做相关的扩展。可以结合下面的图分析
也就是 SqlSessionFactory
对象的构建和 SqlSession
对象创建的核心过程。已经具体的数据库操作的请求是如何实现的。这块也是面试官比较感兴趣的内容。
MyBatis缓存的作用是提高数据库访问性能。它将查询结果缓存到内存中,当下次有相同的查询请求时,直接从缓存中取出结果,避免了再次访问数据库,从而提高了查询的响应速度。
一级缓存是SqlSession级别的缓存,它默认是开启的。当同一个SqlSession执行相同的SQL语句时,会先从缓存中查找,如果找到了对应的结果,则直接返回缓存中的结果,而不会再次访问数据库。
要关闭一级缓存:
<setting name="localCacheScope" value="STATEMENT"/>
二级缓存是Mapper级别的缓存,它默认是关闭的。当不同的SqlSession执行相同的SQL语句时,如果开启了二级缓存,则会先从缓存中查找,如果找到了对应的结果,则直接返回缓存中的结果,而不会再次访问数据库。
要开启二级缓存:
首先是在全局配置文件中设置:
<setting name="cacheEnabled" value="true"/>
然后需要在具体的映射文件中设置 cache
标签
<cache />
如果映射文件中的某个方法不想开启缓存可以设置 useCache="false"
处理
当我们开启了二级缓存后。查询操作是先走二级缓存还是先走一级缓存?
三级缓存是在分布式环境下把存储在内存中的数据持久化到第三方数据源的过程。比如Redis中。这块需要重写 Cache
接口
MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括: https://mybatis.net.cn/configuration.html#plugins
MyBatis拦截器是MyBatis提供的一种插件机制,可以在SQL执行过程中拦截SQL语句并进行相关操作。
拦截器可以用于实现一些通用的功能,如日志记录、权限校验、性能监控等。它可以拦截SQL的执行、参数的设置、结果的处理等环节。
要实现一个拦截器,需要实现MyBatis提供的Interceptor接口,并重写其中的方法。Interceptor接口中定义了3个方法:
拦截器在MyBatis的配置文件中进行配置,可以通过标签将拦截器添加到MyBatis的拦截链中。
使用拦截器可以方便地扩展MyBatis的功能,实现一些通用的需求,并且可以灵活地控制拦截器的顺序。
使用的步骤:
先定义
// ExamplePlugin.java
@Intercepts({@Signature(
type= Executor.class,
method = "update",
args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
private Properties properties = new Properties();
public Object intercept(Invocation invocation) throws Throwable {
// implement pre processing if need
Object returnObject = invocation.proceed();
// implement post processing if need
return returnObject;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
}
再注册
<plugins>
<plugin interceptor="org.mybatis.example.ExamplePlugin">
<property name="someProperty" value="100"/>
plugin>
plugins>
上面的插件将会拦截在 Executor 实例中所有的 “update” 方法调用, 这里的 Executor 是负责执行底层映射语句的内部对象。
实际的应用:分页,SQL检查。黑白名单。分库分表等
在MyBatis中,SqlSession是一个线程不安全的对象,主要原因如下:
不一致性
或者 事务的混乱
。缓存机制
也是基于当前线程的,如果多个线程共享同一个SqlSession实例,可能会导致缓存的数据混乱或者不一致。为了保证数据的安全性和一致性,通常建议在每个线程中使用独立的SqlSession实例,可以通过工厂模式创建新的SqlSession对象,或者使用MyBatis提供的线程安全的SqlSessionFactory实例来创建SqlSession。另外,可以使用ThreadLocal来保证每个线程中使用的SqlSession对象是唯一的。
https://dpb-bobokaoya-sm.blog.csdn.net/article/details/117415102
在Spring中,可以通过使用 SqlSessionTemplate
来解决SqlSession数据不安全的问题。SqlSessionTemplate
是MyBatis-Spring提供的一个实现了 SqlSession
接口的类,它会自动管理SqlSession的生命周期,并保证每个线程都有自己的SqlSession实例。使用 SqlSessionTemplate
时,只需要将其配置为Spring的Bean,并注入到需要使用SqlSession的地方即可,Spring会自动为每个线程提供一个独立的SqlSession实例。
我们在定义的时候需要满足相关的规则: User.xml User.java com.msb.entity.User
User proxy = SqlSessin.getMapper(User.class);
在执行的时候我们会调用SqlSession的getMapper方法。通过getMapper方法来获取接口的代理对象。执行相关操作的时候会通过代理对象的invoke 方法来找到对应的SQL执行。并返回结果。
User proxy = SqlSessin.getMapper(User.class);
proxy.insertUser(user); --> insert …
不可以。因为根据接口中的方法来获取对应的xml中的标签是通过 全限定名+方法名的方式来获取的。如果重载会找到多个。
Mybatis 仅支持 association 关联对象和 collection 关联集合对象的延迟加载,association 指的就是一对一,collection 指的就是一对多查询。在 Mybatis 配置文件中,可以配置是否启用延迟加载 lazyLoadingEnabled=true|false。
它的原理是,使用 CGLIB 创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用 a.getB().getName(),拦截器 invoke()方法发现 a.getB()是 null值,那么就会单独发送事先保存好的查询关联 B 对象的 sql,把 B 查询上来,然后调用 a.setB(b),于是 a 的对象 b 属性就有值了,接着完成 a.getB().getName()方法的调用。这就是延迟加载的基本原理。
日志模块中提供了一个jdbc模块。通过代理的方式记录相关的操作日志信息。
提供了两种方式:
从文件中读取:从哪来的?
在我们的项目中,我们采用以下方式实现异常的统一处理:
通过以上的方式,我们能够实现异常的统一处理,提高代码的可维护性和可读性,并且能够更好地进行日志记录和错误信息返回。