spring原理
1. 说说你对spring的理解
Spring有两大特性:控制反转IOC 和 面向切面编程AOP,解决了传统代码的高耦合性与代码不可复用的问题,能很方便的整合各种开源框架,是一个非侵入式的,高效的开源框架。
IOC:控制反转,将创建对象的权利交给Spring,由Spring帮我们管理Bean。在面向对象的编程过程中,要想使用某个对象,就需要先实例化这个对象,需要我们用new XXX(),若该对象不存在就会报错。而在Spring中,我们不需要显式的去new,解决了代码的高耦合性。
AOP:面向切面编程,它是一种思想,是横向的。OOP:面向对象编程,是纵向的。先说说纵向的编程思想,举例:在JAVA中,有一个类A有一个方法,类B也想使用,那么怎么解决呢,就需要类B去继承类A,如果类C也想使用,那么需要去继承类A或类B。而横向编程的思想就是:将类A的公共方法提取出来,哪些类需要,就横切穿插进去,这就是AOP面向切面的横向编程思想,解决了代码的不可复用性。
spring的出现,解决了传统代码的高耦合与代码重复问题。
利用IOC控制反转与AOP,将创建对象的权利交给Spring,让Spring帮我们管理Bean。
利用AOP思想,把重复性的代码作为切面横切进程序,达到代码复用的功能。
但是Spring需要编写很多XML配置且配置繁琐固定,不同的开发场景需要导入多个不同的依赖。
SpringBoot是Spring的一个重大升级,它也可以归纳为有2大特性【自动配置,起步依赖】,它是基于“约定优于配置的思想”。
SptingBoot将Spring的XML配置,改成配置类,让我们不需要在类与XML中进行切换。
SpringBoot提供了起步依赖,需要开放相应的场景时,只需要导入相应的起步依赖。
2. 说说Spring的核心容器
Spring的主要功能是通过其核心容器来实现的。Spring提供了两种核心容器,分别为BeanFactory与ApplicationContext。
BeanFactory:是基础类型的IOC容器,提供了完整的IOC服务支持。简单来说,就是一个管理Bean的工厂,主要负责初始化各种Bean,并调用他们的生命周期方法。
采用延迟加载策略来初始化Bean,即在真正用到Bean(调用getBean())的时候才去实例化Bean,减小了服务器的压力,但是若Bean的某一属性并没有注入成功或Bean配置错误等,那么真正用到这个Bean时才报错,这不便于我们及时发现错误。
ApplicationContext:是BeanFactory的子接口,也被称为应用上下文。它不仅包含了BeanFactory的所有功能,还添加了对国际化,资源访问,事件传播等方面的支持。
在项目初始化启动的时候,就去实例化所有Bean,相当于一来就来了次自检,这样便于我们及时发现错误。
3. Bean的实例化方式有哪些
Bean:需要被Spring容器管理的对象。
实例化方式:
1. 构造器实例化 通过对Bean的构造方法来实例化。<bean id = "userService" class = "com.lihao.service.impl.UserServiceImpl">
2. 静态工厂实例化 要求开发者创建一个静态工厂的方法来创建Bean的实例,其Bean配置中的class属性所指定的不再是Bean实例的实现类,而是静态工厂类,同时还需要使用factpry-methond属性来指定所创建的静态工厂方法。
3. 实例工厂实例化 要求开发者创建一个工厂类,工厂类用普通方法创建Bean实例,同时Bean配置中不再是通过class属性指向实例化类,而是通过factory-bean属性指向配置的实例工厂,然后用factory-method属性指定工厂中创建实例的那个方法。
4. 注解实例化 @Controller @Service @Repository @Component[可作用于任何层次]
4. Bean的作用域
Spring中Bean的实例定义了七种作用域。
1. singleton 默认作用域,无论多少个Bean引用它,都会指向同一个实例对象。
2. prototype 每次获取的实例对象都是一个新实例。
3. request
4. session
5. globalSession
6. application
7. websocket
5. Bean依赖注入的方式有哪些
依赖注入:将一个Bean注入到另一个对象中。
注入方式:
基于XML:
构造器注入 在实例化Bean时,使用<bean>的子标签<constructor-arg>来定义构造方法的参数。
setter注入 在实例化Bean是,使用<bean>的子标签<property>来为各个属性进行注入值。【需要为被注入的属性提供setter方法】
基于注解
@Autowired 默认按照类型进行注入
@Resource 默认按照实例名注入,有两个属性,name和type。可以自己按需指定。
@Qualifier 按实例名注入,实例名由@Qualifier的参数指定。【一般用于由多个同类型Bean时】
@AutoWired与@Qualifier的理解:https://blog.csdn.net/weixin_43351375/article/details/101014896
6. 说说你对SpringAop的理解
AOP:
面向切面编程,是对OOP的一种补充。AOP采用横向抽取机制,将分散在各个方法中的重复代码抽取出来,然后在程序编译或运行时,再将这些抽取的代码应用到需要的地方。这就是AOP横向编程思想。
虽然传统的OOP通过继承或组合的方式也能达到代码的重用,但如果需要实现某个功能(如日志记录),需要将代码编写到各个方法中,一旦想关闭或对其修改,就需要对所有相关方法进行修改。
目前常用的AOP框架有 SpringAOP与AspectJ。
SpringAOP:纯Java代码实现,在运行期间通过代理方式向目标类织入增强的代码。
AspectJ:一个基于Java语言的AOP框架。
SpringAOP:
Spring中的AOP代理,可以是JDK动态代理(默认),也可以是CGLIB动态代理。
JDK动态代理:(针对实现了某个接口的类)
通过Proxy类来实现,通过调用其newProxyInstance()方法来创建代理对象。对于使用业务接口的类,Spring默认会使用JDK动态代理。
newProxyInstance():参数1:当前类加载器 参数2:被代理后的对象 参数3:代理类本身
代理类需要实现InvocationHandler接口,并编写代理方法invoke()。
invoke():参数1:被代理后的对象 参数2:将要被执行的方法 参数3:执行方法时需要的参数
JDK动态代理的实现:
https://blog.csdn.net/jiankunking/article/details/52143504
=================================================
CGLIB动态代理:(针对没有实现接口的类)
JDK动态代理有一定的局限性————使用动态代理的对象必须实现一个或多个接口。
对于没有实现接口的类进行代理,那么可以使用CGLIB代理。
底层采用字节码技术,对指定的目标类生成一个子类,通过对子类进行增强,从而达到对目标对象的增强。
代理类需要实现MethodInterceptor接口,并实现方法intercept()。
intercept():参数1:指定父类生成的代理对象 参数2:拦截的方法 参数3:拦截方法的参数数组 参数4:方法的代理对象,用于执行父类的方法
CGLIB动态代理的实现:
https://www.cnblogs.com/leifei/p/8263448.html
//////////////////////////////////
Spring种的AOP代理默认就是使用JDK动态代理的方式来实现的。具体就不贴代码了。
----------------------------------
Spring种的通知类型:
前置通知
后置通知
环绕通知
异常通知
最终通知
7. AspectJ
AspectJ:一个AOP框架,实现AOP的方式有两种,基于XML方式与基于注解的方式。【这里只介绍注解,XML用的太少了】
@Aspect
@Pointcut
@Before
@AfterReturning
@Around
@AfterThrowing
@After
@DeclareParents
AspectJ做AOP日志管理:https://www.cnblogs.com/jelly12345/p/14950731.html
8. Spring对于事物的管理
Spring提供了专门的API做事物处理,简化了传统的事物管理流程,在一定程度上减少了开发者的工作量。
Spring中的事物管理分为两种方式,传统的编程式事物管理与声明式事务管理。【编程式事务管理就是编写代码来管理事务,需要定义事物的开始,提交和异常时的回滚,这里不讲解此种】
声明式事物管理:
是通过AOP实现的,主要思想就是将事物管理作为一个切面代码单独编写,然后通过AOP技术横切织入到代码中。
实现方式:
1. 基于XML
2. 基于注解【这里只讲注解方式】
注解方式:
@Transational()
常用参数说明:
isolation:指定事物的隔离级别
propagation:指定事物的传播行为
read-only:指定事物是否只读
rollbackFor:指定事物遇到特定异常时强制回滚
timeout:指定事物的超时时长
// 使用@Transational()注解时 为什么往往需要指定 rabbackFor = Exception.class
// https://www.cnblogs.com/itlihao/p/16583513.html
在Java中使用@Transactional注解时,通常需要指定rollbackFor参数为Exception.class,是因为在事务处理过程中,如果出现异常,如果没有指定rollbackFor参数,默认会回滚RuntimeException以及Error,但是其他的异常则不会回滚。
RuntimeException和Exception都是Java中的异常类,但它们有所不同:
RuntimeException及其子类被称为运行时异常,它们通常在代码执行时发生,例如空指针引用、数组下标越界等。这些异常不需要显式地使用try-catch块捕获,如果没有捕获它们,程序会在出现异常时直接终止。
常见的RuntimeException包括NullPointerException、ArrayIndexOutOfBoundsException、IllegalArgumentException等。
Exception及其子类是除了RuntimeException及其子类以外的所有异常类。它们通常表示代码中可能会出现的错误情况,例如文件不存在、网络连接失败等。
这些异常需要在代码中显式地使用try-catch块捕获,否则程序无法通过编译。
9. Spring对于事物管理中的传播行为
传播行为:指当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行。【说得通俗一点就是多个具有事务控制的service的相互调用时所形成的复杂的事务边界控制】
传播行为详解:https://blog.csdn.net/weixin_39625809/article/details/80707695
10. Spring对于事物管理中的隔离级别
在mysql中,隔离级别主要是:1. 读未提交 2. 读已提交 3. 可重复度 4. 串行化
而在Spring中,多了一种隔离级别:default(默认)
default:意思是使用数据库本身使用的隔离级别。 Spring建议的是使用DEFAULT,就是数据库本身的隔离级别,配置好数据库本身的隔离级别,无论在哪个框架中读写数据都不用操心了。
11. SpringBoot怎么整合拦截器, 过滤器 【拦截器与过滤器的区别】 注意这里不讲解 SpringMVC整合拦截器,只讲SpringBoot整合拦截器与过滤器
SpringBoot整合拦截器:
(1)实现HandlerInterceptor,重写如下方法
preHandle(HttpServletRequest request, HttpServletResponse response, java.lang.Object handler) 方法前执行
post(HttpServletRequest request, HttpServletResponse response, java.lang.Object handler, ModelAndView modelAndView) 方法后执行,视图解析之前
afterCompletion(HttpServletRequest request, HttpServletResponse response, java.lang.Object handler, java.lang.Exception ex) 整个请求完成之后执行
(2)编写拦截器配置类
// 在SpringBoot中,为我们提供已经整合了SpriingMVC相关的配置,如果需要更改,就实现WebMvcConfigurer,重写里面相应的方法即可。
// 如:addCorsMapping——跨域 addFormatters——格式化器 addInterceptors——拦截器 addViewControllers——视图控制映射器
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
@Autowired
private LogInterceptor logInterceptor;
//配置拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
//指定拦截器,指定拦截路径
registry.addInterceptor(logInterceptor).addPathPatterns("/**");
}
}
================================
/////////////////////////// SpringBoot整合过滤器 //////////////////////////////////
https://blog.csdn.net/m0_37989911/article/details/99241066
********************************
过滤器与拦截器的区别:
①拦截器是基于java的反射机制的,而过滤器是基于函数回调。
②拦截器不依赖与servlet容器,过滤器依赖与servlet容器。
③拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。
④拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。
⑤在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。
⑥拦截器可以获取IOC容器中的各个bean,而过滤器就不行,这点很重要,在拦截器里注入一个service,可以调用业务逻辑。
12. SpringBoot自动装配原理与一些相关面试题
https://www.cnblogs.com/itlihao/p/14973077.html
13. SpringBoot怎么创建配置类
@Configuration
public class DogConfiguration {
// @Bean是用在方法上,将当前方法的返回值对象放到容器当中!
// 如果@Bean不指定name属性,那么实例名默认为方法名
// 若想指定实例名,就在@Bean("指定你想设置的实例名")
@Bean
public Dog dog() {
Dog dog = new Dog();
dog.setName("小黑");
dog.setType("柯基");
return dog;
}
}
14. @ComponentScan
此注解默认会扫描同一个包下的所有组件,可传入参数改变默认扫描路径。
14. @Componet
@Componet注解为Bean的定义
表示此类为Spring容器中的一个Bean,将该类交给Spring管理
相当于<bean id=" " class=" "></bean>
15. @Configuration
@Configuration声明一个类为配置类,用于取代bean.xml配置文件
常见的搭配使用1个:@Bean
@Bean等价于Spring中的bean标签用于注册bean对象的,给容器中添加组件。
一般以方法名作为组件的id,配置类里面使用@Bean标注在方法上给容器注册组件,默认是单实例的。
16. @Bean
@Bean是一个方法级别上的注解,主要用在@Configuration注解的类里。
@Bean是用在方法上,将当前方法的返回值对象放到容器当中!
默认情况下bean的实例名就是方法名,你也可以使用name属性来指定实例名
@Configuration
public class Myconfig {
// 给容器中加入组件,以方法名作为组件id
@Bean
public User user01() {
User user = new User();
return user;
}
}
// 效果相当于下面在xml里面注册bean
<beans>
<bean id="user01" class="com.gzl.cn.User"/>
</beans>
17. 配置文件属性值的注入
// 方式一:@Value注解【单个属性注入,要求被注入的属性必须是简单类型】
jdbc.username=root
jdbc.password=123
@Value("${jdbc.username}")
String username;
@Value("${jdbc.password}")
String password;
// 方式二:@ConfigurationProperties注解【批量注入】(1. 被注入的类需要加入IOC 2. 需提供setter方法)
// @ConfigurationProperties(prefix = “person”)注解的作用是将配置文件中以person开头的属性值通过setXX()方法注入到实体类对应属性中。
// @Component注解的作用是将当前注入属性值的Person类对象作为Bean组件放到Spring容器中,只有这样才能被@ConfigurationProperties注解进行赋值。
person.id=1
person.name=tom
person.hobby=音乐,篮球,阅读
person.family=father,mother
person.map.k1=v1 // 由于Person实体类中变量map是Map类型,是键值对的形式,所以在赋值是需要以k1,k2的形式指定key-value
person.map.k2=v2
person.pet.type=dog // 由于pet变量是Pet实体类,因此也需要三层调用指定对应Pet实体类每个变量的值。
person.pet.name=旺财
public class Pet {
private String type;
private String name;
public String getType(){
return type;
}
public void setType(String type) {
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Data
@ToString
@Component
@ConfigurationProperties(prefix = "person") //将配置文件中以person开头的属性注入到该类
public class Person {
private int id; //id
private String name; //名称
private List hobby; // 爱好
private String[] family; // 家庭
private Map map;
private Pet pet; // 宠物
}
18. SpringMVC工作流程
1)用户发送请求至前端控制器 DispatcherServlet。
2)DispatcherServlet 收到请求调用 HandlerMapping 处理器映射器。
3)处理器映射器找到具体的处理器(可以根据 xml 配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给 DispatcherServlet。
4)DispatcherServlet 调用 HandlerAdapter 处理器适配器。
5)HandlerAdapter 经过适配调用具体的处理器(Controller,也叫后端控制器)。
6)Controller 执行完成返回 ModelAndView
19. 请解释Spring Bean的生命周期?
(1)默认情况下,IOC容器中bean的生命周期分为五个阶段:
● 调用构造器 或者是通过工厂的方式创建Bean对象
● bean对象的属性注入值
● 调用初始化方法,进行初始化,初始化方法是通过init-method来指定的.
● 使用
● IOC容器关闭时, 销毁Bean对象.
(2)当加入了Bean的后置处理器后,IOC容器中bean的生命周期分为七个阶段:
● 调用构造器 或者是通过工厂的方式创建Bean对象
● 给bean对象的属性注入值
● 执行Bean后置处理器中的 postProcessBeforeInitialization
● 调用初始化方法,进行初始化,初始化方法是通过init-method来指定的.
● 执行Bean的后置处理器中 postProcessAfterInitialization
● 使用
● IOC容器关闭时, 销毁Bean对象
只需要回答出第一点即可,第二点也回答可适当 加分。
20. @RequestParam注解
该注解用于解决请求参数与控制器方法接收形参不一致的问题。
常用属性:
value:属性别名
required:是否必须
defaultValue:默认值
21. @PathVariable
该注解用于映射URL绑定的占位符
@RequestMapping("/user/{id}")
public String testPathVariable(@PathVariable("id") String id){
System.out.println("路径上的占位符的值="+id);
return "success";
}
22. Mybatis的mapper中如何传递多个参数
方法1:xml里面用#{0},#{1}....
方法2:@Param注解
方法3:封装成Map对象
方法4:封装成JavaBean对象
23. <foreach常用属性
collection
open
close
speparator
index 指定一个名称,用于标识当前迭代次数
item
<delete id="deleteBatch">
delete from user where id in
<foreach collection="array" item="id" index="index" open="(" close=")" separator=",">
#{id}
</foreach>
</delete>
24. SpringMvc 的控制器是不是单例模式,如果是,有什么问题,怎么解决
Spring中的bean是线程安全的吗:https://cnblogs.com/myseries/p/11729800.html
是单例模式,所以在多线程访问的时候有线程安全问题。
解决方法:
1)不要再成员位置定义成员变量
2)如果非要定义成员变量,在类上用@Scope("prototype"),且共享属性不能被static修饰=========================每次获取Bean的时候会有一个新的实例,当请求数越多,性能会降低,因为每次回创建新的实例。
3)使用ThreadLocal
25. #{}与${}的区别
#{}是预编译处理 能防止SQL注入
${}是字符串替换
MyBatis在处理#{}时,会将SQL中的#{}替换为?号,使用PreparedStatement的set方法来赋值;
MyBatis在处理${}时,就是把${}替换成变量的值。
select * from user where name like concat('%', #{name}, '%')
26. 怎么防止重复提交表单
重复提交:由于用户互操作,或者网络原因延迟导致用户重复提交,亦或是恶意用户使用postMan恶意重复提交。
上述行为会导致服务器负载,因此防止表单重复提交具有一定必要性!
解决方法:
https://www.jb51.net/article/270859.htm
https://blog.csdn.net/u013733643/article/details/124047999
1. 数据库添加唯一约束。【比如名字不能重复之类的】
2. 用户点击提交按钮后,屏蔽提交按钮。【不能防止postMan恶意提交】
3. 在 session 中存放一个特殊标志。当表单页面被请求时,生成一个特殊的字符标志串,存在 session 中,同时放在表单的隐藏域里。接受处理表单数据时,检查标识字串是否存在,并立即从 session 中删除它,然后正常处理数据。 如果发现表单提交里没有有效的标志串,这说明表单已经被提交过了,忽略这次提交。【一般用的很少了,现在不怎么用session】
4. 使用AOP自定切入实现 https://www.cnblogs.com/LarryBlogger/p/17283711.html
自定义防止重复提交标记(@AvoidRepeatableCommit)。
对需要防止重复提交的Congtroller里的mapping方法加上该注解。
新增Aspect切入点,为@AvoidRepeatableCommit加入切入点。
每次提交表单时,Aspect都会保存当前key到reids(须设置过期时间)。例如:key是IP+token+接口路径作为key,存入redis,每次查询redis是否有值
重复提交时Aspect会判断当前redis是否有该key,若有则拦截。
5. 拦截器(本质和第4点差不多,原理是:判断请求url和数据是否和上一次相同)
27. cookie与session
cookie:存放在客户端。浏览器发起HTTP请求,服务器会进行cookie设置,cookie里面有key和value两个属性,服务器会把里面的key和value内容补充完整发给浏览器,浏览器就会保存下来,下一次就带着这个cookie去访问服务器。【打开浏览器就能看到我们保存了哪些cookie,所以把用户名和密码放进去是很不安全的】
session:存放在服务器。浏览器发起HTTP请求,服务器核对了用户名和密码,若正确,就会在服务器创建一个sessionID与一个会话结束时间【不知道用户啥时候关闭浏览器,所以由服务器这边给定一个用户认证过期的时间】,然后将其发送回浏览器。浏览器拿到后,就保存这个sessionID,下次访问就带着这个sessionID来访问。
session虽然解决了安全性的问题,但是若用户量大,在特定时间服务器需要保存的sessionID就很多,就可以多来几台服务器,但是就要解决session共享问题,也比较麻烦。
redis:将sessionID存入redis,redis速度快,但是也存在redis宕机或达到瓶颈后,需要做集群。
JWT:它是一个非常轻巧的规范,它允许我们在使用JWT在用户和服务器之间传递安全可靠的信息。
他不需要我们存储登录的相关信息。
https://www.cnblogs.com/itlihao/p/14846026.html
28. Spring是如何解决循环
什么是循环依赖:
一个或多个对象实例之间存在直接或间接的依赖关系,这种依赖关系构成了构成一个环形调用。
总结为3种情况:
1. 自己直接依赖自己
2. 两个对象之间的直接依赖
3. 多个对象之间的间接依赖【因为业务代码调用层级很深,不容易识别出来】
循环依赖的类别:
1. 构造器的循环依赖
2. 单例模式下的setter属性注入的循环依赖【Spring自动解决】
3. 多例模式下的setter属性注入的循环依赖【不能解决,会走到AbstractBeanFactory类中下面的判断,抛出异常。】
构造器的循环依赖,Spring不能解决!!! 我们可以在ConstructorA或者ConstructorB构造函数的参数上加上@Lazy注解就可以解决
@Autowired
public ConstructorB(@Lazy ConstructorA constructorA) {
this.constructorA = constructorA;
}
属性的循环依赖,Spring能解决!!!【单例的setter注入能解决,多例的setter注入不能解决】
前言:按道理来说,例如 A 要依赖 B,发现 B 还没创建。于是开始创建 B ,创建的过程发现 B 要依赖 A, 而 A 还没创建好呀,因为它要等 B 创建好。就这样它们俩就搁这卡 bug 了呀。
探究Spring是如何解决循环以来的:
https://blog.csdn.net/qq_44750696/article/details/124150536
https://blog.csdn.net/u013733643/article/details/124003573
https://blog.csdn.net/CRMEB/article/details/122316641
https://www.cnblogs.com/dw3306/p/15995834.html
29. 全局异常处理
SpringBoot中有一个ControllerAdvice的注解,使用该注解表示开启了全局异常的捕获,
我们只需在自定义一个方法使用ExceptionHandler注解然后定义捕获异常的类型即可对这些捕获的异常进行统一的处理。
// 自定义异常类
public class BizException extends RuntimeException {
/**
* 错误码
*/
protected String errorCode;
/**
* 错误信息
*/
protected String errorMsg;
public BizException() {
super();
}
// setter getter方法
}
// 全局异常,捕获自定义异常
@ControllerAdvice
public class MyExceptionHandler {
@ExceptionHandler(value =BizException.class)
public String exceptionHandler(Exception e){
System.out.println("未知异常!原因是:"+e);
return e.getMessage();
}
}
@PostMapping("/user")
public boolean insert(@RequestBody User user) {
System.out.println("开始新增...");
//如果姓名为空就手动抛出一个自定义的异常!
if(user.getName()==null){
throw new BizException("-1","用户姓名不能为空!");
}
return true;
}
30. Mybatis 结果集的映射方式有几种,并分别解释每种映射方式如何使用。
自动映射:通过resultType来指定要映射的类型即可。
自定义映射:通过resultMap来完成具体的映射规则,指定将结果集中的哪个列映射到对象的哪个属性。
31. MyBatis如何获取自动生成的(主)键值
<insert id=”insertname” usegeneratedkeys=”true” keyproperty=”id”>
insert into names (name) values (#{name})
</insert>
32. 简述Mybatis的动态SQL,列出常用的6个标签及作用
<if> : 进行条件的判断
<where>:在<if>判断后的SQL语句前面添加WHERE关键字,并处理SQL语句开始位置的AND 或者OR的问题
<trim>:可以在SQL语句前后进行添加指定字符 或者去掉指定字符.
<set>: 主要用于修改操作时出现的逗号问题
<choose> <when> <otherwise>:类似于java中的switch语句.在所有的条件中选择其一
<foreach>:迭代操作
32. 跨域请求是什么
跨域:是指浏览器在发起网络请求时,会检查该请求所对应的协议,域名,端口是否和当前网页一致,若不一致则浏览器会进行限制。之所以浏览器要做这个限制,是为了用户信息安全。
解决:response添加header,直射response.setHeader("Access-Control-Allow-Origin","*"); 表示可以访问所有网站,不受是否同源的限制。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
2021-01-20 java反射详解