小黑子—spring:第三章 AOP开发

news2024/12/28 19:15:08

spring入门3.0

  • 三 小黑子的springAOP开发
    • 1. AOP简介
      • 1.1 AOP的概念
      • 1.2 AOP思想的实现方案
      • 1.3 模拟AOP思想实现的基础代码
      • 1.4 AOP的相关概念
    • 2. 基于xml配置的AOP
      • 2.1 XML方式AOP快速入门
      • 2.2 XML方式AOP配置详解
      • 2.3 xml方式AOP的原理解析
        • 2.3.1 AOP底层两种生成Proxy的方式
    • 3. 基于注解配置的AOP
      • 3.1、注解方式AOP基本使用
      • 3.2 注解方式AOP配置详解
      • 3.3 注解方式AOP原理解析

三 小黑子的springAOP开发

1. AOP简介

1.1 AOP的概念

AOP,Aspect Oriented Programming,面向切面编程,是对面向对象编程OOP的升华。OOP是纵向对一个事物的抽象,一个对象包括静态的属性信息,包括动态的方法信息等。而AOP是横向的对不同事物的抽象,属性与属性、方法与方法、对象与对象都可以组成一个切面,而用这种思维去设计编程的方式叫做面向切面编程

再打比方地说面向切面就是:

有一台手机,用面向对象的思想去设计这个手机。手机内部有一些对应的属性,屏幕、硬件什么的都是静态属性,但是也有动态行为:打电话和照相等,把其封装成相应的方法。要打电话的方法,就能执行其功能。这个就是对各个事务进行一个纵向的抽象:这个事务包含哪些动态的行为、哪些静态的属性,都可以用方法和属性去表达。可是,在众多对象当中,可能要抽取每个对象当中的一部分功能,再临时组装成一个对象进行执行。

在这里插入图片描述

1.2 AOP思想的实现方案

代理技术
动态代理技术,在运行期间,对目标对象的方法进行增强,代理对象同名方法内可以执行原有逻辑的同时嵌入执行其他增强逻辑或其他对象的方法
在这里插入图片描述

1.3 模拟AOP思想实现的基础代码

其实在之前学习BeanPostProcessor时,在BeanPostProcessor的after方法中使用动态代理对Bean进行了增强,实际存储到单例池singleObjects中的不是当前目标对象本身,而是当前目标对象的代理对象Proxy,这样在调用目标对象方法时,实际调用的是代理对象Proxy的同名方法,起到了目标方法前后都进行增强的功能,对该方式进行一下优化,将增强的方法提取出去到一个增强类中,且只对com.itheima.service.impl包下的任何类的任何方法进行增强

public interface UserService {

    void show1();
    void show2();
}
public class UserServiceImpl implements UserService {
    @Override
    public void show1() {
        System.out.println("show1......");
    }

    @Override
    public void show2() {
        System.out.println("show2.......");
    }
}
//增强类,内部提供增强方法
public class MyAdvice {
    public void beforeAdvice(){
        System.out.println("前置的增强。。。");
    }

    public void afterAdvice(){
        System.out.println("后置的增强。。。");
    }
ublic class MockAopBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {

        //目的:岁UserServiceImpl中show1和show2方法进行增强,怎去方法存在于MyAdvice中
        //问题1:筛选service.impl包下的所有类的所有方法都可以进行增强,解决方案:if-else
        //问题2:MyAdvice怎么获取到?解决方案:从spring容器中获取MyAdvice
        if(bean.getClass().getPackage().getName().equals("com.itheima.service.impl")){
            //生成当前Bean的Proxy对象
            Object beanProxy = Proxy.newProxyInstance(
                    bean.getClass().getClassLoader(),
                    bean.getClass().getInterfaces(),
                    (Object proxy, Method method, Object[] args) -> {
                        //执行增强对象的before方法
                        MyAdvice myAdvice = applicationContext.getBean(MyAdvice.class);
                        myAdvice.beforeAdvice();
                        //执行目标对象的目标方法
                        Object result = method.invoke(bean, args);
                        //执行增强对象的after方法
                        myAdvice.afterAdvice();
                        return result;
                    }
            );
            return beanProxy;

        }

        return bean;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

    <bean id="userService" class="com.itheima.service.impl.UserServiceImpl"></bean>
    <bean id="myadvice" class="com.itheima.advice.MyAdvice"></bean>

    <bean class="com.itheima.MockAopBeanPostProcessor"></bean>
  • 测试
    @Test
    public void aopTest1(){
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = app.getBean(UserService.class);
        userService.show1();
    }

在这里插入图片描述

1.4 AOP的相关概念

概念单词解释
目标对象Target被增强的方法所在的对象
代理对象Proxy对目标对象进行增强后的对象,客户端实际调用的对象
连接点Joinpoint目标对象中可以被增强的方法
切入点Pointcut目标对象中实际被增强的方法
通知 \ 增强Advice增强部分的代码逻辑
切面Aspect增强和切入点的组合
织入Weaving将通知和切入点组合动态组合的过程

在这里插入图片描述

2. 基于xml配置的AOP

2.1 XML方式AOP快速入门

前面编写的AOP基础代码还是存在一些问题的,主要如下

        if(bean.getClass().getPackage().getName().equals("com.itheima.service.impl")){
            //生成当前Bean的Proxy对象
            Object beanProxy = Proxy.newProxyInstance(
                    bean.getClass().getClassLoader(),
                    bean.getClass().getInterfaces(),
                    (Object proxy, Method method, Object[] args) -> {
                        //执行增强对象的before方法
                        MyAdvice myAdvice = applicationContext.getBean(MyAdvice.class);
                        myAdvice.beforeAdvice();
                        //执行目标对象的目标方法
                        Object result = method.invoke(bean, args);
                        //执行增强对象的after方法
                        myAdvice.afterAdvice();
                        return result;
                    }
            );
                        return beanProxy;

缺点:

  • 被增强的包名在代码写死了
  • 通知对象的方法在代码中写死了

通过配置文件的方式去解决上述问题

  • 配置哪些包、哪些类、哪些方法需要被增强
  • 配置目标方法要被哪些通知方法所增强,在目标方法执行之前还是之后执行增强

配置方式的设计、配置文件(注解)的解析工作,Spring已经帮我们封装好了

xml方式配置AOP的步骤:

  1. 导入AOP相关坐标;
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.6</version>
</dependency>
  1. 准备目标类、准备通知类,并配置给Spring管理;
<!--    配置的目标类-->
    <bean id="userService" class="com.itheima.service.impl.UserServiceImpl"></bean>
<!--    配置的通知类-->
    <bean id="myadvice" class="com.itheima.advice.MyAdvice"></bean>
  1. 配置切点表达式(哪些方法被增强);
    配置切点表达式的时候,就要用到spring对应的一个命名空间,用到aop的标签

  2. 配置织入(切点被哪些通知方法增强,是前置增强还是后置增强)。

<?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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
">

<!--    配置的目标类-->
    <bean id="userService" class="com.itheima.service.impl.UserServiceImpl"></bean>
<!--    配置的通知类-->
    <bean id="myadvice" class="com.itheima.advice.MyAdvice"></bean>

<!--    <bean class="com.itheima.MockAopBeanPostProcessor"></bean>-->

<!--    aop配置-->
    <aop:config>
<!--        配置切点表达式,目的是要指定哪些方法被增强-->
        <aop:pointcut id="myPointcut" expression="execution(void com.itheima.service.impl.UserServiceImpl.show1())"/>
        <!--配置织入 目的:指定哪些切点与哪些通知结合-->
        <aop:aspect ref="myadvice">
            <aop:before method="beforeAdvice" pointcut-ref="myPointcut"></aop:before>

        </aop:aspect>
    </aop:config>

</beans>
  • 测试
@Test
    public void aopTest1(){
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = app.getBean(UserService.class);
        userService.show1();
    }

在这里插入图片描述

2.2 XML方式AOP配置详解

xml配置AOP的方式还是比较简单的,下面看一下AOP详细配置的细节:

  • 切点表达式的配置方式

    • 可以配置多个切点

      	<!--配置aop-->
      	<aop:config>
      	    <!--配置切点表达式 目的:指定哪些方法被增强-->
      	<aop:pointcut id="mtPointcut" expression="execution(void com.itheima.service.Impl.UserServiceImpl.show1())"/>
        <aop:pointcut id="mtPointcut2" expression="execution(void com.itheima.service.Impl.UserServiceImpl.show2())"/>
      	</aop:config>
      
    • pointcut属性可以再后面直接写上要结合的切点

      <!--配置aop-->
      <aop:config>
       <!--配置切点表达式 目的:指定哪些方法被增强-->
      	<aop:pointcut id="mtPointcut" expression="execution(void com.itheima.service.Impl.UserServiceImpl.show1())"/>
      	<!--配置织入 目的:指定哪些切点与哪些通知结合-->
      	<aop:aspect ref="myadvice">
      		<aop:before method="beforeAdvice" pointcut-ref="mtPointcut"></aop:before>
      		<aop:after method="afterAdvice" pointcut="execution(void com.itheima.service.Impl.UserServiceImpl.show1())">			</aop:after>
      </aop:aspect>
      </aop:config>
      
  • 切点表达式的配置语法

execution([访问修饰符]返回值类型 包名.类名.方法名(参数))

其中,

  • 访问修饰符可以省略不写;
  • 返回值类型、某一级包名、类名、方法名可以使用*表示任意;
  • 包名与类名之间使用单点.表示该包下的类,使用双点..表示该包及其子包下的类;
  • 参数列表可以使用两个点..表示任意参数。
//表示访问修饰符为public、无返回值、在com.itheima . aop包下的TargetImpl类的无参方法show
execution(public void com.itheima.aop.TargetImpl.show())
//表述com.itheima.aop包下的TargetImpl类的任意方法
execution(* com.itheima.aop.TargetImpl.*(..))
//表示com.itheima.aop包下的任意类的任意方法
execution(* com.itheima.aop.*.*(..))
//表示com.itheima. aop包及其子包下的任意类的任意方法
execution(* com.itheima.aop..*.*(..))
//表示任意包中的任意类的任意方法
execution(* *..*.*(..) )
  • 通知的类型
通知名称配置方式执行时机
前置通知<aop:before >目标方法执行之前执行
后置通知<aop:after-returning >目标方法执行之后执行,目标方法异常时,不在执行
环绕通知<aop:around >目标方法执行前后执行,目标方法异常时,环绕后方法不在执行
异常通知<aop:after-throwing >目标方法抛出异常时执行
最终通知<aop:after >不管目标方法是否有异常,最终都会执行
<?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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
">

<!--    配置的目标类-->
    <bean id="userService" class="com.itheima.service.impl.UserServiceImpl"></bean>
<!--    配置的通知类-->
    <bean id="myadvice" class="com.itheima.advice.MyAdvice"></bean>

<!--    <bean class="com.itheima.MockAopBeanPostProcessor"></bean>-->

<!--    aop配置-->
    <aop:config>
<!--        配置切点表达式,目的是要指定哪些方法被增强-->
<!--        <aop:pointcut id="myPointcut" expression="execution(void com.itheima.service.impl.UserServiceImpl.show1())"/>-->
        <aop:pointcut id="myPointcut2" expression="execution(* com.itheima.service.impl.*.*(..))"/>
        <!--配置织入 目的:指定哪些切点与哪些通知结合-->
        <aop:aspect ref="myadvice">
<!--            前置通知-->
<!--            <aop:before method="beforeAdvice" pointcut-ref="myPointcut2"></aop:before>-->
<!--&lt;!&ndash;          后置通知&ndash;&gt;-->
<!--            <aop:after-returning method="afterReturningAdvice" pointcut-ref="myPointcut2"></aop:after-returning>-->

<!--            环绕通知-->
            <aop:around method="around" pointcut-ref="myPointcut2"></aop:around>
<!--        异常抛出通知-->
            <aop:after-throwing method="afterThrowingAdvice" pointcut-ref="myPointcut2"></aop:after-throwing>
<!--        最终通知-->
            <aop:after method="afterAdvice" pointcut-ref="myPointcut2"></aop:after>
        </aop:aspect>
    </aop:config>

</beans>

通知方法再被调用时,Spring可以为其传递一些必要的参数

参数类型作用
JoinPoint连接点对象,任何通知都可使用,可以获得当前目标对象、目标方法参数等信息
ProceedingJoinPointJoinPoint子类对象,主要是在环绕通知中执行proceed(),进而执行目标方法
Throwable异常对象,使用在异常通知中,需要在配置文件中指出异常对象名称
public void 通知方法名称(JoinPoint joinPoint){
	//获得目标方法的参数
	system.out.println(joinPoint.getArgs()) ;
	//获得目标对象
	system.out.println(joinPoint.getTarget());
	//获得精确的切点表达式信息
	System.out.println(joinPoint.getstaticPart());
}
  • Throwable对象
public void afterThrowing (JoinPoint joinPoint, Throwable th) {
//获得异常信息
	System.out.println("异常对象是:"+th+"异常信息是:"+th.getMessage());
}
<aop:after-throwing method="afterThrowing" pointcut-ref="myPointcut" throwing="th"/>
  • AOP配置的两种方式

    • 使用<advisor>配置切面
    • 使用<aspect>配置切面

spring定义了一个Advice接口,实现了该接口的类都可以作为通知类出现

public interface Advice{
}

AOP配置的两种语法形式不同点:

  1. 语法形式不同:

    • advisor是通过实现接口来确认通知的类型
    • aspect是通过配置确认同的类型,更加灵活
  2. 科配置的切面数量不同:

    • 一个advisor只能配置一个固定通知和一个切点表达式
    • 一个aspect可以配置多个通知和多个切点表达式任意组合
  3. 使用场景不同

    • 允许随意搭配情况下可以使用aspect进行配置
    • 如果通知类型单一、切面单一的情况下可以使用advisor进行配置
    • 在通知类型已经固定,不用人为指定通知类型时,可以使用advisor进行配置,例如后面要学习的spring事务控制的配置

2.3 xml方式AOP的原理解析

xml方式AOP的原理解析

通过xml方式配置AOP时,引入AOP的命名空间,根据讲解,要去spring-aop包下的META-INF,再去找spring.handlers文件
在这里插入图片描述
最终加载的是AopNamespaceHandler,该Handler的init方法中注册了config标签对应的解析器

this.registerBeanDefinitionParser("config",new ConfigBeanDefinitionParser());

以ConfigBeanDefinitionParser作为入口进行源码剖析,最终会注册一个AspectJAwareAdvisorAutoProxyCreator进入到Spring容器中,那该类作用是什么呢?看一下集成体系图
在这里插入图片描述

AspectJAwareAdvisorAutoProxyCreator的上上级父类AbstractAutoProxyCreator中的postProcessAfterlnitialization方法

//参数bean:为目标对象
public Object postProcessAfterInitialization (@Nullable Object bean,String beanName) {
if (bean != nul1){
	Object cacheKey = this.getCacheKey (bean.getClass (), beanName);
	if(this.earlyProxyReferences.remove (cacheKey)!= bean){
	//如果需要被增强,则wrapIfNecessary方法最终返回的就是一个Proxy对象
		return this.wrapIfNecessary (bean,beanName,cacheKey);
	}
}
return bean;
}

通过断点方式观察,当bean是匹配切点表达式时,this.wraplfNecessary(bean, beanName, cacheKey)返回的是一个JDKDynamicAopProxy
在这里插入图片描述

可以在深入一点,对wraplfNecessary在剖析一下,看看是不是我们熟知的通过JDK的
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)的方式创建的代理对象呢? 经过如下一系列源码跟踪
在这里插入图片描述

2.3.1 AOP底层两种生成Proxy的方式

动态代理的实现的选择,在调用getProxy()方法时,我们可选用的AopProxy接口有两个实现类,如上图,这两种都是动态生成代理对象的方式,一种就是基于JDK的,一种是基于Cglib的

代理技术使用条件配置方式
JDK动态代理技术目标类有接口,是基于接口动态生成实现类的代理对象目标类有接口的情况下,默认方式
Cglib动态代理技术目标类无接口且不能使用final修饰,是基于被代理对象动态生成子对象为代理对象 目标类无接口时,默认使用该方式;目标类有接口时,手动配置<aop:config proxy-target-class="true”>强制使用Cglib方式

在这里插入图片描述

下面看Cglib基于超类的动态代理:

Target target = new Target() ;//目标对象
Advices advices = new Advices();//通知对象
Enhancer enhancer = new Enhancer();//增强器对象
enhancer.setSuperclass(Target.class);//增强器设置父类
//增强器设置回调
enhancer.setCallback((MethodInterceptor) (o,method,objects,methodProxy)->{
	advices.before ( ) ;
	object result = method.invoke(target,objects);
	advices.afterReturning();
	return result;
});
//创建代理对象
Target tagetProxy = (Target) enhancer.create();
//测试
String result = targetProxy.show("haohao");

3. 基于注解配置的AOP

3.1、注解方式AOP基本使用

Spring的AOP也提供了注解方式配置,使用相应的注解替代之前的xml配置,xml配置AOP时,我们主要配置了三部分:目标类被Sprina容器管理、通知类被Spring管理、通知与切点的织入(切面),如下:

<!--配置目标-->
<bean id="target" class="com.itheima.aop.TargetImpl"></bean>
<!--配置通知-->
<bean id="advices" class="com.itheima.aop.Advices"></bean>
<!--配置aop-->
<aop:config proxy-target-class="true">
	<aop:aspect ref="advices">
		<aop:around method="around" pointeut="execution(* com.itheima.aop.*.*(..))"/>
	</aop:aspect>
</aop:config>

配置aop,其实配置aop主要就是配置通知类中的哪个方法(通知类型)对应的切点表达式是什么
在这里插入图片描述

注解@Aspect@Around需要被Spring解析,所以在Spring核心配置文件中需要配置aspectj的自动代理

<!--使用注解配置AOP,需要开启AOP的自动代理-->
<aop:aspectj-autoproxy>

3.2 注解方式AOP配置详解

各种注解方式通知类型

//前置通知
@Before("execution( *com. itheima.aop.*.*(..))")
public void before(JoinPoint joinPoint){}
//后置通知
@AfterReturning("execution(* com.itheima.aop.*.*(..))")
public void afterReturning(JoinPoint joinPoint){}
//环绕通知
@Around("execution (* com.itheima.aop.*.*(..))")
public void around(ProceedingJoinPoint joinPoint) throws Throwable{}
//异常通知
@AfterThrowing("execution(* com.itheima.aop.*.*(..))")
public void afterThrowing(JoinPoint joinPoint){}
//最终通知
@After("execution(* com.itheima.aop.*.*(..))")
public void after(JoinPoint joinPoint){}
//增强类,内部提供增强方法
@Component
@Aspect
public class MyAdvice {

    //切点表达式的抽取
    @Pointcut("execution(* com.itheima.service.impl.*.*(..))")
    public void pointcut(){};

//    @Before("execution(* com.itheima.service.impl.*.*(..))")
    @Before("MyAdvice.pointcut()")
    public void before(){
        System.out.println("前置增强");
    }

    @AfterReturning("execution(* com.itheima.service.impl.*.*(..))")
//    @AfterReturning("MyAdvice.pointcut()")
    public void afterreturning(){
        System.out.println("后置增强");
    }

//    @Around("execution(* com.itheima.service.impl.*.*(..))")
    @Around("MyAdvice.pointcut()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("环绕前增强。。。");
        Object proceed = proceedingJoinPoint.proceed();//执行目标方法
        JoinPoint.StaticPart staticPart = proceedingJoinPoint.getStaticPart();
        Object target = proceedingJoinPoint.getTarget();
        System.out.println("表达式:"+staticPart);
        System.out.println("当前目标对象为:"+target);
        System.out.println("环绕后增强。。。");
        return proceed;
    }

    //@AfterThrowing("execution(* com.itheima.service.impl.*.*(..))")
    @AfterThrowing("MyAdvice.pointcut()")
    public void afterThrowAdvice(){
        System.out.println("异常抛出通知。。。报异常时执行");
    }

    //@After("execution(* com.itheima.service.impl.*.*(..))")
    @After("MyAdvice.pointcut()")
    public void after(){
        System.out.println("最终增强");
    }
}
  • 使用配置类替代进行spring的配置
@Configuration
@ComponentScan("com.itheima")//<context:component-scan base-package="com.itheima"/>
@EnableAspectJAutoProxy//<aop:aspectj-autoproxy/>
public class SpringConfig {
}
  • 测试
    @Test
    public void aopTest2(){
        ApplicationContext app2 = new AnnotationConfigApplicationContext(SpringConfig.class);
        UserService userService = app2.getBean(UserService.class);
        userService.show2();
    }
@Service("service")
public class UserServiceImpl implements UserService {
    @Override
    public void show1() {
        System.out.println("show1......");
    }

    @Override
    public void show2() {
        System.out.println("show2.......");
//        int i = 1/0;
    }
}

在这里插入图片描述

3.3 注解方式AOP原理解析

注解方式AOP原理解析

之前在使用xml配置AOP时,是借助的Spring的外部命名空间的加载方式完成的,使用注解配置后,就抛弃了<aop.config>标签,而该标签最终加载了名为AspectJAwareAdvisorAutoProxyCreatorBeanPostProcessor ,最终,在该BeanPostProcessor中完成了代理对象的生成。

同样,从aspectj-autoproxy标签的解析器入手

this.registerBeanDefinitionParser("aspectj-autoproxy",new AspectJAutoProxyBeanDefinitionParser());

之前在使用xml配置AOP时,是借助的Spring的外部命名空间的加载方式完成的,使用注解配置后,就抛弃了<aop:config>标签,而该标签最终加载了名为AspectJAwareAdvisorAutoProxyCreatorBeanPostProcessor,最终,在该BeanPostProcessor中完成了代理对象的生成。

<!--开启aop aspectj的自动代理-->
<aop:aspectj-autoproxy/>

同样,从aspectj-autoproxy标签解析器入手

this.registerBeanDefinitionParser("aspectj-autoproxy",new AspectJAutoProxyBeanDefinitionParser());

而AspectJAutoProxyBeanDefinitionParser代码内部,最终也是执行了和xml方式AOP一样的代码

registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class,register,source);
  • 如果使用的是核心配置类的话

    	@Configuration
    	@ComponentScan("com.itheima.aop")
    	@EnableAspectJAutoProxy
    	public class ApplicationContextConfig(){
    	}
    
  • 查看@EnableAspectAutoProxy源码,使用的也是@Import导入相关解析类

    	@Target({ElementType.TYPE})
    	@Retention(RetentionPolicy.RUNTIME)
    	@Documented
    	@Import({AspectJAutoProxyRegistrar.class})
    	public @interface EnableAspectJAutoProxy {
    		boolean proxyTargetClass() default false;
    		boolean exposeProxy() default false;
    		}
    
  • 使用@Import导入的AspectJAutoProxyRegistrar源码,一路追踪下去,最终还是注册了AnnotationAwareAspectJAutoProxyCreator这个类
    在这里插入图片描述

在这里插入图片描述

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

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

相关文章

一个老旧优盘从2M变回8G的逆袭之路

前言 最近收拾资料&#xff0c;发现了一个比较老的优盘&#xff0c;上面标记8G内存&#xff0c;就好奇里边存了点啥。用电脑打开&#xff0c;啥内容都没有&#xff0c;结果大小还显示2M&#xff1f;&#xff1f;&#xff1f;看看今天能不能救活吧。 正文 步骤一、清空磁盘 …

并发安全问题之超卖问题

并发安全问题之超卖问题 乐观锁总结&#xff1a; 优点&#xff1a;不加锁性能好。 缺点&#xff1a;同时请求成功率低&#xff08;即只要发现数据变了就放弃了&#xff09;。 乐观锁思想的具体体现&#xff1a;一共两步&#xff0c;第一步&#xff0c;先查询状态。第二步&…

Java VMTranslator Part I

目录 堆栈运算命令 基本思路 核心代码 Parser Code Writer Main 实验结果&#xff0c;使用SimpleAdd、StackTest进行验证 内存访问命令 基本思路 核心代码 Parser Code Writer Main 实验结果&#xff0c;使用进行验证。对比生成的二进制代码文件。 用Java写一个翻…

MySQL6:索引使用原则,联合索引,联合主键/复合主键,覆盖索引、什么是回表?索引条件下推,索引的创建与使用,索引的创建与使用,索引失效

MySQL6&#xff1a;索引使用原则&#xff0c;联合索引&#xff0c;联合主键/复合主键&#xff0c;覆盖索引、什么是回表&#xff1f;索引条件下推&#xff0c;索引的创建与使用&#xff0c;索引的创建与使用&#xff0c;索引失效 索引使用原则列的离散(sdn)度 联合索引创建联合…

【C++初探:简单易懂的入门指南】二

【C初探&#xff1a;简单易懂的入门指南】二 1.引用1.1引用做函数的参数1.2 引用做返回值1.2.1 关于引用做返回值的几点补充 1.3 多引用(对一个变量取多个别名)1.4 引用类型一致性原则以及权限的问题阐述1.5引用的效率问题1.6引用和指针的比较 2.auto关键字2.1 auto关键字的使用…

BSTree二叉树讲解

二叉搜索树的概念&#xff1a; 二叉搜索树又称二叉排序树&#xff0c;它或者是一棵空树&#xff0c;或者是具有以下性质的二叉树: 若它的左子树不为空&#xff0c;则左子树上所有节点的值都小于根节点的值 若它的右子树不为空&#xff0c;则右子树上所有节点的值…

重置 VCSA 6.7 root密码和SSO密码

原贴地址&#xff1a;https://www.cnblogs.com/airoot/p/16059033.html 问题描述 1、用root用户登录 VMware vCenter Server Appliance虚拟机失败&#xff0c;无法登录 2、vCenter Server Appliance 6.7 U1的root帐户错误尝试次数超过3次已锁定或帐户已过期 官方说明 在VC…

【Spring Boot 源码学习】RedisAutoConfiguration 详解

Spring Boot 源码学习系列 RedisAutoConfiguration 详解 引言往期内容主要内容1. Spring Data Redis2. RedisAutoConfiguration2.1 加载自动配置组件2.2 过滤自动配置组件2.2.1 涉及注解2.2.2 redisTemplate 方法2.2.3 stringRedisTemplate 方法 总结 引言 上篇博文&#xff0…

【C++基础入门】44.C++中对象模型分析(上)

一、回归本质 class 是一种特殊的 struct 在内存中 class 依旧可以看作变量的集合class 与 struct 遵循相同的内存对齐规则class 中的成员函数与成员变量是分开存放的 每个对象有独立的成员变量所有对象共享类中的成员函数值得思考的问题 下面看一个对象内存布局的代码&#x…

Go学习第十七章——Gin中间件与路由

Go web框架——Gin中间件与路由 1 单独注册中间件1.1 入门案例1.2 多个中间件1.3 中间件拦截响应1.4 中间件放行 2 全局注册中间件3 自定义参数传递4 路由分组4.1 入门案例4.2 路由分组注册中间件4.3 综合使用 5 使用内置的中间件6 中间件案例权限验证耗时统计 1 单独注册中间件…

Java项目之网络考试系统

视频教程&#xff1a; 01-创建数据库_哔哩哔哩_bilibili 源码下载&#xff1a;百度网盘 请输入提取码 准备工作 创建数据库配置IDEA后端导入前端 前言&#xff1a; 把代码掰开写进博客里&#xff0c;主要是让自己在整理笔记的过程中&#xff0c;多去思考完成这个功能的核心…

基于深度学习的单图像人群计数研究:网络设计、损失函数和监控信号

摘要 https://arxiv.org/pdf/2012.15685v2.pdf 单图像人群计数是一个具有挑战性的计算机视觉问题,在公共安全、城市规划、交通管理等领域有着广泛的应用。近年来,随着深度学习技术的发展,人群计数引起了广泛的关注并取得了巨大的成功。通过系统地回顾和总结2015年以来基于深…

rust学习——智能指针Rc

文章目录 Rc 与 ArcRcRc::clone观察引用计数的变化不可变引用一个综合例子Rc 简单总结 多线程无力的 RcArcArc 的性能损耗 总结 Rc 与 Arc Rust 所有权机制要求一个值只能有一个所有者&#xff0c;在大多数情况下&#xff0c;都没有问题&#xff0c;但是考虑以下情况&#xff1…

二维码智慧门牌管理系统升级解决方案:采集要素为智慧城市建设提供精准数据支持

文章目录 前言一、二维码智慧门牌管理系统的升级需求二、采集要素在系统升级中的应用三、消防栓、井盖等采集要素的应用 前言 随着城市化进程的加速&#xff0c;智慧城市的建设已成为未来城市发展的必然趋势。其中&#xff0c;二维码智慧门牌管理系统作为智慧城市的重要组成部…

基于Spring Boot的大学课程排课系统设计与实现

摘 要 大学课程排课是现代教育管理中重要的一环。目前&#xff0c;传统的排课方式已经无法满足日益增长的课程需求和学生个性化的诉求。因此&#xff0c;研究一种基于遗传算法的大学课程排课系统是非常必要的。本研究旨在开发一种基于SpringBoot Vue的大学课程排课系统&#x…

【Java 进阶篇】在Java Web应用中获取ServletContext对象详解

在Java Web应用开发中&#xff0c;ServletContext对象扮演着重要的角色&#xff0c;它允许你在整个Web应用程序中存储和共享数据。ServletContext对象是Servlet容器提供的一种用于管理Web应用程序的全局信息的方式。本文将详细探讨ServletContext对象的概念、用途以及如何在Jav…

算法笔记【8】-合并排序算法

文章目录 一、前言二、合并排序算法基本原理三、实现步骤四、优缺点分析 一、前言 合并排序算法通过采用分治策略和递归思想&#xff0c;实现了高效、稳定的排序功能。本文将深入探讨合并排序算法的原理、实现步骤&#xff0c;并讨论其优缺点。 二、合并排序算法基本原理 合…

AntDB数据库荣获 “2023年信创物联网优秀服务商”

日前&#xff0c;在2023世界数字经济大会暨第十三届智博会 2023京甬信创物联网产融对接会上&#xff0c;AntDB数据库再获殊荣&#xff0c;获评“2023年信创物联网优秀服务商”。 图1&#xff1a;2023年信创物联网优秀服务商颁奖现场 信创物联网是信息技术应用创新与物联网的结…

网络爬虫入门导学

一、内容组织 2、常用的python IDE工具 比较推荐以下几种&#xff1a; 其中IDLE是python自带的/默认的/常用的/入门级编写工具&#xff0c;包含交互式和文件式 适用于&#xff1a;简单直接/入门级/代码不超过300行 Sublime Text是专为程序员开发的第三方专用编程工具&#xff…

OPNET <<< Program Abort >>> Standard function stack imbalance

OPNET <<< Program Abort >>> Standard function stack imbalance OPNET 问题原因及解决办法 OPNET 问题 OPNET仿真时遇到此问题&#xff1a; <<< Program Abort >>> Standard function stack imbalance 原因及解决办法 出现此问题是因…