-
参数的二维码的应用
2021-01-08 17:55:27这个功能最直接的作用就是分析二维码的扫描情况,制作不同的二维码,用于不同的传播渠道,,那么我们就可以统计每个渠道的传播效果,便于找到最有效的推广方式,节约成本。 2、渠道销售情况监控 为每个Sales渠道生成...二维码应用极其广泛,但大家对于天天都接触的二维码真的了解吗?
或许你还不知道二维码背后有着强大的数据录入系统,不止是一个与客户联系的切入口,功能远超大家想象。二维码能对扫码源头数据追踪,实质上就是带参数的二维码,它会应用在哪些方面呢?下面将举例说明。
一、带参数二维码应用
1、广告效果,扫描情况分析
这个功能最直接的作用就是分析二维码的扫描情况,制作不同的二维码,用于不同的传播渠道,,那么我们就可以统计每个渠道的传播效果,便于找到最有效的推广方式,节约成本。2、渠道销售情况监控
为每个Sales渠道生成二维码,渠道利用自己专用的二维码进行销售成交,所有的相关数据都会统计到自己的二维码下面。商家就可以用这种方式来监控到每个工作人员的业绩。3、返利模式,让粉丝助力品牌影响力
为粉丝分配二维码,通过粉丝成交的订单都可以为粉丝返利,商家可以用这种方式让粉丝成为自己的销售员,大大地节约成本。4、与营销活动结合
为营销活动生成特定的二维码,用户在扫描之后公众平台直接回复活动图文回复,用户点击即可参与,减少用户操作成本,也可以配合进行宣传。这些带参数二维码是从何而来的呢?
有二维彩虹二维码生成器就够了,二维彩虹二维码生成器以用户账户体系为核心,致力于构建“客户数据资产私有化”的生态圈。企业通过后台数据分析来制定营销策略,永葆企业创新活力,驱动企业向前不断发展。二、二维码其他功能
(1)二维码美化功能
有了二维彩虹二维码,就不需要再为不会用PS修改二维码发愁,只需要注册登录简单几步快速更改二维码样式,将原二维码上传至二维彩虹官网,可对二维码样式、颜色、图眼、边框等内容进行编辑修改,以此来提升二维码的美观度,赋予二维码更高的辨识度。
(2)文档转换成二维码
通过二维彩虹网站的文档在线转换功能,可以把PPT、Word、PDF、Excel、H5等办公文档格式自动转换成可以通过手机在线查看的格式,方便用户扫码查看各种文档。
(3)微信二维码
同一个动态二维码,扫描可以进入不同的微信二维码。
(4)音视频转成二维码
MP3录音、音频生成二维码,扫描二维码即可播放。
(5)视频转成二维码
用户可将视频资料上传到二维彩虹对应窗口,后台会转码成二维码形式,降低视频大小,方便配合内容分发传播。
(6)网址链接转成二维码
不管是什么推广内容,只要有一个网址链接,就可以生成二维码。扫描二维码即可进入网站。
(7)多链接转成二维码
可以设置使用同一张二维码,在不同地点,不同时间,扫描不同次数的用户进入不同的网址链接。
(8)批量转成二维码
可以批量生成网址链接二维码。
(9)电子名片转成二维码
将个人信息生成一个电子名片二维码,比纸质名片囊括更多的信息,扫描二维码即可保存联系人信息到手机通讯录。
(10)Wifi 二维码
虽然大多数二维码都是链接到网页上的。但是二维码的作用不止如此,还可以用来链接wifi,也为您连接至网络增加了安全。把WiFi信息做成一个二维码,扫描就可以自动连上WiFi。原文链接:
带参数的码相关阅读:
胖婶煎饼店的新款动态收款码 -
激光工艺参数对离体皮肤显微组织特征的影响
2021-01-26 08:29:54深入分析激光焊接离体皮肤显微组织的变化,以基于灰度共生矩阵计算所得的二阶统计特征量作为表征离体皮肤显微组织特征的主要量化指标,通过响应面法分析得到了各激光参数以及它们之间的交互作用对激光焊接离体皮肤显微... -
基于迭代学习控制的原子力显微镜PID参数智能整定方法
2021-03-06 01:31:42比例积分微分(PID)参数在原子力显微镜(AFM)的成像过程中起着至关重要的作用。 传统的参数调整方法需要大量人力,并且在无人值守的工作环境中很难设置PID参数。 本文提出了一种基于迭代学习控制的PID参数智能整定... -
激光参数对纳米金靶向细胞膜通透性的影响
2021-02-10 11:36:10激光参数,比如脉宽、照射能量、脉冲数目以及照射方式等在各个方面尤其是在生物医学应用中对生物组织的不同效应都起着重要作用。采用纳米金颗粒靶向细胞, 激光照射后细胞膜的通透性会发生改变。采用不同的激光光源和... -
预氧化煤体的力学参数和导热特性关系研究
2020-05-11 18:14:05选取新疆硫磺沟长焰煤,使用程序升温箱对煤样进行30、80、140、200℃的预氧化处理,再借助MTS880电液伺服试验机、LFA457激光导热分析仪和FF35 CT工业断层扫描仪机来研究煤体力学参数、导热系数及内部孔裂隙的变化情况,... -
激光金属直接成形工艺参数对形貌自愈合能力的影响
2021-02-25 22:29:28探寻主要影响因素,以指导制定合理成形工艺、提高成形精度,通过正交实验方法,在具有“凸起”特征的基板上,采用激光金属直接成形技术获得了多组单道薄壁试样,分析工艺参数对试样尺寸的影响规律。对316 L不锈钢的... -
构建高质量的C#代码 完整扫描版
2014-06-04 12:24:488.1 创建类的作用与目的 8.2 C#中的类 8.2.1 创建类 8.2.2 创建类的实例 8.2.3 构造函数与析构函数 8.2.4 构造函数的重载 8.2.5 构造函数链 8.3 类成员的可访问性 8.4 变量 8.5 属性 8.6 方法 8.6.1 创建方法 8.6.2 ... -
-
石墨烯结构特征偏振参数非直观超显微的初步研究
2021-02-05 02:18:42精确地调制入射光波的偏振状态,利用光波与物质的耦合作用变化和携带近场物质结构信息的远场光场变化,通过分析不同偏振条件下所得图像变化,反演计算得到近场光波参数图像和物质结构特征,实现对物质结构和形貌的50... -
斜齿面激光熔覆的开裂倾向分析及控制研究
2021-02-05 03:39:03针对斜齿轮轴齿面激光熔覆层的开裂...采用两种扫描方法和熔覆参数进行组合,对斜齿轮齿面进行了熔覆试验,试验结果表明:双向扫描工艺对于齿面熔覆层开裂具有一定的抑制作用,并最终获得了适合齿面熔覆的工艺和参数。 -
激光强化渗氮过程的数值模型及分析
2021-02-26 12:34:28根据傅里叶传热方程和修正的菲克定律,建立了瞬态激光氮化温度场和浓度场的耦合模型,考虑移动热源形成的温度梯度对...增大热流量和降低扫描速度,都将使氮化深度增加,但参数的选择太高或太低,均得不到高质量的氮化结果。 -
煤岩微观结构分析及其与压裂液设计的关系研究
2020-04-17 18:48:25以指导压裂液设计,提高压裂液效率和降低煤储层伤害,采用显微光度计、环境扫描电子显微镜和CT扫描等微观结构分析方法,得到了不同维度和角度的煤样裂隙结构图,并定量分析了煤样裂隙参数。结果表明:不同区块煤样的主... -
Oracle优化日记:一个金牌DBA的故事 白鳝.扫描版
2015-02-06 13:42:03优化小技巧 STATSPACK报告在 优化项目中的作用 优化小技巧 STATSPACK报告头的解读 6月12日 阴谋 今日点评 优化小技巧 分析STATSPACK的负载数据 优化小技巧 重要的命中率指标 6月13日 演戏 今日点评 优化小技巧 Top ... -
激光热力效应对不同材料电化学刻蚀形貌的作用研究
2021-02-07 20:28:17通过扫描电子显微镜(SEM)对比分析了不同材料复合加工表面的微观形貌;并通过超景深三维显微镜对不同材料的加工深度、宽度与深宽比进行了分析。试验结果表明,在激光热力作用下,铝合金的电化学刻蚀质量和表面形貌较... -
小窑采空区对东露天矿安全生产的影响及危险性分析
2020-05-31 15:14:20以平朔东露天矿采空区勘查与治理为工程实例,分析了采空区对大型露天煤矿安全生产造成的严重影响,介绍了钻孔式三维激光扫描仪在确定采空区尺寸参数中的作用,提出了露天煤矿境界范围内采空区的危险性分析方法,同时给出... -
SpringBoot(四) SpringBootBean扫描介入及自动装配
2020-12-30 19:06:52这里开始分析SpringBoot如何让spring扫描并自动装配。 BeanDefition 这里需要提到Spring一个重要知识点—BeanDefition。Spring实例化bean是一个复杂的过程,不仅仅金是new出实例,Bean有很多属性例如作用域、懒加载...这里开始分析SpringBoot如何让spring扫描并自动装配。
BeanDefition
这里需要提到Spring一个重要知识点—BeanDefition。Spring实例化bean是一个复杂的过程,不仅仅金是new出实例,Bean有很多属性例如作用域、懒加载、别名等。Spring通过BeanDefition记录bean构造时的属性值、构造函数参数值以及具体实现提供的进一步信息。
核心-SpringBoot扫描bean
Spring 启动过程首先扫描获取所有BeanDefition,放入一个map中,然后执行初始化步骤,将所有单例bean初始化(实例化)并放入容器中。
而SpringBoot的自动装配,即是在扫描阶段,把需要自动装配的bean加入BeanDefition Map中。然后Spring自动初始化这些bean。
SpringBoot如何介入SpringBean扫描阶段,通过扫描获取到这些用户没有配置但会自动装配的BeanDefition 呢?
下面是Spring容器Context刷新的源码,仅看名字,貌似也看不出来哪个方法执行了bean扫描。每个方法点进去都是庞大的一堆代码,好在这段代码结构比较清晰,我们通过断点一步步查看,到底哪个方法扫描了需要自动装配的BeanDefition 。
@Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }
执行完postProcessBeanFactory方法后,beanDefinitionMap中存在6个BeanDefition,除了入口类,都是processor,显然不对,继续走。。
执行完invokeBeanFactoryPostProcessors方法,beanDefinitionMap中的对象暴增到124了,其中还有我自己定义的一个bean myConfig。
可猜测invokeBeanFactoryPostProcessors承载了SpringBoot接入bean扫描的工作,看方法描述主要作用是实例化并调用所有已注册的BeanFactoryPostProcessor bean。invokeBeanFactoryPostProcessors方法比较长,大致的处理逻辑如下:
分离出BeanDefinitionRegistryPostProcessor和BeanFactoryPostProcessor类型的处理器,优先对BeanDefinitionRegistryPostProcessor类型处理,然后处理BeanFactoryPostProcessor类型,且每次都按照优先级及处理,优先级为:
实现接口PriorityOrdered > 实现接口Ordered > 剩下的而符合BeanDefinitionRegistryPostProcessor且实现接口PriorityOrdered的处理器为:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor这是容器创建时AnnotatedBeanDefinitionReader自动添加了其对应的BeanDefinition,对应的class为ConfigurationClassPostProcessor。
ConfigurationClassPostProcessor是一个重量级的处理器,加载所有Configuration配置类public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor, PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware
按照上述逻辑,ConfigurationClassPostProcessor是最早被执行的,因为它既是BeanDefinitionRegistryPostProcessor类型,而且实现了PriorityOrdered接口,所以首先对其进行实例化。
然后开始执行BeanDefinition的发现与注册,发现步骤用于解析获取Configuration中定义了哪些获取bean的方式,注册步骤用于将所有定义的bean转化注册,会立即注册扫描到的Configuration类。
BeanDefinition发现
- 使用ConfigurationClassParser工具类转化配置类(如SpringBoot项目入口main方法的类)。
- 解析配置类的元数据处理转化为ConfigurationClass,doProcessConfigurationClass,包括@PropertySource、@ComponentScan、@Import(延时处理)、@ImportResource、@Bean及接口方法等,基本的configuration已经处理完毕
。 - 处理@Import涉及的所有配置的Configuration,SpringBoot项目时使用EnableAutoConfiguration.AutoConfigurationImportSelector将META-INF/spring.factories下的所有自动注入类,用于接下来转化为ConfigurationClass,这个注解是在入口类的SpringBootApplication里的EnableAutoConfiguration注解中设置的。
/** * Return the auto-configuration class names that should be considered. By default * this method will load candidates using {@link SpringFactoriesLoader} with * {@link #getSpringFactoriesLoaderFactoryClass()}. * @param metadata the source metadata * @param attributes the {@link #getAttributes(AnnotationMetadata) annotation * attributes} * @return a list of candidate configurations */ protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()); Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you " + "are using a custom packaging, make sure that file is correct."); return configurations; }
BeanDefinition条件判断与跳过
上一步中获取需要注入的类后,执行ConfigurationClassParser.parse方法,将所有扫描到的configurationClass进一步处理,判断是否需要转化,如需要则递归获取configuration类。
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException { if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) { return; } ConfigurationClass existingClass = this.configurationClasses.get(configClass); if (existingClass != null) { if (configClass.isImported()) { if (existingClass.isImported()) { existingClass.mergeImportedBy(configClass); } // Otherwise ignore new imported config class; existing non-imported class overrides it. return; } else { // Explicit bean definition found, probably replacing an import. // Let's remove the old one and go with the new one. this.configurationClasses.remove(configClass); this.knownSuperclasses.values().removeIf(configClass::equals); } } // Recursively process the configuration class and its superclass hierarchy. SourceClass sourceClass = asSourceClass(configClass, filter); do { sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter); } while (sourceClass != null); this.configurationClasses.put(configClass, configClass); }
这就是springboot最核心的东西了,上面代码中的判断是否跳过,基于Spring条件注解,满足注解条件则会加载类,最终定义的bean会被加入到BeanDefinitionMap中,最终会自动装配,否则会被忽略。
条件判断
条件判断的支持来源于autoconfiguration包,判断是否是web项目,判断是否存在配置,判断是否存在类等。
如下示例,如果配置文件中配置了“myconfig”则类会被加载并实例化到容器中,否则不会。/** * <p></p> * * @author nec * @since 2020-10-09 15:18 */ @Component @ConditionalOnProperty({"myconfig"}) public class MyConfig { @Bean public TestUtil getTestUtil() { System.out.println("============>start init getTestUtil!!!"); return new TestUtil(); } }
如果判断条件是否满足呢,根据类头上的条件注解,就像上述类,注解了@ConditionalOnProperty({“myconfig”}),那么判断这个类是否可以加载就在于condition.matches能否匹配到Property中存在"myconfig"这个条件,存在就返回true,这个类就会执行转化操作最终获取beanDefination,否则跳过处理下一个configuration。
/** * Determine if an item should be skipped based on {@code @Conditional} annotations. * @param metadata the meta data * @param phase the phase of the call * @return if the item should be skipped */ public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) { if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) { return false; } if (phase == null) { if (metadata instanceof AnnotationMetadata && ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) { return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION); } return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN); } List<Condition> conditions = new ArrayList<>(); for (String[] conditionClasses : getConditionClasses(metadata)) { for (String conditionClass : conditionClasses) { Condition condition = getCondition(conditionClass, this.context.getClassLoader()); conditions.add(condition); } } AnnotationAwareOrderComparator.sort(conditions); for (Condition condition : conditions) { ConfigurationPhase requiredPhase = null; if (condition instanceof ConfigurationCondition) { requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase(); } if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) { return true; } } return false; }
再进一步,怎么匹配的?如果是ConditionalOnProperty条件,则由对应的OnPropertyCondition处理,会根据条件值,从environment中找是否存在对应的配置。找到返回匹配,否则不匹配!
示例-自动开启aop
最后来看一个完整的自动装配示例,aop如何自动启用。
Spring项目如果需要开启AOP,需要手动设置@EnableAspectJAutoProxy。
而SpringBoot AopAutoConfiguration默认是支持自动启用aop的,因为matchIfMissing =true表示即使未配置spring.aop.auto,也会匹配,如果要关闭自动配置,则需要设置spring.aop.auto=false
当存在Advice.class时,AspectJAutoProxyingConfiguration 会自动启用,然后根据spring.aop.proxy-target-class是否配置来决定使用jdk动态代理还是cglib动态代理,可以看到
JdkDynamicAutoProxyConfiguration 的匹配matchIfMissing = false,表示条件匹配不到不生效
CglibAutoProxyConfiguration 的匹配matchIfMissing = true,表示条件匹配不到也会生效所以springboot默认启用的是cglib动态代理!
看下源码,注释表示自动开启aop相当于手动启用@EnableAspectJAutoProxy。
Equivalent to enabling {@link EnableAspectJAutoProxy @EnableAspectJAutoProxy} in your configuration./** * {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration * Auto-configuration} for Spring's AOP support. Equivalent to enabling * {@link EnableAspectJAutoProxy @EnableAspectJAutoProxy} in your configuration. * <p> * The configuration will not be activated if {@literal spring.aop.auto=false}. The * {@literal proxyTargetClass} attribute will be {@literal true}, by default, but can be * overridden by specifying {@literal spring.aop.proxy-target-class=false}. * * @author Dave Syer * @author Josh Long * @since 1.0.0 * @see EnableAspectJAutoProxy */ @Configuration(proxyBeanMethods = false) @ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true) public class AopAutoConfiguration { @Configuration(proxyBeanMethods = false) @ConditionalOnClass(Advice.class) static class AspectJAutoProxyingConfiguration { @Configuration(proxyBeanMethods = false) @EnableAspectJAutoProxy(proxyTargetClass = false) @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false) static class JdkDynamicAutoProxyConfiguration { } @Configuration(proxyBeanMethods = false) @EnableAspectJAutoProxy(proxyTargetClass = true) @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true) static class CglibAutoProxyConfiguration { } } @Configuration(proxyBeanMethods = false) @ConditionalOnMissingClass("org.aspectj.weaver.Advice") @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true) static class ClassProxyingConfiguration { ClassProxyingConfiguration(BeanFactory beanFactory) { if (beanFactory instanceof BeanDefinitionRegistry) { BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory; AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry); AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); } } } }
下面来验证下
首先引入aop的starter<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
编写aop切面代码
aop配置类@Aspect @Component public class AopTest { @Before("execution(* cn.nec.test.util.TestUtil.sayHi())") public void around() { System.out.println("aop before"); } }
被切的类
@Component public class TestUtil { public void sayHi() { System.out.println("Hi!"); } }
测试类
@RestController @RequestMapping("/test") public class TestController { @Autowired public TestUtil testUtil; @GetMapping("/test") public String test(){ testUtil.sayHi(); return "success"; } }
- 配置文件不做任何配置直接启动,访问接口http://127.0.0.1:8080/test/test,结果如下
- 手动关闭aop自动配置,再次测试,结果如下
- 关闭aop自动配置后,手动开启aop,结果如下
- 总结:springboot aop自动装配相当于帮我们自动设置了@EnableAspectJAutoProxy,而如果不引入aop-starter,则需要手动引入aop依赖jar的maven配置,并且设置@EnableAspectJAutoProxy
-
灰色关联分析选区激光烧结成型研究
2021-02-05 01:58:03用灰色关联分析法(GRA)研究工艺参数对成型精度的影响。结果表明,对于翘曲量及尺寸精度最重要的影响因子为扫描速度。综合工艺参数对翘曲量和尺寸误差的影响,得到激光烧结PP 制件的优化工艺参数:扫描速度为1.9 m/s... -
Fortify-SCA-扫描工具指导手册.pdf
2019-09-06 21:33:30fortify扫描工具的说明手册,对实际工作有指导作用,讲的比较清晰。 Fortify SCA分析原理 Front-End 3rd party IDE Java Pug-In C/C++ MicrOsoL NET IBM.eclipse Audit workbench PLSQL XML Analysis Engine ... -
Fe-C混合粉末激光烧结成形致密度分析
2021-02-12 11:34:51分析了金属粉末激光选区烧结成形过程致密化机制。...成形参数对致密度的影响归结于烧结过程中产生的液相量, 激光功率的增加、扫描间隔的减小、扫描速度的降低和切片层厚的减少都会提高烧结件的致密度。 -
数值分析 使用c语言 源码_如何使用数值端口进行 RF 分析
2021-01-07 23:13:26通过添加频域或自适应频率扫描研究,可以获得 S 参数和史密斯图。数值端口还能使我们计算工作在横向电磁(TEM)模式下的传输线的特征阻抗。入门:波导适配器示例波导适配器的作用是最大限度减少工作频率的反射。下述...使用 COMSOL Multiphysics® 软件中提供的数值端口功能和附加 RF 模块,可以通过边界模式分析以数值方式计算具有任意形状的端口模式。通过添加频域或自适应频率扫描研究,可以获得 S 参数和史密斯图。数值端口还能使我们计算工作在横向电磁(TEM)模式下的传输线的特征阻抗。
入门:波导适配器示例
波导适配器的作用是最大限度减少工作频率的反射。下述波导适配器模型介绍了如何使用数值端口来计算这类适配器的散射参数(S 参数)。在该示例中,适配器用于传播介于矩形和椭圆形波导之间的微波。
“玫瑰之旅”:当未变形的波传播时,可以添加形状优美的等值面图来可视化电场分量。是否可以通过边界条件使任意形状的波导和传输线与所需模式完美匹配、激励和终止?
矩形端口(端口 1)由横向电(TE)波激励,该横向电(TE)波在传播方向上不具有电场分量。选择激发频率使 TE10 模式是通过矩形波导的唯一传播模式。尽管端口 1 处的 TE10 模式的形状是解析已知的,但该模型使用了能处理具有任意形状的端口边界的数值端口。
波导的椭圆形端也使用数值端口(端口 2)建模。此端口是被动的,但仍需要特征值分析来设置模拟的边界条件。由于数值端口的模式形状未知,因此每个端口都需要进行边界模式分析研究,然后进行频域研究。
在波导适配器模型中,可识别的频率范围在 6.6 GHz 和 14.7 GHz 之间,其中 TE 10 模式通过端口1激励。在频率为 10 GHz 下模拟电场的 x 分量和 S 参数,如下图所示。
图中显示了 S11 和 S21 参数(以 dB 为单位)与频率的函数关系图。S11 参数描述了反射,S21 参数测量了波导适配器在端口 1 被激励时通过端口 2 传输的波。注意:
如果要计算S参数,则不可能一次激励多个端口
我们必须为每个数值端口添加一个边界模式分析节点,无论它是否被激励,这将导致研究在模型开发器中节点下的“边界模式分析 1,边界模式分析 2,…,频域 1,…”等研究序列
使用数值端口将电场分析为TEM模式
对于 TEM 波或准 TEM 波,到端口边界的电场法向分量(即与传播方向平行的纵向分量)可以忽略不计。这种情况下,数值端口可以选择将场作为TEM模式分析。
在使用分离环谐振器模型的陷波滤波器中,裂环谐振器耦合到微带线,该微带线由数值 TEM 端口激励和终止。该电路用作陷波(也称为带阻)滤波器,能够抑制特定的信号频率范围。带阻频率接近 2.4 GHz,如 S 参数图所示。S11 和 S21 参数(以 dB 为单位)与频率的函数关系图。
有趣的是,在 2.1 GHz(通带)和 2.4 GHz(阻带)两个频率下传播的电场可视化结果如下图所示。
在 2.1 GHz(左)和 2.4 GHz(右)电场下的 z 分量。-两个动画中的 xy- 切面均已变形以用于可视化。(
在此模型中,数值 TEM 端口用于通过选中“ 作为 TEM 场分析”选项来分析波传播。完成此操作后,端口模式场将按计算阻抗和参考阻抗的比率进行缩放。因此,我们必须在端口节点下定义电场和磁场积分线来计算特征阻抗。端口 1 的“设置”窗口如下所示,端口 2 的积分线也可以类似的方式设置。
陷波滤波器模型中端口 1 的电压积分线(上)和电流积分线(下)子功能的设置窗口。用模态分析计算特征阻抗
当波以 TEM 模式传播时,可以计算传输线的特征阻抗。案例库中提供的两个示例是同轴电缆传输线和平行线传输线模型。横截面在二维中模拟。对于两个传输线示例,电压(V)计算为导体之间电场的线积分。电流(I)计算为沿着导体边界之一的磁场的线积分或任何将导体之间区域分成两部分的闭合轮廓的线积分。两个模型的积分线如下图所示。
特征阻抗计算为电压除以电流。要做到这一点,我们需要定义两个积分耦合算子,int_E 和 int_H,分别计算的电压和电流。定义如下表所示:
这里,t1x 和 t1y 是沿积分边界的切向矢量分量(“1” 指的是边界维度)。emw 前缀给出了电场和磁场矢量分量的正确物理场接口范围。名称 表达式 单位 描述 V int_E( – emw.Ex*t1x – emw.Ey*t1y ) V 电压 I – int_H( emw.Hx*t1x + emw.Hy*t1y ) A 电流 Z V/I Ω 特征阻抗 对于三维模型,边界模式分析与数值端口结合用于分析端口边界。如上述二维模型所描述,通过计算沿着子功能定义的边界,如电压积分线和电流积分线的电压与电流的比率来计算特征阻抗。
结语本文回顾了 RF 模块中使用数值端口功能的一般过程。通过端口功能中的数值类型功能,我们可以为任意形状的波导和传输线计算所需模式,以及激励和终止结构。为了找到模场,我们必须在执行频域研究之前为每个数值端口添加边界模式分析研究步骤,以计算 S 参数,无论端口是否被激励。此外,我们还演示了当波在 TEM 模式下传播时,使用边界模式分析研究结合数值端口计算特征阻抗的几个示例。在计算电压、电流和特征阻抗时,使用定义积分线的方法总结了两个典型的传输线示例。
推荐阅读
数值 TEM 端口在模拟中的使用
-
Unity 3.x游戏开发经典教程,完整扫描版
2014-10-09 13:57:43本书通过从零开始创建一个完整的Unity游戏项目,在对游戏进行一步步创建、完善的...另外,书中对于每个工具的使用方法、操作小技巧都有介绍,对每个组件中各个参数的作用都有分析,还拓展了一些游戏开发方面的知识。 -
C程序设计 第四版 谭浩强 高清扫描版 带完整书签目录 加 学习辅导
2018-04-06 11:31:153.3.1 C语句的作用和分类 3.3.2 最基本的语句——赋值语句 3.4 数据的输入输出 3.4.1 输入输出举例 3.4.2 有关数据输入输出的概念 3.4.3 用printf函数输出数据 3.4.4 用scanf函数输入数据 3.4.5 字符数据的输入输出 ... -
-
[Visual Studio程序员箴言] 美 福特 扫描版
2013-12-27 08:51:33本书的作者是一位经验丰富的Visual Studio测试工程师 她将自己平日博客上的Visual Studio使用技巧归纳成书 并通过图例让读者对技巧的作用了如指掌 无论是对Visual Studio初学者还是经验丰富的用户来说 了解 掌握并在... -
JavaScript语言精粹(修订版) 中文版高清扫描pdf
2015-02-05 19:04:02分析JavaScript 一个简单的试验场 第2 章 语法 空白 标识符 数字 字符串 语句 表达式 字面量 函数 第3 章 对象 对象字面量 检索 更新 引用 原型 反射 枚举 删除 减少全局... -
[Oracle.11g权威指南(第2版)].谷长勇.扫描版.pdf
2013-06-23 21:16:09本章讲述了联机重做日志的作用和管理,联机重做日志是数据库正常运行不可或缺的文件,对于实例出现故障时的正常恢复是十分重要的。 11.1 管理重做日志文件 284 11.2 日志文件组、日志切换和日志归档 284 11.3 了解... -
硫酸盐腐蚀与冻融共同作用下井壁混凝土损伤劣化研究
2020-05-15 04:40:13通过试验研究了初始损伤井壁混凝土随硫酸盐腐蚀和冻融循环次数的增加,其质量、超声波传播速度及强度等的变化,运用损伤力学对损伤进行定量评价和参数拟合并借助环境扫描电镜观测分析混凝土微结构的变化。试验结果表明... -
Springboot集成tkMybatis源码分析
2021-03-04 16:40:26前言 本文不介绍springboot如何集成tkMybatis,只介绍下集成的原理 ...我们直接从入口开始,tkMybatis的扫描注解MapperScan,该注解使用时需要配置扫描的路径,参数为value @Retention(RetentionPolicy.R前言
本文从源码层面介绍springboot如何集成tk Mybatis,至于如何配置,本文不做介绍。
本文适用于使用过tk mybatis,但是不清楚其机制的同学
什么是tk Mybatis
tk Mybatis是对Mybatis封装的一个框架,有点类似于hibernate的jpa,其作用是可以简化增删改查的sql语句,以及可以支持不使用mapper.xml配置文件,并且在接口上也可以支持编写sql语句等功能
源码
我们直接从入口开始,tk Mybatis的扫描注解MapperScan,该注解使用时需要配置扫描的路径,参数为value
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import(tk.mybatis.spring.annotation.MapperScannerRegistrar.class) public @interface MapperScan { String[] value() default {}; ... }
可以看到使用了@Import导入了一个MapperScannerRegistrar,@Import接口属于springboot的知识,可以理解为会加载该类,该类实现了ImportBeanDefinitionRegistrar接口,所有springboot启动时会调用他的registerBeanDefinitions方法
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware { public static final Logger LOGGER = LoggerFactory.getLogger(MapperScannerRegistrar.class); private ResourceLoader resourceLoader; private Environment environment; @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { // 核心代码1 AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName())); ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry); ...... // 核心代码2 List<String> basePackages = new ArrayList<String>(); for (String pkg : annoAttrs.getStringArray("value")) { if (StringUtils.hasText(pkg)) { basePackages.add(pkg); } } for (String pkg : annoAttrs.getStringArray("basePackages")) { if (StringUtils.hasText(pkg)) { basePackages.add(pkg); } } for (Class<?> clazz : annoAttrs.getClassArray("basePackageClasses")) { basePackages.add(ClassUtils.getPackageName(clazz)); } ...... scanner.registerFilters(); // 核心代码3 scanner.doScan(StringUtils.toStringArray(basePackages)); } ..... }
这里不是核心的代码就删掉了,避免看的混乱,只分析核心代码,核心代码1和2即获取MapperScan配置的value值,拿到要扫描的包路径
核心代码3,即扫描该包路径,注册bean,核心代码3的源码
@Override public Set<BeanDefinitionHolder> doScan(String... basePackages) { // 核心代码1 Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages); if (beanDefinitions.isEmpty()) { logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration."); } else { // 核心代码2 processBeanDefinitions(beanDefinitions); } return beanDefinitions; }
核心代码1,调用super.doScan,super.doScan是spring的扫描方法, 最后就是扫描出标记了@Component的bean,最后会在spring的BeanDefinition的map容器中注册BeanDefinition的信息,key是bean的名称,value是封装的BeanDefinition
beanDefinitionMap 定义:
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(64);核心代码2
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) { GenericBeanDefinition definition; for (BeanDefinitionHolder holder : beanDefinitions) { definition = (GenericBeanDefinition) holder.getBeanDefinition(); if (logger.isDebugEnabled()) { logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + definition.getBeanClassName() + "' mapperInterface"); } // the mapper interface is the original class of the bean // but, the actual class of the bean is MapperFactoryBean definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59 // 核心代码1 definition.setBeanClass(this.mapperFactoryBean.getClass()); ...... } }
这个方法是给BeanDefinitionHolder增加了很多额外的属性,在后面会用到,其中比较重要的是核心代码1处setBeanClass,设置的beanClass为class tk.mybatis.spring.mapper.MapperFactoryBean
然后扫描工作就完成了,在spring的beanDefinitionMap 容器中注册了所有bean的信息,这里存储的是所有bean的信息,不止mybatis的bean
然后就要涉及到spring中的bean的创建了,在bean创建时会调用AbstractBeanFactory的doGetBean,结果层层调用来到了AbstractAutowireCapableBeanFactory的getSingletonFactoryBeanForTypeCheck方法,这里可以打断点看看,调用链太长,此处无法分析
private FactoryBean<?> getSingletonFactoryBeanForTypeCheck(String beanName, RootBeanDefinition mbd) { synchronized (getSingletonMutex()) { BeanWrapper bw = this.factoryBeanInstanceCache.get(beanName); ...... if (instance == null) { // 核心代码1 bw = createBeanInstance(beanName, mbd, null); instance = bw.getWrappedInstance(); } } finally { // Finished partial creation of this bean. afterSingletonCreation(beanName); } FactoryBean<?> fb = getFactoryBean(beanName, instance); if (bw != null) { this.factoryBeanInstanceCache.put(beanName, bw); } return fb; } }
核心代码1,调用createBeanInstance获得了一个bw,createBeanInstance会根据我们之前的BeanDefinition的beanClass创建一个MapperFactoryBean实例,并且由bw包装,调用bw.getWrappedInstance()后获得了一个MapperFactoryBean实例,方法里面代码太长,这里就不贴了
然后来到了依赖注入,当我们注入我们的Mybatis接口时,会调用spring的AbstractAutowireCapableBeanFactory的doCreateBean
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { // Instantiate the bean. BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { // 核心代码1 instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { instanceWrapper = createBeanInstance(beanName, mbd, args); } final Object bean = instanceWrapper.getWrappedInstance(); Class<?> beanType = instanceWrapper.getWrappedClass(); if (beanType != NullBean.class) { mbd.resolvedTargetType = beanType; } // Allow post-processors to modify the merged bean definition. synchronized (mbd.postProcessingLock) { if (!mbd.postProcessed) { try { applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); } catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed", ex); } mbd.postProcessed = true; } } // Eagerly cache singletons to be able to resolve circular references // even when triggered by lifecycle interfaces like BeanFactoryAware. boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { if (logger.isTraceEnabled()) { logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } // Initialize the bean instance. Object exposedObject = bean; try { populateBean(beanName, mbd, instanceWrapper); // 核心代码2 exposedObject = initializeBean(beanName, exposedObject, mbd); } }
核心代码1,从spring的factoryBeanInstanceCache里面获取beanName对应的实例,这里可以获得一个BeanWrapper,BeanWrapper里面就是包装了我们需要的MapperFactoryBean实例
核心代码2
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) { if (System.getSecurityManager() != null) { AccessController.doPrivileged((PrivilegedAction<Object>) () -> { invokeAwareMethods(beanName, bean); return null; }, getAccessControlContext()); } else { invokeAwareMethods(beanName, bean); } Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } try { // 核心代码3 invokeInitMethods(beanName, wrappedBean, mbd); } catch (Throwable ex) { throw new BeanCreationException( (mbd != null ? mbd.getResourceDescription() : null), beanName, "Invocation of init method failed", ex); } if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wrappedBean; }
核心代码3
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd) throws Throwable { boolean isInitializingBean = (bean instanceof InitializingBean); if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) { if (logger.isTraceEnabled()) { logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'"); } if (System.getSecurityManager() != null) { try { AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> { ((InitializingBean) bean).afterPropertiesSet(); return null; }, getAccessControlContext()); } catch (PrivilegedActionException pae) { throw pae.getException(); } } else { // 核心代码1 ((InitializingBean) bean).afterPropertiesSet(); } } ...... }
核心代码1,调用bean的afterPropertiesSet方法,MapperFactoryBean最终继承了DaoSupport类,DaoSupport实现了InitializingBean,所以会调用afterPropertiesSet方法
public abstract class DaoSupport implements InitializingBean { /** Logger available to subclasses. */ protected final Log logger = LogFactory.getLog(getClass()); @Override public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException { // Let abstract subclasses check their configuration. // 核心代码 checkDaoConfig(); // Let concrete implementations initialize themselves. try { initDao(); } catch (Exception ex) { throw new BeanInitializationException("Initialization of DAO failed", ex); } } ...... }
核心代码处调用了checkDaoConfig,这里使用的是模板方法模式,所以会调用到MapperFactoryBean的checkDaoConfig
protected void checkDaoConfig() { super.checkDaoConfig(); notNull(this.mapperInterface, "Property 'mapperInterface' is required"); Configuration configuration = getSqlSession().getConfiguration(); if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) { try { // 核心代码 configuration.addMapper(this.mapperInterface); } catch (Exception e) { logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e); throw new IllegalArgumentException(e); } finally { ErrorContext.instance().reset(); } } }
核心代码,调用了configuration.addMapper,这个属于Mybatis的核心代码了
public <T> void addMapper(Class<T> type) { mapperRegistry.addMapper(type); }
继续跟
public <T> void addMapper(Class<T> type) { if (type.isInterface()) { if (hasMapper(type)) { throw new BindingException("Type " + type + " is already known to the MapperRegistry."); } boolean loadCompleted = false; try { // 核心代码 knownMappers.put(type, new MapperProxyFactory<>(type)); // It's important that the type is added before the parser is run // otherwise the binding may automatically be attempted by the // mapper parser. If the type is already known, it won't try. MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type); parser.parse(); loadCompleted = true; } finally { if (!loadCompleted) { knownMappers.remove(type); } } } }
核心代码,在knownMappers里面put了接口和MapperProxyFactory的对应关系,然后这里就结束了
依赖注入时注入的Mapper对象是通过MapperFactoryBean调用getObject得到的,所以我们查看getObject方法
@Override public T getObject() throws Exception { return getSqlSession().getMapper(this.mapperInterface); }
然后调用SqlSessionTemplate的getMapper
public <T> T getMapper(Class<T> type) { return getConfiguration().getMapper(type, this); }
然后调用到Configuration的getMapper
public <T> T getMapper(Class<T> type, SqlSession sqlSession) { return mapperRegistry.getMapper(type, sqlSession); }
public <T> T getMapper(Class<T> type, SqlSession sqlSession) { // 核心代码1 final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type); if (mapperProxyFactory == null) { throw new BindingException("Type " + type + " is not known to the MapperRegistry."); } try { // 核心代码2 return mapperProxyFactory.newInstance(sqlSession); } catch (Exception e) { throw new BindingException("Error getting mapper instance. Cause: " + e, e); } }
核心代码1,从knownMappers 获取我们之前put进去的对象,也就是MapperProxyFactory
核心代码2,调用MapperProxyFactory的newInstance,这里就可以看出来,这个newInstance和我们设计模式的动态代理非常像
查看MapperProxyFactory类,果然是使用了jdk的动态代理模式
public class MapperProxyFactory<T> { private final Class<T> mapperInterface; private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>(); public MapperProxyFactory(Class<T> mapperInterface) { this.mapperInterface = mapperInterface; } public Class<T> getMapperInterface() { return mapperInterface; } public Map<Method, MapperMethod> getMethodCache() { return methodCache; } @SuppressWarnings("unchecked") protected T newInstance(MapperProxy<T> mapperProxy) { return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); } public T newInstance(SqlSession sqlSession) { final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); } }
也就是调用我们的mapper接口时会使用动态代理,最终调用到MapperProxy的invoke方法。
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, args); } else if (isDefaultMethod(method)) { return invokeDefaultMethod(proxy, method, args); } } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } final MapperMethod mapperMethod = cachedMapperMethod(method); // 核心代码 return mapperMethod.execute(sqlSession, args); }
接下去的代码全部都是mybatis核心类库的代码,主要是调用mapperMethod.execute执行增删改查,我们就不细看了。
至此我们的整体流程是可以梳理清楚的
总结
从上文可以看出,tk mybatis里面整合spring时,用到了大量spring的扩展机制,以及BeanDefinition、FactoryBean等都属于spring的类库,如果对spring不熟悉的话,看起来还是比较费解的。
最后再梳理下整体流程:
- 提供MapperScan配置扫描路径,并且依赖其注解@Import来注册BeanDefinition到spring的beanDefinitionMap容器中
- 把我们mapper接口对应的bean包装为MapperFactoryBean,即工厂bean
- MapperFactoryBean继承了DaoSupport类,DaoSupport实现了InitializingBean,所以会调用afterPropertiesSet,通过DaoSupport的afterPropertiesSet的模板模式方法checkDaoConfig调用到MapperFactoryBean的checkDaoConfig,然后在mybatis自己的Configuration对象中注册了mapper信息
- 注入时调用的是MapperFactoryBean的getObject,从而到mybatis的Configuration中根据接口获取了实例MapperProxyFactory,并且调用newInstance获取了一个真正的实例对象MapperProxy
- MapperProxy是一个动态代理类,所以调用时也就是调用了MapperProxy的invoke方法,然后在invoke中调用mybatis自己的execute进行增删改查
以上就是tk mybatis整合到springboot时的一些流程
-
激光熔覆镍基合金形貌优化及残余应力分析
2021-02-21 21:24:54研究结果表明:扫描速度和离焦量对宽高比的影响最显著,离焦量和激光功率对稀释率的影响最显著,且激光功率和扫描速度的交互作用对稀释率存在影响。最后试验验证了预测模型的准确性。选取优化后的熔覆层形貌和工艺参数... -
TiAl合金表面激光重熔复合陶瓷涂层温度场数值模拟及组织分析
2021-02-10 13:54:37在优化的工艺参数下, 采用相对较低的激光重熔功率和较低的扫描速度能够获得厚度较大的重熔区和烧结区。实验结果表明,重熔后的陶瓷涂层形成了晶粒细小且致密的等轴晶重熔区、烧结区和片层状残余等离子体喷涂区, 并且...