JetCache 使用简单案例到源码解析读这一篇就够

news2025/2/27 13:59:33

背景

github.com/alibaba/jet…

一、使用方法:

1. 创建项目— 此处使用springboot项目

2. 引入starter

<dependency> 
    <groupId>com.alicp.jetcache</groupId> 
        <artifactId>jetcache-starter-redis</artifactId>
    <version>2.5.14</version> 
</dependency>

3. 配置application.yaml文件

为测试方便,本机先安装redis
brew install redis

启动redis
1、修改conf  --- /opt/homebrew/etc/redis.conf
	daemonize no改成yes 以守护进程的方式启动
2、启动redis
brew services restart redis

3、客户定链接测试



jetcache:
  statIntervalMinutes: 15
  areaInCacheName: false
  local:
    default:
      type: linkedhashmap
      keyConvertor: fastjson
  remote:
    default:
      type: redis
      keyConvertor: fastjson
      valueEncoder: java
      valueDecoder: java
      poolConfig:
        minIdle: 5
        maxIdle: 20
        maxTotal: 50
      host: 127.0.0.1
      port: 6379

4. 配置启动扫描

EnableMethodCache,EnableCreateCacheAnnotation这两个注解分别激活Cached和CreateCache注解,其他和标准的Spring Boot程序是一样的。这个类可以直接main方法运行、

package com.ghq.jetcachelearn;

import com.alicp.jetcache.anno.config.EnableCreateCacheAnnotation;
import com.alicp.jetcache.anno.config.EnableMethodCache;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableMethodCache(basePackages = "com.ghq.jetcachelearn")
@EnableCreateCacheAnnotation
public class JetcacheLearnApplication {

	public static void main(String[] args) {
		SpringApplication.run(JetcacheLearnApplication.class, args);
	}

}

5. 使用案例:

User类:

package com.ghq.learn.jetcachelearn.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

/**
 * @author tobedawn 
 * @date 2022/5/13 10:44 上午
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {

    private Long id;
    private String name;

}

UserService:

package com.ghq.learn.jetcachelearn.service;

import com.alicp.jetcache.anno.CacheType;
import com.alicp.jetcache.anno.Cached;
import com.ghq.learn.jetcachelearn.entity.User;

/**
 * @author 
 * @date 2022/5/13 10:43 上午
 */
public interface UserService {

    @Cached(expire = 10,cacheType = CacheType.LOCAL)
    User getUser(Long id);
}

UserSercviceImpl

package com.ghq.learn.jetcachelearn.service.impl;

import com.ghq.learn.jetcachelearn.entity.User;
import com.ghq.learn.jetcachelearn.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

/**
 * @author 
 * @date 2022/5/13 10:43 上午
 */
@Slf4j
@Service
public class UserServiceImpl implements UserService {
    @Override
    public User getUser(Long id) {
        log.info("userId {}", id);
        return new User(id, "" + id);
    }
}

测试:

package com.ghq.learn.jetcachelearn;

import com.ghq.learn.jetcachelearn.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;

@SpringBootTest
class JetcacheLearnApplicationTests {

	@Resource
	UserService userService;

	@Test
	void contextLoads() {
	}

	@Test
	void testUser(){
		userService.getUser(1L);
		userService.getUser(1L);
	}

}

6. 注意事项:

  1. 缓存实体必须序列化
  2. 使用过程中可能会出现包冲突:
    1. 解决方法: jetcache-starter-redis 依赖redis 是2.9.0 ,Maven 打包时可能打出来的包时3.7.x 此时启动报错,修改pom文件强制指定redis 版本

        

        <dependency>
 			<groupId>redis.clients</groupId>
 			<artifactId>jedis</artifactId>
 			<version>2.9.0</version>
 		</dependency>
 
 		<dependency>
 			<groupId>com.alicp.jetcache</groupId>
 			<artifactId>jetcache-starter-redis</artifactId>
 			<version>2.5.14</version>
 			<exclusions>
 				<exclusion>
 					<groupId>redis.clients</groupId>
 					<artifactId>jedis</artifactId>
 				</exclusion>
 			</exclusions>
 		</dependency>

源码解析:

相关链接:

jetCache源码地址:github.com/alibaba/jet…

一、初始化过程

1、自动装配入口:

jetcache-autoconfigure

spring.factorys

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.alicp.jetcache.autoconfigure.JetCacheAutoConfiguration

2、JetCacheAutoConfiguration :

功能:

  1. 初始化_globalCacheConfig ,最终生成Bean - GlobalCacheConfig;
  2. Import 不同不同类型的AutoConfiguration 对应不同的缓存方式 —AutoConfigurationg对应继承图;

  1. 不同AutoConfiguration对应生成不同的CacheBuilder ,存储在AutoConfigureBeans

    package com.alicp.jetcache.autoconfigure;
    
    import com.alicp.jetcache.AbstractCacheBuilder;
    import com.alicp.jetcache.CacheBuilder;
    import com.alicp.jetcache.anno.support.ConfigProvider;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.InitializingBean;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.core.env.ConfigurableEnvironment;
    import org.springframework.util.Assert;
    
    import java.util.*;
    
    /**
     * Created on 2016/11/29.
     *
     * @author <a href="mailto:areyouok@gmail.com">huangli</a>
     */
    public abstract class AbstractCacheAutoInit implements InitializingBean {
    
        private static Logger logger = LoggerFactory.getLogger(AbstractCacheAutoInit.class);
    
        @Autowired
        protected ConfigurableEnvironment environment;
    
        @Autowired
        protected AutoConfigureBeans autoConfigureBeans;
    
        @Autowired
        protected ConfigProvider configProvider;
    
        protected String[] typeNames;
    
        private boolean inited = false;
    
        public AbstractCacheAutoInit(String... cacheTypes) {
            Objects.requireNonNull(cacheTypes,"cacheTypes can't be null");
            Assert.isTrue(cacheTypes.length > 0, "cacheTypes length is 0");
            this.typeNames = cacheTypes;
        }
    
        @Override
        public void afterPropertiesSet() {
            if (!inited) {
                synchronized (this) {
                    if (!inited) {
                        process("jetcache.local.", autoConfigureBeans.getLocalCacheBuilders(), true);
                        process("jetcache.remote.", autoConfigureBeans.getRemoteCacheBuilders(), false);
                        inited = true;
                    }
                }
            }
        }
    
        private void process(String prefix, Map cacheBuilders, boolean local) {
            ConfigTree resolver = new ConfigTree(environment, prefix);
            Map<String, Object> m = resolver.getProperties();
            Set<String> cacheAreaNames = resolver.directChildrenKeys();
            for (String cacheArea : cacheAreaNames) {
                final Object configType = m.get(cacheArea + ".type");
                boolean match = Arrays.stream(typeNames).anyMatch((tn) -> tn.equals(configType));
                if (!match) {
                    continue;
                }
                ConfigTree ct = resolver.subTree(cacheArea + ".");
                logger.info("init cache area {} , type= {}", cacheArea, typeNames[0]);
                CacheBuilder c = initCache(ct, local ? "local." + cacheArea : "remote." + cacheArea);
    	//将子类生成的CacheBuilder 放进autoConfigureBeans 中
                cacheBuilders.put(cacheArea, c); 
            }
        }
    
        protected void parseGeneralConfig(CacheBuilder builder, ConfigTree ct) {
            AbstractCacheBuilder acb = (AbstractCacheBuilder) builder;
            acb.keyConvertor(new FunctionWrapper<>(() -> configProvider.parseKeyConvertor(ct.getProperty("keyConvertor"))));
    
            String expireAfterWriteInMillis = ct.getProperty("expireAfterWriteInMillis");
            if (expireAfterWriteInMillis == null) {
                // compatible with 2.1
                expireAfterWriteInMillis = ct.getProperty("defaultExpireInMillis");
            }
            if (expireAfterWriteInMillis != null) {
                acb.setExpireAfterWriteInMillis(Long.parseLong(expireAfterWriteInMillis));
            }
    
            String expireAfterAccessInMillis = ct.getProperty("expireAfterAccessInMillis");
            if (expireAfterAccessInMillis != null) {
                acb.setExpireAfterAccessInMillis(Long.parseLong(expireAfterAccessInMillis));
            }
    
        }
    
        protected abstract CacheBuilder initCache(ConfigTree ct, String cacheAreaWithPrefix);
    }
    

代码:

package com.alicp.jetcache.autoconfigure;

import com.alicp.jetcache.anno.support.GlobalCacheConfig;
import com.alicp.jetcache.anno.support.SpringConfigProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

/**
 * Created on 2016/11/17.
 *
 * @author <a href="mailto:areyouok@gmail.com">huangli</a>
 */
@Configuration
@ConditionalOnClass(GlobalCacheConfig.class)
@ConditionalOnMissingBean(GlobalCacheConfig.class)
@EnableConfigurationProperties(JetCacheProperties.class)
//此处IMport对应不同类型缓存配置初始化
@Import({RedisAutoConfiguration.class,
        CaffeineAutoConfiguration.class,
        MockRemoteCacheAutoConfiguration.class,
        LinkedHashMapAutoConfiguration.class,
        RedisLettuceAutoConfiguration.class,
        RedisSpringDataAutoConfiguration.class})
public class JetCacheAutoConfiguration {

    public static final String GLOBAL_CACHE_CONFIG_NAME = "globalCacheConfig";

    private SpringConfigProvider _springConfigProvider = new SpringConfigProvider();

    private AutoConfigureBeans _autoConfigureBeans = new AutoConfigureBeans();

    private GlobalCacheConfig _globalCacheConfig;

    @Bean
    @ConditionalOnMissingBean
    public SpringConfigProvider springConfigProvider() {
        return _springConfigProvider;
    }

    @Bean
    public AutoConfigureBeans autoConfigureBeans() {
        return _autoConfigureBeans;
    }

    @Bean
    public static BeanDependencyManager beanDependencyManager(){
        return new BeanDependencyManager();
    }

    @Bean(name = GLOBAL_CACHE_CONFIG_NAME)
    public GlobalCacheConfig globalCacheConfig(AutoConfigureBeans autoConfigureBeans, JetCacheProperties props) {
        if (_globalCacheConfig != null) {
            return _globalCacheConfig;
        }
        _globalCacheConfig = new GlobalCacheConfig();
        _globalCacheConfig.setHiddenPackages(props.getHiddenPackages());
        _globalCacheConfig.setStatIntervalMinutes(props.getStatIntervalMinutes());
        _globalCacheConfig.setAreaInCacheName(props.isAreaInCacheName());
        _globalCacheConfig.setPenetrationProtect(props.isPenetrationProtect());
        _globalCacheConfig.setEnableMethodCache(props.isEnableMethodCache());
        _globalCacheConfig.setLocalCacheBuilders(autoConfigureBeans.getLocalCacheBuilders());
        _globalCacheConfig.setRemoteCacheBuilders(autoConfigureBeans.getRemoteCacheBuilders());
        return _globalCacheConfig;
    }

}

二、Cache解析

Cache作为jetCache缓存最上层的抽象接口,不同类型的的Cache对应一个子类实现。

不同的子类Cache ,对应不同的CacheBuilder生成器,这里需要关注一下MultiLevelCacheBuilder (用于支持多级缓存)

基于上面使用案例可知道JetCache 在接口或者实现接口上增加相应的注解(@Cached,@CachedUpdate 等),缓存就会自动生效,可以想到JetCache使用的是SpringAOP对方法进行了增强,AOP的代码不在此处展示,直接看缓存相关的关键逻辑

AOP相关代码类列在此处,感兴趣可以自行查阅

CacheAdvisor — advisor

CachePointcut —pointCut

JetCacheInterceptor— 真正切面增强逻辑在这里

JetCacheProxyConfiguration — spring config 类

缓存逻辑 (只贴了关键代码):

#JetCacheInterceptor
@Override
    public Object invoke(final MethodInvocation invocation) throws Throwable {
        if (configProvider == null) {
            configProvider = applicationContext.getBean(ConfigProvider.class);
        }
        if (configProvider != null && globalCacheConfig == null) {
            globalCacheConfig = configProvider.getGlobalCacheConfig();
        }
        if (globalCacheConfig == null || !globalCacheConfig.isEnableMethodCache()) {
            return invocation.proceed();
        }

        Method method = invocation.getMethod();
        Object obj = invocation.getThis();
        CacheInvokeConfig cac = null;
        if (obj != null) {
            String key = CachePointcut.getKey(method, obj.getClass());
            cac  = cacheConfigMap.getByMethodInfo(key);
        }

        if (cac == null || cac == CacheInvokeConfig.getNoCacheInvokeConfigInstance()) {
            return invocation.proceed();
        }

				
				//从这里,idea跟进代码到最终实现逻辑
        CacheInvokeContext context = configProvider.getCacheContext().createCacheInvokeContext(cacheConfigMap);
        context.setTargetObject(invocation.getThis());
        context.setInvoker(invocation::proceed);
        context.setMethod(method);
        context.setArgs(invocation.getArguments());
        context.setCacheInvokeConfig(cac);
        context.setHiddenPackages(globalCacheConfig.getHiddenPackages());
        return CacheHandler.invoke(context);
    }
# CacheContext
public CacheInvokeContext createCacheInvokeContext(ConfigMap configMap) {
        CacheInvokeContext c = newCacheInvokeContext();
        c.setCacheFunction((invokeContext, cacheAnnoConfig) -> {
            Cache cache = cacheAnnoConfig.getCache();
            if (cache == null) {
                if (cacheAnnoConfig instanceof CachedAnnoConfig) {
                    cache = createCacheByCachedConfig((CachedAnnoConfig) cacheAnnoConfig, invokeContext);
                } else if ((cacheAnnoConfig instanceof CacheInvalidateAnnoConfig) || (cacheAnnoConfig instanceof CacheUpdateAnnoConfig)) {
                    CacheInvokeConfig cacheDefineConfig = configMap.getByCacheName(cacheAnnoConfig.getArea(), cacheAnnoConfig.getName());
                    if (cacheDefineConfig == null) {
                        String message = "can't find @Cached definition with area=" + cacheAnnoConfig.getArea()
                                + " name=" + cacheAnnoConfig.getName() +
                                ", specified in " + cacheAnnoConfig.getDefineMethod();
                        CacheConfigException e = new CacheConfigException(message);
                        logger.error("Cache operation aborted because can't find @Cached definition", e);
                        return null;
                    }
                    cache = createCacheByCachedConfig(cacheDefineConfig.getCachedAnnoConfig(), invokeContext);
                }
                cacheAnnoConfig.setCache(cache);
            }
            return cache;
        });
        return c;
    }
#CacheHandler
private static Object invokeWithCached(CacheInvokeContext context)
            throws Throwable {
        CacheInvokeConfig cic = context.getCacheInvokeConfig();
        CachedAnnoConfig cac = cic.getCachedAnnoConfig();
	//此处apply BiFunction 的实现逻辑  见 CacheContext.createCacheInvokeContext
        Cache cache = context.getCacheFunction().apply(context, cac);

        if (cache == null) {
            logger.error("no cache with name: " + context.getMethod());
            return invokeOrigin(context);
        }

        Object key = ExpressionUtil.evalKey(context, cic.getCachedAnnoConfig());
        if (key == null) {
            return loadAndCount(context, cache, key);
        }

        if (!ExpressionUtil.evalCondition(context, cic.getCachedAnnoConfig())) {
            return loadAndCount(context, cache, key);
        }

        try {
            CacheLoader loader = new CacheLoader() {
                @Override
                public Object load(Object k) throws Throwable {
                    Object result = invokeOrigin(context);
                    context.setResult(result);
                    return result;
                }

                @Override
                public boolean vetoCacheUpdate() {
                    return !ExpressionUtil.evalPostCondition(context, cic.getCachedAnnoConfig());
                }
            };
            Object result = cache.computeIfAbsent(key, loader);
            return result;
        } catch (CacheInvokeException e) {
            throw e.getCause();
        }
    }
# AbstractCache
static <K, V> V computeIfAbsentImpl(K key, Function<K, V> loader, boolean cacheNullWhenLoaderReturnNull,
                                               long expireAfterWrite, TimeUnit timeUnit, Cache<K, V> cache) {
        AbstractCache<K, V> abstractCache = CacheUtil.getAbstractCache(cache);
        CacheLoader<K, V> newLoader = CacheUtil.createProxyLoader(cache, loader, abstractCache::notify);
        CacheGetResult<V> r;
	// @CacheRefresh  走这一块
        if (cache instanceof RefreshCache) {
            RefreshCache<K, V> refreshCache = ((RefreshCache<K, V>) cache);
            r = refreshCache.GET(key);
            refreshCache.addOrUpdateRefreshTask(key, newLoader);
        } else {
            r = cache.GET(key);
        }
        if (r.isSuccess()) {
            return r.getValue();
        } else {
            Consumer<V> cacheUpdater = (loadedValue) -> {
                if(needUpdate(loadedValue, cacheNullWhenLoaderReturnNull, newLoader)) {
                    if (timeUnit != null) {
                        cache.PUT(key, loadedValue, expireAfterWrite, timeUnit).waitForResult();
                    } else {
                        cache.PUT(key, loadedValue).waitForResult();
                    }
                }
            };

            V loadedValue;
            if (cache.config().isCachePenetrationProtect()) {
                loadedValue = synchronizedLoad(cache.config(), abstractCache, key, newLoader, cacheUpdater);
            } else {
                loadedValue = newLoader.apply(key);
		//生产者消费者模式,调用上面的cache.PUT 方法
                cacheUpdater.accept(loadedValue);
            }

            return loadedValue;
        }
    }
# 具体Cache  put逻辑,此处使用RedisCache来做案例
@Override
    protected CacheResult do_PUT(K key, V value, long expireAfterWrite, TimeUnit timeUnit) {
        try (Jedis jedis = config.getJedisPool().getResource()) {
            CacheValueHolder<V> holder = new CacheValueHolder(value, timeUnit.toMillis(expireAfterWrite));
            byte[] newKey = buildKey(key);
            String rt = jedis.psetex(newKey, timeUnit.toMillis(expireAfterWrite), valueEncoder.apply(holder));
            if ("OK".equals(rt)) {
                return CacheResult.SUCCESS_WITHOUT_MSG;
            } else {
                return new CacheResult(CacheResultCode.FAIL, rt);
            }
        } catch (Exception ex) {
            logError("PUT", key, ex);
            return new CacheResult(ex);
        }
    }

其他

  • jetcache-anno-api:定义jetcache的注解和常量,不传递依赖。如果你想把Cached注解加到接口上,又不希望你的接口jar传递太多依赖,可以让接口jar依赖jetcache-anno-api。
  • jetcache-core:核心api,完全通过编程来配置操作Cache,不依赖Spring。两个内存中的缓存实现LinkedHashMapCacheCaffeineCache也由它提供。
  • jetcache-anno:基于Spring提供@Cached和@CreateCache注解支持。
  • jetcache-redis:使用jedis提供Redis支持。
  • jetcache-redis-lettuce(需要JetCache2.3以上版本):使用lettuce提供Redis支持,实现了JetCache异步访问缓存的的接口。
  • jetcache-starter-redis:Spring Boot方式的Starter,基于Jedis。
  • jetcache-starter-redis-lettuce(需要JetCache2.3以上版本):Spring Boot方式的Starter,基于Lettuce。

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

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

相关文章

Servlet3.0 新特性全解

Servlet3.0新特性全解 tomcat 7以上的版本都支持Servlet 3.0 Servlet 3.0 新增特性 注解支持&#xff1b;Servlet、Filter、Listener无需在web.xml中进行配置&#xff0c;可以通过对应注解进行配置&#xff1b;支持Web模块&#xff1b;Servlet异步处理&#xff1b;文件上传AP…

DEV SIT UAT PET SIM PRD PROD常见环境英文缩写含义

一、英文缩写 英文中文 DEV development开发 SIT System Integrate Test系统整合测试&#xff08;内测&#xff09; UAT User Acceptance Test用户验收测试 PET Performance Evaluation Test性能评估测试&#xff08;压测&#xff09; SIM simulation仿真 PRD/PROD produ…

我们花一个月调研了小红书种草的新机会和增长策略

分析师&#xff1a;Jane、彬超 编辑&#xff1a;yolo 出品&#xff1a;增长黑盒研究组 *本报告为增长黑盒独立研究系列&#xff0c; 与第三方不存在任何利益关系 随着618的临近&#xff0c;小红书再次成为了品牌重要的“营销战场”。面临着经济环境的不确定性&#xff0c;想必各…

数据结构_串

目录 1. 串的定义和实现 1.1 串的定义 1.2 串的存储结构 1.2.1 定长顺序存储表示 1.2.2 堆分配存储表示 1.2.3 块链存储表示 1.3 串的基本操作 2. 串的模式匹配 2.1 简单的模式匹配算法 2.2 串的模式匹配算法——KMP算法 2.2.1 字符串的前缀、后缀和部分匹配值 2.2…

你不知道的裁员攻略:真正的离职赔偿是2N起步,不是N+1!计算赔偿金时,年终奖要计入总收入!...

最近裁员现象猖獗&#xff0c;许多人都不知道如何维护自己的合法权益&#xff0c;在和公司撕扯时屡屡被坑。一位曾经和字节撕过且成功的网友&#xff0c;友情给大家写了一份离职攻略。但要注意&#xff0c;这份攻略只针对那些守规矩的大公司&#xff0c;如果是不守规矩的小公司…

得ChatGPT者,得智能客服天下?

‍数据智能产业创新服务媒体 ——聚焦数智 改变商业 在现代社会&#xff0c;高效、专业的客服服务已成为企业、组织机构竞争力的关键要素。智能客服系统应运而生&#xff0c;智能客服系统对客服的赋能作用和价值主要表现在提高效率、降低成本、优化用户体验、深度挖掘用户需求…

【综述】结构化剪枝

目录 摘要 分类 1、依赖权重 2、基于激活函数 3、正则化 3.1 BN参数正则化 3.2 额外参数正则化 3.3 滤波器正则化 4、优化工具 5、动态剪枝 6、神经架构搜索 性能比较 摘要 深度卷积神经网络&#xff08;CNNs&#xff09;的显著性能通常归因于其更深层次和更广泛的架…

QML转换(Transformation)

目录 一 QML介绍 二 QML的使用场合 三 实例演示 一 QML介绍 QML是Qt Quick的缩写&#xff0c;它是一种新型的、面向对象的、跨平台的脚本语言&#xff0c;可以用来描述用户界面或应用程序的交互逻辑。QML可以在Qt应用程序中使用&#xff0c;也可以在其他JavaScript应用程序中…

Python入门(九)字典(二)

字典&#xff08;二&#xff09; 1.遍历字典1.1 遍历所有键值对1.2 遍历字典中的所有键1.3 按特定顺序遍历字典中的所有键 2.嵌套2.1 字典列表2.2 在字典中存储列表2.3 在字典中存储字典 作者&#xff1a;xiou 1.遍历字典 一个Python字典可能只包含几个键值对&#xff0c;也可…

【源码解析】SpringBoot自定义条件及@Conditional源码解析

自定义注解 自定义条件类 public class MessageCondition extends SpringBootCondition {Overridepublic ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {MultiValueMap<String, Object> attrs metadata.getAllAnnota…

成年人的崩溃只在一瞬间,程序员凌晨三点写的代码竟被女友删了...

对于恋爱中的情侣来说&#xff0c;吵架是很正常的事情&#xff0c;就算是再怎么亲密&#xff0c;也难免会出现意见不合的时候。 吵架不可怕&#xff0c;可怕的是&#xff0c;受吵架情绪的影响&#xff0c;做出一些比较“极端”的事情。 之前某社交平台上一位女生吐槽自己的男…

Ubuntu 本地部署 Stable Diffusion web UI

Ubuntu 本地部署 Stable Diffusion web UI 0. 什么是 Stable Diffusion1. 什么是 Stable Diffusion web UI2. Github 地址3. 安装 Miniconda34. 创建虚拟环境5. 安装 Stable Diffusion web UI6. 启动 Stable Diffusion web UI7. 访问 Stable Diffusion web UI8. 其他 0. 什么是…

我这里取出来的数据(最后边的excel)有点问题,我没有要取性别的数据,但是表里有...

点击上方“Python爬虫与数据挖掘”&#xff0c;进行关注 回复“书籍”即可获赠Python从入门到进阶共10本电子书 今 日 鸡 汤 上穷碧落下黄泉&#xff0c;两处茫茫皆不见。 大家好&#xff0c;我是皮皮。 一、前言 前几天在Python钻石群【不争】问了一个Python自动化办公的问题&…

PyCharm安装教程,图文教程(超详细)

「作者简介」&#xff1a;CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」&#xff1a;对网络安全感兴趣的小伙伴可以关注专栏《网络安全入门到精通》 PyCharm 一、PyCharm下载安装二、Python下载安装三、创建项目四、安装模块1、pip安装2、P…

商城系统商品属性的数据库设计思路

京东商城的数据库是如何搭建的&#xff0c;那么多商品&#xff0c;每种商品的参数各不相同&#xff0c;是怎样设计数据库的&#xff1f; 在提及这种设计思路前&#xff0c;首先得了解数据表可以分为两种结构: 1\横表,也就是我们经常用到的表结构&#xff0c; 2\纵表,这种结构…

ASEMI代理LT8609AJDDM#WTRPBF原装ADI车规级芯片

编辑&#xff1a;ll ASEMI代理LT8609AJDDM#WTRPBF原装ADI车规级芯片 型号&#xff1a;LT8609AJDDM#WTRPBF 品牌&#xff1a;ADI /亚德诺 封装&#xff1a;DFN-10 批号&#xff1a;2023 安装类型&#xff1a;表面贴装型 引脚数量&#xff1a;10 工作温度:-40C~125C 类型…

MySQL学习---13、存储过程与存储函数

1、存储过程概述 MySQL从5.0版本开始支持存储过程和函数。存储过程和函数能够将负杂的SQL逻辑封装在一起&#xff0c;应用程序无序关注存储过程和函数内部复杂的SQL逻辑&#xff0c;而只需要简单的调用存储过程和函数就可以。 1.1 理解 含义&#xff1a;存储过程的英文是Sto…

ASEMI代理LTC2954CTS8-2#TRMPBF原装ADI车规级芯片

编辑&#xff1a;ll ASEMI代理LTC2954CTS8-2#TRMPBF原装ADI车规级芯片 型号&#xff1a;LTC2954CTS8-2#TRMPBF 品牌&#xff1a;ADI/亚德诺 封装&#xff1a;TSOT-23-8 批号&#xff1a;2023 引脚数量&#xff1a;23 工作温度&#xff1a;-40C~85C 安装类型&#xff1a…

若依框架(RuoYI)项目打包(jar)方法,部署到 Linux 服务器

序言 在若依框架的 bin 目录下&#xff0c;存在着三个 bat 文件&#xff0c;一个是清除之前的依赖的自动化 bat 脚本&#xff08;clean.bat&#xff09;&#xff0c;一个是自动化项目打包的 bat 脚本&#xff08;package.bat&#xff09;&#xff0c;一个是运行若依项目的脚本…

云原生CAx软件:技术约束

Pivotal公司的Matt Stine于2013年首次提出Cloud Native(云原生)的概念&#xff0c;从概念提出到技术落地&#xff0c;云原生还处于不断发展过程中&#xff0c;关于云原生的定义也不断在完善。 云原生为使用开源软件堆栈来创建容器化、动态编排和面向微服务的应用程序。 Heroku创…