【Spring源码核心篇-03】精通spring的aop的底层原理和源码实现

news/2024/11/25 10:58:36/

Spring源码核心篇整体栏目


内容链接地址
【一】Spring的bean的生命周期https://zhenghuisheng.blog.csdn.net/article/details/143441012
【二】深入理解spring的依赖注入和属性填充https://zhenghuisheng.blog.csdn.net/article/details/143854482
【三】精通springaop的底层原理和源码实现https://zhenghuisheng.blog.csdn.net/article/details/144012934

深入理解spring的依赖注入和属性填充

  • 一,精通springaop的底层原理
    • 1,proxyFactory获取代理对象
    • 2,invoke获取代理对象的某个方法
      • 2.1,pointcutAdvisor匹配
      • 2.2,IntroductionAdvisor匹配
    • 3,proceed责任链调用
    • 4,springaop后置处理器
    • 5,@AspectJ注解获取advisor
    • 6,实现Advisor类获取advisor
    • 7,匹配advisor,创建动态代理
    • 8,aop总结

如需转载,请附上链接:https://blog.csdn.net/zhenghuishengq/article/details/144012934

springaop_12">一,精通springaop的底层原理

前面两篇分析了spring的bean的生命周期,以及属性填充的底层实现,接下来这篇主要讲解的是Spring的另一个核心特性 AOP ,在了解aop之前,需要先了解动态代理的具体实现cglib和jdk动态代理。

1,proxyFactory获取代理对象

spring源码中,实现动态代理的方式主要是通过这个proxyFactory工厂实现,如下面这段代码,先创建一个动态代理的工厂,然后从工厂中获取到对应的target

ProxyFactory proxyFactory = new ProxyFactory();
UserService proxy = (UserService)proxyFactory.getProxy();

直接进入这个getProxy中可以发现会先创建一个Aop的动态代理,再通过调用对应的代理方法拿到具体的实现类

public Object getProxy() {return createAopProxy().getProxy();
}

首先先看这个 createAopProxy 方法,判断是创建cglib动态代理还是创建jdk的动态代理

  • config指的就是上面的userService对应的 proxy 对象,因此第一步就是对proxy 对象属性进行判断
  • 随后拿到target代理类,判断是否接口,以及判断proxy是否是jdk代理,否则则使用jdk动态代理
  • 如果不是接口或者不是使用的jdk动态代理,那么就是使用cglib代理,cglib底层也是通过实现父类实现

在这里插入图片描述

这个 getProxy 方法的AopProxy接口中,主要有两种方式实现动态代理,一种是基于cglib的 cglibAopProxy ,一种是基于jdk动态代理的 JdkDynamicAopProxy

在这里插入图片描述

使用jdk动态代理的实现如下,直接诶通过调用这个 newProxyInstance 实现,从而拿到代理对象

@Override
public Object getProxy(@Nullable ClassLoader classLoader) {Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

使用这个cglib的动态代理如下,从而拿到代理对象

@Override
public Object getProxy(@Nullable ClassLoader classLoader) {createProxyClassAndInstance(enhancer, callbacks)
}

2,invoke获取代理对象的某个方法

在上面已经通过proxyFactory获取到了一个代理对象,在有了对象之后,就需要获取到需要代理的方法,如在jdk动态代理中,主要是通过这个invoke方法来获取代理对象的方法

  • 首先不会执行代理对象的equal方法,hashcode方法
  • 若执行的class对象是DecoratingProxy 则不会对其应用切面进行方法的增强
  • 如果目标对象实现的Advised接口,则不会对其应用切面进行方法的增强。 直接执行方法

在这里插入图片描述

接下来继续往这个方法下面走,会有一个提前暴露的操作,如果设置为true,那么就将这个代理存入到一个ThreadLocal里面,后续只需要去这个ThreadLocal中获取即可,如事务失效的情况,就可以在这个ThreadLocal中获取

if (this.advised.exposeProxy) {//把我们的代理对象暴露到线程变量中oldProxy = AopContext.setCurrentProxy(proxy);setProxyContext = true;
}

继续看这个类中的方法,会调用一个 getInterceptorsAndDynamicInterceptionAdvice 方法,把aopadvisor 全部转化为拦截器, 通过责任链模式依次调用,通过该类筛选出符合的 advisor

List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

通过筛选出来的数组进行值判断,对有代理和没有代理,会有两种形式去获取方法

  • 如果筛选的链路为空,代表不需要代理,那么就会直接的通过反射的方式进行获取对象和方法
  • 如果筛选后发现链路不为空,那么就需要将代理类,目标类,目标方法,参数,class以及链路传入到 ReflectiveMethodInvocation 方法进行调用,然后返回一个重要的方法MethodInvocation对象,最终调用这个 proceed 方法进行责任链方法的执行

在这里插入图片描述

2.1,pointcutAdvisor匹配

接下来回到上面的那个 getInterceptorsAndDynamicInterceptionAdvice 筛选责任链的方法,在spring中,每一个advice都会被封装成一个advisor,每一个advisor会包括一个advice和一个pointcut切入点。

在这里插入图片描述

因此在这个筛选方法中,会去遍历全部的advisor,pointcut主要是作为匹配使用,因此需要将这个advisor先转换成这个pointcutAdvisor,然后继续执行的流程如下,通过以下流程确定对应的类和方法。可能会匹配到多个链路,因此最终返回一个一个list数组,包含 MethodInterceptor 类型的对象

  • pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass) 先通过这个方法进行类的匹配
  • MethodMatchers.matches(mm, method, actualClass, hasIntroductions) 再通过这个方法进行方法匹配

2.2,IntroductionAdvisor匹配

上面这种切入点匹配直接是通过类方法进行比较进行匹配,而下面的这种 IntroductionAdvisor 方式是通过一些适配器匹配的方式进行匹配

在这里插入图片描述

其核心是下面这个getInterceptors方法,其匹配的流程如下,先判断是否是他的MethodInterceptor实现类 ,然后判断是否 AdvisorAdapter 适配器匹配模式

@Override
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {List<MethodInterceptor> interceptors = new ArrayList<>(3);Advice advice = advisor.getAdvice();if (advice instanceof MethodInterceptor) {interceptors.add((MethodInterceptor) advice);}for (AdvisorAdapter adapter : this.adapters) {if (adapter.supportsAdvice(advice)) {interceptors.add(adapter.getInterceptor(advisor));}}if (interceptors.isEmpty()) {throw new UnknownAdviceTypeException(advisor.getAdvice());}return interceptors.toArray(new MethodInterceptor[0]);
}

核心就是看这个适配器匹配模式,在构造这个 DefaultAdvisorAdapterRegistry 类时,会有三个具体的适配器模式,分别是 MethodBeforeAdviceAdapterAfterReturningAdviceAdapterThrowsAdviceAdapter ,为什么没有around这种模式,其实很简单,这三种模式的总集合就是around模式

public DefaultAdvisorAdapterRegistry() {registerAdvisorAdapter(new MethodBeforeAdviceAdapter());registerAdvisorAdapter(new AfterReturningAdviceAdapter());registerAdvisorAdapter(new ThrowsAdviceAdapter());
}

随便查看一个 MethodBeforeAdviceAdapter 的实现如下,最左会返回一个 MethodInterceptor 对象,说明肯定是可以匹配成功的

在这里插入图片描述

3,proceed责任链调用

springaop中,其底层主要是通过责任链模式实现,其底层主要是通过调用这个 proceed 方法实现,在上面匹配方法时,就已经进行了这个方法的调用

invocation.proceed();

在这里插入图片描述

直接进入这个 ReflectiveMethodInvocation 类中的proceed方法中,假设有三个调用链,那么第一步会先记录调用链的个数,随后每调用一个会加1,知道累加的个数等于调用链的个数时则会直接停止

在这里插入图片描述

接下来看这段执行完毕的方法,当执行的责任链路次数达到链路的个数时,那么就会执行这个 invokeJoinpoint 方法

if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1){return invokeJoinpoint();
}

接下来进入这个 invokeJoinpoint 方法,发现只有一行代码,就是调用这个 invokeJoinpointUsingReflection

@Nullable
protected Object invokeJoinpoint() throws Throwable {return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);
}

最后查看这个 invokeJoinpointUsingReflection 方法,发现最终就是执行上面的invoke方法,这里就是执行代理对象的那个方法

在这里插入图片描述

也就是说再匹配完所有的链路之后,才会最终的执行这个代理对象所对应的方法

springaop_198">4,springaop后置处理器

接下来又得回到spring的生命周期中,直接看这个 initializeBean 初始化方法,里面会有一个 applyBeanPostProcessorsAfterInitialization 调用bean的后置处理器的方法,主要是解析aop

在这里插入图片描述

在这个 applyBeanPostProcessorsAfterInitialization 方法中,最主要的就是这个 postProcessAfterInitialization 核心方法

@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)throws BeansException {Object result = existingBean;//获取我们容器中的所有的bean的后置处理器for (BeanPostProcessor processor : getBeanPostProcessors()) {Object current = processor.postProcessAfterInitialization(result, beanName);//若只有有一个返回null 那么直接返回原始的if (current == null) {return result;}result = current;}return result;
}

最后进入这个 postProcessAfterInitialization 核心方法,并且进入里面的实现类 AbstractAutoProxyCreator

在这里插入图片描述

进入该类后看到的方法就是 postProcessAfterInitialization ,最后进入这个重要的 wrapIfNecessary 方法

@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {if (bean != null) {//获取缓存keyObject cacheKey = getCacheKey(bean.getClass(), beanName);// 之前循环依赖创建的动态代理 如果是现在的bean 就不再创建,,并且移除if (this.earlyProxyReferences.remove(cacheKey) != bean) {// 该方法将会返回动态代理实例return wrapIfNecessary(bean, beanName, cacheKey);}}return bean;
}

wrapIfNecessary方法的详细流程如下:

  • 已经被处理过,就是自己实现创建动态代理逻辑,那么可以直接返回
  • 不需要增加的bean,也直接返回
  • 是不是基础的bean,是不是需要跳过的,重复判断
  • 最后就是来到了重点,根据当前bean找到匹配的advisor,最后加入到缓存中

在这里插入图片描述

spring中创建真正的代理对象的方法如下,首先创建一个 ProxyFactory 代理工厂,然后判断是cglib的动态代理还是cglib的动态代理,然后构建Advisors数组,随后将数组加入到代理工厂中,最后调用这个 getProxy 方法获取真正的代理对象,这里的getProxy方法,又回到了最上面使用cglib还是jdk动态代理的方法

protected Object createProxy(Class<?> beanClass, @Nullable String beanName,@Nullable Object[] specificInterceptors, TargetSource targetSource) {if (this.beanFactory instanceof ConfigurableListableBeanFactory) {AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);}//创建一个代理对象工厂ProxyFactory proxyFactory = new ProxyFactory();proxyFactory.copyFrom(this);//为proxyFactory设置创建jdk代理还是cglib代理// 如果设置了 <aop:aspectj-autoproxy proxy-target-class="true"/>不会进if,说明强制使用cglibif (!proxyFactory.isProxyTargetClass()) {// 内部设置的 ,   配置类就会设置这个属性if (shouldProxyTargetClass(beanClass, beanName)) {proxyFactory.setProxyTargetClass(true);}else {// 检查有没有接口evaluateProxyInterfaces(beanClass, proxyFactory);}}//把我们的specificInterceptors数组中的Advisor转化为数组形式的Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);//为我们的代理工厂加入通知器,proxyFactory.addAdvisors(advisors);//设置targetSource对象proxyFactory.setTargetSource(targetSource);customizeProxyFactory(proxyFactory);proxyFactory.setFrozen(this.freezeProxy);// 代表之前是否筛选advise.// 因为继承了AbstractAdvisorAutoProxyCreator , 并且之前调用了findEligibleAdvisors进行筛选, 所以是trueif (advisorsPreFiltered()) {proxyFactory.setPreFiltered(true);}//真正的创建代理对象return proxyFactory.getProxy(getProxyClassLoader());
}

再来看一下这个 getAdvicesAndAdvisorsForBean 方法,如何根据bean找到匹配的advisor,进入这个 AbstractAdvisorAutoProxyCreator 实现类,最后查看这个方法里面的 findEligibleAdvisorsadvisor的方法

List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);

找到合适的Advisor的具体实现如下,先拿到接口方式的aop,再判断通知能否作用到类上,最后进行一个排序

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {// 拿到接口方式的AOP (这次是从缓存中拿了)List<Advisor> candidateAdvisors = findCandidateAdvisors();//判断我们的通知能不能作用到当前的类上(切点是否命中当前Bean)List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);extendAdvisors(eligibleAdvisors);//对我们的advisor进行排序if (!eligibleAdvisors.isEmpty()) {eligibleAdvisors = sortAdvisors(eligibleAdvisors);}return eligibleAdvisors;
}

接下来就是查看这个 AnnotationAwareAspectJAutoProxyCreator 类中获取advisor的方法,可以发现有两种方式去找这个advisor对象,一种是直接通过xml配置或者实现原生Aop的Advisor的接口,另一种是加了@AspectJ注解的类

在这里插入图片描述

AspectJadvisor_332">5,@AspectJ注解获取advisor

spring中通过@AspectJ使用aop时,@Before对应的就是advice注解,execution内部那一串就是pointcut

@Before("execution(* com.example.service..*.*(..))")

再来查看一下这个 @Aspect 这个注解是如何获取到封装的Advisor对象的,接下来直接看这个 buildAspectJAdvisors 方法,其内部核心步骤实现如下,就是遍历IOC容器中的全部bean,判断bean上面是否带有这个@AspectJ注解,找到这个注解之后,再去对应的类中解析一些 @Before,@After等注解,会将这些注解全部的封装成对应的Advisor

在这里插入图片描述

接下来在这个方法中,查看下面这段代码,真正的去获取我们的通知对象

List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);

接下来进入这个 getAdvisors 方法,看@AspectJ是如何去解析这些切面通知的

public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {//获取我们的标记为Aspect的类Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();//获取我们的切面类的名称String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();//校验我们的切面类validate(aspectClass);//我们使用的是包装模式来包装我们的MetadataAwareAspectInstanceFactory 构建为MetadataAwareAspectInstanceFactoryMetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);List<Advisor> advisors = new ArrayList<>();//获取到切面类中的所有方法,但是该方法不会解析标注了@PointCut注解的方法for (Method method : getAdvisorMethods(aspectClass)) {//挨个去解析我们切面中的方法Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);if (advisor != null) {advisors.add(advisor);}}
}    

再查看这个 getAdvisorMethods 方法,首先会过滤掉没有pointcut的方法,随后进行一个sort的排序

private List<Method> getAdvisorMethods(Class<?> aspectClass) {final List<Method> methods = new ArrayList<>();ReflectionUtils.doWithMethods(aspectClass, method -> {// Exclude pointcutsif (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) {methods.add(method);}});methods.sort(METHOD_COMPARATOR);return methods;
}

ReflectiveAspectJAdvisorFactory 类的静态代码块中,有一段排序的接口,其先执行的的优先级如下:Around —> Before —> After —> AfterReturning ,如果有多个相同的切面,如有两个After的切面,那么会根据方法名字符串的ascII码进行比较,谁小谁先执行

static {Comparator<Method> adviceKindComparator = new ConvertingComparator<>(new InstanceComparator<>(Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class),(Converter<Method, Annotation>) method -> {AspectJAnnotation<?> annotation =AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(method);return (annotation != null ? annotation.getAnnotation() : null);});Comparator<Method> methodNameComparator = new ConvertingComparator<>(Method::getName);METHOD_COMPARATOR = adviceKindComparator.thenComparing(methodNameComparator);
}

在查看getAdvisors 方法中的 getAdvisor 方法,上面的getAdvisorMethods方法是过滤掉没有pointcut匹配的方法,那么这个方法就是用于解析这些方法,封装成对应的advisor

在这里插入图片描述

advisor_416">6,实现Advisor类获取advisor

在整个aop的动态代理思路中,有一步核心就是找到全部的advisor,这里讲解的第一种方式就是通过实现一些advisor类来获取。如举个例子,直接使用aop的原生注解的实现类来实现aop的动态增强

public class AopAdvisorTest implements MethodBeforeAdvice {@Overridepublic void before(Method method, Object[] args, Object target) throws Throwable {if (method.getName().equals("test")){System.out.println("test方法前置增加");}}
}

那么来通过源码来查看底层是如何实现的,直接进入这个 getAdvisor 方法,内部的核心方法就是调用了 InstantiationModelAwarePointcutAdvisorImpl 方法去封装成Advisor对象

在这里插入图片描述

通过外部传进来的参数生成一个Advisor对象

在这里插入图片描述

在这个对象的核心方法就是 getAdvice 方法,后序会调用这个 instantiateAdvice 方法,后面再次调用getadvice方法

在这里插入图片描述

在这个方法中,首先会获取切面类,然后获取方法上面的注解

在这里插入图片描述

后面会将获取到的注解进行匹配,通过不同的类型进行对应的封装,最后生成一个advice对象返回,5个注解对应5个advice

在这里插入图片描述

以before来看,内部就会提供一个before的实现方法,不同的advice内部会有对应的advice方法

在这里插入图片描述

advice不会以单独的方式存在,在 DefaultAdvisorAdapterRegistry 的适配器类中,最终通过这种责任链的调用模式,会将这些单独存在advice封装成一个个的advisor

在这里插入图片描述

advisor_466">7,匹配advisor,创建动态代理

接下来还是得回到4里面的 findEligibleAdvisors 方法,在5和6中主要都是在 findCandidateAdvisors 执行这个方法,就是如何在程序启动之后去获取这些advisor,主要有两种方法:一种是直接通过xml配置或者实现原生Aop的Advisor的接口,另一种是加了@AspectJ注解的类 ,这两种方法在找到对应的bean实例之后,最后都会封装成对应的advisor

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {// 拿到接口方式的AOP (这次是从缓存中拿了)List<Advisor> candidateAdvisors = findCandidateAdvisors();//判断我们的通知能不能作用到当前的类上(切点是否命中当前Bean)List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);extendAdvisors(eligibleAdvisors);//对我们的advisor进行排序if (!eligibleAdvisors.isEmpty()) {eligibleAdvisors = sortAdvisors(eligibleAdvisors);}return eligibleAdvisors;
}

接下来就是查看这个 findAdvisorsThatCanApply 方法,里面的核心方法就是 findAdvisorsThatCanApply 方法,从候选的通知器中找到当前bean关联的advisors对象

在这里插入图片描述

findAdvisorsThatCanApply 方法核心如下,

在这里插入图片描述

接下来查看第一个canApply方法,又到了熟悉的环节,上面也讲过,起流程如下:

  • 先判断是否IntroductionAdvisor的实现类,是的话则根据类进行过滤判断
  • 再判断是否是PointcutAdvisor的实现类,是的话再次调用这个canApply方法再次进行过滤

在这里插入图片描述

平常时开发用的最多的也是实现pointcut的实现类,因此主要看这个里面 canApply 中的过滤方法

在这里插入图片描述

其核心代码如下,首对class类进行过滤,匹配出代理类class对象,找到全部符合的对象之后,再去遍历这些class类对象,对里面的方法进行匹配,先通过反射获取类中的所有方法,在判断类上面是否有@AspectJ注解,方法上面的pointcut是否匹配等,如果都匹配的话,那么就会认为这了类上面的这个方法需要进行动态代理,后续就会去创建一个动态代理对象

//创建一个集合用于保存targetClass 的class对象
Set<Class<?>> classes = new LinkedHashSet<>();
//判断当前class是不是代理的class对象
if (!Proxy.isProxyClass(targetClass)) {//加入到集合中去classes.add(ClassUtils.getUserClass(targetClass));
}
//获取到targetClass所实现的接口的class对象,然后加入到集合中
classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
//循环所有的class对象
for (Class<?> clazz : classes) {//通过class获取到所有的方法Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);//循环我们的方法for (Method method : methods) {//通过methodMatcher.matches来匹配我们的方法if (introductionAwareMethodMatcher != null ?// 通过切点表达式进行匹配 AspectJ方式introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :// 通过方法匹配器进行匹配 内置aop接口方式methodMatcher.matches(method, targetClass)) {// 只要有1个方法匹配上了就创建代理return true;}}
}

aop_538">8,aop总结

  • aop主要是用于切面,在原来的代码逻辑上,通过无侵入的方式实现代码的动态增强,内部主要通过代理方式cglib代理和jdk动态代理实现。
  • aop主要是在bean初始化之后,通过bean的后置处理器实现,会将需要进行aop的方法找出全部封装成advisor,生成advisor的方式主要有两种,一种是直接实现advice接口,另一种是通过@AspectJ的方式实现。
  • 每一个advisor包含一个advice和一个pointcut,advice主要用于增强使用,pointcut主要由于后续的寻找bean的匹配。
  • 当把全部的advisor全部找到之后,会进行一个匹配的操作,将全部的获取到的advisor和进行aop的bean进行匹配,先通过class类和对应的method进行匹配,匹配成功之后再通过pointcut切入点进行匹配,当全部匹配成功之后,那么就会创建一个动态代理。

http://www.ppmy.cn/news/1549801.html

相关文章

移动光猫[HS8545M5-10]获取超密

移动光猫[HS8545M5-10]获取超级密码 1、缘由2、前期准备2.1、确保本地开通telnet客户端功能2.2、准备好相关软件 3、开始查找超密 1、缘由 最近想折腾一下ipv6ddns打通内外网&#xff0c;查询资料说是需要将光猫桥接到外网&#xff1b;但是使用光猫后边的用户名密码根本就找不到…

Android开发实战班 - 应用架构 - LiveData/Flow 数据流

在 MVVM 架构中&#xff0c;数据流是连接 ViewModel 和 View 的重要桥梁&#xff0c;用于实现数据的观察和响应。Jetpack 提供了两种主要的数据流机制&#xff1a;LiveData 和 Flow。本章节将深入讲解 LiveData 和 Flow 的概念、使用方法、区别以及在实际开发中的应用场景&…

Claude Opus MetaPrompt 系统详解

Claude Opus MetaPrompt 系统详解 简介 MetaPrompt系统是由Anthropic提出的&#xff0c;旨在帮助用户为AI助手Claude生成高质量的提示。它指导用户定义任务和变量、结构化指令和细化输出。 具体内容 特点 主要针对Claude 3 Opus版本&#xff0c;并且适用于单轮对话。 核心…

windows C#-生成和使用异步流(上)

异步流建立流式处理数据源模型。 数据流经常异步检索或生成元素。 它们为异步流式处理数据源提供了自然编程模型。 先决条件 需要将计算机设置为运行 .NET&#xff0c;包括 C# 编译器。 C# 编译器随附于 Visual Studio 2022 或 .NET SDK。 将需要创建 GitHub 访问令牌&#…

[系统安全]PE文件头中的重定位表

PE加载的过程 任何一个EXE程序会被分配4GB的内存空间&#xff0c;用户层处理低2G的内存&#xff0c;驱动处理高2G的内存。 1、双击EXE程序&#xff0c;操作系统开辟一个4GB的空间。2、从ImageBase决定了加载后的基址&#xff0c;ImageSize决定了程序有多大。3、然后加载DLL 大…

Python Flask快速开发网站

在Web开发领域,Python拥有多种优秀的Web框架,例如Django、Flask、Pyramid等。其中Flask是一个微型框架,核心代码非常精简,只包含了Web应用最基本的功能。这使得Flask非常轻量级,容易上手,适合快速开发小型Web应用。本文将介绍如何使用Flask快速搭建一个简单的网站。 © ivw…

[2024.11.17-24] 一周科技速报

2024 世界青年科学家峰会全体大会 时间地点&#xff1a;11 月 17 日在浙江温州举行。 参会人员&#xff1a;吸引了来自 71 个国家和地区以及 63 个国际科技组织的近 800 名科学家、企业家、教育家和青年科技人才代表。 会议内容&#xff1a;以 “汇聚天下英才共创美好未来”…

云计算期中作业:Spark机器学习问题解决

在原有pdf教程教程上&#xff0c;做一个补充 idea内搭建环境 导入依赖 就直接利用之前的作业工程项目里直接写&#xff0c;所以依赖基本上不用再导入了&#xff0c;如果要导入&#xff0c;看自己依赖的版本号&#xff0c;不要直接复制教程&#xff0c;比如我的&#xff1a; …