Spring框架的扩展点

Spring框架是一个非常流行的Java应用程序框架,它提供了一系列的扩展点,使得开发者可以自定义和增强框架的功能。这些扩展点包括:

Bean生命周期回调

Spring允许通过实现特定的接口或使用注解来在Bean的生命周期的不同阶段(如初始化或销毁)插入自定义逻辑。主要的接口有InitializingBeanDisposableBean,注解方式主要是@PostConstruct@PreDestroy

在Spring框架中,Bean的生命周期是由多个阶段组成的,从Bean的定义被加载到Spring容器中,到Bean的初始化、使用和最终的销毁。Spring提供了多种方式来允许开发者在这些生命周期阶段中插入自定义逻辑。下面详细解释两种主要的方法:通过实现特定的接口和使用注解。

通过接口实现

  1. InitializingBean接口

    • 作用:当Bean的所有属性都被Spring容器设置完毕后,afterPropertiesSet()方法会被自动调用,这个方法用于Bean的初始化逻辑。
    • 使用场景:适用于Bean需要执行某些操作来完成设置过程,比如检查强制属性是否已经设置,或者需要根据属性值初始化一些内部状态。
  2. DisposableBean接口

    • 作用:在Spring容器销毁Bean之前,destroy()方法会被调用,这里可以放置清理资源的代码,比如关闭文件流或数据库连接等。
    • 使用场景:适用于Bean需要在销毁前释放资源或执行其他清理任务。

通过注解方式

Spring还提供了基于注解的方式来处理Bean的生命周期事件,这通常比实现接口更灵活且便于使用。

  1. @PostConstruct注解

    • 作用:该注解用于修饰一个非静态的void()方法,用于初始化方法。在Bean的属性被设置之后,且InitializingBeanafterPropertiesSet方法之前调用。
    • 使用场景:适用于执行初始化后的配置检查或初始化那些依赖于注入完毕的属性的组件。
  2. @PreDestroy注解

    • 作用:在Spring容器销毁Bean之前调用,用于清理资源。
    • 使用场景:当Bean需要在销毁前进行一些清理工作,如关闭网络连接或释放其他资源时使用。

使用示例

import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.DisposableBean;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;public class MyBean implements InitializingBean, DisposableBean {private String someProperty;// 使用接口方式@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("Initializing bean");// 初始化代码}@Overridepublic void destroy() throws Exception {System.out.println("Destroying bean");// 清理代码}// 使用注解方式@PostConstructpublic void init() {System.out.println("Post Construct method called");// 更多初始化代码}@PreDestroypublic void cleanup() {System.out.println("Pre Destroy method called");// 更多清理代码}// getter和setter方法public void setSomeProperty(String someProperty) {this.someProperty = someProperty;}public String getSomeProperty() {return someProperty;}
}

通过上述方法,Spring的使用者可以非常灵活地控制Bean的生命周期,确保资源的高效使用和优雅的释放。

Bean后处理器

实现BeanPostProcessor接口可以在Spring容器的Bean创建之后进行额外的处理。这允许开发者在Bean初始化前后添加自定义逻辑。

BeanPostProcessor 是 Spring 框架中的一个强大功能,它允许开发者在 Spring 容器的 Bean 初始化过程中的两个关键点插入自定义逻辑:初始化前初始化后。这个接口定义了两个方法,postProcessBeforeInitializationpostProcessAfterInitialization,开发者可以通过实现这个接口来修改或包装 Spring 管理的 Bean。

实现 BeanPostProcessor 接口

  1. postProcessBeforeInitialization(Object bean, String beanName)

    • 作用:在任何 Bean 初始化回调(如 @PostConstructafterPropertiesSet)之前调用。
    • 返回值:可以返回原始的 Bean,也可以返回一个要使用的替代版本。
    • 使用场景:用于在 Bean 的初始化之前执行一些预处理操作,例如检查 Bean 的某些属性,设置默认值,或者基于特定条件动态地改变 Bean。
  2. postProcessAfterInitialization(Object bean, String beanName)

    • 作用:在 Bean 的初始化后立即调用,即所有初始化回调之后。
    • 返回值:与 postProcessBeforeInitialization 一样,可以返回原始 Bean 或修改后的版本。
    • 使用场景:适用于需要在 Bean 完全初始化后执行的后处理操作。常用于包装 Bean,例如通过动态代理增加额外的行为。

使用示例

下面是一个简单的 BeanPostProcessor 实现示例,该示例展示了如何在初始化前后打印 Bean 的名称。

 
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;@Component
public class MyBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) {System.out.println("Before Initialization : " + beanName);return bean;  // 可以返回任何对象,这里返回原始Bean}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) {System.out.println("After Initialization : " + beanName);return bean;  // 可以对Bean进行增强或修改,并返回}
}

注意事项

  • Bean处理的顺序:如果有多个 BeanPostProcessor,它们将按照定义的顺序应用。这一点在设计系统时需要特别注意,以确保 Bean 的处理顺序符合预期。
  • 对 Bean 的影响:实现 BeanPostProcessor 时,要注意不要在 postProcessBeforeInitialization 方法中进行太重的处理,因为这可能会影响整个应用的启动性能。
  • 应用场景BeanPostProcessor 是一个非常强大的工具,可以用于许多高级的 Spring 功能,如安全框架、远程访问框架等。

通过这种方式,BeanPostProcessor 提供了一个非常灵活的机制来干预和修改 Spring 容器管理的 Bean 的生命周期和行为。

Bean定义注册后处理器

通过实现BeanDefinitionRegistryPostProcessor接口,可以在标准初始化之前修改应用程序上下文的bean定义数据。

BeanDefinitionRegistryPostProcessor 是 Spring 框架中的一个高级特性,它继承自 BeanFactoryPostProcessor。这个接口允许开发者在常规的 Bean 初始化之前,对 Bean 的定义(BeanDefinition)进行修改或添加。实现这个接口的类可以直接操作 Bean 定义的注册表,这为动态修改 Bean 定义提供了极大的灵活性。

BeanDefinitionRegistryPostProcessor 的主要方法

  1. postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)

    • 作用:这个方法允许在所有常规 Bean 定义加载之后,但在 Bean 实例化之前,对 Bean 定义进行修改或添加新的定义。
    • 使用场景:用于动态添加更多的 Bean 到 Spring 容器,或者根据某些条件修改已有的 Bean 定义。例如,可以基于某些外部配置动态决定是否注册某个 Bean。
  2. postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)

    • 作用:这是从 BeanFactoryPostProcessor 继承来的方法,允许在所有 Bean 定义加载之后,但在 Bean 被实例化之前,对整个 Bean 工厂进行配置。
    • 使用场景:适用于需要修改 Bean 工厂的配置或整体修改 Bean 定义的情况。

使用示例

以下是一个实现 BeanDefinitionRegistryPostProcessor 接口的示例,演示如何动态注册一个新的 Bean 定义:

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.BeanDefinitionRegistry;
import org.springframework.beans.factory.config.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.stereotype.Component;@Component
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {// 动态注册新的Bean定义RootBeanDefinition beanDefinition = new RootBeanDefinition(MyNewBean.class);registry.registerBeanDefinition("myNewBean", beanDefinition);}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {// 在这里可以进行Bean工厂的配置修改System.out.println("Configuring bean factory");}
}public class MyNewBean {// 这是一个示例Bean,可以包含具体的业务逻辑
}

注意事项

  • 执行顺序BeanDefinitionRegistryPostProcessorpostProcessBeanDefinitionRegistry 方法会在 BeanFactoryPostProcessorpostProcessBeanFactory 方法之前执行。这是因为你需要首先注册所有的 Bean 定义,之后才能进行进一步的处理。
  • 影响范围:通过这个接口修改的 Bean 定义将影响整个应用上下文中的所有 Bean。这种改变是全局性的,因此使用时需要小心。

通过使用 BeanDefinitionRegistryPostProcessor,开发者可以非常灵活地控制 Spring 应用上下文中的 Bean 定义,这对于需要动态调整 Spring 配置的复杂应用或基础设施组件特别有用。

属性编辑器

自定义PropertyEditor,这可以在Spring将配置文件中的字符串转换为复杂对象时使用。

在Spring框架中,PropertyEditor 是用于实现属性类型转换的机制。这个机制允许开发者定义如何将字符串转换成复杂的对象或者从一种类型转换成另一种类型。这在处理Spring配置文件(如 XML 或注解配置)中的属性时非常有用,因为它允许开发者自定义如何解析和注入属性值。

使用场景

PropertyEditor 主要用于以下场景:

  • 自定义类型转换:当你需要将配置文件中的字符串转换为自定义的复杂类型时。
  • 数据格式化:比如日期、时间格式的解析和转换。
  • 兼容性处理:允许旧版系统通过特定方式解析配置数据。

实现自定义 PropertyEditor

实现自定义 PropertyEditor 通常涉及以下步骤:

  1. 继承 java.beans.PropertyEditorSupport:这个类提供了 PropertyEditor 接口的默认实现,你可以覆盖必要的方法来实现自定义逻辑。
  2. 重写 setAsText(String text) 方法:这是最关键的方法,用于将输入的字符串转换成相应的对象。
  3. 重写 getAsText() 方法:这个方法应返回一个可以表示内部属性值的字符串,通常用于UI显示或调试。

示例代码

假设你有一个自定义类 Coordinates,它包含纬度和经度信息,你需要从一个格式为 "纬度,经度" 的字符串中解析它:

 
import java.beans.PropertyEditorSupport;public class CoordinatesEditor extends PropertyEditorSupport {@Overridepublic void setAsText(String text) throws IllegalArgumentException {if (text == null || text.isEmpty()) {setValue(null);} else {String[] parts = text.split(",");if (parts.length != 2) {throw new IllegalArgumentException("Invalid format for Coordinates. Correct format is 'latitude,longitude'.");}try {double latitude = Double.parseDouble(parts[0]);double longitude = Double.parseDouble(parts[1]);Coordinates value = new Coordinates(latitude, longitude);setValue(value);} catch (NumberFormatException e) {throw new IllegalArgumentException("Coordinates must be valid numbers.", e);}}}@Overridepublic String getAsText() {Coordinates coordinates = (Coordinates) getValue();return coordinates != null ? coordinates.getLatitude() + "," + coordinates.getLongitude() : "";}
}

在Spring中注册自定义 PropertyEditor

在Spring配置中注册自定义 PropertyEditor 可以通过几种方式实现,最常见的是在配置类中使用 CustomEditorConfigurer

 
import org.springframework.beans.factory.config.CustomEditorConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class AppConfig {@Beanpublic CustomEditorConfigurer editorConfigurer() {CustomEditorConfigurer configurer = new CustomEditorConfigurer();Map<Class<?>, Class<? extends PropertyEditor>> customEditors = new HashMap<>();customEditors.put(Coordinates.class, CoordinatesEditor.class);configurer.setCustomEditors(customEditors);return configurer;}
}

通过上述方式,当Spring框架遇到需要将字符串转换为 Coordinates 类型的属性时,会使用你定义的 CoordinatesEditor 来进行转换。这样,你就可以灵活地控制属性的解析和转换过程。

自定义事件

Spring支持使用应用事件来进行组件之间的通信。开发者可以创建和发布自定义事件,这些事件可以被任何实现了ApplicationListener接口的Bean捕获。

Spring框架的事件发布机制是一种基于观察者模式的功能,允许各个组件之间进行松散耦合的通信。通过这种机制,一个组件可以发布事件,而其他组件可以监听并响应这些事件。这对于处理应用程序中的各种状态变化或者重要的业务事件非常有用。

创建自定义事件

要创建一个自定义事件,你需要继承 ApplicationEvent 类。这个类存储事件的状态并提供了获取事件源的方法。

 
import org.springframework.context.ApplicationEvent;public class CustomEvent extends ApplicationEvent {private String message;public CustomEvent(Object source, String message) {super(source);this.message = message;}public String getMessage() {return message;}
}

在上述例子中,CustomEvent 类包含一个字符串消息,它继承了 ApplicationEvent 类并通过构造函数传递事件的源对象和消息内容。

发布事件

要发布事件,你需要在组件中注入 ApplicationEventPublisher 接口,并使用它来发布事件。这个接口由 Spring 容器提供,可以通过自动装配来注入。

 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;@Component
public class EventPublisherBean {@Autowiredprivate ApplicationEventPublisher publisher;public void publish(String message) {CustomEvent customEvent = new CustomEvent(this, message);publisher.publishEvent(customEvent);}
}

在这个例子中,EventPublisherBean 类使用 ApplicationEventPublisher 发布 CustomEvent。任何时候调用 publish 方法都会触发一个新的事件。

监听事件

要监听这些事件,你可以实现 ApplicationListener 接口或者使用 @EventListener 注解。这两种方法都可以根据你的偏好和具体情况来选择使用。

使用 ApplicationListener 接口
 
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;@Component
public class CustomEventListener implements ApplicationListener<CustomEvent> {@Overridepublic void onApplicationEvent(CustomEvent event) {System.out.println("Received custom event - " + event.getMessage());}
}

使用 @EventListener 注解

如果你更喜欢使用注解而非实现接口的方式,可以使用 @EventListener,这使得代码更简洁。

 
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;@Component
public class AnnotatedEventListener {@EventListenerpublic void handleCustomEvent(CustomEvent event) {System.out.println("Received custom event - " + event.getMessage());}
}

Spring的事件机制提供了一种强大的方式来促进应用组件之间的交流,而无需组件之间有直接的依赖关系。这不仅有助于降低组件间的耦合度,还能够使事件的处理逻辑更加清晰。通过自定义事件、事件发布者和事件监听者,开发者可以灵活地处理应用中的多种业务场景。

自定义注解

Spring允许开发者创建自定义注解,并通过实现BeanPostProcessor或其他机制来处理这些注解,从而添加特定的逻辑。

在Spring框架中,自定义注解是一个非常强大的特性,允许开发者定义新的注解并在应用程序的不同层次和组件上应用它们,从而增强应用程序的功能和灵活性。通过结合 BeanPostProcessor 或其他Spring提供的扩展点,你可以在运行时处理这些注解,执行特定的业务逻辑。

创建自定义注解

自定义注解的创建涉及定义注解的本身,它的保留策略(RetentionPolicy),以及它的作用目标(ElementType)。

 
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.METHOD) // 可以指定注解用在方法上
@Retention(RetentionPolicy.RUNTIME) // 注解在运行时可用,以便反射机制读取
public @interface MyCustomAnnotation {String value() default "default value"; // 为注解定义属性
}

在上述代码中,MyCustomAnnotation 注解被定义为可用于方法,并在运行时可用。value 属性允许在使用注解时指定一个字符串。

使用 BeanPostProcessor 处理注解

BeanPostProcessor 提供了一种方式来在Spring容器的bean生命周期的特定点进行操作。这可以用来检查bean的注解并执行相应的逻辑。

 
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;@Component
public class MyAnnotationBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {Method[] methods = bean.getClass().getDeclaredMethods();for (Method method : methods) {MyCustomAnnotation annotation = method.getAnnotation(MyCustomAnnotation.class);if (annotation != null) {// 对有MyCustomAnnotation注解的方法执行某些操作System.out.println("Method " + method.getName() + " has annotation with value: " + annotation.value());}}return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {return bean;}
}

在这个例子中,MyAnnotationBeanPostProcessor 类实现了 BeanPostProcessor 接口。它在初始化前检查每个bean的方法,看是否有方法被 MyCustomAnnotation 注解标记,并执行一些逻辑。

整合和应用

你可以将这个注解应用到任何Spring管理的bean的方法上:

 
import org.springframework.stereotype.Component;@Component
public class SomeBean {@MyCustomAnnotation(value = "特定值")public void someMethod() {// 方法实现}
}

通过自定义注解和相应的处理器,你可以为Spring应用添加灵活且强大的行为定制,使得你可以在不修改现有代码结构的情况下,注入新的逻辑或行为。这种方法非常适用于那些需要高度可配置且可扩展性强的应用程序。

AOP(面向切面编程)

Spring的AOP功能允许开发者定义方法拦截器(advice)和切点(pointcuts),用于执行横切关注点(如日志、事务管理等)。

在Spring框架中,面向切面编程(AOP)是一种编程范式,它允许开发者将某些功能模块化,并将这些模块(通常是跨多个点的关注点或横切关注点,例如日志记录、事务管理等)应用于程序的其他部分,而不需要修改这些部分的代码。AOP通过定义切点和通知(advice),可以在不触碰主业务逻辑的情况下,增强功能。

核心概念

  1. 切点(Pointcut):定义了何处执行切面的“位置”,即在哪些类或方法上应用切面的规则。
  2. 通知(Advice):定义了切面的“什么”和“何时”执行,即在目标方法执行的哪个阶段(如方法调用前、调用后、方法抛出异常后等)执行何种逻辑。
  3. 连接点(Join Point):在程序执行过程中可以插入切面的点,如方法调用或属性访问。在Spring AOP中,连接点总是表示方法的执行。
  4. 切面(Aspect):通常是一个类,定义了切点和通知的组合。

AOP的通知类型

  • 前置通知(Before advice):在方法执行之前运行的通知。
  • 后置通知(After returning advice):在方法成功执行后运行的通知。
  • 异常通知(After throwing advice):在方法抛出异常退出时执行的通知。
  • 最终通知(After (finally) advice):无论方法通过何种方式结束,都将执行的通知。
  • 环绕通知(Around advice):在方法执行前后都可以执行的通知,能在方法调用前后完成自定义的行为,甚至可以决定是否执行方法。

使用Spring AOP的示例

以下是一个简单的Spring AOP示例,展示了如何实现一个日志切面:

 
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;@Aspect
public class LoggingAspect {// 定义切点表达式@Pointcut("execution(* com.example.service.*.*(..))")private void selectAllMethods() {}// 前置通知@Before("selectAllMethods()")public void beforeAdvice(JoinPoint joinPoint) {System.out.println("Before method:" + joinPoint.getSignature().getName());}// 环绕通知@Around("selectAllMethods()")public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {System.out.println("Before method execution");Object returnValue = joinPoint.proceed();System.out.println("After method execution");return returnValue;}
}

在上面的示例中,我们定义了一个切面 LoggingAspect,它包含一个切点和两个通知(一个前置通知和一个环绕通知)。切点表达式指定了切面将应用于 com.example.service 包下所有类的所有方法。

配置

在Spring Boot应用中,你需要启用AOP支持,这通常通过添加 @EnableAspectJAutoProxy 注解来实现:

 
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
}

通过使用Spring AOP,开发者可以将应用程序中的关注点分离,使得代码更加模块化,易于管理和维护。这对于日志记录、事务管理、安全性控制等功能尤为重要,因为这些功能通常是横切多个点的。

自定义作用域

虽然Spring默认支持单例(singleton)和原型(prototype)等作用域,但开发者也可以通过实现Scope接口来定义新的作用域。

在Spring框架中,作用域(Scope)定义了Spring容器如何创建和管理Bean的生命周期。Spring默认提供了几种作用域,如单例(singleton)、原型(prototype)、请求(request)、会话(session)和全局会话(global session)。这些默认的作用域覆盖了大多数常见的使用场景。然而,有时候开发者可能需要更具体的控制或者根据特定条件来管理Bean的生命周期,这时就可以通过实现自定义作用域来实现。

实现 Scope 接口

要创建一个自定义的作用域,你需要实现Spring的 Scope 接口。这个接口包含几个方法,每个方法都是管理Bean生命周期的关键部分:

  • Object get(String name, ObjectFactory<?> objectFactory):返回给定名称的当前作用域的对象。如果对象不存在,则使用提供的 ObjectFactory 创建这个对象。
  • Object remove(String name):从当前作用域中移除给定名称的对象,并返回它。如果找不到对象,返回 null
  • void registerDestructionCallback(String name, Runnable callback):注册一个回调,当给定名称的对象从作用域中移除时执行。
  • Object resolveContextualObject(String key):解析在当前作用域中特定的上下文数据。例如,可以用于解析与当前请求相关的数据。
  • String getConversationId():返回当前作用域的对话ID,这个ID可以是任何字符串,标识作用域的当前状态,通常用于区分不同的会话或请求。

示例:实现一个简单的自定义作用域

以下是一个自定义作用域的示例,这个作用域在应用程序的生命周期内只创建一个Bean的实例,但可以在需要时重置这个实例(例如,基于某个特定条件)。

 
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;public class SimpleCustomScope implements Scope {private Object instance;private String name = "simpleCustom";@Overridepublic Object get(String name, ObjectFactory<?> objectFactory) {if (instance == null) {instance = objectFactory.getObject();}return instance;}@Overridepublic Object remove(String name) {Object oldInstance = instance;instance = null;return oldInstance;}@Overridepublic void registerDestructionCallback(String name, Runnable callback) {// 对于简单实现,可以忽略销毁回调}@Overridepublic Object resolveContextualObject(String key) {return null;}@Overridepublic String getConversationId() {return name;}
}

在Spring配置中注册自定义作用域

创建完自定义作用域后,需要在Spring配置中注册这个作用域,以便Spring容器能够识别和使用它。

 
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Configuration;@Configuration
public class AppConfig {@AutowiredConfigurableBeanFactory beanFactory;@PostConstructpublic void registerCustomScope() {beanFactory.registerScope("simpleCustom", new SimpleCustomScope());}
}

在这个配置类中,registerCustomScope 方法在Bean工厂中注册了自定义作用域。这样,你就可以在Spring管理的Bean上使用新的作用域:

 
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;@Component
@Scope("simpleCustom")
public class ScopedBean {// Bean的实现
}

通过实现 Scope 接口,开发者可以创建高度定制的作用域,以满足特定的业务需求。这为Spring应用提供了极大的灵活性和控制力,可以根据不同的应用场景动态管理Bean的生命周期。

这些扩展点让Spring非常灵活,可以适用于各种不同的开发需求。通过使用这些扩展点,开发者可以高度定制Spring的行为,以适应特定的项目需求。

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

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

相关文章

深入理解 ICMP 协议

目录 前言 1. 概述 特性与功能 报文封装与格式 2. ICMP差错报告 3. ICMP查询 4. ICMP应用 总结 前言 ICMP&#xff08;Internet Control Message Protocol&#xff09;是互联网控制报文协议&#xff0c;是TCP/IP协议族中的一个重要组成部分。作为网络层的协议之一&#…

保研面试408复习 2——操作系统、计网

文章目录 1、操作系统一、进程、线程的概念以及区别&#xff1f;二、进程间的通信方式&#xff1f; 2、计算机网络一、香农准则二、协议的三要素1. 语法2. 语义3. 时序 标记文字记忆&#xff0c;加粗文字注意&#xff0c;普通文字理解。 1、操作系统 一、进程、线程的概念以及…

利用反射和自定义注解优化参数处理

文章目录 自定义注解类定义反射和注解取值格式化参数测试 自定义注解 Target(ElementType.FIELD) Retention(RetentionPolicy.RUNTIME) public interface PropertyAnnotation {//字段类型: STRING->0,Number 1,Decimal 2,DateTime 3, Date 4 ,int 5,Float 6,Double 7int ty…

力扣经典150题第五十五题:逆波兰表达式求值

目录 题目描述和要求示例解释解题思路算法实现复杂度分析测试和验证总结和拓展参考资料 题目描述和要求 给你一个字符串数组 tokens&#xff0c;表示一个根据逆波兰表示法表示的算术表达式。请你计算该表达式&#xff0c;并返回一个表示表达式值的整数。 注意&#xff1a; 有…

团队经理口才训练教案(3篇)

团队经理口才训练教案&#xff08;3篇&#xff09; **篇&#xff1a;基础口才训练 一、教学目标 让团队经理了解口才在团队管理中的重要性。 教授基础口才技巧&#xff0c;如发音、语速、语调等。 二、教学内容 口才的重要性 强调团队经理的口才能力对团队凝聚力、沟通…

牛客NC383 主持人调度(一)【简单 排序 Java/Go/C++】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/e160b104354649b69600803184094adb 思路 直接看代码&#xff0c;不难Java代码 import java.util.*;public class Solution {/*** 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修改&#xff0c;直接返…

Android Binder机制

一.简介 Binder是什么&#xff1f; Android系统中&#xff0c;涉及到多进程间的通信底层都是依赖于Binder IPC机制。 例如当进程A中的Activity要向进程B中的Service通信&#xff0c;这便需要依赖于Binder IPC。不仅于 此&#xff0c;整个Android系统架构中&#xff0c;大量采…

BI不等同数据分析,别搞错了!

✅作者简介&#xff1a;《数据运营&#xff1a;数据分析模型撬动新零售实战》作者、《数据实践之美》作者、数据科技公司创始人、多次参加国家级大数据行业标准研讨及制定、高端企培合作讲师。 &#x1f338;公众号&#xff1a;风姑娘的数字视角&#xff0c;免费分享数据应用相…

小红的循环移位

题目描述&#xff1a;小红拿到了一个数字串&#xff0c;她每次操作可以使得其向左循环移动一位。将串 ss0 s1...sn−1s ​ 向左循环移动一位&#xff0c;将得到串s1...sn−1s0。小红想知道&#xff0c;使得该数字串变成4的倍数&#xff0c;需要最少操作多少次&#xff1f;&…

leetCode80. 删除有序数组中的重复项 II

leetCode80. 删除有序数组中的重复项 II 具体思路见我上篇博客&#xff1a;只不过哪里是只能出现1次&#xff0c;这里只能出现两次 删除有序数组中的重复项打卡博客 代码 class Solution { public:int removeDuplicates(vector<int>& nums) {int k 0;for(auto x :…

步态识别论文(6)GaitDAN: Cross-view Gait Recognition via Adversarial Domain Adaptation

摘要: 视角变化导致步态外观存在显着差异。因此&#xff0c;识别跨视图场景中的步态是非常具有挑战性的。最近的方法要么在进行识别之前将步态从原始视图转换为目标视图&#xff0c;要么通过蛮力学习或解耦学习提取与相机视图无关的步态特征。然而&#xff0c;这些方法有许多约…

2010NOIP普及组真题 2. 接水问题

线上OJ&#xff1a; 一本通&#xff1a;http://ybt.ssoier.cn:8088/problem_show.php?pid1950 解法一、朴素模拟 核心思想&#xff1a; 朴素模拟&#xff1a; 1、先给每个b[i]水龙头分配一个人a[i]&#xff0c;b[i] 表示水龙头的剩余时间。同时标记该水龙头为 used 使用中 2…

FFmpeg———encode_video(学习)

目录 前言源码函数最终效果 前言 encode_video:实现了对图片使用指定编码进行编码&#xff0c;生成可播放的视频流&#xff0c;编译时出现了一些错误&#xff0c;做了一些调整。 基本流程&#xff1a; 1、获取指定的编码器 2、编码器内存申请 3、编码器上下文内容参数设置 4、…

政安晨:【Keras机器学习示例演绎】(三十五)—— 使用 LayerScale 的类注意图像变换器

目录 简介 导入 层刻度层 随机深度层 类注意力 会说话的头注意力 前馈网络 其他模块 拼凑碎片&#xff1a;CaiT 模型 定义模型配置 模型实例化 加载预训练模型 推理工具 加载图像 获取预测 关注层可视化 结论 政安晨的个人主页&#xff1a;政安晨 欢迎 &#…

计算机专业就业前景以及工资待遇水平怎么样

计算机专业毕业生的就业前景不错的&#xff0c;计算机专业人才的需求量大&#xff0c;各行各业都需要这类人才来进行软件开发、数据分析、网络安全管理、系统维护等工&#xff0c;工资待遇水平较高&#xff0c;以下是上大学网&#xff08;www.sdaxue.com)整理的计算机专业就业前…

SMB协议之-那些隐藏/非隐藏共享share

在前面的文章中&#xff0c;介绍了SMB协议相关的内容&#xff0c;详见我的专栏《网络攻防协议实战分析》&#xff0c;连接这里。在SMB协议中往往会使用 tree connect命令连接到对应的远程共享地址。如果你经常接触到SMB协议&#xff0c;相信你对于ADMIN$ ,IPC$ ,C$ 等多种共享一…

质因数分解(cpp实现)--一种快速求得一个数有多少个因子的黑魔法

前言 最近机试没少吃不会质因数分解的亏&#xff0c;用传统的求得因子个数只能过一点点…(ex, 20%) 质因数分解后&#xff0c;可以将因子问题转化为 集合的组合问题&#xff0c;因此会很快&#xff0c;目测是 l o g n log n logn (n是该整数的值)。 传统解法 假设输入整数的…

每天学习一个Linux命令之dd

每天学习一个Linux命令之dd dd命令是Linux系统下一个非常强大的工具&#xff0c;它能够进行数据的复制和转换。本文将详细介绍dd命令的使用方法及常用选项。 1. dd命令的基本介绍 dd命令是一个进行数据转换和复制的工具&#xff0c;可以从输入文件中读取数据&#xff0c;并按…

动态规划 ------ 背包问题

文章目录 1. 01 背包问题1.二维解决2. 一维优化 2. 完全背包问题1.暴力3 for.2. 二维优化3. 一维优化 3. 多重背包问题Ⅰ.1. 二维解决2. 一维优化 4. 多重背包问题Ⅱ5. 混合背包问题6. 二维费用背包问题7. 分组背包问题 背包问题是动态规划中非常典型的一些题&#xff0c;本篇文…

<网络安全>《74 微课堂<容器是什么?>》

1 容器 容器&#xff0c;是一种虚拟化技术。允许一个操作系统上用户空间被分割成几个独立的单元在内核中运行&#xff0c;彼此互不干扰&#xff0c;这样一个独立的空间&#xff0c;就被称之为一个容器。 2 容器技术为什么得到快速发展 随着智能制造的推进&#xff0c;企业开…