Java重要面试名词整理(二):SpringMyBatis

news2024/12/25 9:29:06

文章目录

  • Spring篇
    • Spring核心
      • 推断构造方法
      • AOP
        • 动态代理
        • Advice的分类
        • Advisor的理解
        • AOP相关的概念
      • 定义Bean
      • ASM技术
      • JFR
      • 依赖注入
      • 循环依赖
      • Lifecycle
      • Spring AOT
    • Spring事务
      • Spring事务传播机制
        • Spring事务传播机制是如何实现的呢?
        • Spring事务传播机制分类
    • SpringMVC
      • Handler
      • HandlerMapping
      • HandlerAdapter
      • @RequestMapping方法参数
      • SPI
      • Spring MVC不常用注解
        • @InitBinder
        • @SessionAttributes
        • @RequestAttribute 与 @SessionAttribute
        • @ModelAttribute
      • flashMap
  • MyBatis篇
      • MyBatis执行SQL流程分析
      • MyBatis重要类分析
      • MyBatis的二级缓存原理
      • OGNL表达式
          • Mybatis中的OGNL表达式
          • Mybatis中的OGNL表达式拓展

Spring篇

Spring核心

推断构造方法

Spring会根据入参的类型和入参的名字去Spring中找Bean对象(以单例Bean为例,Spring会从单例池那个Map中去找):

  1. 先根据入参类型找,如果只找到一个,那就直接用来作为入参
  2. 如果根据类型找到多个,则再根据入参名字来确定唯一一个,会根据入参名字来找Map里面一样名字的作为入参。
  3. 最终如果没有找到,则会报错,无法创建当前Bean对象

确定用哪个构造方法,确定入参的Bean对象,这个过程就叫做推断构造方法

AOP

AOP全称是 Aspect Oriented Programming 即:面向切面编程。是OOP的延续,也是Spring框架中的一个重要内容,是函数式编程的一种衍生泛型。简单的说他就是把我们程序重复的代码抽取出来,在需要执行的时候使用动态代理技术在不修改源码的基础上,对我们的已有方法进行增强。

动态代理

代理模式的解释:为其他对象提供一种代理以控制对这个对象的访问,增强一个类中的某个方法,对程序进行扩展。

在Spring中进行了封装,封装出来的类叫做ProxyFactory,表示是创建代理对象的一个工厂,使用起来会比JDK动态代理、cglib动态代理更加方便。

UserService target = new UserService();

ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.addAdvice(new MethodInterceptor() {
 @Override
 public Object invoke(MethodInvocation invocation) throws Throwable {
  System.out.println("before...");
  Object result = invocation.proceed();
  System.out.println("after...");
  return result;
 }
});

UserInterface userService = (UserInterface) proxyFactory.getProxy();
userService.test();

通过ProxyFactory,我们可以不再关心到底是用cglib还是jdk动态代理了,ProxyFactory会帮我们去判断,如果UserService实现了接口,那么ProxyFactory底层就会用jdk动态代理,如果没有实现接口,就会用cglib技术,上面的代码,就是由于UserService实现了UserInterface接口,所以最后产生的代理对象是UserInterface类型。

Advice的分类
  1. Before Advice:方法之前执行
  2. After returning advice:方法return后执行
  3. After throwing advice:方法抛异常后执行
  4. After (finally) advice:方法执行完finally之后执行,这是最后的,比return更后
  5. Around advice:这是功能最强大的Advice,可以自定义执行顺序
Advisor的理解

跟Advice类似的还有一个Advisor的概念,一个Advisor是有一个Pointcut和一个Advice组成的,通过Pointcut可以指定要需要被代理的逻辑,比如一个UserService类中有两个方法,按上面的例子,这两个方法都会被代理,被增强,那么我们现在可以通过Advisor,来控制到具体代理哪一个方法,比如:

  UserService target = new UserService();

  ProxyFactory proxyFactory = new ProxyFactory();
  proxyFactory.setTarget(target);
  proxyFactory.addAdvisor(new PointcutAdvisor() {
   @Override
   public Pointcut getPointcut() {
    return new StaticMethodMatcherPointcut() {
     @Override
     public boolean matches(Method method, Class<?> targetClass) {
      return method.getName().equals("testAbc");
     }
    };
   }

   @Override
   public Advice getAdvice() {
    return new MethodInterceptor() {
     @Override
     public Object invoke(MethodInvocation invocation) throws Throwable {
      System.out.println("before...");
      Object result = invocation.proceed();
      System.out.println("after...");
      return result;
     }
    };
   }

   @Override
   public boolean isPerInstance() {
    return false;
   }
  });

  UserInterface userService = (UserInterface) proxyFactory.getProxy();
  userService.test();

上面代码表示,产生的代理对象,只有在执行testAbc这个方法时才会被增强,会执行额外的逻辑,而在执行其他方法时是不会增强的。

AOP相关的概念
  1. Aspect:表示切面,比如被@Aspect注解的类就是切面,可以在切面中去定义Pointcut、Advice等等
  2. Join point:表示连接点,表示一个程序在执行过程中的一个点,比如一个方法的执行,比如一个异常的处理,在Spring AOP中,一个连接点通常表示一个方法的执行。
  3. Advice:表示通知,表示在一个特定连接点上所采取的动作。Advice分为不同的类型,后面详细讨论,在很多AOP框架中,包括Spring,会用Interceptor拦截器来实现Advice,并且在连接点周围维护一个Interceptor链
  4. Pointcut:表示切点,用来匹配一个或多个连接点,Advice与切点表达式是关联在一起的,Advice将会执行在和切点表达式所匹配的连接点上
  5. Introduction:可以使用@DeclareParents来给所匹配的类添加一个接口,并指定一个默认实现
  6. Target object:目标对象,被代理对象
  7. AOP proxy:表示代理工厂,用来创建代理对象的,在Spring Framework中,要么是JDK动态代理,要么是CGLIB代理
  8. Weaving:表示织入,表示创建代理对象的动作,这个动作可以发生在编译时期(比如Aspejctj),或者运行时,比如Spring AOP

定义Bean

Spring中定义Bean的方式可以分为两类,分别是声明式和编程式。顾名思义,声明式可以理解为用一个的标记去标识相关的信息,代码底层再对这些标识符进行一个解析处理;编程式则更多侧重于使用代码的形式去处理相关逻辑。前者使用更友好,后者更底层。

声明式:标签、@Bean注解、@Component注解

编程式:BeanDefinition接口、FactoryBean接口、Supplier接口

ASM技术

在Spring中需要去解析类的信息,比如类名、类中的方法、类上的注解,这些都可以称之为类的元数据,所以Spring中对类的元数据做了抽象,并提供了一些工具类。MetadataReader表示类的元数据读取器,默认实现类为SimpleMetadataReader。需要注意的是,SimpleMetadataReader去解析类时,使用的ASM技术

ASM 是一个 Java 字节码操控框架。它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class 文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。ASM并没有让这个类加载到JVM。

JFR

JFR资料介绍:JFR 是 Java Flight Record (Java飞行记录) 的缩写,是 JVM 内置的基于事件的JDK监控记录框架。这个起名就是参考了黑匣子对于飞机的作用,将Java进程比喻成飞机飞行。顾名思义,这个记录主要用于问题定位和持续监控。

依赖注入

依赖注入(Dependency Injection, DI)是一种设计模式,也是Spring框架的核心概念之一。其作用是去除Java类之间的依赖关系,实现松耦合,以便于开发测试。依赖注入分为手动注入和自动注入两种注入方式。

手动注入:在XML中定义Bean时,就是手动注入,因为是程序员手动给某个属性指定了值。手动注入分为两种:

  1. set方法注入

    <bean name="userService" class="com.luban.service.UserService">
     <property name="orderService" ref="orderService"/>
    </bean>
    
  2. 构造方法注入

    <bean name="userService" class="com.luban.service.UserService">
     <constructor-arg index="0" ref="orderService"/>
    </bean>
    

自动注入:分为两种:

  1. XML的autowire自动注入

    在XML中,我们可以在定义一个Bean时去指定这个Bean的自动注入模式:

    1. byType
    2. byName
    3. constructor
    4. default
    5. no
    <bean id="userService" class="com.luban.service.UserService" autowire="byType"/>
    
  2. @Autowired注解的自动注入

    @Autowired注解可以写在:

    1. 属性上:先根据属性类型去找Bean,如果找到多个再根据属性名确定一个
    2. 构造方法上:先根据方法参数类型去找Bean,如果找到多个再根据参数名确定一个
    3. set方法上:先根据方法参数类型去找Bean,如果找到多个再根据参数名确定一个

循环依赖

简单来说就是A对象依赖了B对象,B对象依赖了A对象。

如果不考虑Spring,循环依赖并不是问题,因为对象之间相互依赖是很正常的事情。但是,在Spring中循环依赖就是一个问题了,因为在Spring中,一个对象并不是简单new出来了,而是会经过一系列的Bean的生命周期,就是因为Bean的生命周期所以才会出现循环依赖问题。

Spring解决循环依赖使用了三级缓存。三级缓存是通用的叫法。
一级缓存为:singletonObjects
二级缓存为:earlySingletonObjects
三级缓存为**:singletonFactories**

  • singletonObjects中缓存的是已经经历了完整生命周期的bean对象。

  • earlySingletonObjects比singletonObjects多了一个early,表示缓存的是早期的bean对象。早期是什么意思?表示Bean的生命周期还没走完就把这个Bean放入了earlySingletonObjects。

  • singletonFactories中缓存的是ObjectFactory,表示对象工厂,表示用来创建早期bean对象的工厂。

Lifecycle

Lifecycle表示的是ApplicationContext的生命周期,可以定义一个SmartLifecycle来监听ApplicationContext的启动和关闭

@Component
public class isPaintingLifecycle implements SmartLifecycle {

 private boolean isRunning = false;

 @Override
 public void start() {
  System.out.println("启动");
  isRunning = true;
 }

 @Override
 public void stop() {
        // 要触发stop(),要调用context.close(),或者注册关闭钩子(context.registerShutdownHook();)
  System.out.println("停止");
  isRunning = false;
 }

 @Override
 public boolean isRunning() {
  return isRunning;
 }
}

Spring AOT

Spring 6 提供了一项新功能,有望优化应用程序的性能: Ahead-of-Time(AOT) 编译支持。

Ahead-of-Time(AOT,提前编译或预编译)是一种在应用程序运行前将字节码预编译为本地机器码的技术。

通过 AOT 编译构建的应用程序在性能和资源消耗方面具有多重优势:

  • 消除死代码:AOT 编译器可以消除运行时从未执行过的代码。这样可以减少需要执行的代码量,从而提高性能。
  • 内联:内联是 AOT 编译器用函数的实际代码,替换函数调用的一种技术。这可以减少函数调用的开销,从而提高性能。
  • 常量传播:AOT 编译器通过在编译时确定变量的常量值来替换变量,从而优化性能。这样就无需进行运行时计算,从而提高了性能。
  • 过程间优化:AOT 编译器可通过分析程序的调用图来优化跨多个函数的代码。这可以通过减少函数调用的开销和识别常见的子表达式来提高性能。
  • Bean 定义:Spring 6 中的 AOT 编译器可减少不必要的 BeanDefinition 实例,从而提高应用程序的效率。

因此,让我们使用 AOT 优化命令来构建应用程序:

mvn clean compile spring-boot:process-aot package

然后使用命令运行应用程序:

java -Dspring.aot.enabled=true -jar <jar-name>

我们可以设置构建插件,默认启用 AOT 编译:

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <executions>
    <execution>
        <id>process-aot</id>
        <goals>
        <goal>process-aot</goal>
        </goals>
    </execution>
    </executions>
</plugin>

Spring事务

Spring事务传播机制

Spring事务传播机制是如何实现的呢?

在执行某个方法时,判断当前是否已经存在一个事务,就是判断当前线程的ThreadLocal中是否存在一个数据库连接对象,如果存在则表示已经存在一个事务了。

Spring事务传播机制分类

REQUIRED (默认传播行为),支持当前事务,如果当前没有事务,就新建一个事务,这个当前事务指的是上一个方法的事务,是别人传递过去的,类似于重入锁,A方法和B方法都有事务,A方法调用B方法,A的事务会传递给B,使它们共用同一个事务,我起了个名字叫做重入事务
SUPPORTS 如果存在一个事务,支持当前事务,如果没有事务,则非事务执行,
REQUIRES_NEW 开启一个新的事务。如果一个事务已经存在,则先将这个存在的事务挂起
MANDATORY 如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常
NOT_SUPPORTED 总是非事务地执行,并挂起任何存在的事务
NEVER 总是非事务地执行,不加入任何事务;
NESTED 如果一个活动的事务存在,则运行在一个嵌套的事务中。 如果没有活动事务, 则按 REQUIRED 属性执行。

SpringMVC

Handler

Handler表示请求处理器,在SpringMVC中有四种Handler:
1、实现了Controller接口的Bean对象
2、实现了HttpRequestHandler接口的Bean对象
3、添加了@RequestMapping注解的方法
4、一个HandlerFunction对象

HandlerMapping

HandlerMapping负责去寻找Handler,并且保存路径和Handler之间的映射关系。

因为有不同类型的Handler,所以在SpringMVC中会由不同的HandlerMapping来负责寻找Handler

HandlerAdapter

由于有不同种类的Handler,所以执行方式是不一样的。所以,按逻辑来说,找到Handler之后,我们得判断它的类型。把不同种类的Handler适配成一个HandlerAdapter,后续再执行HandlerAdapter的handle()方法就能执行不同种类Hanlder对应的方法。

针对不同的Handler,会有不同的适配器。

@RequestMapping方法参数

当SpringMVC接收到请求,并找到了对应的Method之后,就要执行该方法了,不过在执行之前需要根据方法定义的参数信息,从请求中获取出对应的数据,然后将数据传给方法并执行。

一个HttpServletRequest通常有:

1、request parameter

2、request attribute

3、request session

4、reqeust header

5、reqeust body

比如如下几个方法:

public String test(String username) {
    return "ispainting";
}

表示要从request parameter中获取key为username的value

public String test(@RequestParam("uname") String username) {
    return "ispainting";
}

表示要从request parameter中获取key为uname的value

public String test(@RequestAttribute String username) {
    return "ispainting";
}

表示要从request attribute中获取key为username的value

public String test(@SessionAttribute String username) {
    return "ispainting";
}

表示要从request session中获取key为username的value

public String test(@RequestHeader String username) {
    return "ispainting";
}

表示要从request header中获取key为username的value

public String test(@RequestBody String username) {
    return "ispainting";
}

表示获取整个请求体

SPI

跟Tomcat的提供的扩展机制有关,在SpringMVC中有这样一个类:

@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {

	@Override
	public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
			throws ServletException {
    	// ...
	}

}

这个类实现了javax.servlet.ServletContainerInitializer接口,并且在SpringMVC中还有这样一个文件:META-INF/services/Tomcatjavax.servlet.ServletContainerInitializer,文件内容为org.springframework.web.SpringServletContainerInitializer。

很明显,是SPI,所以Tomcat在启动过程中会找到这个SpringServletContainerInitializer,并执行onStartup()

SPI(Service Provider Interface),是JDK内置的一种服务提供发现机制,可以用来启用框架扩展和替换组件。Java的SPI机制可以为某个接口寻找服务实现。Java中SPI机制主要思想是将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要,其核心思想就是解耦

SPI与API区别:

  • API是调用并用于实现目标的类、接口、方法等的描述;
  • SPI是扩展和实现以实现目标的类、接口、方法等的描述;

Spring MVC不常用注解

@InitBinder

在参数绑定时进行可以针对复杂对象自定义参数绑定逻辑,比如:

@GetMapping("/testInitBinder")
public String testInitBinder(Date date){
    return date.toString();
}

此时如果访问:http://localhost:8080/tuling-web/app/testInitBinder?date=1111-1-1,会报错。

此时可以在当前Controller中添加:

@InitBinder
public void initBinder(WebDataBinder binder) {
    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
	dateFormat.setLenient(false);
	binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
}

有了这个就相当于添加了一个自定义的日期格式转换器,这样就能正常访问了。

注意,如果我们想把String类型转成我们自定义的User类,那么在User类中得提供一个String类型的构造方法

@SessionAttributes

只能写在类上,通过@SessionAttributes注解指定model中哪些key的value存到session中。

@RequestAttribute 与 @SessionAttribute

都只能写在方法参数前面,表示从相对应request.getAttribute()、session.getAttribute()中获取值传递给方法参数

@ModelAttribute

可以写在某个方法上,@ModelAttribute可以定义在一个Controller中,当请求这个Controller中的方法时,会先调用@ModelAttribute所修饰的方法,方法返回的值会添加到model中,比如以下代码就会向model中添加一个key为user,values为请求中user参数所传递的值。

flashMap

在重定向时,FlashMap机制提供了这么一种方式让一个请求传递一些参数给接下来的某个请求,FlashMap机制保证了参数的隐蔽性,不需要将参数传递到前端。

我们可以通过FlashMap来进行传递:

@Controller
public class IsPaintingController {

	@GetMapping("/a")
	public String test(HttpServletRequest request, Model model) {
		FlashMap outputFlashMap = RequestContextUtils.getOutputFlashMap(request);
		outputFlashMap.put("username", "ispainting");
		return "redirect:/b";
	}

	@GetMapping("/b")
	@ResponseBody
	public String a(HttpServletRequest request, Model model) {
		Map<String, ?> inputFlashMap = RequestContextUtils.getInputFlashMap(request);
		String username = (String) inputFlashMap.get("username");
		return username;
	}
}

把要传递的参数存入outputFlashMap,在b请求里通过inputFlashMap就可以拿到了,底层是基于session来实现的。

MyBatis篇

ORM是"Object-Relational Mapping"的缩写,中文通常翻译为"对象关系映射"。

传统JDBC规范(掌握四个核心对象):

DriverManager:用于注册驱动
Connection: 表示与数据库创建的连接
Statement: 操作数据库sql语句的对象
ResultSet: 结果集或一张虚拟表

MyBatis执行SQL流程分析

Executor分成两大类,一类是CacheExecutor,另一类是普通Executor。

CacheExecutor其实是封装了普通的Executor,和普通的区别是在查询前先会查询缓存中是否存在结果,如果存在就使用缓存中的结果,如果不存在还是使用普通的Executor进行查询,再将查询出来的结果存入缓存。

普通Executor又分为三种基本的Executor执行器,SimpleExecutor、ReuseExecutor、BatchExecutor。

  • SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。
  • ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map内,供下一次使用。简言之,就是重复使用Statement对象。
  • BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理。与JDBC批处理相同。

作用范围:Executor的这些特点,都严格限制在SqlSession生命周期范围内。

MyBatis重要类分析

重要类

  • MapperRegistry:本质上是一个Map,其中的key是Mapper接口的全限定名,value的MapperProxyFactory;
  • MapperProxyFactory:这个类是MapperRegistry中存的value值,在通过sqlSession获取Mapper时,其实先获取到的是这个工厂,然后通过这个工厂创建Mapper的动态代理类;
  • MapperProxy:实现了InvocationHandler接口,Mapper的动态代理接口方法的调用都会到达这个类的invoke方法;
  • MapperMethod:判断你当前执行的方式是增删改查哪一种,并通过SqlSession执行相应的操作;
  • SqlSession:作为MyBatis工作的主要顶层API,表示和数据库交互的会话,完成必要数据库增删改查功能;
  • Executor:MyBatis执行器,是MyBatis 调度的核心,负责SQL语句的生成和查询缓存的维护;

StatementHandler:封装了JDBC Statement操作,负责对JDBC statement 的操作,如设置参数、将Statement结果集转换成List集合。

ParameterHandler:负责对用户传递的参数转换成JDBC Statement 所需要的参数,

ResultSetHandler:负责将JDBC返回的ResultSet结果集对象转换成List类型的集合;

TypeHandler:负责java数据类型和jdbc数据类型之间的映射和转换

MappedStatement:MappedStatement维护了一条节点的封装,

SqlSource:负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回

BoundSql:表示动态生成的SQL语句以及相应的参数信息

Configuration:MyBatis所有的配置信息都维持在Configuration对象之中。

MyBatis的二级缓存原理

mybatis使用的是溢出淘汰机制。二级缓存在结构设计上采用装饰器+责任链模式

mybatis缓存分为一级缓存和二级缓存

  • 一级缓存,又叫本地缓存,是PerpetualCache类型的永久缓存,保存在执行器中(BaseExecutor),而执行器又在SqlSession(DefaultSqlSession)中,所以一级缓存的生命周期与SqlSession是相同的。
  • 二级缓存,又叫自定义缓存,实现了Cache接口的类都可以作为二级缓存,所以可配置如encache等的第三方缓存。二级缓存以namespace名称空间为其唯一标识,被保存在Configuration核心配置对象中。

二级缓存对象的默认类型为PerpetualCache,如果配置的缓存是默认类型,则mybatis会根据配置自动追加一系列装饰器。

OGNL表达式

OGNL是一种表达式语言,专门用于处理对象图中的数据。

OGNL表达式的语法非常简洁明了,类似于XPath表达式。它使用点符号(.)来访问对象的属性,使用方括号([])来访问集合和数组元素。以下是一些常见的OGNL语法示例:

  1. 访问属性:假设有一个名为person的对象,其中包含属性name和age,我们可以使用如下OGNL表达式来获取属性值:

    person.nameperson.age
    
  2. 调用方法:假设有一个名为order的对象,其中包含方法getTotal(),我们可以使用如下OGNL表达式来调用该方法

    order.getTotal()
    
  3. 访问集合和数组:假设有一个名为items的List对象,我们可以使用如下OGNL表达式来访问其中的元素:

    items[0]items[1]
    
Mybatis中的OGNL表达式

在Mybatis中,可以在SQL映射文件中使用${和}符号来嵌入OGNL表达式,如:

<select id="findPersonById" resultType="Person">
  SELECT * FROM person WHERE id = #{id} AND city = #{address.city}
</select>

其中,#{id}和#{address.city}是OGNL表达式,用于访问Person对象的id属性和Address对象的city属性。

Mybatis中的OGNL表达式拓展

在Mybatis中,可以使用Hutool的OGNL工具类来实现OGNL表达式的拓展。

<select id="findPersons" resultType="Person">
  SELECT * FROM person
  <if test="@cn.hutool.core.util.StrUtil@isNotBlank(name)">
    AND name LIKE CONCAT('%', #{name}, '%')
  </if>
  <if test="age != null">
    AND age = #{age}
  </if>
  <if test="@cn.hutool.core.util.StrUtil@isNotBlank(city)">
    AND city = #{city.substring(0, 2)}
  </if>
</select>

其中,@cn.hutool.core.util.StrUtil@isNotBlank(name)和@cn.hutool.core.util.StrUtil@isNotBlank(city)是OGNL表达式,用于调用StrUtil工具类的isNotBlank方法,并将name和city属性作为参数传递。CONCAT(‘%’, #{name}, ‘%’)是SQL函数,用于实现字符串的拼接。#{city.substring(0, 2)}是OGNL表达式,用于实现字符串的截取。

需要注意的是,在使用Hutool的OGNL工具类时,需要在Mybatis的配置文件中进行相应的配置,如:

<configuration>
  <settings>
    <setting name="ognl.classResolver" value="cn.hutool.ognl.HutoolClassResolver"/>
  </settings>
</configuration>

其中,ognl.classResolver是Mybatis的配置项,用于指定OGNL表达式的类加载器。cn.hutool.ognl.HutoolClassResolver是Hutool的OGNL类加载器,用于实现OGNL表达式的拓展。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2264409.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

常见八股文03

35.autowired、qualifier和Resource区别 Autowired&#xff1a;基于类型的注入 Qualifier&#xff1a;基于名称进行注入 Resource:按名称装配注入&#xff0c;如果找不到与名称匹配的bean&#xff0c;则按类型装配注入&#xff0c;可以用于字段和方法上 36.代理模式 动态代…

Pytorch | 利用NI-FGSM针对CIFAR10上的ResNet分类器进行对抗攻击

Pytorch | 利用NI-FGSM针对CIFAR10上的ResNet分类器进行对抗攻击 CIFAR数据集NI-FGSM介绍背景算法流程 NI-FGSM代码实现NI-FGSM算法实现攻击效果 代码汇总nifgsm.pytrain.pyadvtest.py 之前已经针对CIFAR10训练了多种分类器&#xff1a; Pytorch | 从零构建AlexNet对CIFAR10进行…

redis开发与运维-redis02-redis数据类型与命令总结

文章目录 【README】【1】redis通用命令与数据结构【1.1】通用命令【1.2】数据结构与内部编码【1.3】redis单线程架构【1.3.1】redis单线程优缺点 【2】字符串&#xff08;值的类型为字符串&#xff09;【2.1】常用命令【2.1.1】设置值【2.1.2】获取值【2.1.3】批量设置值【2.1…

机器学习《西瓜书》学习笔记《待续》

如果说&#xff0c;计算机科学是研究关于“算法”的学问&#xff0c;那么机器学习就是研究关于“学习算法”的学问。 目录 绪论引言基本术语 扩展向量的张成-span使用Markdown语法编写数学公式希腊字母的LaTex语法插入一些数学的结构插入定界符插入一些可变大小的符号插入一些函…

电脑开机提示error loading operating system怎么修复?

前一天电脑还能正常运行&#xff0c;但今天启动时却显示“Error loading operating system”&#xff08;加载操作系统错误&#xff09;。我已经仔细检查了硬盘、接线、内存、CPU和电源&#xff0c;确认这些硬件都没有问题。硬盘在其他电脑上可以正常使用&#xff0c;说明不是硬…

git企业开发的相关理论(一)

目录 一.初识git 二.git的安装 三.初始化/创建本地仓库 四.配置用户设置/配置本地仓库 五.认识工作区、暂存区、版本库 六.添加文件__场景一 七.查看 .git 文件/添加到本地仓库后.git中发生的变化 1.执行git add后的变化 index文件&#xff08;暂存区&#xff09; lo…

Linux网络——网络基础

Linux网络——网络基础 文章目录 Linux网络——网络基础一、计算机网络的发展背景1、网络的定义&#xff08;1&#xff09; 独立模式&#xff08;2&#xff09;网络互联 2、局域网 LAN3、广域网 WAN4、比较局域网和广域网5、扩展 —— 域域网和互联网 二、协议1、协议的概念2、…

react中实现导出excel文件

react中实现导出excel文件 一、安装依赖二、实现导出功能三、自定义列标题四、设置列宽度五、样式优化1、安装扩展库2、设置样式3、扩展样式功能 在 React 项目中实现点击按钮后导出数据为 Excel 文件&#xff0c;可以使用 xlsx 和 file-saver 这两个库。 一、安装依赖 在项目…

7-Zip 加密功能使用教程:如何设置密码保护压缩文件

压缩包如何加密&#xff1f;7-Zip 是一款开源的文件归档工具&#xff0c;支持多种压缩格式&#xff0c;并提供了对压缩文件进行加密的功能。使用 7-Zip 可以轻松创建和解压 .7z、.zip 等格式的压缩文件&#xff0c;并且可以通过设置密码来保护压缩包中的数据不被未授权访问。 准…

[计算机网络]ARP协议的故事:小明找小红的奇妙旅程

1.ARP小故事 在一个繁忙的网络世界中&#xff0c;每个设备都有自己的身份标识——MAC地址&#xff0c;就像每个人的身份证号码一样。在这个故事里&#xff0c;我们的主角小明&#xff08;主机&#xff09;需要找到小红&#xff08;目标主机&#xff09;的MAC地址&#xff0c;才…

新版国标GB28181设备端Android版EasyGBD支持国标GB28181-2022,支持语音对讲,支持位置上报,开源在Github

经过近3个月的迭代开发&#xff0c;新版本的国标GB28181设备端EasyGBD安卓Android版终于在昨天发布到Github了&#xff0c;最新的EasyGBD支持了国标GB28181-2022版&#xff0c;还支持了语音对讲、位置上报、本地录像等功能&#xff0c;比原有GB28181-2016版的EasyGBD更加高效、…

McDonald‘s Event-Driven Architecture 麦当劳事件驱动架构

原文链接 1 mcdonalds-technical-blog/ 原文链接 2 mcdonalds-technical-blog/ 麦当劳在异步、事务性和分析性处理用例中使用跨技术栈的事件&#xff0c;包括移动订单进度跟踪和向客户发送营销通信&#xff08;交易和促销&#xff09;。 统一事件平台&#xff08;unified eve…

【CSS in Depth 2 精译_089】15.2:CSS 过渡特效中的定时函数

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第五部分 添加动效 ✔️【第 15 章 过渡】 ✔️ 15.1 状态间的由此及彼15.2 定时函数 ✔️ 15.2.1 定制贝塞尔曲线 ✔️15.2.2 阶跃 ✔️ 15.3 非动画属性 文章目录 15.2 定时函数 Timing function…

一个开源的自托管虚拟浏览器项目,支持在安全、私密的环境中使用浏览器

大家好&#xff0c;今天给大家分享一个开源的自托管虚拟浏览器项目Neko&#xff0c;旨在利用 WebRTC 技术在 Docker 容器中运行虚拟浏览器&#xff0c;为用户提供安全、私密且多功能的浏览体验。 项目介绍 Neko利用 WebRTC 技术在 Docker 容器中运行虚拟浏览器&#xff0c;提供…

AW36518芯片手册解读(3)

接前一篇文章&#xff1a;AW36518芯片手册解读&#xff08;2&#xff09; 二、详述 3. 功能描述 &#xff08;1&#xff09;上电复位 当电源电压VIN降至预定义电压VPOR&#xff08;典型值为2.0V&#xff09;以下时&#xff0c;该设备会产生复位信号以执行上电复位操作&#x…

浅谈目前我开发的前端项目用到的设计模式

浅谈目前我开发的前端项目用到的设计模式 前言 设计模式很多&#xff0c;看到一个需求&#xff0c;项目&#xff0c;我们去开发的时候&#xff0c;肯定是做一个整体的设计进行开发&#xff0c;而在这次我项目中&#xff0c;我也做了一个整体的设计&#xff0c;为什么要设计&a…

线性规划中的几种逻辑表达式

线性规划中的几种逻辑表达式 注意&#xff1a; 摘录字刘博士的《数学建模与数学规划》&#xff0c; 以便用时可查。 实际上Gurobi API 中自身放啊变的逻辑表达式函数&#xff0c;下面列出自定义的实现方式。 1 逻辑与 如果 x 1 1 x_1 1 x1​1, x 2 1 x_2 1 x2​1, 那…

JVM对象分配内存如何保证线程安全?

大家好&#xff0c;我是锋哥。今天分享关于【JVM对象分配内存如何保证线程安全&#xff1f;】面试题。希望对大家有帮助&#xff1b; JVM对象分配内存如何保证线程安全&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 在JVM中&#xff0c;对象的内存分配…

Antd react上传图片格式限制

限制分辨率&#xff08;像素&#xff09; <a-upload :before-upload"beforeUpload">// 上传图片宽高比例限制const beforeUpload file > {return new Promise((resolve, reject) > {// // 图片类型限制// let isJpgOrPng file.type image/png || fil…

基于 iAP2 协议 的指令协议,用于对安防设备的 MCU 进行操作

协议设计目标 1. 安全性&#xff1a;通过 iAP2 协议与 MCU 设备进行安全通信。 2. 通用性&#xff1a;支持对安防设备的常见功能进行操作&#xff0c;如状态查询、设备控制、参数配置等。 3. 高效性&#xff1a;数据结构简洁清晰&#xff0c;易于解析和扩展。 4. 扩展性&#x…