JAVA后端知识点记录(持续ing)

1、@Autowired和@Resource的区别


-->功能上几乎一样,都是依赖注入
-->提供方不同,@Autowired是spring提供的,@Resource是jdk提供的[基于jsr250的规范,所有的ioc容

器都会支持]
-->作用域不同,@Autower可以作用在字段构造器和setter方法上,@Resource可以作用在类字段和

setter方法上
-->属性的匹配方式不同,@Autower会先按照byType的方式匹配,其次才会采用byName匹配,@Resource

的匹配正好相反

2、@Bean必须要搭配@Configuration么?


-->答案是否定的,只要是@Component注解或者派生注解都可以
-->被@Configuration修饰的配置类,被springioc管理的对象生成一个CGLIB代理对象,通过该代理对象多次调用@Bean修饰的对象只会产生一个实例,而被@Component修士的类bean对象会生成多个
[
AnnotationConfigApplicationContext applicationContext = AnnotationConfigApplicationContext

()  
AppConfig1 appConfig1 = applicationContext .getBean("appConfig",AppConfig1.class)
appConfig1.user();
]

-->不通过方法调用直接获取@Bean对象,不管使用哪种方式都是单例
[
AnnotationConfigApplicationContext applicationContext = AnnotationConfigApplicationContext

();
applicationContext .getBean("user");
]

3、String创建问题


(1)String a = new String ("ddy") 创建了1或2个对象;
-->堆中创建一个实例对象,让a引用指向该对象
-->jvm那字面"abc"去字符串常量池获取对应的引用,若存在就让堆对象引用到这个量,若不存在就直接

创建并将引用保持到常量池中

(2)string a = "abd";创建0或1个对象
-->jvm在常量池中找"abc"
,存在就直接引用,不存在就创建丙保存到字符串常量池中

(3)String a = "abc" + "d" 创建了0 到1个对象
-->因为<常量折叠>的关系,加减乘除运算在编译过程中求职,运行期间就生成abcd

(4)String str = new String("a"+"b") 创建了4个对象
-->a是一个,b是一个,ab是一个,str是一个

(5)String str = new String("a") + "a"  一共创建了3个对象
-->第一个a是一个,第二个a指向第一个a,aa是一个(都在常量池),str是一个(堆)

4、深拷贝和浅拷贝


浅拷贝:拷贝目标的基本数据类型变量,引用变量只进行引用传递
-->改变引用变量的时候会影响被拷贝对象的值
-->JAVA的clone方法是浅拷贝,引用类型依然在传递引用

深拷贝:拷贝目标的基本数据类型,引用数据类型创建并分配新的地址
-->在堆内存中开辟空间存储一模一样的对象
-->改变引用对象值的时候,不会更改被拷贝的目标值


实现深拷贝的方法

方法一:序列化该对方,然后在反序列化回来

//原始对象
TestObj obj1 = new TestObj();
obj1.setName("张三");

//输出流
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
//写入字节数组流
oos.writeObject(obj1);

//输入流
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
//读取字节数组流
TestObj obj2 = (TestObj) ois.readObject();

//判断两个对象是否相同
System.out.println(obj1 == obj2); //false
System.out.println(obj1.getName().equals(obj2.getName())); //true

-->我用的就是这种方法,当时数据要做加密处理,数据保存之后就被框架弄成了加密字串,所以只能拷贝出来一份继续做其他的操作,示例如下
           List<Supplier> listCP = new ArrayList<>();
            Supplier supplierCPVo = null;
            ByteArrayOutputStream bos = null;
            ObjectOutputStream oos = null;
            ByteArrayInputStream bis = null;
            ObjectInputStream ois = null;
            for (Supplier i : list) {
                bos = new ByteArrayOutputStream();
                oos = new ObjectOutputStream(bos);
                oos.writeObject(i);

                bis = new ByteArrayInputStream(bos.toByteArray());
                ois = new ObjectInputStream(bis);
                supplierCPVo = (Supplier) ois.readObject();

                listCP.add(supplierCPVo);//原数据只更新要给状态
            }

方法二:[重载clone()]


用clone()方法,将该对象的引用对象再实现一次clone()方法。
-->且引用类型成员变量发生变动需要修改方法,不满足开闭原则[可以在实体类里定义clone()方法,直接调用super.clone()]
-->且引用类型成员变量发生变动需要修改方法,不满足开闭原则;
-->不具有可复用性;


方法三:通过构造方法手动创建(比较原始,比较非力气)

方法四:使用jar包[Jackson]来实现


-->jar包内的始线比较复杂,不过可以不用去关注
public User copyUser4() throws JsonProcessingException {
           ObjectMapper objectMapper = new ObjectMapper();
           return objectMapper.readValue(objectMapper.writeValueAsString(this),User.class);
}
 

5、过滤器和拦截器

过滤器和拦截器

过滤器(Filter)
过滤器是将request、respobse提前过滤一些信息[比如:非法请求/不是. do的请求],就想筛子一样筛选目标

拦截器(interceptor)
拦截器是面相切面(AOP aspect oriented program),在server或者方法前后调用的方法(案例:动态代理)

两者的区别

-->拦截器是基于JAVA反射,过滤器是基于函数回调
-->拦截器不依赖servlet容器,过滤器依赖servlet容器
-->拦截器只对action请求起作用,过滤器对几乎所以请求起作用
-->拦截器可以访问action上下文、值、栈里的对象,过滤器不行
-->在action的生命周期里,拦截器可以被多次调用,过滤器只在容器初始化的时候调用一次
-->拦截器可以获取ioc容器里的各个bean,但过滤器不行[依靠这一特点可以注入sercer后编写业务逻辑]
-->过滤器是请求进入tomcat容器后,sercelt之前的过滤操作,结束返回的时候也是这样;拦截器是在sercelt之内;

备注:过滤器、拦截器、tomcat等只见的关->tomcat>过滤器>sercelt>拦截器>controller

过滤器使用

过滤器的本质就是一个实现了Filter接口的java类

1、过滤器配置
(1)xml配置
<filter>
<filter-name>filterDemo</filter-name>    ->指定filter名字
<filter-class>package com.web.filter.FilterDemo</filter-class>  ->指定filter全类名,带包名
</filter>


<filter-mapping>
<filter-name>filterDemo</filter-name>    ->上面的filter中的名字
<url-pattern>/*</url-pattern>            ->设置filter拦截路径
</filter-mapping>


备注:/index.jsp[index.jsp执行过滤]     /index/*[index下的所有资源都过滤]     *.jsp[jsp的资

源被过滤]    /*[任意资源都被过滤]

声明异常类处理
<error-page>
<exception-type>xxx</exception-type>
<error-page>

(2)注解配置
@WebFilter(filterName="FilterDemo1",urlPatter="/*")


案例
@Configuration
@Order(value=0)
@WebFilter(filterName = "CorsFilterConfig",urlPatterns="/*")
public class CorsFilterConfig implements Filter{

@Override
public void init(FilterConfig filterConfig)throws ServletException{
这个方法就是初始化方法,在Filter创建时调用
}

@Override
public void doFilter(ServletRequest sercletRequest,ServletResponse sevletResponse){
这个方法是过滤和拦截的方法,当请求和拦截匹配的时候调用
}

@Override
public void destroy(){
这个方法是销毁方法,在Filter销毁前调用
}

}

拦截器使用

定义配置类

spring-servlet.xml配置拦截器

<mvc:interceptors>
<mvc:interceptor>
可以拦截路径不过少层
<mvc:mapping path="/**">
<bean id ="loginInterceptor" class ="com.xiaoqing.Logininterceptor">
</mvc:interceptor>
</mvc:interceptors>

配置拦截器
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**">
<bean id ="loginInterceptor" class="com.xiaoqing.Logininterceptor">
</mvc:interceptor>

</mvc:interceptors>

注解定义
@Configuration
public class WebConfig implements WebMvcConfigurer{

@Override
public void addinterceptors(interceptorRegistry registry){
registry.addinterceptor(loginCheckInterceptor).addPathPatterns("/**").excludePathPatterns

("/login");
}
}

定义一个类实现使HandlerInterceptor接口
@Component
publicclass LoginCheckInterceptor implements HandlerInterceptor{

@Override
public boolean preHandle(HttpServletRequest request,HttpServletResponse response,Object

handler)throws Exception{
请求前运行
}

@Override
public boolean postHandle(HttpServletRequest request,HttpServletResponse response,Object

handler)throws Exception{
请求处理后运行
}

@Override
public boolean afterCompletion(HttpServletRequest request,HttpServletResponse

response,Object handler)throws Exception{
整个请求结束后运行
}
}

拦截器和过滤器这个讲的很详细:拦截器与过滤器的区别_过滤器拦截器的区别-CSDN博客

6、spring两个id相同的bean会报错么

(1)同一个xml文件里不能存在两个id相同的bean,否则spring容器在启动的时候就会报错
(2)id是bean里的唯一标志,启动的时候会验证id的唯一性,重复就会报错,错误发生在spring对xml解析转化为bean的时候
(3)在不同的xml中,可以存在两个相同的bean,ioc容易在加载bean的时候会把id相同的bean进行覆盖
(4)spring3.X里面,提供了一个@Configuration注解来声明一个配置类,然后用@bean注解来声明,这种方式取代了xml的配置形式,这个时候在同一个配置类里声明同同一个名字的bean,ioc容易只会注册第一个bean实例,后面同名的就不会注册了

当在使用@Autowired进行实例注入的时候,会提示找不到北覆盖的bean,如果使用@Resource根据名字注入,只找到了被加载的bean,这样会提示类型不匹配的错误,错误发生在bean初始化之后,依赖注入阶段

7、Mybatis的缓存

-->缓存是为了提升数据的检索效率,避免每次数据访问都需要去查询数据库

一级缓存:

一级缓存是sqlsession级别的缓存(本地缓存),每个用户在执行查询的时候都需要使用sqlsession来执行,为了避免每次都去差数据库,mybatis会把查询出来的数据保存到sqlsession的本地缓存中,后续的sql如果命中缓存就可以直接从本地缓存中去读取

二级缓存:

(多个sqlsession共享)如果想要跨sqlsession级别的缓存,那么一级缓存就无法实现了,因此在mybatis里实现了二级缓存,当多个用户在查询数据的时候,只要有任何一次sqlsession拿到了数据,就会放入到二级缓存中,其他的sqlsession就可以直接从二级缓存里去加载数据


开启方法:yml配置
mybatis:
  configuration:
    cache-enabled:true
然后在对应mapper.xml里面加入配置<cache eviction="FIFO" flushinterval="6000" readOnly="false" size ="50"</cache>>
属性参数:type:缓存实现类;size最大缓存对象数量;eviction:回收侧学(缓存淘汰算法);

flushinterval:定时自动清理缓存间隔;readObly:是否只读;blocking:是否使用可重入锁实现缓存的并发控制
-->开启二级缓存后,对应的pojo一定要是先S俄日阿里咋办了,否则在序列化的时候会报错
readOblyL是否只读,值为true时,mybatis认为所有从缓存中读取的数据皂搓都是只读,不会修改数据。mybatis为了加快读取速度,直接会在将数据在缓存中的引用交给用户,不安全,但是毒素块。
值为false时,mybatis觉得获取的数据可能会被修改,mybatis会利用序列化和反序列化技术克隆一分心的数据,安全但是速度慢

三级缓存:

一级缓存和二级缓存只适合单体项目,在分布式或者微服务况下会出现数据不一致的问题,所以mybatis

为我们提供了自定义缓存,我们可以继承很多三方中间件来做缓存


备注:
(1)一级缓存的作用域是一个sqlsession,默认是开启的
(2)二级缓存作用域是针对mapper的,需要手动配置
(3)一级缓存sqlsession执行insert、update、delete等操作commit后会清空缓存区域,

sqlsession.close()后一级缓存就没有了,但效果sqlsession后回家里面的缓存放到二级缓存里。
(4)二级缓存cache中的readonly属性如果为false,那么相应的pojo类必须实现serializable接口,并且

缓存查询的对象都是通过序列化或者反序列化克隆的,所以对象之间是两两不相等
(5)二级缓存的生命周期和应用同步,他是用来解决一级缓存不能跨回话共享数据的问题,范围是

namespace级别的,可以被多个回话共享(直接要是同一个接口的相同方法都可以共享)
(6)只要没有显示的设置cacheenabled为false,都会使用cachingexector装饰基本执行器(simple、

reuse、batch),二级缓存总是默认开启,但每个mapper的二级开光是默认关闭的
(7)二级缓存进行增删改等操作也会刷新二级缓存,导致二级缓存失效

8、spring循环依赖的问题

问题产生:直接依赖、间接依赖、自我依赖

spring设计了三级缓存来解决依赖问题
一级缓存里存储完整的bean实例,这些bean实例可是随时使用
二级缓存存储的是实例化以后但还没有设置属性的实例,bean里面的依赖注入还没有做
三级缓存是用来存储bean工厂的,他主要用来生成原始的bean对象并放到二级缓存里
三级缓存的中心思想就是把bean的实例化和bean里面的依赖注入分离,采用一级缓存储存完整的bean实例i,二级缓存存储不完整的bean实例,不完整的bean实例来解决循环依赖问题,三级缓存用来解决代理对象的循环依赖问题

spring只能解决单实例的循环依赖问题,一下四种情况是无法解决的:
1、多实例的setter注入导致的循环依赖
-->需要把bean改成单例
2、构造器注入导致的循环依赖
-->可以通过@lazy注解来解决
3、DependsOn导致的循环依赖
-->找到注解循环依赖的地方,迫使他不循环依赖
4、单例代理对象导致的循环依赖
-->可以使用@lazy注解或者DependsOn注解来制定加载先后关系

循环依赖的根源问题还是在设计上,模块的耦合度高就可能会出现循环依赖的问题,尽可能从系统设计的问题去解决

9、mysql表设计原则

1、表名和字段名使用小写字母或数字[①不能使用数字开头;②不能使用拼音;③一般不适用英文缩写]

主键索引名(pk_字段名) :ALTER TABLE users ADD PRIMARY KEY (column_name);
-->一张表只有一个主键索引、主键不允许重复、不允许为null

唯一索引名(uk_字段名) :ALTER TABLE users ADD UNIQUE INDEX index_name (column_name);
-->一张表客有多个唯一索引、唯一索引值不允许重复、但允许为null

普通索引名(idx_字段名):ALTER TABLE users ADD INDEX index_name ON column(length)
-->一战不过可以有多个普通索引,普通索引的值允许重复,允许为null

2、主键设计要合理
主键最好不要与业务逻辑有关联,最好是毫无意义的独立不重复数字(比如uuid、自增组件或者雪花算法主键)

3、不要使用保留字
如果字段名为保留字,在语句里没有加上反引号的话,就会按照保留字解析语句报错或者执行结果与预期不一致(例如:desc、case、revoke)

4、选择何是的字段类型和长度
尽可能选择存储空间小的字段类型(比如:tinyint[很小的整数默认1]、smallint[小证数默认2]、int[整数默认4]、bigint[大整数默认8]->从左往右开始选择)

小数类型(如金额),选则decimal,不要使用float和double(float和double都存在静读损失的问题),分钟类的可以使用整数(需转换)

如果存储的字符长度几乎相等(比如枚举),可以使用char定长字符串类型。

varchar是可变长字符串,不预先分配存储空间,长度不要超过5000;

如果存储的值太大,建议字段类型修改为text,同事抽出单独一张表,用主键与之对应;


5、字段注释
每个字段都加上注释说明,尤其是枚举类字段,字段类型的含义一定熬描述清楚
ALTER TABLE user MODIFY COLUMN 字段名 字段类型 comment '字段备注';

6、表必备的字段
每张表都需要添加几个通用字段(例如:id、create_time、create_user、update_time、update_user、is_delete)
主键建议使用无符号的bigint,20位已经足够我们的业务需求了

7、尽可能使用not null定义字段
每个默认值位null的字段会在行头额外战局一个字节的存储空间,还可能会产生查询不走索引一级sql产生预期外的结果

8、如果存储的值太大,建议字段类型修改位text,同事抽出单独一张表,用逐渐与之对应,如果有中非常大的数据,可以保存到mongodb中,然后再业务表保存对应mongodb的id即可

9、一张表的字段不宜过多
表字段太长会影响效率,一般来说每张表的字段尽量不要超过20个,索引最好不要查过6个,

10、索引不要乱加
根据业务逻辑评估好那些字段需要添加索引,让查询更快,占用空间小
①主键一定要建立索引②外键建立索引③经常查询的使用索引④范围选择和频繁查询列增加索引⑤经常where字段的列增加索引⑥需要order by、group by、distinct的字段加索引
符合索引的索引字段顺序要和关键字后面的字段顺序一致,否则索引不会被使用

11、不必强求数据库三范式
<三范式>产生的背景是资源紧张不足,我们只能尽可能的节约资源,从而获得更高的幸能
但服务器的幸能越来越好,设计表的时候适当的字段冗余来解决查询性能

12、存储隐形默认选择innodb
两大常用存储引擎,myisam和innodb,myisam不支持食物,支持全文索引,innondb两者都支持
再使用mysqldump命令进行数据库备份的时候,会开启一个食物来确保拿到一致性视图,如果存在myisam的表,那么只能通过ftwrl方法加全局锁,这是数据库是只读状态

13、选择何是同意的字符
数据库、表、开放程序等都需要同意字符,通常中英文环境用utf8
mysql支持的字符集有utf8[支持中英文魂和场景国际通用3字符长度]、utf8mb4[兼容utf84字节长度,一般emoji表情需要用到他]、gbk[支持中文不支持国际通用字符2长度]、latin1[mysql默认字符集1个字节长度]等

14、数据删除方法
为了保证数据安全性,最好使用虑逻辑删除,这样方便数据追回

15、不搞外键关联,再代码中维护
使用外键存在幸能、并发死锁等问题,做delete或者update都要烤炉外键约束,开放测试的时候都很难受,分库分表的时候也不呢个使用外键

10、雪花算法

大型分布式系统中,如何生成全局唯一id是一个很棘手的问题,雪花算法就很好的解决了这个问题。
雪花算法是Twitter开元的一个分布式唯一id生成,雪花算法拥有简洁、高性能、低延迟、id按时间趋势有序的等特点

雪花算法是一个64位的整数:第一位是一个符号(0代表正数、1代表负数)、接下来的41位是一个毫秒级别的时间戳,表示从特定时间到现在的毫秒数,41位的时间戳可以表示约69年的时间,
接下来的10位是用来记录工作机器的id,可以部署在1024个节点,包括5位的datacenterid和5位的workerid


最后的12位是一个序列号,表示毫秒内的计数,12位的计数顺序号支持每个节点每毫秒产生4096个id序列号。

雪花算法有一个很大的问题,如果服务器的时间被调回到了过去,那么就会申城重复id,这个问题的解

决方法如下:
1、当检测到时钟回拨以后可以抛出异常停止id生成,等时间正常了在生成
-->但这种方法会暂停id的生成服务,从而影响系统的可用性
2、当检测到时钟回拨以后,让线程睡眠,等时间回复正常在继续生成
-->同样会让生成id服务停用,影响可用性
3、生成备用id,当检测到时钟回拨之后,使用备用生成id,比如使用uuid或者随机数或者等其他方法
-->这种方式需要引入其他的方法来保证id的全局唯一
4、多时钟法,既然系统已经发现了时间回拨,那就可以认为原来的时钟已经不可用了,就使用新的时钟来生成
5、采用当前最大的时间
6、追刚时间,加1024后判断时候追赶上了,没追上就再加1024,知道追赶上为止

11、创建线程的2种方法(简)

①集成Thread类实现重写run方法

-->优点:简单,直接就可以是使用

-->缺点:业务关联度会比较高、不能再继承其他类、不能使用线程池(创建和销毁线程开销比较大)、不能得到线程的返回值

②实现Runnable接口重写run方法

使用示例:new Thread(new 实现类).start;

-->优点:创建线程和业务分离、可以交给线程池管理(开销降低)

-->缺点:不方便传参、不能得到返回值、run方法没法通过this获取当前线程的信息

③实现Callable接口,实现call方法

使用示例:FutureTask<Boolean> task = new FutureTask<>(new 实现类);

④通过线程池创建

线程池示例:ExectorService a = Executors.newFixedThreadpool(10);

12、spring bean的生命周期


-->bean的常规生命周期只有5种:
1、singleton(单例)
-->spring容器里只有一个bean实例对象
2、prototype(原型)
-->每次从ioc容器获取指定bean的时候,都会返回一个新的实例对象
3、在spring框架的web应用里增加了一个会话维度来控制bean的生命周期
-->这部分有三个选项:
①request:每一次http请求都会创建一个新的bean
②session:以session会话为维度,同一个session共享一个bean实例,不同的session产生不同的bean

实例
③globalSession:全局session维度共享同一个bean实例


13、mysql的回表


-->回表是在进行索引查询时,通过索引定位到页,然后再根据行的物理地址找到数据[也就是说:回表是根据索引查到主键值,再去访问索引,从而查到完整的数据]


-->回表操作通常需要访问两次索引,增加io开销和cpu消耗,对性能有一些影响


-->回表是根据物理地址查询数据的,过程中遇到数据修改可能会读到不一致的数据

触发回表的场景

①返回列不再索引上
-->索引中没有改列,这样即便通过索引定位了行,仍然需要回表查询

②使用了非聚簇索引

-->只包含索引列的副本以及指向主键的引用,查询的时候需要通过回表查询

备注:以逐渐作为B+树索引的键值,而构成的B+树索引就是聚簇索引

③使用了覆盖索引,但字段超长

-->InnoDB引擎中,每个索引的最大长度是767字节,如果返回的字段超长,就会触发回表操作

备注:覆盖索引是一种优化技术,在查询中只使用索引而不需要回表到数据源也来获取数据

14、跨域的问题


-->在WEB应用中,由于同源策略的限制,导致浏览器既无法发送跨域请求,也无法跨域相应
-->同源策略是浏览器的一种安全策略,他要求web应用只能访问与当前页面具有相同协议、主机名和端口号的资源

1)、跨域场景


①正常情况[例如:当前页面域名为http://www.test.com;请求的页面http://www.test.com/index.html]-->不跨
②域名不同[例如:当前页面域名为http://www.test.com;请求资源为http://www.baidu.com]-->跨
   域名不同[例如:当前页面域名为http://www.test.com;请求资源为http://blog.test.com]-->跨
③协议不同[例如:当前页面协议为https://www.test.com;请求的资源协议为http://www.test.com]-->跨
④端口不同[例如:当前页面端口为http://www.test.com:8080;请求资源端口为http://www.test.com:9090]-->跨

2)、跨域解决


①后端的@CrossOrigin注解
-->在SpringBoot的Controller层中通过@CrossOrigin注解来实现跨域访问
-->注解中的origins属性标识允许跨域的请求源地址,多个地址逗号分隔,不指定表示允许所有,具体案例如下:
@RestController
@RequestMapping("/test")
public class Test{
@GetMapping("/test")
@CrossOrigin(origins="http://localhost:8080")
Public Text getTest(@PathVariable Long id){
return test.getTest(id);
}
}

②后端的WebMvcConfigurer解决跨域
-->实现WebMvcConfigurer接口,重写addCorsMappings方法,具体示例如下:
@Configuration
public class CorsConfig implements WebMvcConfigurer{
public void addCorsMappings(CorsRegistry registry){
registry.addMapping("/test/**")                            -->允许跨域方位的映射路径
.allowedOrigins("http://localhost:8080")                   -->允许访问的来源
.allowedMethods("GET","POST","PUT","DELETE")  -->允许方位的http方法
.allowCredentials(true)                                           -->允许发送Cookie
.maxAge(3600);                                                     -->响应的缓存时间
}
}


③后端的Filter解决跨域
-->自定义Filter解决跨域,这种方法灵活但比较繁琐,具体示例如下:
@Component
public class CorsFilter implements Filter{
publi void doFilter(ServletRequest req,ServletResponse res,FilterChain chain) throws Exception{
HttpServletResponse response = (HttpServletResponse)res;
response.setHeader("Access-Control-Allow-Origin","http://localhost:8080");  -->设置为"*"表示所有来源
response.setHeader("Access-Control-Allow-Methods","GET","POST","PUT","DELETE");-->志的请求方法
response.setHeader("Access-Control-Allow-Credentials","true");
response.setHeader("Access-Control-Mas-Age","3600");-->预检查请求的有效时间
chain.doFilter(req,res);
}
}

④前端解决跨域的方案
-->JSONP、跨域资源共享(CORS)、Nginx代理跨域、nodejs中间件代理跨域、document.domain+iframe跨域、location.hash+iframe跨域、window.name+iframe跨越、postMessage跨域、WebSocket协议跨域

15、springBoot的资源目录优先级

-->resources目录是存放项目的静态资源目录,一些配置文件和heml和js相关的文件都会放在这里。
-->resources目录下也可以继续放置目录(这也是约定的)
-->放置的录入有如下几种:①classpath:/public/②classpath:/resources/③classpath:/static/④classpath:/META-INF/resources/
-->几个目录的优先级[以放置同一个html文件为测试基准]:优先级最高的是classpath:/META-INF/resources,第二的是classpath:/resources/,第三的是classpath:/static/,第四的是classpath:/public/
 

  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值