spring-aop源码分析(2)_AnnotationAwareAspectJAutoProxyCreator后置处理器

news2024/11/23 19:22:09

本文将通过阅读AnnotationAwareAspectJAutoProxyCreator后置处理器的源码,分析其解析AOP通知、匹配切入点和创建AOP代理的流程。

入口

AnnotationAwareAspectJAutoProxyCreator是一个BeanPostProcessor实现,他在容器进行initializeBean的时候被调用:

在这里插入图片描述

核心的BeanPostProcessor方法实现都在其父类AbstractAutoProxyCreator中实现。

wrapIfNecessary方法

在其父类AbstractAutoProxyCreator的postProcessAfterInitialization方法中。

核心功能:

  • 查找AOP Advisor
  • 创建AOP代理
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
	// 最开始是一些是否需要AOP的判断,如果不需要或者已经创建代理,则直接返回了

	// Create proxy if we have advice.
    // 查找AOP Advisor
	Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
	if (specificInterceptors != DO_NOT_PROXY) {
		this.advisedBeans.put(cacheKey, Boolean.TRUE);
        // 创建AOP代理
		Object proxy = createProxy(
				bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
		this.proxyTypes.put(cacheKey, proxy.getClass());
		return proxy;
	}

	this.advisedBeans.put(cacheKey, Boolean.FALSE);
	return bean;
}

getAdvicesAndAdvisorsForBean方法

查找AOP Advisor

protected Object[] getAdvicesAndAdvisorsForBean(
		Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {

	List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
	if (advisors.isEmpty()) {
		return DO_NOT_PROXY;
	}
	return advisors.toArray();
}

// Find all eligible Advisors for auto-proxying this class.
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    // 此处分为两部分:
    // 1. 查找Spring容器里面注入的Advisor
    // 2. 解析所有的AspectJ AOP并创建Advisor
    // 实现逻辑在AnnotationAwareAspectJAutoProxyCreator类中
	List<Advisor> candidateAdvisors = findCandidateAdvisors();
    // Search the given candidate Advisors to find all Advisors that can apply to the specified bean.
	List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    // Add an ExposeInvocationInterceptor to the beginning of the advice chain.
    // This additional advice is needed when using AspectJ pointcut expressions and 
    // when using AspectJ-style advice.
	extendAdvisors(eligibleAdvisors);
	if (!eligibleAdvisors.isEmpty()) {
		eligibleAdvisors = sortAdvisors(eligibleAdvisors);
	}
	return eligibleAdvisors;
}

// AnnotationAwareAspectJAutoProxyCreator.findCandidateAdvisors
protected List<Advisor> findCandidateAdvisors() {
	// Add all the Spring advisors found according to superclass rules.
    // 查找Spring容器里面注入的Advisor,代码就不再展开记录
	List<Advisor> advisors = super.findCandidateAdvisors();
	// Build Advisors for all AspectJ aspects in the bean factory.
    // 解析所有的AspectJ AOP并创建Advisor
	if (this.aspectJAdvisorsBuilder != null) {
		advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
	}
	return advisors;
}

过滤当前Bean可用Advisor

findAdvisorsThatCanApply方法用于过滤可用Advisor:

protected List<Advisor> findAdvisorsThatCanApply(
		List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {

	ProxyCreationContext.setCurrentProxiedBeanName(beanName);
	try {
		return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
	} finally {
		ProxyCreationContext.setCurrentProxiedBeanName(null);
	}
}

AopUtils.findAdvisorsThatCanApply方法:

// Determine the sublist of the candidateAdvisors list that is applicable to the given class.
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
	List<Advisor> eligibleAdvisors = new ArrayList<>();
	for (Advisor candidate : candidateAdvisors) {
		if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
			eligibleAdvisors.add(candidate);
		}
	}
	boolean hasIntroductions = !eligibleAdvisors.isEmpty();
	for (Advisor candidate : candidateAdvisors) {
		if (candidate instanceof IntroductionAdvisor) {
			// already processed
			continue;
		}
		if (canApply(candidate, clazz, hasIntroductions)) {
			eligibleAdvisors.add(candidate);
		}
	}
	return eligibleAdvisors;
}

public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
	if (advisor instanceof IntroductionAdvisor) {
		return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
	} else if (advisor instanceof PointcutAdvisor) {
		PointcutAdvisor pca = (PointcutAdvisor) advisor;
        // 使用pointcut判断
		return canApply(pca.getPointcut(), targetClass, hasIntroductions);
	} else {
		return true;
	}
}


排序

在查找Advisor集的过程中,有一步是Advisor集排序:

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
	List<Advisor> candidateAdvisors = findCandidateAdvisors();
	List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
	extendAdvisors(eligibleAdvisors);
	if (!eligibleAdvisors.isEmpty()) {
        // 排序
		eligibleAdvisors = sortAdvisors(eligibleAdvisors);
	}
	return eligibleAdvisors;
}

protected List<Advisor> sortAdvisors(List<Advisor> advisors) {
	List<PartiallyComparableAdvisorHolder> partiallyComparableAdvisors = new ArrayList<>(advisors.size());
	for (Advisor advisor : advisors) {
		partiallyComparableAdvisors.add(
				new PartiallyComparableAdvisorHolder(advisor, DEFAULT_PRECEDENCE_COMPARATOR));
	}
	List<PartiallyComparableAdvisorHolder> sorted = PartialOrder.sort(partiallyComparableAdvisors);
	if (sorted != null) {
		List<Advisor> result = new ArrayList<>(advisors.size());
		for (PartiallyComparableAdvisorHolder pcAdvisor : sorted) {
			result.add(pcAdvisor.getAdvisor());
		}
		return result;
	} else {
		return super.sortAdvisors(advisors);
	}
}

核心的排序逻辑都在DEFAULT_PRECEDENCE_COMPARATOR中,他是一个Comparator实现:

public int compare(Advisor o1, Advisor o2) {
	int advisorPrecedence = this.advisorComparator.compare(o1, o2);
    // 如果advisorComparator比较结果相同,将使用AspectJ相关的方法排序,此部分不分析
	if (advisorPrecedence == SAME_PRECEDENCE && declaredInSameAspect(o1, o2)) {
		advisorPrecedence = comparePrecedenceWithinAspect(o1, o2);
	}
	return advisorPrecedence;
}

advisorComparator也是一个Comparator实现,他内部有一些从Ordered实现方法、Order注解获取顺序的逻辑,此处不展开分析,了解即可。

创建AOP代理

// 创建AOP代理
Object proxy = createProxy(
		bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));

createProxy入口方法

protected Object createProxy(Class<?> beanClass, String beanName,
		Object[] specificInterceptors, TargetSource targetSource) {

    // Expose the given target class for the specified bean, if possible.
	if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
		AutoProxyUtils
            .exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
	}

	ProxyFactory proxyFactory = new ProxyFactory();
	proxyFactory.copyFrom(this);

	if (!proxyFactory.isProxyTargetClass()) {
		if (shouldProxyTargetClass(beanClass, beanName)) {
			proxyFactory.setProxyTargetClass(true);
		}
		else {
			evaluateProxyInterfaces(beanClass, proxyFactory);
		}
	}

    // 构建AOP通知
	Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
	proxyFactory.addAdvisors(advisors);
	proxyFactory.setTargetSource(targetSource);
	customizeProxyFactory(proxyFactory);

	proxyFactory.setFrozen(this.freezeProxy);
	if (advisorsPreFiltered()) {
		proxyFactory.setPreFiltered(true);
	}

    // 创建代理
	return proxyFactory.getProxy(getProxyClassLoader());
}

// 构建AOP通知
protected Advisor[] buildAdvisors(String beanName, Object[] specificInterceptors) {
	Advisor[] commonInterceptors = resolveInterceptorNames();

	List<Object> allInterceptors = new ArrayList<>();
	if (specificInterceptors != null) {
		allInterceptors.addAll(Arrays.asList(specificInterceptors));
		if (commonInterceptors.length > 0) {
			// 不重要
		}
	}

	Advisor[] advisors = new Advisor[allInterceptors.size()];
	for (int i = 0; i < allInterceptors.size(); i++) {
		advisors[i] = this.advisorAdapterRegistry.wrap(allInterceptors.get(i));
	}
	return advisors;
}

proxyFactory.getProxy方法里面判断使用Cglib还是JdkProxy创建代理:

public Object getProxy(@Nullable ClassLoader classLoader) {
	return createAopProxy().getProxy(classLoader);
}

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
	if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
		Class<?> targetClass = config.getTargetClass();
		if (targetClass == null) {
			// throw exception
		}
		if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
			return new JdkDynamicAopProxy(config);
		}
		return new ObjenesisCglibAopProxy(config);
	} else {
		return new JdkDynamicAopProxy(config);
	}
}

Cglib代理

从上面的代码可以看到,Cglib代理是使用ObjenesisCglibAopProxy实现类创建的。

继承关系是这样的:

AopProxy
  |-- CglibAopProxy
    |-- ObjenesisCglibAopProxy

Spring Cglib示例

下面代码演示如何是Spring Cglib创建一个代理类对象:

public static void main(String[] args) throws InstantiationException, IllegalAccessException {

  ClassLoader classLoader = CglibTest.class.getClassLoader();

  Enhancer enhancer = createEnhancer();

  // 设置目标类型
  enhancer.setSuperclass(LogService.class);

  enhancer.setClassLoader(classLoader);
  enhancer.setUseCache(false);
  enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
  enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));

  // callback集
  Callback[] callbacks = new Callback[]{
      new MyMethodInterceptor()
  };
  Class<?>[] types = new Class<?>[callbacks.length];
  for (int x = 0; x < types.length; x++) {
    types[x] = callbacks[x].getClass();
  }
  enhancer.setCallbackTypes(types);

  // 创建代理类并实例化
  Class<?> proxyClass = enhancer.createClass();
  Object proxyInstance = proxyClass.newInstance();

  ((Factory) proxyInstance).setCallbacks(callbacks);

  LogService o = (LogService) proxyInstance;
  o.testServiceLog();
}

protected static Enhancer createEnhancer() {
  return new Enhancer();
}

private static class MyMethodInterceptor implements MethodInterceptor {

  @Override
  public Object intercept(
      Object enhancedConfigInstance, Method beanMethod,
      Object[] beanMethodArgs, MethodProxy cglibMethodProxy) throws Throwable {

    System.out.printf("MyMethodInterceptor %s args: %s\n",
        beanMethod.getName(), Arrays.toString(beanMethodArgs));

    return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
  }
}

CglibAopProxy.getProxy实现

public Object getProxy(ClassLoader classLoader) {

	try {
		Class<?> rootClass = this.advised.getTargetClass();

		Class<?> proxySuperClass = rootClass;
		if (rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {
			proxySuperClass = rootClass.getSuperclass();
			Class<?>[] additionalInterfaces = rootClass.getInterfaces();
			for (Class<?> additionalInterface : additionalInterfaces) {
				this.advised.addInterface(additionalInterface);
			}
		}

		// Validate the class, writing log messages as necessary.
		validateClassIfNecessary(proxySuperClass, classLoader);

		// Configure CGLIB Enhancer...
		Enhancer enhancer = createEnhancer();
		if (classLoader != null) {
			enhancer.setClassLoader(classLoader);
			if (classLoader instanceof SmartClassLoader &&
					((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
				enhancer.setUseCache(false);
			}
		}
		enhancer.setSuperclass(proxySuperClass);
		enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
		enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
		enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));

        // 通过advisor创建cglib代理callback
		Callback[] callbacks = getCallbacks(rootClass);
		Class<?>[] types = new Class<?>[callbacks.length];
		for (int x = 0; x < types.length; x++) {
			types[x] = callbacks[x].getClass();
		}
		// fixedInterceptorMap only populated at this point, after getCallbacks call above
		enhancer.setCallbackFilter(
            new ProxyCallbackFilter(
				this.advised.getConfigurationOnlyCopy(),
                this.fixedInterceptorMap,
                this.fixedInterceptorOffset));
		enhancer.setCallbackTypes(types);

		// Generate the proxy class and create a proxy instance.
        // 生成代理对象并返回
		return createProxyClassAndInstance(enhancer, callbacks);
	} catch (CodeGenerationException | IllegalArgumentException ex) {
		// throw new AopConfigException
	} catch (Throwable ex) {
		// throw new AopConfigException
	}
}

getCallbacks

创建cglib callback对象,最核心的就是使用spring aop advisor创建了一个DynamicAdvisedInterceptor对象,让其作为代理对象的处理逻辑。

DynamicAdvisedInterceptor这个类会在后续解析AOP执行流程时再做介绍。

下图就是该方法封装DynamicAdvisedInterceptor对象的结构:

  • target - 封装原始的Bean对象
  • advisors - spring aop advisor集

在这里插入图片描述

createProxyClassAndInstance

这个方法在ObjenesisCglibAopProxy类实现:

protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
    // Generate a new class if necessary and return it without creating a new instance.
    // This ignores any callbacks that have been set.
    // To create a new instance you will have to use reflection,
    // and methods called during the constructor will not be intercepted.
    // To avoid this problem, use the multi-arg create method.
    // 生成代理类
	Class<?> proxyClass = enhancer.createClass();
	Object proxyInstance = null;

	// 创建proxyInstance代理对象,实际上就是使用反射创建一个proxyClass的实例,代码省略

    // 设置callback代理拦截
	((Factory) proxyInstance).setCallbacks(callbacks);
	return proxyInstance;
}

Cglib生成代理类(暂时不分析)

不太懂,暂时不分析。

JDK代理

JDK代理示例

public static void main(String[] args) {

  LogService logService = new LogServiceImpl();

  Object proxyInstance = Proxy.newProxyInstance(
      SpringJdkProxyDemo.class.getClassLoader(),
      new Class[]{LogService.class},
      new MyInvocationHandler(logService));

  LogService o = (LogService) proxyInstance;
  o.log();
}

static class MyInvocationHandler implements InvocationHandler {

  private final Object object;

  public MyInvocationHandler(Object object) {
    this.object = object;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    System.out.printf("MyInvocationHandler %s args: %s\n",
        method.getName(), Arrays.toString(args));
    return method.invoke(this.object, args);
  }
}

interface LogService {
  void log();
}

static class LogServiceImpl implements LogService {

  @Override
  public void log() {
    System.out.println("LogServiceImpl.log()");
  }
}

JdkDynamicAopProxy.getProxy

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

代码比较简单,就是使用java的Proxy创建代理对象,第三个参数传递的是this,因为JdkDynamicAopProxy实现了InvocationHandler接口。

小结

在本文中,我们了解到了AnnotationAwareAspectJAutoProxyCreator后置处理器如何为Bean对象解析Advisor集、匹配可用Advisor集和创建代理对象的过程。

后续,我们将继续阅读CglibAopProxy和JdkDynamicAopProxy的代码,分析Spring AOP的执行流程。

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

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

相关文章

[附源码]计算机毕业设计汽车美容店管理系统Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

TDH社区版上新宽表数据库Hyperbase,轻松实现海量数据的毫秒级精确检索

日前&#xff0c;为了降低用户接触使用大数据技术的门槛以及成本&#xff0c;星环科技推出TDH社区版&#xff08;Transwarp Data Hub Community Edition&#xff09;来帮助企业用户、高校师生、科研机构以及其他专业开发人更简单、更便捷的进行大数据分析。为了满足更多用户在存…

IT统一运维软件发展趋势浅析

在企业IT数字化转型趋势下&#xff0c;为支撑业务敏捷、稳定、创新发展&#xff0c;IT运维的重要性已日益凸显。伴随着IT运维管理目标、管理范围、管理对象及管理深度的转变&#xff0c;IT运维平台建设呈现管理一体化、规模扩大化、业务可视化、运维自动化、运维智能化的关键趋…

成功实施企业内容管理(ECM)的 5 个技巧

成功实施企业内容管理&#xff08;ECM&#xff09;的 5 个技巧 俗话说&#xff0c;唯一不变的就是变化。在您不懈努力发展业务的过程中&#xff0c;您需要改变业务流程、不断升级产品、使用技术实现自动化、让您的员工和客户满意。 改变管理方式是实施企业内容管理的一个关键考…

Vue Element UI 中 el-table 树形数据 tree-props 多层级使用避坑

实现效果&#xff1a; element官网提示设置tree-props为{children: ‘children’,hasChildren: ‘hasChildren’}&#xff0c;data数据需要设置children和hasChildren属性&#xff0c;row-key也绑定了数据的唯一值变量id&#xff0c;但是树形结构的第三级就是出不来 如图 可以…

02、交换机

目录 1.、Exchange(交换机)的作用 2、Exchange(交换机)的类型 2.1.直连交换机&#xff1a;Direct Exchange 2.2.主题交换机&#xff1a;Topic Exchange 2.3.扇形交换机&#xff1a;Fanout Exchange 2.4.首部交换机&#xff1a;Headers exchange 2.5.默认交换机 2.6.Dead…

MySQL基础三问:底层逻辑、正在执行、日志观察

背景及目录&#xff1a; 经常面试会遇到且实际工作中也会应用到的三个场景&#xff1a; 一.mysql查询时的底层原理是什么&#xff1f; 二.如何查看正在执行的mysql语句&#xff1f; 三.如何观察mysql运行过程中的日志信息&#xff1f; - - - - - - - - - -分割线- - - - -…

【html5期末大作业】基于HTML+CSS+JavaScript管理系统页面模板

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

pyinstaller 操作以及常见问题解决

最近有需要用pyinstaller打包 直接上命令&#xff1a; pyinstaller -F xxx.py -w -i img.ico-F 指定需要打包的主文件&#xff0c;-w是让打包后的exe&#xff0c;运行不再出现cmd框&#xff0c;一般打包都会加上&#xff0c;-i 是指定打包后的图标ico文件 在线生成ico链接&am…

(14)点云数据处理学习——RGBD 里程计

1、主要参考 &#xff08;1&#xff09;官网 RGBD Odometry — Open3D 0.16.0 documentation 2、原理和实现 2.1 RGBD Odometry主要作用 RGBD里程计在两个连续的RGBD图像对之间查找相机运动。输入是RGBDImage的两个实例。输出是刚体变换形式的运动。Open3D实现了[Steinbruc…

Transformer——台大李宏毅详讲Transformer

文章目录李宏毅老师讲TransformerEncoderDecoderDecoder整体逻辑non-autoregressiveDecoder中的Cross Attention训练Seq2seq的一些Tips老师讲的超级棒&#xff0c;激动哭了&#xff1a; 视频链接&#xff1a;台大李宏毅21年机器学习课程 self-attention和transformer 李宏毅老师…

Mac版Word设置从第K页开始显示页码

tags: Word Tips 写在前面 最近有同学问我怎么在word文档中从第四页开始设置页码, 也就是首页以及第二页第三页的说明页都不编页码, 从第四页开始页码为1, 然后往后编号这种. 一开始我以为直接在页码设置处就有这个选项的(毕竟写东西几乎不用word, 也不熟悉), 后来发现还是t…

从Unity Robotics Hub入门Unity-ROS

0.简介 对于Unity而言&#xff0c;其拥有非常完备的物理特性&#xff0c;这对于机器人仿真是非常有用的&#xff0c;但是实际上Unity和ROS之间的通信一直是摆在两者之间的难题&#xff0c;正好看到宇宙爆肝锦标赛冠军写的这个系列&#xff0c;所以个人想参照为数不多的资料来进…

2022年创新药行业研究报告

第一章 行业概况 创新药&#xff0c;也称为原研药&#xff0c;是一个相对于仿制药的概念&#xff0c;指的是从机理开始源头研发&#xff0c;具有自主知识产权&#xff0c;具备完整充分的安全性有效性数据作为上市依据&#xff0c;首次获准上市的药物。新药上市要经历化合物的发…

学个Antenna:Matlab天线工具箱知多少(二)

学个Antenna是以天线仿真和调试为主&#xff0c;理论原理为辅的干货天线技术专栏&#xff0c;包括天线入门知识以及各类天线的原理简介、仿真软件建模、设计、调试过程及思路。如有想看到的内容或技术问题&#xff0c;可以在文尾写下留言。 ​摘要&#xff1a; 上节推文学个Ant…

A-Level商务例题解析及练习Sources of finance

今日知识点&#xff1a;Sources of finance 例题 Q: Discuss the benefits and drawbacks to a business of using only internal sources of finance to fund expansion. 解析 Answers could include:Outline of types of internal funding sources, such as building up rese…

我与足球以及世界杯的过往

中东土豪卡塔尔斥资2000亿举办的世界杯正在如火如荼的进行中&#xff0c;我也是几乎一场不落的看完了每一场的比赛直播【最近都没怎么更文。。。】&#xff0c;目前小组赛已经结束&#xff0c;我们也见证了很多球星的落寞背影与不甘&#xff0c;也见证了新星的天赋与成长&#…

E语言基本特征码/时钟反调试/窗体push大法

E语言基本特征码/时钟反调试/窗体push大法 该篇文章有以下内容&#xff1a; 1. 易语言字符串比较函数 test edx,3 F7C203000000 2. 按钮事件特征码&#xff1a;FF55FC5F5E,可以被修改恒成立 3. 易语言体 FF 25 4. 时钟的反调试 5. Push窗体大法 一、利用 test…

Qt-FFmpeg开发-音频解码为PCM文件(9)

Qt-FFmpeg开发-使用libavcodec API的音频解码示例&#xff08;MP3转pcm&#xff09; 文章目录Qt-FFmpeg开发-使用libavcodec API的音频解码示例&#xff08;MP3转pcm&#xff09;1、概述2、实现效果3、主要代码4、完整源代码更多精彩内容&#x1f449;个人内容分类汇总 &#x…

关于 registerForActivityResult()的使用方法,不能说详细,只能说略懂得例子

目录 1.情况说明 2.registerForActivityResult()的使用方法 1.情况说明 startActivityForResult();函数过时 使用了 registerForActivityResult()进行了代替 2.registerForActivityResult()的使用方法 数据来源 (2条消息) registerForActivityResult()的使用方法例子_发狂…