Spring系列 AOP实现过程
文章目录
- 实现原理
- EnableAspectJAutoProxy
- AnnotationAwareAspectJAutoProxyCreator 代理创建过程
- wrapIfNecessary
- getAdvicesAndAdvisorsForBean
- findCandidateAdvisors
- findAdvisorsThatCanApply
- createProxy
- @AspectJ注解处理
- 代理调用过程
实现原理
本文源码基于spring-aop-5.3.31版本,测试代码如下:
@Aspect
@Component
public class UserAspect {@Pointcut("execution(* org.example..*..UserService.*(..))")private void pointcut() {}@Around("pointcut()")public Object aroundUserServiceOperation(ProceedingJoinPoint pjp) throws Throwable { //2long start = System.currentTimeMillis();Object retVal = pjp.proceed();long duration = System.currentTimeMillis() - start;System.out.printf("time for pointcut method is %d seconds%n", duration);return retVal;}
}@Service
public class UserService {public String login(String username, String password) {return String.format(" %s login with password: %s\n", username, password);}
}@RestController
@RequestMapping(value = "/api/user")
public class UserController {@ResourceUserService userService;@PostMapping("/login")public String login(@RequestParam("username") String username, @RequestParam("password") String password) {return userService.login(username, password);}
}
启动类
@SpringBootApplication
@EnableAspectJAutoProxy
public class Main {public static void main(String[] args) {SpringApplication.run(Main.class, args);}
}
EnableAspectJAutoProxy
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {/*** 指定代理方式是CGLIB还是基于接口的动态代理*/boolean proxyTargetClass() default false;/*** 是否暴露代理对象,暴露可以通过AopContext拿到*/boolean exposeProxy() default false;
}
导入了这个AspectJAutoProxyRegistrar,实际上是一个ImportBeanDefinitionRegistrar实现。我们知道,@Import + ImportBeanDefinitionRegistrar结合使用的效果就是向容器中注入一些Bean。
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {/*** 注册, 配置AspectJ代理*/@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {// 这一步就是所有的注册过程AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);AnnotationAttributes enableAspectJAutoProxy =AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);if (enableAspectJAutoProxy != null) {if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);}if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);}}}
}
通过AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);可以看到
@Nullable
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, null);
}@Nullable
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, @Nullable Object source) {return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}@Nullable
private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {Assert.notNull(registry, "BeanDefinitionRegistry must not be null");// AUTO_PROXY_CREATOR_BEAN_NAME// org.springframework.aop.config.internalAutoProxyCreatorif (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);if (!cls.getName().equals(apcDefinition.getBeanClassName())) {int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());int requiredPriority = findPriorityForClass(cls);if (currentPriority < requiredPriority) {apcDefinition.setBeanClassName(cls.getName());}}return null;}RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);beanDefinition.setSource(source);beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);return beanDefinition;
}
从上面的内容来看,AspectJAutoProxyRegistrar的工作就是注册一个AspectJAutoProxyCreator的东西,默认是org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator,如果容器种已经有了一个名称为org.springframework.aop.config.internalAutoProxyCreator的Bean,那么会优先使用它,否则使用AnnotationAwareAspectJAutoProxyCreator
AnnotationAwareAspectJAutoProxyCreator 代理创建过程
AspectJAwareAdvisorAutoProxyCreator的子类,负责处理所有AspectJ注解,以及所有Spring的Advisor。所有AspectJ注解的类都会被自动识别

主要关注点,它是一个BeanPostProcessor

最终org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator找到了Bean的后置处理逻辑
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {Object cacheKey = getCacheKey(beanClass, beanName);if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {if (this.advisedBeans.containsKey(cacheKey)) {return null;}if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {this.advisedBeans.put(cacheKey, Boolean.FALSE);return null;}}// Create proxy here if we have a custom TargetSource.// Suppresses unnecessary default instantiation of the target bean:// The TargetSource will handle target instances in a custom fashion.TargetSource targetSource = getCustomTargetSource(beanClass, beanName);if (targetSource != null) {if (StringUtils.hasLength(beanName)) {this.targetSourcedBeans.add(beanName);}Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);this.proxyTypes.put(cacheKey, proxy.getClass());return proxy;}return null;
}/*** Create a proxy with the configured interceptors if the bean is* identified as one to proxy by the subclass.* @see #getAdvicesAndAdvisorsForBean*/
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {// bean 就是被代理对象if (bean != null) {Object cacheKey = getCacheKey(bean.getClass(), beanName);if (this.earlyProxyReferences.remove(cacheKey) != bean) {return wrapIfNecessary(bean, beanName, cacheKey);}}return bean;
}
代理是需要创建代理对象和被代理对象的
wrapIfNecessary
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {return bean;}if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {return bean;}// 下面四个类不会被代理:Advice, Pointcut, Advisor, AopInfrastructureBeanif (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {this.advisedBeans.put(cacheKey, Boolean.FALSE);return bean;}// Create proxy if we have advice.Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);if (specificInterceptors != DO_NOT_PROXY) {this.advisedBeans.put(cacheKey, Boolean.TRUE);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
对于指定的Bean,获取可用的切面,如果没有切面,则无需生成代理
org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator实现了这个方法
@Override
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();
}protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {// 获取所有的AdvisorList<Advisor> candidateAdvisors = findCandidateAdvisors();// 找到与beanClass相关联的那些AdvisorList<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);extendAdvisors(eligibleAdvisors);if (!eligibleAdvisors.isEmpty()) {eligibleAdvisors = sortAdvisors(eligibleAdvisors);}return eligibleAdvisors;
}
findCandidateAdvisors
/*** Find all candidate Advisors to use in auto-proxying.* @return the List of candidate Advisors*/
protected List<Advisor> findCandidateAdvisors() {Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available");return this.advisorRetrievalHelper.findAdvisorBeans();
}
AnnotationAwareAspectJAutoProxyCreator中继承了此方法
@Override
protected List<Advisor> findCandidateAdvisors() {// Add all the Spring advisors found according to superclass rules.List<Advisor> advisors = super.findCandidateAdvisors();// Build Advisors for all AspectJ aspects in the bean factory.if (this.aspectJAdvisorsBuilder != null) {advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());}return advisors;
}
切面的查找实际上委托给了BeanFactoryAdvisorRetrievalHelper和BeanFactoryAspectJAdvisorsBuilder这两个类来完成的
BeanFactoryAdvisorRetrievalHelper用于从BeanFactory中获取org.springframework.aop.Advisor接口的实现类,核心逻辑如下(省略了部分不重要的代码)
public List<Advisor> findAdvisorBeans() {// Determine list of advisor bean names, if not cached already.String[] advisorNames = this.cachedAdvisorBeanNames;if (advisorNames == null) {// 获取所有Advisor类型的Bean名称,同时缓存到 this.cachedAdvisorBeanNamesadvisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Advisor.class, true, false);this.cachedAdvisorBeanNames = advisorNames;}if (advisorNames.length == 0) {return new ArrayList<>();}List<Advisor> advisors = new ArrayList<>();for (String name : advisorNames) {if (isEligibleBean(name)) {// 忽略正在创建的Beanif (this.beanFactory.isCurrentlyInCreation(name)) {// 记录日志} else {// ....advisors.add(this.beanFactory.getBean(name, Advisor.class));// ....}}}return advisors;
}
BeanFactoryAspectJAdvisorsBuilder
在项目启动的过程中,项目中的所有切面会被 AnnotationAwareAspectJAutoProxyCreator 解析,它会找到切面中的每一个通知以及通知对应的切点,拿这二者构建一个新的对象,这个对象就是 Advisor。最后将所有解析得到的增强器注入到容器中。
BeanFactoryAspectJAdvisorsBuilder这个类的操作是获取所有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);}
}
判断Advisor和目标类是否匹配
- IntroductionAdvisor:使用ClassFilter类进行匹配
- PointcutAdvisor:使用Pointcut来进行匹配
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;return canApply(pca.getPointcut(), targetClass, hasIntroductions);}else {// It doesn't have a pointcut so we assume it applies.return true;}
}

PointCut类型的
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {Assert.notNull(pc, "Pointcut must not be null");if (!pc.getClassFilter().matches(targetClass)) {return false;}MethodMatcher methodMatcher = pc.getMethodMatcher();if (methodMatcher == MethodMatcher.TRUE) {// No need to iterate the methods if we're matching any method anyway...return true;}// AspectJExpressionPointcutIntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;if (methodMatcher instanceof IntroductionAwareMethodMatcher) {introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;}Set<Class<?>> classes = new LinkedHashSet<>();if (!Proxy.isProxyClass(targetClass)) {classes.add(ClassUtils.getUserClass(targetClass));}classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));for (Class<?> clazz : classes) {Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);for (Method method : methods) {if (introductionAwareMethodMatcher != null ?introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :methodMatcher.matches(method, targetClass)) {return true;}}}return false;
}
AspectJExpressionPointcut是一个MethodMatcher的实现,代表了使用AspectJ注解@Pointcut定义的一个切点,比如:
@Pointcut("execution(* org.example..*..UserService.*(..))")
private void pointcut() {
}
createProxy
找到Advisor之后下一步就是创建代理对象

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);if (proxyFactory.isProxyTargetClass()) {// Explicit handling of JDK proxy targets and lambdas (for introduction advice scenarios)if (Proxy.isProxyClass(beanClass) || ClassUtils.isLambdaClass(beanClass)) {// Must allow for introductions; can't just set interfaces to the proxy's interfaces only.for (Class<?> ifc : beanClass.getInterfaces()) {proxyFactory.addInterface(ifc);}}}else {// No proxyTargetClass flag enforced, let's apply our default checks...if (shouldProxyTargetClass(beanClass, beanName)) {proxyFactory.setProxyTargetClass(true);}else {evaluateProxyInterfaces(beanClass, proxyFactory);}}Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);proxyFactory.addAdvisors(advisors);proxyFactory.setTargetSource(targetSource);customizeProxyFactory(proxyFactory);proxyFactory.setFrozen(this.freezeProxy);if (advisorsPreFiltered()) {proxyFactory.setPreFiltered(true);}// Use original ClassLoader if bean class not locally loaded in overriding class loaderClassLoader classLoader = getProxyClassLoader();if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader()) {classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();}return proxyFactory.getProxy(classLoader);
}
@AspectJ注解处理
@Aspect注解表示一个AspectJ配置类,里面可以配置切点和切面,以及二者之间的关系
注意:@Aspect在Spring中没有专门定义一个组件去扫描它,因此需要其他额外的配置将@AspectJ配置的那个类放入Spring容器
-
加@Component注解
-
使用@ComponentScan注解
-
如果使用XML配置,可以使用context:component-scan,还需要配置
<context:include-filter/>,其中context:component-scan这个标签是扫描@Component、@Controller、@Service等这些注解的类,则把这些类注册为bean。它不扫描@Aspect注解的。所以需要在子标签添加 <context:include-filter type="annotation"expression=“org.aspectj.lang.annotation.Aspect”/>
代理调用过程
如果想跟踪Advice是如何执行的,以及Advice执行到被代理类方法执行的过程,该怎么办呢?

仅需要进行简单的设置,即可输出cglib以及jdk动态代理产生的class文件,然后工具查看生成的动态代理类
// 输出cglib动态代理产生的类
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\class");
// 输出jdk动态代理产生的类
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
基于CGlib的代理类命名方式为:被代理类类名 + $$EnhancerBySpringCGLIB$$ + 8个随机字符,例如:UserService$$EnhancerBySpringCGLIB$$efcfcced

使用jd-gui工具查看反编译的字节码,如下图所示

可以在里面找到login方法

下面将login方法调用的相关内容拿出来看看
import java.lang.reflect.Method;
import org.aopalliance.aop.Advice;
import org.springframework.aop.Advisor;
import org.springframework.aop.SpringProxy;
import org.springframework.aop.TargetClassAware;
import org.springframework.aop.TargetSource;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.framework.AopConfigException;
import org.springframework.cglib.core.ReflectUtils;
import org.springframework.cglib.core.Signature;
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.Dispatcher;
import org.springframework.cglib.proxy.Factory;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.cglib.proxy.NoOp;public class UserService$$EnhancerBySpringCGLIB$$efcfcced extends UserService implements SpringProxy, Advised, Factory {private MethodInterceptor CGLIB$CALLBACK_0;private static final MethodProxy CGLIB$login$0$Proxy;private static final Method CGLIB$login$0$Method;public final String login(String paramString1, String paramString2) {if (this.CGLIB$CALLBACK_0 == null)CGLIB$BIND_CALLBACKS(this); return (this.CGLIB$CALLBACK_0 != null) ? (String)this.CGLIB$CALLBACK_0.intercept(this, CGLIB$login$0$Method, new Object[] { paramString1, paramString2 }, CGLIB$login$0$Proxy): super.login(paramString1, paramString2);}
}
MethodInterceptor是spring-core提供的一个接口
public interface MethodInterceptor extends Callback {Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable;
}
intercept(Object obj, Method method, Object[] args, MethodProxy proxy)是 MethodInterceptor 的核心方法。在方法被调用时,Spring 会调用此方法,传递如下参数:
- obj:被代理的目标对象,即UserService
- method:被调用的方法,即login方法
- args:方法的参数
- proxy:用于调用目标方法的 MethodProxy 对象
现在只知道CGLIB$CALLBACK_0是一个MethodInterceptor,但到底是哪个实现呢,我们可以通过Debug查看,如下图所示,可以看到它的实际类型是org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor

@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {Object oldProxy = null;boolean setProxyContext = false;Object target = null;// this.advised -> ProxyFactoryTargetSource targetSource = this.advised.getTargetSource();try {if (this.advised.exposeProxy) {// Make invocation available if necessary.oldProxy = AopContext.setCurrentProxy(proxy);setProxyContext = true;}// Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...target = targetSource.getTarget();Class<?> targetClass = (target != null ? target.getClass() : null);// 获取拦截器链,即@Around, @Before等修饰的方法List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);Object retVal;// Check whether we only have one InvokerInterceptor: that is,// no real advice, but just reflective invocation of the target.if (chain.isEmpty() && CglibMethodInvocation.isMethodProxyCompatible(method)) {// We can skip creating a MethodInvocation: just invoke the target directly.// Note that the final invoker must be an InvokerInterceptor, so we know// it does nothing but a reflective operation on the target, and no hot// swapping or fancy proxying.Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);retVal = invokeMethod(target, method, argsToUse, methodProxy);}else {// We need to create a method invocation...retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();}retVal = processReturnType(proxy, target, method, retVal);return retVal;}finally {if (target != null && !targetSource.isStatic()) {targetSource.releaseTarget(target);}if (setProxyContext) {// Restore old proxy.AopContext.setCurrentProxy(oldProxy);}}
}
相关文章:
Spring系列 AOP实现过程
文章目录 实现原理EnableAspectJAutoProxyAnnotationAwareAspectJAutoProxyCreator 代理创建过程wrapIfNecessarygetAdvicesAndAdvisorsForBeanfindCandidateAdvisorsfindAdvisorsThatCanApply createProxy AspectJ注解处理代理调用过程 实现原理 本文源码基于spring-aop-5.3.…...
C语言 getchar 函数完全解析:掌握字符输入的关键
前言 在C语言中,getchar 是一个非常实用的函数,用于从标准输入流(通常是键盘)读取单个字符。这对于处理文本输入非常有用,尤其是在需要逐个字符处理的情况下。本文将深入探讨 getchar 函数的用法和特点,并…...
Docker安装mysql8并配置主从复制
1. 安装mysql8 1.1 新增挂载文件 # 新增mysql挂载文件夹 mkdir -p /root/docker/mysql/m01/log mkdir -p /root/docker/mysql/m01/data mkdir -p /root/docker/mysql/m01/conf1.2 新增mysql配置文件 # 新增mysql配置文件 cd /root/docker/mysql/m01/conf vim my.cnf # 下面是…...
快手:数据库升级实践,实现PB级数据的高效管理|OceanBase案例
本文作者:胡玉龙,快手技术专家 快手在较初期采用了OceanBase 3.1版本成功替换了多个核心业务、数百套的MySQL集群。至2023年,快手的数据量已突破800TB大关,其中最大集群的数据量更是达到了数百TB级别。为此,快手将数据…...
基于Node.js+Express+MySQL+VUE实现的计算机毕业设计共享单车管理网站
单车信息选择骑行 骑行状态留言公告/springboot/javaWEB/J2EE/MYSQL数据库/vue前后分离小程序 目录 功能图 界面展示 开发目标 开发背景意义 开发意义 开发目的 项目概述 技术选型与理由 系统设计与功能实现 项目可执行性分析 系统架构需求 性能需…...
人工智能辅助的神经康复
人工智能辅助的神经康复是通过应用人工智能(AI)技术来改善神经系统损伤患者的康复过程。此领域结合了深度学习、数据分析和机器人技术,旨在提升康复效果、个性化治疗方案和监测进展。以下是该领域的关键组成部分和应用: 1. 康复评…...
KKT实际运用 -MATLAB
FMINCON函数可以很方便的求出:fun:目标函数,即需要最小化的函数,输入参数为向量x,输出为标量f(x)。x0:初始点,即求解过程的起始点,可以是标量、向量或矩阵。A和b:线性不等…...
php在线相册
1、将静态页面效果完成 解压到www里 整个数据 暂时是错误的 建立连接密码为root 运行sql文件 右键根目录刷新 刷新后成功 开始 测试 如果需要上传照片,点击创建相册,选择上传文件,选择文件后退出 导入alumbenew2 2.提交表单方式 3.利用ph…...
Xcode手动安装SDK模拟器
1.下载SDK模拟器&Xcode SDK和Xcode官方下载地址 2.下载好后使用命令将SDK导入到Xcode中如下命令 注:我是在/Applications 目录下执行的命令,模拟其地址直接拖拽过来 sudo xcode-select -s Xcode.app xcodebuild -runFirstLaunch xcodebuild -imp…...
Docker安装consul + go使用consul + consul知识
1. 什么是服务注册和发现 假如这个产品已经在线上运行,有一天运营想搞一场促销活动,那么我们相对应的【用户服务】可能就要新开启三个微服务实例来支撑这场促销活动。而与此同时,作为苦逼程序员的你就只有手动去 API gateway 中添加新增的这…...
JWT 漏洞 - 学习手册
0x01:JWT 前导知识 0x0101:JWT 详解 0x02:JWT 漏洞介绍 0x0201:JWT 漏洞介绍 0x03:JWT 挖掘思路 JWT 漏洞挖掘思路 - JWT Payload 敏感信息泄露 备注:通过泄露的 JWT Payload 获取用户的敏感信息&#…...
HTML【知识改变命运】03font 字体标签
题目:在页面上显示"北京"两个字,字体为微软雅黑,颜色为红色,大小为40xp; font标签可以修饰字体的大小,颜色,和字体 属性:color颜色,face字体,size大…...
集师专属知识付费小程序搭建 心理咨询小程序搭建
一、产品简介 集师SaaS知识付费软件,为知识创业者或商家提供一站式内容交付解决方案,助力商家搭建集品牌传播、商业变现和用户运营于一体的线上知识服务系统,覆盖全渠道经营场景,占据每个流量入口,使流量变现快速高效…...
https://www.aitoolpath.com/ 一个工具数据库,目前储存了有2000+各种工具。每日更新
AI 工具爆炸?别怕,这个网站帮你整理好了! 哇塞,兄弟们!AI 时代真的来了!现在各种 AI 工具跟雨后春笋似的,噌噌噌地往外冒。AI 写作、AI 绘画、AI 代码生成……简直是要逆天啊! 可是…...
科技的成就(六十三)
583、八小时工作制 最先提出这种理念的人竟然也是一名企业家,而且还是一名空想社会主义者。这名叫做罗伯特欧文的英国人,也凭借先进的人本管理理念成为了现代人事管理之父。 584、SDN(软件定义网络) "SDN(软件定…...
浅谈抗量子密码学:保护未来的数字安全
一、引言 随着量子计算机技术的发展,传统的加密算法面临前所未有的挑战。量子计算机利用量子位(qubits)的特性,能够在理论上比经典计算机更快地破解现有的加密系统。为了应对这一威胁,研究者们正在开发所谓的“抗量子…...
10款物联网开源嵌入式操作系统对比分析
摘要 本文对目前市场上广受欢迎的10款物联网开源嵌入式操作系统进行了深度对比分析,包括Huawei LiteOS、RT-Thread、AliOS Things等。通过探讨这些操作系统的实时性、可扩展性、特点、运行要求、开发社区活跃度和应用领域等方面,帮助开发者更好地理解它…...
Elasticsearch深度攻略:核心概念与实践应用
目录 一、Elasticsearch简介1、Elasticsearch是什么2、Elasticsearch的应用场景3、Elasticsearch的核心概念 二、Elasticsearch安装与配置1、安装Elasticsearch(1)系统要求(2)下载Elasticsearch(3)解压安装…...
TLS详解
什么是TLS TLS(Transport Layer Security)传输层安全性协议 ,它的前身是SSL(Secure Sockets Layer)安全套接层,是一个被应用程序用来在网络中安全的通讯协议, 防止电子邮件、网页、消息以及其他协议被篡改或是窃听。是用来替代SSL的…...
正则表达式中的特殊字符
正则表达式中的特殊字符 字符类预定义字符类量词锚点分组和捕获选择、分支和条件反义和否定转义字符示例 这是我在这个网站整理的笔记,有错误的地方请指出,关注我,接下来还会持续更新。 作者:神的孩子都在歌唱 在正则表达式中,有许…...
使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...
Linux离线(zip方式)安装docker
目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1:修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本:CentOS 7 64位 内核版本:3.10.0 相关命令: uname -rcat /etc/os-rele…...
uniapp 字符包含的相关方法
在uniapp中,如果你想检查一个字符串是否包含另一个子字符串,你可以使用JavaScript中的includes()方法或者indexOf()方法。这两种方法都可以达到目的,但它们在处理方式和返回值上有所不同。 使用includes()方法 includes()方法用于判断一个字…...
PHP 8.5 即将发布:管道操作符、强力调试
前不久,PHP宣布了即将在 2025 年 11 月 20 日 正式发布的 PHP 8.5!作为 PHP 语言的又一次重要迭代,PHP 8.5 承诺带来一系列旨在提升代码可读性、健壮性以及开发者效率的改进。而更令人兴奋的是,借助强大的本地开发环境 ServBay&am…...
tomcat指定使用的jdk版本
说明 有时候需要对tomcat配置指定的jdk版本号,此时,我们可以通过以下方式进行配置 设置方式 找到tomcat的bin目录中的setclasspath.bat。如果是linux系统则是setclasspath.sh set JAVA_HOMEC:\Program Files\Java\jdk8 set JRE_HOMEC:\Program Files…...
Python 训练营打卡 Day 47
注意力热力图可视化 在day 46代码的基础上,对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...
【FTP】ftp文件传输会丢包吗?批量几百个文件传输,有一些文件没有传输完整,如何解决?
FTP(File Transfer Protocol)本身是一个基于 TCP 的协议,理论上不会丢包。但 FTP 文件传输过程中仍可能出现文件不完整、丢失或损坏的情况,主要原因包括: ✅ 一、FTP传输可能“丢包”或文件不完整的原因 原因描述网络…...
Pydantic + Function Calling的结合
1、Pydantic Pydantic 是一个 Python 库,用于数据验证和设置管理,通过 Python 类型注解强制执行数据类型。它广泛用于 API 开发(如 FastAPI)、配置管理和数据解析,核心功能包括: 数据验证:通过…...
【HarmonyOS 5】鸿蒙中Stage模型与FA模型详解
一、前言 在HarmonyOS 5的应用开发模型中,featureAbility是旧版FA模型(Feature Ability)的用法,Stage模型已采用全新的应用架构,推荐使用组件化的上下文获取方式,而非依赖featureAbility。 FA大概是API7之…...
13.10 LangGraph多轮对话系统实战:Ollama私有部署+情感识别优化全解析
LangGraph多轮对话系统实战:Ollama私有部署+情感识别优化全解析 LanguageMentor 对话式训练系统架构与实现 关键词:多轮对话系统设计、场景化提示工程、情感识别优化、LangGraph 状态管理、Ollama 私有化部署 1. 对话训练系统技术架构 采用四层架构实现高扩展性的对话训练…...
