对于Spring boot之中一些常用注解的理解
IOC
控制反转(Inversion of Control),像我们平常写代码,需要哪个对象需要我们自己实例化对象,这样会导致代码之间的耦合性变高,所以这里Spring就提供了IOC控制反转这个概念。将实例化对象的这个权力交给Spring,当我们需要的时候Spring就注入到我们的代码中,所以将这个实例化对象的这些代码反转给了Spring。
ioc的思想最核心的地方在于,资源不由使用资源的双方管理,而由不使用资源的第三方管理,这可以带来很多好处。第一,资源集中管理,实现资源的可配置和易管理。第二,降低了使用资源双方的依赖程度,也就是我们说的耦合度。
也就是说,甲方要达成某种目的不需要直接与乙方交互,它只需要达到的目的告诉第三方就可以完成,比如甲方需要购买手机,而乙方它卖手机,它要把手机卖出去,并不需要自己去直接找到一个卖家来完成手机的卖出。它也只需要找第三方,告诉别人我是个卖手机的。然后甲乙双方进行交易活动,都不需要自己直接去找卖家,相当于程序内部开放接口,卖家由第三方作为参数传入。甲乙互相不依赖,而且只有在进行交易活动的时候,甲才和乙产生联系。反之亦然。这样做什么好处么呢,甲乙可以在对方不真实存在的情况下独立存在,而且保证不交易时候无联系,想交易的时候可以很容易的产生联系。甲乙交易活动不需要双方见面,避免了双方的互不信任造成交易失败的问题。因为交易由第三方来负责联系,而且甲乙都认为第三方可靠。那么交易就能很可靠很灵活的产生和进行了。
这就是ioc的核心思想。生活中这种例子比比皆是,支付宝在整个淘宝体系里就是庞大的ioc容器,交易双方之外的第三方,提供可靠性可依赖可灵活变更交易方的资源管理中心,类似的还有购房中介等。
AOP
1.@Autowired
自动导入依赖的bean,byType方式,如果要允许null 值,可以设置它的required属性为false,如:@Autowired(required=false)
1.1 问题:为什么有时选用final,有时候使用@autowired注入
@Autowired注入bean,相当于在配置文件中配置bean,并且使用setter注入。
首先明确Java变量的初始化顺序:静态变量或静态语句块–>实例变量或初始化语句块–>构造方法–>@Autowired->@PostConstruct(注释的方法)
让我们来看下面一个例子
上面这段代码能不能正常执行呢?
答案是不阔能,按照上面的初始化顺序,执行构造方法时,person里面是没有内容的,所以执行上面的那段代码会出现 java.lang.NullPointerException。
解决方法一:
使用构造方法注入:
这样就可以明确变量注入时的加载顺序。
加上final:
Springboot官方建议使用final来修饰成员变量,然后通过构造方法来进行注入原因:final修饰的成员变量是不能够被修改的,反射那就没办法了,@Autowired注入的对象无法使用final关键字,因为final对象必须在构造器中初始化。
这样有如下两个优点:
1.这样当test类创建的时候,强制依赖person对象,确保创建每个test对象的对象都是有效状态
2.构造器中可以添加对象初始化的校验逻辑
3.可以清楚的区分对象是通过setter方法注入的(非final对象)还是通过强制依赖注入的(final对象)
1.1.1 注解注入的的优缺点:
– 写更少的代码,-- 代码变得不安全,-- 单元测试会比较复杂,-- 无法使用fianl对象,-- 违反单一职责原则变得很容易,-- 对受影响的类隐藏自己的依赖关系
1.1.2 构造注入的优缺点:
– 更安全的代码,-- 测试友好,-- 依赖添加代价较高,显式的表明代码的bad smell,-- 在受影响的类中显式的表明依赖关系,-- 需要写更多的业务代码(可以通过Lombok解决)
1.2 @Qualifier
当有多个同一类型的Bean时,可以用@Qualifier(“name”)来指定。与@Autowired配合使用。Qualifier的意思是合格者,通过这个标示,表明了哪个实现类才是我们所需要的,添加@Qualifier注解,需要注意的是@Qualifier的参数名称为我们之前定义@Service注解的名称之一
例子:
1.3 @PostConstruct 和 @PreDestory
@PostConstruct注解好多人以为是Spring提供的。其实是Java自己的注解。
Java中该注解的说明:@PostConstruct该注解被用来修饰一个非静态的void()方法。被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次。PostConstruct在构造函数之后执行,init()方法之前执行。
网上截图:
2.@Resource
@Resource(name=”name”,type=”bean的class”):没有括号内内容的话,默认byName。与@Autowired干类似的事。
这个注解是属于J2EE的,减少了与spring的耦合。
3.@Bean
用@Bean标注方法等价于XML中配置的bean,@Bean注解告诉Spring这个方法将会返回一个对象,这个对象要注册为Spring应用上下文中的bean。通常方法体中包含了最终产生bean实例的逻辑。
4.@Component
@Component注解表明一个类会作为组件类,并告知Spring要为这个类创建bean。
4.1@Bean和@Component
不同点:@Component 通常是通过类路径扫描来自动侦测以及自动装配到Spring容器中。
@Bean 注解通常是我们在标有该注解的方法中定义产生这个bean的逻辑。
@Component (@Controller @Service @Respository)作用于类上,只有在我们的SpringBoot应用程序启用了组件扫描并且包含了被注解的类时才有效。通过组件扫描,Spring将扫描整个类路径,并将所有@Component注释类添加到Spring Context,这里有的不足就是会把整个类当成bean注册到spring 容器上,如果这个类中并不是所有方法都需要注册为bean的话,会出现不需要的方法都注册成为bean,这时候必须确保这些不需要的方法也能注册为bean或者在扫描中加filter 过滤这些不需要的bean,否者spring将无法成功启动。
@Bean相对来说就更加灵活了,它可以独立加在方法上,按需注册到spring容器,而且如果你要用到第三方类库里面某个方法的时候,你就只能用@Bean把这个方法注册到spring容器,因为用@Component你需要配置组件扫描到这个第三方类路径而且还要在别人源代码加上这个注解,很明显是不现实的。
4.2 @Scope
@Scope(“singleton”)//默认,单实例,IOC容器启动的时候,就会调用方法创建对象并放在IOC容器中,以后每次获取都是,从容器当中拿同一个对象。
@Scope(“prototype”)//多实例,IOC容器启动的时候,并不会调用方法创建对象,而是当每次从容器获取的时候,才会调用方法创建对象。
@Scope(“request”)表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效。
@Scope(“session”)表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP session内有效。
@Scope(“global session”)作用域类似于标准的HTTP Session作用域,不过它仅仅在基于portlet的web应用中才有意义。Portlet规范定义了全局Session的概念,它被所有构成某个 portlet web应用的各种不同的portlet所共享。在global session作用域中定义的bean被限定于全局portlet Session的生命周期范围内。如果你在web中使用global session作用域来标识bean,那么web会自动当成session类型来使用。
request、session、global session使用的时候首先要在初始化web的web.xml中做如下配置:
<web-app>
...
<listener>
<listener-class>
org.springframework.web.context.request.RequestContextListener
</listener-class>
</listener>
...
</web-app>
5.@Import
用来导入其他配置类
6. @ImportResource
用来加载xml配置文件
7.@Controller(作用Controller层)
@Controller用于标记在一个类上,使用它标记的类就是一个SpringMvc Controller对象,分发处理器会扫描使用该注解的类的方法,并检测该方法是否使用了@RequestMapping注解。
@Controller只是定义了一个控制器类,而使用@RequestMapping注解的方法才是处理请求的处理器。
@Controller标记在一个类上还不能真正意义上说它就是SpringMvc的控制器,应为这个时候Spring还不认识它,这个时候需要把这个控制器交给Spring来管理。
@Controller 将当前修饰的类注入SpringBoot IOC容器,使得从该类所在的项目跑起来的过程中,这个类就被实例化。
<context:component-scan base-package=“com”/>
7.1 @RestController
观察@RestController的源码,我们可以知道@RestController = @Controller + @ResponseBody组成
@ResponseBody 它的作用就是指该类中所有的API接口返回的数据,无论你对应的方法返回Map或是其他Object,它会以Json的形式返回给客户端,如果返回的是String类型,则仍然是String。
8.@Service(作用service层)
一般用于修饰service层的组件
9.@Repository(作用DAO层)
使用@Repository注解可以确保DAO或者repositories提供异常转译,这个注解修饰的DAO或者repositories类会被ComponetScan发现并配置,同时也不需要为它们提供XML配置项。
为什么 @Repository 只能标注在 DAO 类上呢?
这是因为该注解的作用不只是将类识别为Bean,同时它还能将所标注的类中抛出的数据访问异常封装为 Spring 的数据访问异常类型。 Spring本身提供了一个丰富的并且是与具体的数据访问技术无关的数据访问异常结构,用于封装不同的持久层框架抛出的异常,使得异常独立于底层的框架。
示例:
@Repository(value=“userDao”)
该注解是告诉Spring,让Spring创建一个名字叫“userDao”的UserDaoImpl实例。
当Service需要使用Spring创建的名字叫“userDao”的UserDaoImpl实例时,就可以使用@Resource(name = “userDao”)注解告诉Spring,Spring把创建好的userDao注入给Service即可。
10.@SpringBootApplication
包含了@ComponentScan、@Configuration和@EnableAutoConfiguration注解,SpringBootApplication启动时会默认扫描主类当前包及子包,如果需要扫描主类当前包外的其他包或不扫描当前包下的特定包或类,可通过下列属性实现。
Class<?>[] exclude() default {};
String[] excludeName() default {};
String[] scanBasePackages() default {};
Class<?>[] scanBasePackageClasses() default {};
示例:
10.1 @ComponentScan
让spring Boot扫描到Configuration类并把它加入到程序上下文,@ComponentScan(value = “io.mieux.controller”),可以指定要扫描的包
10.2 @Configuration
等同于spring的XML配置文件;使用Java代码可以检查类型安全。
10.3 @EnableAutoConfiguration
打开自动配置的功能,也可以关闭某个自动配置的选项
示例:
@SpringBootApplication(exclude = { xxxConfig.class })
11.@Documented
在自定义注解的时候可以使用@Documented来进行标注,如果使用@Documented标注了,在生成javadoc的时候就会把@Documented注解给显示出来
带@Documented生成的javadoc
不带@Documented生成的javadoc
12.@EntityScan(“com.sdses”)
用来扫描双引号所填入包下的实体类,一般在启动类加上。
13.@EnableAsync和@Async
为什么会使用这两个注解:
在我们的日常开发中,我们偶尔会遇到在业务层中我们需要同时修改多张表的数据并且需要有序的执行,如果我们用往常的同步的方式,也就是单线程的方式来执行的话,可能会出现执行超时等异常造成请求结果失败,及时成功,前端也需要等待较长时间来获取响应结果,这样不但造成了用户体验差,而且会经常出现请求执行失败的问题,在这里我们一般会采用3种方式来处理。
1.创建线程池,采用多线程来完成
@RestController
public class TestAsyncController {
@Autowired
private TestAsyncService asyncService;
/**
使用传统方式测试
*/
@GetMapping("/test")
public void test() {
System.out.println("获取主线程名称:" + Thread.currentThread().getName());
ThreadPoolExecutor executor = new ThreadPoolExecutor(1,5,50000, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
executor.execute(new Runnable() {
@Override
public void run() {
asyncService.serviceTest();
}
});
System.out.println("执行完成,向用户响应成功信息");
}
}
2.在Service层采用@EnableAsync和@Async
@Service
@EnableAsync
public class TestAsyncService {
/**
采用异步执行
*/
@Async
public void serviceTest() {
// 这里执行实际的业务逻辑,在这里我们就是用一个简单的遍历来模拟
Arrays.stream(new int[]{1,2,3,4,5,6,7,8,9,10}).forEach( t -> {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("获取number为:" + t) ;
});
}
此注解也可使用在启动类上
3.使用MQ
当我们涉及的请求在业务逻辑中一次性操作很多很多的数据,例如:一个请求执行相关业务操作后,将操作日志插入到数据库中,我们可以使用@Async来实现,但是这样增加了业务和非业务关系的冗余性(同时如何并发量很大,我们使用@Async处理,无法提升我们系统的整体系统,这样很容易造成服务器宕机),所以我们对于这种情况,我们会采用mq来实现,将业务逻辑和非业务逻辑进行隔离执行,互不影响,非业务逻辑不会影响到执行业务逻辑的结果和主机性能
示例:
pom.xml:
application.yml:
消息接收者:
将监听到的数据转换成需要的类型
为什么要使用消息队列?
解耦、异步、削峰。
过几天会写一篇关于MQ(消息队列)的文章。
14.@Configuration
通过@Configuration的源码我们可以看到@Component注解,它也可以注册为bean,也可以使用@Autowired注入对应需要它的位置。
1.@Configuration不可以是final类型;
2.@Configuration不可以是匿名类;
3.嵌套的configuration必须是静态类。
@Configuation等价于配置为spring容器,来管理其中的bean
@Bean等价于
@ComponentScan等价于<context:component-scan base-package=”com.dxz.demo”/>
而且比较重要的一点是,加入@Configuation后可以防止对同一对象的二次实例化,维护了spring默认的单例设计原则。
加上@Configuration注解主要是给我们的类加上了cglib代理。在执行我们的配置类的方法时,会执行cglib代理类中的方法,其中有一个非常重要的判断,当我们的执行方法和我们的调用方法是同一个方法时,会执行父类的方法new(cglib代理基于继承);当执行方法和调用方法不是同一个方法时会调用beanFactory.getBean获取。
14.1条件注解@ConditionalOnBean:
条件注解是Spring4
提供的一种bean加载特性,主要用于控制配置类和bean初始化条件。
问题:使用 @ConditionalOnBean 注解时会遇到不生效的情况
@Configuration
public class Configuration1 {
@Bean
@ConditionalOnBean(Bean2.class)
public Bean1 bean1() {
return new Bean1();
}
}
@Configuration
public class Configuration2 {
@Bean
public Bean2 bean2(){
return new Bean2();
}
}
@ConditionalOnBean(Bean2.class)
返回false。明明定义的有bean2,bean1
却未加载。
首先要明确一点,条件注解的解析一定发生在spring ioc的bean definition
阶段,因为 spring bean初始化的前提条件就是有对应的bean definition
,条件注解正是通过判断bean definition
来控制bean能否被解析。
解决方法:
以下两种方式:
- 项目中条件注解依赖的类,大多会交给
spring
容器管理,所以如果要在配置中Bean
通过@ConditionalOnBean
依赖配置中的Bean
时,完全可以用@ConditionalOnClass(Bean2.class)
来代替。 - 如果一定要区分两个配置类的先后顺序,可以将这两个类交与
EnableAutoConfiguration
管理和触发。也就是定义在META-INF\spring.factories
中声明是配置类,然后通过@AutoConfigureBefore、AutoConfigureAfter AutoConfigureOrder
控制先后顺序。之所以这么做是因为这三个注解只对自动配置类的先后顺序生效。
15.@ConfigurationProperties
该注解有一个prefix属性,通过指定的前缀,绑定配置文件中的配置,该注解可以放在类上,也可以放在方法上
15.1 作用于类:
该注解没有指定全名,还可以进行绑定
因为@Value注解,它可以通过全限定名进行配置的绑定,这里的ConfigurationProperties其实就类似于使用多个@Value同时绑定,而且是 隐式绑定 的,意味着在配置文件编写的时候需要与对应类的字段名称 相同。以上就完成了多个数据源的配置,为读写分离做了铺垫。
15.2 作用于方法:比较常见的就是配置读写分离的场景。
当将该注解作用于方法上时,如果想要有效的绑定配置,那么该方法需要有@Bean注解且所属Class需要有@Configuration注解。
和上面对类的配置一样,但是要加上@Bean
15.3 总结:
- @ConfigurationProperties 和 @value 有着相同的功能,但是 @ConfigurationProperties的写法更为方便
- @ConfigurationProperties 的 POJO类的命名比较严格,因为它必须和prefix的后缀名要一致, 不然值会绑定不上, 特殊的后缀名是“driver-class-name”这种带横杠的情况,在POJO里面的命名规则是 下划线转驼峰 就可以绑定成功,所以就是 “driverClassName”
16.@EnableConfigurationProperties
@EnableConfigurationProperties注解的作用是:使使用 @ConfigurationProperties 注解的类生效。
如果一个配置类只配置@ConfigurationProperties注解,而没有使用@Component,那么在IOC容器中是获取不到properties 配置文件转化的bean。说白了 @EnableConfigurationProperties 相当于把使用 @ConfigurationProperties 的类进行了一次注入。
17.@ConditionalOnProperty
以上是该注解的各个属性
name:读取配置文件的属性,如果该值为空则返回false,如果不为空,将返回的值与havingValue相比较,如果相同则表示@Configuration生效,反之不生效
18.@CrossOrigin
作用是解决跨域访问的问题,在Spring4.2以上的版本可直接使用。在类上或方法上添加该注解。
19.@PathVariable
处理requet uri部分,当使用@RequestMapping URI template 样式映射时, 即someUrl/{paramId}, 这时的paramId可通过 @Pathvariable注解绑定它传过来的值到方法的参数上。
@Controller
@RequestMapping(“/owners/{a}”)
public class RelativePathUriTemplateController {
@RequestMapping(“/pets/{b}”)
public void findPet(@PathVariable(“a”) String a,@PathVariable String b, Model model) {
// implementation omitted
}
}
20.@DependsOn
@DependsOn(“bean的名称”)使得加上该注解的bean依赖于括号里的bean
用来控制Bean的加载顺序,比如pub-sub,监听者一定要在发布者实例化之前实例化,要不然会漏掉信息。不使用此注解会让Bean的加载顺序不明确,多次加载导致的结果也不同。
21.@Transactional
21.1 添加位置
1)接口实现类或接口实现方法上,而不是接口类中。
2)访问权限:public 的方法才起作用。@Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。
系统设计:将标签放置在需要进行事务管理的方法上,而不是放在所有接口实现类上:只读的接口就不需要事务管理,由于配置了@Transactional就需要AOP拦截及事务的处理,可能影响系统性能。
3)错误使用:
1.接口中A、B两个方法,A无@Transactional标签,B有,上层通过A间接调用B,此时事务不生效。
2.接口中异常(运行时异常)被捕获而没有被抛出。默认配置下,spring 只有在抛出的异常为运行时 unchecked 异常时才回滚该事务,也就是抛出的异常为RuntimeException 的子类(Errors也会导致事务回滚),而抛出 checked 异常则不会导致事务回滚 。可通过 @Transactional rollbackFor进行配置。
3.多线程下事务管理因为线程不属于 spring 托管,故线程不能够默认使用 spring 的事务, 也不能获取spring 注入的 bean 。在被 spring 声明式事务管理的方法内开启多线程,多线程内的方法不被事务控制。一个使用了@Transactional 的方法,如果方法内包含多线程的使用,方法内部出现异常,不会回滚线程中调用方法的事务。
JPA注解
1.@Entity
表明这是一个实体
2.@Table(name="")
对应数据库的表名,一般与上面的@Entity一起使用,如果表名与类名相同的话,则可以省略@Table
3.@MappedSuperClass
确定是父类的entity上。父类的属性子类可以继承。
4.@PersistenceContext
称为持久化上下文,它一般包含有当前事务范围内的,被管理的实体对象(Entity)的数据。每个EntityManager,都会跟一个PersistenceContext相关联。PersistenceContext中存储的是实体对象的数据,而关系数据库中存储的是记录。
…
推荐文章:
整理的一些Spring boot面试题:
1.Spring boot有哪些优点
- 容易上手,提升开发效率,为 Spring 开发提供一个更快、更广泛的入门体验。
- 开箱即用,远离繁琐的配置。
- 提供了一系列大型项目通用的非业务性功能,例如:内嵌服务器、安全管理、运行数据监控、运行状况检查和外部化配置等。
- 没有代码生成,也不需要XML配置。
- 避免大量的 Maven 导入和各种版本冲突。
2.Spring Boot 自动配置原理是什么?
注解 @EnableAutoConfiguration, @Configuration, @ConditionalOnClass 就是自动配置的核心,
@EnableAutoConfiguration 给容器导入META-INF/spring.factories 里定义的自动配置类。
筛选有效的自动配置类。
3.Spring Boot 是否可以使用 XML 配置 ?
Spring boot 推荐使用Java配置而非XML配置,但是也可以使用XML配置,通过注解@ImportResource引入一个XML配置文件
4.spring boot 核心配置文件是什么?bootstrap.properties 和 application.properties 有何区别 ?
在结合Spring cloud开发时,我们会碰到bootstrap.properties,特别是加载远程配置文件时。
spring boot 核心的两个配置文件:
- bootstrap (. yml 或者 . properties):boostrap 由父 ApplicationContext 加载的,比 applicaton 优先加载,配置在应用程序上下文的引导阶段生效。一般来说我们在 Spring Cloud Config 或者 Nacos 中会用到它。且 boostrap 里面的属性不能被覆盖;
- application (. yml 或者 . properties): 由ApplicatonContext 加载,用于 spring boot 项目的自动化配置。
5.如何实现spring boot的安全性
我会选择与spring boot配套使用的Spring Security,JWT两个依赖,并且要添加安全配置,配置类将必须扩展WebSecurityConfigurerAdapter 并覆盖其方法。
后面会单独写一篇Spring Security JWT 的文章。
6.Spring boot 如何解决跨域问题
有三种解决方案
1.添加@CrossOrigin,可以在类上添加,也可以在方法上添加。
在Spring Boot 中给我们提供了一个注解 @CrossOrigin 来实现跨域,这个注解可以实现方法级别的细粒度的跨域控制。我们可以在类或者方添加该注解,如果在类上添加该注解,该类下的所有接口都可以通过跨域访问,如果在方法上添加注解,那么仅仅只限于加注解的方法可以访问。
@RequestMapping(BASE_URL + "/**")
@CrossOrigin
public Object baseApi(HttpServletRequest request) throws Exception {...}
2.跨源资源共享( CORS )
重写 WebMvcConfigurer 接口中的 addCorsMappings() 方法来实现跨域。
@Configuration
public class WebConfig implements WebMvcConfigurer {
/**
\* 跨域支持
\* @param registry
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowCredentials(true)
.allowedMethods("GET", "POST", "DELETE", "PUT")
.allowedHeaders(*)
.maxAge(3600 * 24);
}
}
addMapping:配置可以被跨域的路径,可以任意配置,可以具体到直接请求路径。
allowedOrigins:允许所有的请求域名访问我们的跨域资源,可以固定单条或者多条内容,如:“http://www.baidu.com”,只有百度可以访问我们的跨域资源。
allowCredentials: 响应头表示是否可以将对请求的响应暴露给页面。返回true则可以,其他值均不可以
allowedMethods:允许输入参数的请求方法访问该跨域资源服务器,如:POST、GET、PUT、OPTIONS、DELETE等。
allowedHeaders:允许所有的请求header访问,可以自定义设置任意请求头信息,如:“X-YAUTH-TOKEN”
maxAge:配置客户端缓存预检请求的响应的时间(以秒为单位)。默认设置为1800秒(30分钟)。
3.Nginx 配置解决跨域问题(这方面我还没接触过,先拿网上的例子给你们展示展示)
如果我们在项目中使用了Nginx,可以在Nginx中添加以下的配置来解决跨域
location / {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Headers X-Requested-With;
add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,OPTIONS;
if ($request_method = 'OPTIONS') {
return 204;
}
}
: 响应头表示是否可以将对请求的响应暴露给页面。返回true则可以,其他值均不可以
allowedMethods:允许输入参数的请求方法访问该跨域资源服务器,如:POST、GET、PUT、OPTIONS、DELETE等。
allowedHeaders:允许所有的请求header访问,可以自定义设置任意请求头信息,如:“X-YAUTH-TOKEN”
maxAge:配置客户端缓存预检请求的响应的时间(以秒为单位)。默认设置为1800秒(30分钟)。
3.Nginx 配置解决跨域问题(这方面我还没接触过,先拿网上的例子给你们展示展示)
如果我们在项目中使用了Nginx,可以在Nginx中添加以下的配置来解决跨域
location / {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Headers X-Requested-With;
add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,OPTIONS;
if ($request_method = 'OPTIONS') {
return 204;
}
}