Spring源码 笔记 雷丰阳 BV1gW411W7wy
组件注册
给容器中注册组件
1、包扫描+组件标注注解(@Controller/@Service/@Repository/@Component)[自己写的类]
2、@Bean[导入的第三方包里面的组件]
3、@Import[快速给容器中导入一个组件]
(1)、@Import(要导入到容器中的组件);容器中就会自动注册这个组件,id默认是全类名
( 2)、ImportSelector:返回需要导入的组件的全类名数组;
( 3)、ImportBeanDefinitionRegistrar:手动注册bean到容器中
4、使用Spring提供的 FactoryBean(工厂Bean);
(1)、默认获取到的是工厂bean调用getObject创建的对象
(2)、要获取工厂Bean本身,我们需要给id前面加一个&
&colorFactoryBean
mybatis被整合进Spring就是因为实现了factorybean接口
工厂bean一开始放进去的是工厂bean,只有获取的时候才会工厂bean调用方法创建产品对象,放进容器
基本上第三方框架整合这个spring就是实现这个接口
mybatis的sqlsession
FactoryBean
该类是SpringIOC容器是创建Bean的一种形式,这种方式创建Bean会有加成方式,融合了简单的工厂设计模式于装饰器模式
一种Bean创建的一种方式,对Bean的一种扩展。对于复杂的Bean对象初始化创建使用其可封装对象的创建细节。
BeanFactory
负责生产和管理Bean的一个工厂接口,提供一个Spring Ioc容器规范,
Bean的生命周期
先属性赋值,后置处理器前置方法,各种初始化方法,后置处理器后置方法
也就是说实现BeanPostProcessor的类都是对注解的反射解析
bean创建---初始化----销毁的过程
容器管理bean的生命周期;
我们可以自定义初始化和销毁方法;容器在bean进行到当前生命周期的时候来调用我们自定义的初始化和销毁方法
构造(对象创建)
单实例:在容器启动的时候创建对象
多实例:在每次获取的时候创建对象\
BeanPostProcessor.postProcessBeforeInitialization
初始化:
对象创建完成,并赋值好,调用初始化方法。。。
BeanPostProcessor.postProcessAfterInitialization
销毁:
单实例:容器关闭的时候
多实例:容器不会管理这个bean;容器不会调用销毁方法;
遍历得到容器中所有的BeanPostProcessor;挨个执行beforeInitialization,
一但返回null,跳出for循环,不会执行后面的BeanPostProcessor.postProcessorsBeforeInitializa
BeanPostProcessor原理
populateBean(beanName, mbd, instanceWrapper);给bean进行属性赋值
initializeBean
{
applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
invokeInitMethods(beanName, wrappedBean, mbd);执行自定义初始化
applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
1、指定初始化和销毁方法;
通过@Bean指定init-method和destroy-method;
2、通过让Bean实现InitializingBean(定义初始化逻辑),
DisposableBean(定义销毁逻辑);
3、可以使用JSR250;
@PostConstruct:在bean创建完成并且属性赋值完成;来执行初始化方法
@PreDestroy:在容器销毁bean之前通知我们进行清理工作
4、BeanPostProcessor【interface】:bean的后置处理器;
在bean初始化前后进行一些处理工作;
postProcessBeforeInitialization:在初始化之前工作
postProcessAfterInitialization:在初始化之后工作
Spring底层对 BeanPostProcessor 的使用;
bean赋值,注入其他组件,@Autowired,生命周期注解功能,@Async,xxx BeanPostProcessor;
属性赋值
使用@Value赋值;
1、基本数值
2、可以写SpEL; #{}
3、可以写${};取出配置文件【properties】中的值(在运行环境变量里面的值)
自动装配
自动装配;
Spring利用依赖注入(DI),完成对IOC容器中中各个组件的依赖关系赋值;
1、@Autowired:自动注入:
1)、默认优先按照类型去容器中找对应的组件:applicationContext.getBean(BookDao.class);找到就赋值
2)、如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找
applicationContext.getBean("bookDao")
3)、@Qualifier("bookDao"):使用@Qualifier指定需要装配的组件的id,而不是使用属性名
4)、自动装配默认一定要将属性赋值好,没有就会报错;
可以使用@Autowired(required=false);
5)、@Primary:让Spring进行自动装配的时候,默认使用首选的bean;
也可以继续使用@Qualifier指定需要装配的bean的名字
BookService{
@Autowired
BookDao bookDao;
}
2、Spring还支持使用@Resource(JSR250)和@Inject(JSR330)[java规范的注解]
@Resource:
可以和@Autowired一样实现自动装配功能;默认是按照组件名称进行装配的;
没有能支持@Primary功能没有支持@Autowired(reqiured=false);
@Inject:
需要导入javax.inject的包,和Autowired的功能一样。没有required=false的功能;
@Autowired:Spring定义的; @Resource、@Inject都是java规范
AutowiredAnnotationBeanPostProcessor:解析完成自动装配功能;
3、 @Autowired:构造器,参数,方法,属性;都是从容器中获取参数组件的值
1)、[标注在方法位置]:@Bean+方法参数;参数从容器中获取;默认不写@Autowired效果是一样的;都能自动装配
2)、[标在构造器上]:如果组件只有一个有参构造器,这个有参构造器的@Autowired可以省略,参数位置的组件还是可以自动从容器中获取
3)、放在参数位置:
4、自定义组件想要使用Spring容器底层的一些组件(ApplicationContext,BeanFactory,xxx);
自定义组件实现xxxAware;在创建对象的时候,会调用接口规定的方法注入相关组件;Aware;
把Spring底层一些组件注入到自定义的Bean中;
xxxAware:功能使用xxxProcessor;
ApplicationContextAware==》ApplicationContextAwareProcessor;
Profile:
Spring为我们提供的可以根据当前环境,动态的激活和切换一系列组件的功能;
开发环境、测试环境、生产环境;
数据源:(/A)(/B)(/C);
@Profile:指定组件在哪个环境的情况下才能被注册到容器中,不指定,任何环境下都能注册这个组件
1)、加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中。默认是default环境
2)、写在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能开始生效
3)、没有标注环境标识的bean在,任何环境下都是加载的;
AOP
简介
指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式;
AOP三步走
1、将业务逻辑组件和切面类都加入到容器中;告诉Spring哪个是切面类(@Aspect)
2、在切面类上的每一个通知方法上标注通知注解,告诉Spring何时何地运行(切入点表达式)
3、开启基于注解的aop模式;@EnableAspectJAutoProxy
AOP原理
【看给容器中注册了什么组件,这个组件什么时候工作,这个组件的功能是什么?】
@EnableAspectJAutoProxy;
1、@EnableAspectJAutoProxy是什么?
@Import(AspectJAutoProxyRegistrar.class):给容器中导入AspectJAutoProxyRegistrar
利用AspectJAutoProxyRegistrar自定义给容器中注册bean;BeanDefinetion
internalAutoProxyCreator=AnnotationAwareAspectJAutoProxyCreator
给容器中注册一个AnnotationAwareAspectJAutoProxyCreator;
2、 AnnotationAwareAspectJAutoProxyCreator:
AnnotationAwareAspectJAutoProxyCreator的继承关系
->AspectJAwareAdvisorAutoProxyCreator
->AbstractAdvisorAutoProxyCreator
->AbstractAutoProxyCreator
implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware
关注后置处理器(在bean初始化完成前后做事情)、自动装配BeanFactory
关注这两个东西,在AnnotationAwareAspectJAutoProxyCreator里都扮演了一个什么样的角色
AbstractAutoProxyCreator.setBeanFactory()
AbstractAutoProxyCreator.有后置处理器的逻辑;
AbstractAdvisorAutoProxyCreator.setBeanFactory()--》initBeanFactory()
AnnotationAwareAspectJAutoProxyCreator.initBeanFactory()
AOP流程:
1、传入配置类,创建ioc容器
2、注册配置类,调用refresh()刷新容器;
3、registerBeanPostProcessors(beanFactory);注册bean的后置处理器来方便拦截bean的创建;
1)、先获取ioc容器已经定义了的需要创建对象的所有BeanPostProcessor
2)、给容器中加别的BeanPostProcessor
3)、优先注册实现了PriorityOrdered接口的BeanPostProcessor;
4)、再给容器中注册实现了Ordered接口的BeanPostProcessor;
5)、注册没实现优先级接口的BeanPostProcessor;
6)、注册BeanPostProcessor,实际上就是创建BeanPostProcessor对象,保存在容器中;
创建internalAutoProxyCreator的BeanPostProcessor【AnnotationAwareAspectJAutoProxyCreator】
(1)、创建Bean的实例
(2)、populateBean;给bean的各种属性赋值
(3)、initializeBean:初始化bean;
【1】、invokeAwareMethods():处理Aware接口的方法回调
【2】、applyBeanPostProcessorsBeforeInitialization():应用后置处理器的postProcessBeforeInitialization()
【3】、invokeInitMethods();执行自定义的初始化方法
(4)、applyBeanPostProcessorsAfterInitialization();执行后置处理器的postProcessAfterInitialization();
ocessor(AnnotationAwareAspectJAutoProxyCreator)创建成功;--》aspectJAdvisorsBuilder
7)、把BeanPostProcessor注册到BeanFactory中;
beanFactory.addBeanPostProcessor(postProcessor);
=以上是创建和注册AnnotationAwareAspectJAutoProxyCreator的过程==
AnnotationAwareAspectJAutoProxyCreator => InstantiationAwareBeanPostProcessor
4、finishBeanFactoryInitialization(beanFactory);完成BeanFactory初始化工作;创建剩下的单实例bean
1)、遍历获取容器中所有的Bean,依次创建对象getBean(beanName);
getBean->doGetBean()->getSingleton()->
2)、创建bean
【AnnotationAwareAspectJAutoProxyCreator在所有bean创建之前会有一个拦截,InstantiationAwareBeanPostProcessor,会调用postProcessBeforeInstantiation()】
(1)、先从缓存中获取当前bean,如果能获取到,说明bean是之前被创建过的,直接使用,否则再创建;
只要创建好的Bean都会被缓存起来
(2)、createBean();创建bean;
AnnotationAwareAspectJAutoProxyCreator 会在任何bean创建之前先尝试返回bean的实例
【BeanPostProcessor是在Bean对象创建完成初始化前后调用的】
【InstantiationAwareBeanPostProcessor是在创建Bean实例之前先尝试用后置处理器返回对象的】
【1】、resolveBeforeInstantiation(beanName, mbdToUse);解析BeforeInstantiation
希望后置处理器在此能返回一个代理对象;如果能返回代理对象就使用,如果不能就继续
1)、后置处理器先尝试返回对象;
bean = applyBeanPostProcessorsBeforeInstantiation():
拿到所有后置处理器,如果是InstantiationAwareBeanPostProcessor;
就执行postProcessBeforeInstantiation
if (bean != null) {
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
[2]、doCreateBean(beanName, mbdToUse, args);真正的去创建一个bean实例;和3.6流程一样;
[3]、
AnnotationAwareAspectJAutoProxyCreator【InstantiationAwareBeanPostProcessor】 的作用:
1、每一个bean创建之前,调用postProcessBeforeInstantiation();
关心MathCalculator和LogAspect的创建
1)、判断当前bean是否在advisedBeans中(保存了所有需要增强bean)
2)、判断当前bean是否是基础类型的Advice、Pointcut、Advisor、AopInfrastructureBean,
或者是否是切面(@Aspect)
3)、是否需要跳过
1)、获取候选的增强器(切面里面的通知方法)【List
每一个封装的通知方法的增强器是 InstantiationModelAwarePointcutAdvisor;
判断每一个增强器是否是 AspectJPointcutAdvisor 类型的;返回true
2)、永远返回false
2、创建对象
postProcessAfterInitialization;
return wrapIfNecessary(bean, beanName, cacheKey);//包装如果需要的情况下
1)、获取当前bean的所有增强器(通知方法) Object[] specificInterceptors
[1]、找到候选的所有的增强器(找哪些通知方法是需要切入当前bean方法的)
[2]、获取到能在bean使用的增强器。
[3]、给增强器排序
2)、保存当前bean在advisedBeans中;
3)、如果当前bean需要增强,创建当前bean的代理对象;
[1]、获取所有增强器(通知方法)
[2]、保存到proxyFactory
[3]、创建代理对象:Spring自动决定
JdkDynamicAopProxy(config);jdk动态代理;
ObjenesisCglibAopProxy(config);cglib的动态代理;
4)、给容器中返回当前组件使用cglib增强了的代理对象;
5)、以后容器中获取到的就是这个组件的代理对象,执行目标方法的时候,代理对象就会执行通知方法的流程;
3、目标方法执行 ;
容器中保存了组件的代理对象(cglib增强后的对象),这个对象里面保存了详细信息(比如增强器,目标对象,xxx);
1)、CglibAopProxy.intercept();拦截目标方法的执行
2)、根据ProxyFactory对象获取将要执行的目标方法拦截器链;
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
[1]、List<Object> interceptorList保存所有拦截器 5
一个默认的ExposeInvocationInterceptor 和 4个增强器;
[2]、遍历所有的增强器,将其转为Interceptor;
registry.getInterceptors(advisor);
[3]、将增强器转为List<MethodInterceptor>;
如果是MethodInterceptor,直接加入到集合中
如果不是,使用AdvisorAdapter将增强器转为MethodInterceptor;
转换完成返回MethodInterceptor数组;
3)、如果没有拦截器链,直接执行目标方法;
拦截器链(每一个通知方法又被包装为方法拦截器,利用MethodInterceptor机制)
4)、如果有拦截器链,把需要执行的目标对象,目标方法,
拦截器链等信息传入创建一个 CglibMethodInvocation 对象,
并调用 Object retVal = mi.proceed();
5)、拦截器链的触发过程;
[1]、如果没有拦截器执行执行目标方法,或者拦截器的索引和拦截器数组-1大小一样(指定到了最后一个拦截器)执行目标方法;
[2]、链式获取每一个拦截器,拦截器执行invoke方法,每一个拦截器等待下一个拦截器执行完成返回以后再来执行;
拦截器链的机制,保证通知方法与目标方法的执行顺序;
总结:
1、 @EnableAspectJAutoProxy 开启AOP功能
2、 @EnableAspectJAutoProxy 会给容器中注册一个组件 AnnotationAwareAspectJAutoProxyCreator
3、AnnotationAwareAspectJAutoProxyCreator是一个后置处理器;
4、容器的创建流程:
(1)、registerBeanPostProcessors()注册后置处理器;创建AnnotationAwareAspectJAutoProxyCreator对象
(2)、finishBeanFactoryInitialization()初始化剩下的单实例bean
[1]、创建业务逻辑组件和切面组件
[2]、AnnotationAwareAspectJAutoProxyCreator拦截组件的创建过程
[3]、组件创建完之后,判断组件是否需要增强
是:切面的通知方法,包装成增强器(Advisor);给业务逻辑组件创建一个代理对象(cglib);
5、执行目标方法:
(1)、代理对象执行目标方法
( 2)、CglibAopProxy.intercept();
[1] 、得到目标方法的拦截器链(增强器包装成拦截器MethodInterceptor)
[2]、利用拦截器的链式机制,依次进入每一个拦截器进行执行;
[3] 、效果:
正常执行:前置通知-》目标方法-》后置通知-》返回通知
出现异常:前置通知-》目标方法-》后置通知-》异常通知
声明式事务
原理
@EnableTransactionManagement
利用TransactionManagementConfigurationSelector给容器中会导入组件
导入两个组件
AutoProxyRegistrar
ProxyTransactionManagementConfiguration
AutoProxyRegistrar:
给容器中注册一个 InfrastructureAdvisorAutoProxyCreator 组件;
InfrastructureAdvisorAutoProxyCreator:?
利用后置处理器机制在对象创建以后,包装对象,返回一个代理对象(增强器),代理对象执行方法利用拦截器链进行调用;
ProxyTransactionManagementConfiguration 做了什么?
1、给容器中注册事务增强器;
1)、事务增强器要用事务注解的信息,AnnotationTransactionAttributeSource解析事务注解
2)、事务拦截器:
TransactionInterceptor;保存了事务属性信息,事务管理器;
他是一个 MethodInterceptor;
在目标方法执行的时候;
执行拦截器链;
事务拦截器:
[1]、先获取事务相关的属性
[2]、再获取PlatformTransactionManager,如果事先没有添加指定任何transactionmanger
最终会从容器中按照类型获取一个PlatformTransactionManager;
[3]、执行目标方法
如果异常,获取到事务管理器,利用事务管理回滚操作;
如果正常,利用事务管理器,提交事务
扩展原理
BeanPostProcessor
bean后置处理器,bean创建对象初始化前后进行拦截工作的
1、BeanFactoryPostProcessor:beanFactory的后置处理器;
在BeanFactory标准初始化之后调用,来定制和修改BeanFactory的内容;
所有的bean定义已经保存加载到beanFactory,但是bean的实例还未创建
BeanFactoryPostProcessor
1)、ioc容器创建对象
2)、invokeBeanFactoryPostProcessors(beanFactory);
如何找到所有的BeanFactoryPostProcessor并执行他们的方法;
1)、直接在BeanFactory中找到所有类型是BeanFactoryPostProcessor的组件,并执行他们的方法
2)、在初始化创建其他组件前面执行
2、BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor
postProcessBeanDefinitionRegistry();
在所有bean定义信息将要被加载,bean实例还未创建的;
优先于BeanFactoryPostProcessor执行;
利用BeanDefinitionRegistryPostProcessor给容器中再额外添加一些组件;
原理
1)、ioc创建对象
2)、refresh()-》invokeBeanFactoryPostProcessors(beanFactory);
3)、从容器中获取到所有的BeanDefinitionRegistryPostProcessor组件。
1、依次触发所有的postProcessBeanDefinitionRegistry()方法
2、再来触发postProcessBeanFactory()方法BeanFactoryPostProcessor;
4)、再来从容器中找到BeanFactoryPostProcessor组件;然后依次触发postProcessBeanFactory()方法
3、ApplicationListener:监听容器中发布的事件。事件驱动模型开发;
public interface ApplicationListener
监听 ApplicationEvent 及其下面的子事件;
步骤
1)、写一个监听器(ApplicationListener实现类)来监听某个事件(ApplicationEvent及其子类)
@EventListener;
原理:使用EventListenerMethodProcessor处理器来解析方法上的@EventListener;
2)、把监听器加入到容器;
3)、只要容器中有相关事件的发布,我们就能监听到这个事件;
ContextRefreshedEvent:容器刷新完成(所有bean都完全创建)会发布这个事件;
ContextClosedEvent:关闭容器会发布这个事件;
4)、发布一个事件:
applicationContext.publishEvent();
原理
ContextRefreshedEvent、IOCTest_Ext$1[source=我发布的时间]、ContextClosedEvent;
1)、ContextRefreshedEvent事件:
1)、容器创建对象:refresh();
2)、finishRefresh();容器刷新完成会发布ContextRefreshedEvent事件
2)、自己发布事件;
3)、容器关闭会发布ContextClosedEvent;
【事件发布流程】:
3)、publishEvent(new ContextRefreshedEvent(this));
1)、获取事件的多播器(派发器):getApplicationEventMulticaster()
2)、multicastEvent派发事件:
3)、获取到所有的ApplicationListener;
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
1)、如果有Executor,可以支持使用Executor进行异步派发;
Executor executor = getTaskExecutor();
2)、否则,同步的方式直接执行listener方法;invokeListener(listener, event);
拿到listener回调onApplicationEvent方法;
【事件多播器(派发器)】
1)、容器创建对象:refresh();
2)、initApplicationEventMulticaster();初始化ApplicationEventMulticaster;
1)、先去容器中找有没有id=“applicationEventMulticaster”的组件;
2)、如果没有this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
并且加入到容器中,我们就可以在其他组件要派发事件,自动注入这个applicationEventMulticaster;
【容器中有哪些监听器】
1)、容器创建对象:refresh();
2)、registerListeners();
从容器中拿到所有的监听器,把他们注册到applicationEventMulticaster中;
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
//将listener注册到ApplicationEventMulticaster中
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
SmartInitializingSingleton 原理:->afterSingletonsInstantiated();
1)、ioc容器创建对象并refresh();
2)、finishBeanFactoryInitialization(beanFactory);初始化剩下的单实例bean;
1)、先创建所有的单实例bean;getBean();
2)、获取所有创建好的单实例bean,判断是否是SmartInitializingSingleton类型的;
如果是就调用afterSingletonsInstantiated();
解决循环依赖
以类A,B互相依赖注入为例
- 根据类A的名称先从singletonObjects获取Bean实例,发现获取不到,就通过doGetBean方法开始创建Bean的流程。
- 根据A的名称找到对应的BeanDefinition,通过doCreateBean()方法创建对象,先确定类A的构造函数,然后选择一个实例化策略去实例化类A。
- 判断容器是否允许循环依赖,如果允许循环依赖,就创建一个ObjectFactory类并实现ObjectFactory接口的唯一的一个方法getObject()用于返回类A。然后将该ObjectFactory添加到singletonFactories中。
- 调用populateBean()为类A进行属性赋值,发现需要依赖类B,此时类B尚未创建,启动创建类B的流程。
- 根据类B的名称先从singletonObjects获取Bean实例,发现获取不到,就开始通过doGetBean方法开始创建Bean的流程
- 找到类B对应的BeanDefinition,确认B的构造函数,然后实例化B。
- 判断容器是否允许循环依赖,创建一个ObjectFactory并实现getObject()方法,用于返回类B,并添加到singletonFactories中。
- 调用populateBean()为类B进行属性赋值,发现需要依赖类A,调用getSingleton方法获取A:A现在已存在于singletonFactories中,getSingleton将A从singletonFactories方法中移除并放入earlySingletonObjects中。
- 调用getSingleton()方法获取B:getSingleton将A从singletonFactories方法中移除并放入earlySingletonObjects中。
- 调用initializeBean初始化bean,最后将新创建出来的类B保存到singletonObjects中
- 调用getSingleton()方法获取A,这时A已在earlySingletonObjects中了,就直接返回A
- 调用initializeBean初始化bean,最后将新创建出来的类B保存到singletonObjects中。
Sevlet3.0
1、web容器在启动的时候,会扫描每个jar包下的META-INF/services/javax.servlet.ServletContainerInitializer
2、加载这个文件指定的类SpringservletContainerInitializer
3、spring的应用一启动会加载感兴趣的WebApplicationInitializer接口的下的所有组件;
4、并且为WebApplicationInitializer组件创建对象(组件不是接口,不是抽象类)
1)、AbstractContextLoaderInitializer:创建根容器;createRootApplicationContext();
2)、AbstractDispatcherServletInitializer:
创建一个web的ioc容器;createServletApplicationContext()
创建了DispatcherServlet;createDispatcherServlet();
将创建的DispatcherServlet添加到ServletContext中;
getservletMappings( );
3)、AbstractAnnotationConfigDispatcherServletInitializer。注解方式配置的DispatcherServlet初始化器
创建根容器:createRootApplicationContext()
getRootConfigclasses();传入一个配置类
创建web的ioc容器: createServletApplicationContext();
获取配置类;getservletConfigclasses();
总结:
以注解方式来启动SpringMNC,继承AbstractAnnotationConfigDispatcherServletInitializer;实现抽象方法指定DispatcherServlet的配置信息;
SpringMVC中的异常处理
__EOF__

本文链接:https://www.cnblogs.com/LiPengFeiii/p/15924663.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~