首页 文章 精选 留言 我的

精选列表

搜索[图数据库],共10000篇文章
优秀的个人博客,低调大师

拒绝 “ghs”,约会软件开源 “色图” 识别 AI

Bumble 是一款在线约会应用程序,也是美国市场中仅次于 Tinder 的第二大约会应用。作为一款主打陌生人社交的软件,Bumble 的用户(主要是女性)以往经常遭受到陌生人发送的色情图片骚扰。 为了打击这样的行为,2019 年,Bumble 在应用中推出了一个名为 Private Detector 的 AI 功能。Private Detector 的工作原理是自动模糊处理聊天时分享的色情图像,接收方可以自己决定是否查看或拦截该图片,觉得这种行为冒犯了自己的也可以向 Bumble 举报。 近日,为了进一步打击 "网络色情内容" ,致力于使互联网变得更加安全,Bumble 决定在 GitHub 上开源 Private Detector,并希望它能够被更广泛的技术社区/企业所采用。 Private Detector 使用了大量数据集进行训练,并使用了经过精心挑选阴性样本(不包含任何色情内容的样本),以更好地反映 “打擦边球” 的情况和人体的非敏感部位(如腿、胳膊),以便不将其标记为滥用;之后迭代地将样本添加到训练数据集中,以反映实际用户的行为或测试错误分类。 该模型还利用 Bumble 由 GPU 驱动的数据中心,并在数据集、网络和超参数(用于加速或提高训练性能的设置)优化的持续练习中进行了训练。 根据 Bumble 的数据分析显示,Private Detector 在不同条件下的性能(离线和在线)均达到了世界一流的水平(在上采样和类似生产的设置中,准确率 > 98%)。 Private Detector 基于 Apache-2.0 协议,开发者可以将其作为标准来实现,以按原样模糊处理色情图像,或者使用额外的训练样本对其进行微调。仓库里除了有 Private Detector 的源代码,还包括大量的文档和如何执行这些操作的指南。 项目地址:https://github.com/bumble-tech/private-detector

优秀的个人博客,低调大师

一张图彻底搞懂Spring循环依赖

1 什么是循环依赖? 如下图所示: BeanA类依赖了BeanB类,同时BeanB类又依赖了BeanA类。这种依赖关系形成了一个闭环,我们把这种依赖关系就称之为循环依赖。同理,再如下图的情况: 上图中,BeanA类依赖了BeanB类,BeanB类依赖了BeanC类,BeanC类依赖了BeanA类,如此,也形成了一个依赖闭环。再比如: 上图中,自己引用了自己,自己和自己形成了依赖关系。同样也是一个依赖闭环。那么,如果出现此类循环依赖的情况,会出现什么问题呢? 2 循环依赖问题复现 2.1 定义依赖关系 我们继续扩展前面的内容,给ModifyService增加一个属性,代码如下: @GPService public class ModifyService implements IModifyService { @GPAutowired private QueryService queryService; ... } 给QueryService增加一个属性,代码如下: @GPService @Slf4j public class QueryService implements IQueryService { @GPAutowired private ModifyService modifyService; ... } 如此,ModifyService依赖了QueryService,同时QueryService也依赖了ModifyService,形成了依赖闭环。那么这种情况下会出现什么问题呢? 2.2 问题复现 我们来运行调试一下之前的代码,在GPApplicationContext初始化后打上断点,我们来跟踪一下IoC容器里面的情况,如下图: 启动项目,我们发现只要是有循环依赖关系的属性并没有自动赋值,而没有循环依赖关系的属性均有自动赋值,如下图所示: 这种情况是怎么造成的呢?我们分析原因之后发现,因为,IoC容器对Bean的初始化是根据BeanDefinition循环迭代,有一定的顺序。这样,在执行依赖注入时,需要自动赋值的属性对应的对象有可能还没初始化,没有初始化也就没有对应的实例可以注入。于是,就出现我们看到的情况。 3 使用缓存解决循环依赖问题 3.1 定义缓存 具体代码如下: // 循环依赖的标识---当前正在创建的实例bean private Set<String> singletonsCurrectlyInCreation = new HashSet<String>(); //一级缓存 private Map<String, Object> singletonObjects = new HashMap<String, Object>(); // 二级缓存: 为了将成熟的bean和纯净的bean分离. 避免读取到不完整的bean. private Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(); 3.2 判断循环依赖 增加getSingleton()方法: /** * 判断是否是循环引用的出口. * @param beanName * @return */ private Object getSingleton(String beanName,GPBeanDefinition beanDefinition) { //先去一级缓存里拿, Object bean = singletonObjects.get(beanName); // 一级缓存中没有, 但是正在创建的bean标识中有, 说明是循环依赖 if (bean == null && singletonsCurrentlyInCreation.contains(beanName)) { bean = earlySingletonObjects.get(beanName); // 如果二级缓存中没有, 就从三级缓存中拿 if (bean == null) { // 从三级缓存中取 Object object = instantiateBean(beanName,beanDefinition); // 然后将其放入到二级缓存中. 因为如果有多次依赖, 就去二级缓存中判断. 已经有了就不在再次创建了 earlySingletonObjects.put(beanName, object); } } return bean; } 3.3 添加缓存 修改getBean()方法,在getBean()方法中添加如下代码: //Bean的实例化,DI是从而这个方法开始的 public Object getBean(String beanName){ //1、先拿到BeanDefinition配置信息 GPBeanDefinition beanDefinition = regitry.beanDefinitionMap.get(beanName); // 增加一个出口. 判断实体类是否已经被加载过了 Object singleton = getSingleton(beanName,beanDefinition); if (singleton != null) { return singleton; } // 标记bean正在创建 if (!singletonsCurrentlyInCreation.contains(beanName)) { singletonsCurrentlyInCreation.add(beanName); } //2、反射实例化newInstance(); Object instance = instantiateBean(beanName,beanDefinition); //放入一级缓存 this.singletonObjects.put(beanName, instance); //3、封装成一个叫做BeanWrapper GPBeanWrapper beanWrapper = new GPBeanWrapper(instance); //4、执行依赖注入 populateBean(beanName,beanDefinition,beanWrapper); //5、保存到IoC容器 factoryBeanInstanceCache.put(beanName,beanWrapper); return beanWrapper.getWrapperInstance(); } 3.4 添加依赖注入 修改populateBean()方法,代码如下: private void populateBean(String beanName, GPBeanDefinition beanDefinition, GPBeanWrapper beanWrapper) { ... try { //ioc.get(beanName) 相当于通过接口的全名拿到接口的实现的实例 field.set(instance,getBean(autowiredBeanName)); } catch (IllegalAccessException e) { e.printStackTrace(); continue; } ... } 4 循环依赖对AOP创建代理对象的影响 4.1 循环依赖下的代理对象创建过程 我们都知道Spring AOP、事务等都是通过代理对象来实现的,而事务的代理对象是由自动代理创建器来自动完成的。也就是说Spring最终给我们放进容器里面的是一个代理对象,而非原始对象。 这里我们结合循环依赖,再分析一下AOP代理对象的创建过程和最终放进容器内的动作,看如下代码: @Service public class MyServiceImpl implements MyService { @Autowired private MyService myService; @Transactional @Override public Object hello(Integer id) { return "service hello"; } } 此Service类使用到了事务,所以最终会生成一个JDK动态代理对象Proxy。刚好它又存在自己引用自己的循环依赖的情况。跟进到Spring创建Bean的源码部分,来看doCreateBean()方法: protected Object doCreateBean( ... ){ ... // 如果允许循环依赖,此处会添加一个ObjectFactory到三级缓存里面,以备创建对象并且提前暴露引用 // 此处Tips:getEarlyBeanReference是后置处理器SmartInstantiationAwareBeanPostProcessor的一个方法, // 主要是保证自己被循环依赖的时候,即使被别的Bean @Autowire进去的也是代理对象 // AOP自动代理创建器此方法里会创建的代理对象 // 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) { // 需要提前暴露(支持循环依赖),注册一个ObjectFactory到三级缓存 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } // 如果发现自己被循环依赖,会执行上面的getEarlyBeanReference()方法,从而创建一个代理对象从三级缓存转移到二级缓存里 // 注意此时候对象还在二级缓存里,并没有在一级缓存。并且此时可以知道exposedObject仍旧是原始对象 populateBean(beanName, mbd, instanceWrapper); exposedObject = initializeBean(beanName, exposedObject, mbd); // 经过这两大步后,exposedObject还是原始对象 // 注意:此处是以事务的AOP为例 // 因为事务的AOP自动代理创建器在getEarlyBeanReference()创建代理后, // initializeBean() 就不会再重复创建了,二选一,下面会有详细描述) ... // 循环依赖校验(非常重要) if (earlySingletonExposure) { // 前面讲到因为自己被循环依赖了,所以此时候代理对象还存放在二级缓存中 // 因此,此处getSingleton(),就会把代理对象拿出来 // 然后赋值给exposedObject对象并返回,最终被addSingleton()添加进一级缓存中 // 这样就保证了我们容器里缓存的对象实际上是代理对象,而非原始对象 Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { // 这个判断不可少(因为initializeBean()方法中给exposedObject对象重新赋过值,否则就是是两个不同的对象实例) if (exposedObject == bean) { exposedObject = earlySingletonReference; } } ... } } 以上代码分析的是代理对象有自己存在循环依赖的情况,Spring用三级缓存很巧妙的进行解决了这个问题。 4.2 非循环依赖下的代理对象创建过程 如果自己并不存在循环依赖的情况,Spring的处理过程就稍微不同,继续跟进源码: protected Object doCreateBean( ... ) { ... addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); ... // 此处注意,因为没有循环引用,所以上面getEarlyBeanReference()方法不会执行 // 也就是说此时二级缓存里并不会存在 populateBean(beanName, mbd, instanceWrapper); // 重点在此 //AnnotationAwareAspectJAutoProxyCreator自动代理创建器此处的postProcessAfterInitialization()方法里,会给创建一个代理对象返回 // 所以此部分执行完成后,exposedObject() 容器中缓存的已经是代理对象,不再是原始对象 // 此时二级缓存里依旧无它,更别提一级缓存了 exposedObject = initializeBean(beanName, exposedObject, mbd); ... // 循环依赖校验 if (earlySingletonExposure) { // 前面讲到一级、二级缓存里都没有缓存,然后这里传参数是false,表示不从三级缓存中取值 // 因此,此时earlySingletonReference = null ,并直接返回 // 然后执行addSingleton()方法,由此可知,容器里最终存在的也还是代理对象 Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { if (exposedObject == bean) { exposedObject = earlySingletonReference; } } ... } 根据以上代码分析可知,只要用到代理,没有被循环引用的,最终存在Spring容器里缓存的仍旧是代理对象。如果我们关闭Spring容器的循环依赖,也就是把allowCircularReferences设值为false,那么会不会出现问题呢?先关闭循环依赖开关。 // 它用于关闭循环引用(关闭后只要有循环引用现象将报错) @Component public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { ((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(false); } } 关闭循环依赖后,上面代码中存在A、B循环依赖的情况,运行程序会出现如下异常: Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference? at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:339) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:215) 此处异常类型也是BeanCurrentlyInCreationException异常,但报错位置在DefaultSingletonBeanRegistry.beforeSingletonCreation 我们来分析一下,在实例化A后给其属性赋值时,Spring会去实例化B。B实例化完成后会继续给B属性赋值,由于我们关闭了循环依赖,所以不存在提前暴露引用。因此B无法直接拿到A的引用地址,只能又去创建A的实例。而此时我们知道A其实已经正在创建中了,不能再创建了。所有就出现了异常。对照演示代码,来分析一下程序运行过程: @Service public class MyServiceImpl implements MyService { // 因为关闭了循环依赖,所以此处不能再依赖自己 // 但是MyService需要创建AOP代理对象 //@Autowired //private MyService myService; @Transactional @Override public Object hello(Integer id) { return "service hello"; } } 其大致运行步骤如下: protected Object doCreateBean( ... ) { // earlySingletonExposure = false 也就是Bean都不会提前暴露引用,因此不能被循环依赖 boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); ... populateBean(beanName, mbd, instanceWrapper); // 若是开启事务,此处会为原生Bean创建代理对象 exposedObject = initializeBean(beanName, exposedObject, mbd); if (earlySingletonExposure) { ... // 因为上面没有提前暴露代理对象,所以上面的代理对象exposedObject直接返回。 } } 由上面代码可知,即使关闭循环依赖开关,最终缓存到容器中的对象仍旧是代理对象,显然@Autowired给属性赋值的也一定是代理对象。 最后,以AbstractAutoProxyCreator为例看看自动代理创建器实现循环依赖代理对象的细节。 AbstractAutoProxyCreator是抽象类,它的三大实现子类InfrastructureAdvisorAutoProxyCreator、AspectJAwareAdvisorAutoProxyCreator、AnnotationAwareAspectJAutoProxyCreator小伙伴们应该比较熟悉,该抽象类实现了创建代理的动作: // 该类实现了SmartInstantiationAwareBeanPostProcessor接口 ,通过getEarlyBeanReference()方法解决循环引用问题 public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware { ... // 下面两个方法是自动代理创建器创建代理对象的唯二的两个节点: // 提前暴露代理对象的引用,在postProcessAfterInitialization之前执行 // 创建好后放进缓存earlyProxyReferences中,注意此处value是原始Bean @Override public Object getEarlyBeanReference(Object bean, String beanName) { Object cacheKey = getCacheKey(bean.getClass(), beanName); this.earlyProxyReferences.put(cacheKey, bean); return wrapIfNecessary(bean, beanName, cacheKey); } // 因为它会在getEarlyBeanReference之后执行,这个方法最重要的是下面的逻辑判断 @Override public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); // 下面的remove()方法返回被移除的value,也就是原始Bean // 判断如果存在循环引用,也就是执行了上面的getEarlyBeanReference()方法, // 此时remove() 返回值肯定是原始对象 // 若没有被循环引用,getEarlyBeanReference()不执行 // 所以remove() 方法返回null,此时进入if执行逻辑,调用创建代理对象方法 if (this.earlyProxyReferences.remove(cacheKey) != bean) { return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; } ... } 根据以上分析可得知,自动代理创建器它保证了代理对象只会被创建一次,而且支持循环依赖的自动注入的依旧是代理对象。由上面分析得出结论,在Spring容器中,不论是否存在循环依赖的情况,甚至关闭Spring容器的循环依赖功能,它对Spring AOP代理的创建流程有影响,但对结果是无影响的。也就是说Spring很好地屏蔽了容器中对象的创建细节,让使用者完全无感知。 本文为“Tom弹架构”原创,转载请注明出处。技术在于分享,我分享我快乐! 如果本文对您有帮助,欢迎关注和点赞;如果您有任何建议也可留言评论或私信,您的支持是我坚持创作的动力。关注微信公众号『 Tom弹架构 』可获取更多技术干货!

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册