一、Spring @Resouce 和 @Autowired 对比?
两者都是用于bean的注入
区别有:①@Autowired是spring提供的注解,而@Resouce是由J2EE提供的,在javax.annotation.Resource包
②@Autowired默认按类型装配,如果要按照名称装配,需要加上@Qualifier注解,如下:
public class TestServiceImpl { @Autowired @Qualifier("userDao") private UserDao userDao; }
@Resource默认通过反射机制,按照名称装配,如果匹配不上则会抛出异常。也可指定type属性进行匹配,通过类型匹配到多个或者匹配不上也会抛出异常
二、Redis
Redis是一个基于内存的高性能的key-value数据库
优点:
(1)速度快,应该数据都在内存中
(2)支持丰富的数据类型,有string、list、set、sorted set、hash
(3)支持事务,操作都是原子性
(4)可设置过期时间,可用于缓存等
1、缓存雪崩:由于原有缓存失效(或者数据未加载到缓存中),新缓存未到期间,所有原本应该访问缓存的请求都去查询数据库了,而对数据库CPU和内存造成巨大压力,严重的会造成数据库宕机,造成系统的崩溃。
解决思路:
- 第一,大多数系统设计者考虑用加锁或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,避免缓存失效时对数据库造成太大的压力,虽然能够在一定的程度上缓解了数据库的压力但是与此同时又降低了系统的吞吐量。
- 第二,分析用户的行为,尽量让缓存失效的时间均匀分布。
- 第三,如果是因为某台缓存服务器宕机,可以考虑做主备,比如:redis主备,但是双缓存涉及到更新事务的问题,update可能读到脏数据,需要好好解决。
2、缓存穿透:指用户查询数据,在数据库没有,自然在缓存中也不会有。这样就导致用户查询的时候,在缓存中找不到,每次都要去数据库中查询。
解决思路:
- 1,如果查询数据库也为空,直接设置一个默认值存放到缓存,这样第二次到缓冲中获取就有值了,而不会继续访问数据库,这种办法最简单粗暴。
- 2,根据缓存数据Key的规则。例如我们公司是做机顶盒的,缓存数据以Mac为Key,Mac是有规则,如果不符合规则就过滤掉,这样可以过滤一部分查询。在做缓存规划的时候,Key有一定规则的话,可以采取这种办法。这种办法只能缓解一部分的压力,过滤和系统无关的查询,但是无法根治。
- 3,采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的BitSet中,不存在的数据将会被拦截掉,从而避免了对底层存储系统的查询压力。关于布隆过滤器,详情查看:基于BitSet的布隆过滤器(Bloom Filter)
3、缓存预热:指系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!
- 1、直接写个缓存刷新页面,上线时手工操作下;
- 2、数据量不大,可以在项目启动的时候自动进行加载;
- 3、定时刷新缓存;
4、缓存更新
- (1)定时去清理过期的缓存;
- (2)当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存。
5、缓存降级:当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。
- (1)一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级;
- (2)警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警;
- (3)错误:比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的最大阀值,此时可以根据情况自动降级或者人工降级;
- (4)严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级。
分布式缓存系统面临的问题缓存一致性问题
- 1:缓存系统与底层数据的一致性。这点在底层系统是“可读可写”时,写得尤为重要
- 2:有继承关系的缓存之间的一致性。为了尽量提高缓存命中率,缓存也是分层:全局缓存,二级缓存。他们是存在继承关系的。全局缓存可以有二级缓存来组成。
- 3:多个缓存副本之间的一致性。为了保证系统的高可用性,缓存系统背后往往会接两套存储系统(如memcache,redis等)
缓存数据的淘汰
- (1) 定时去清理过期的缓存。
- (2)当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存。
缓存算法
- FIFO算法:First in First out,先进先出。原则:一个数据最先进入缓存中,则应该最早淘汰掉。也就是说,当缓存满的时候,应当把最先进入缓存的数据给淘汰掉。
- LFU算法:Least Frequently Used,最不经常使用算法。
- LRU算法:Least Recently Used,近期最少使用算法。请查看:Memcached之你真正理解LRU吗(4)
- LRU和LFU的区别。LFU算法是根据在一段时间里数据项被使用的次数选择出最少使用的数据项,即根据使用次数的差异来决定。而LRU是根据使用时间的差异来决定的
三、多线程
ThreadLocal 保证一个线程使用同一个对象
ThreadPoolExcutor 线程池创建
四、序列化和反序列化
序列化是指把对象转换为字节序列的过程;而反序列化是指把字节序列恢复为对象的过程;
序列化主要的两种用途:
1)把对象的字节序列保存到硬盘中(通常存放在一个文件中)
2)在网络上传送对象的字节序列
什么时候使用序列化:
1)对象序列化可以实现分布式对象,主要应用例如:RMI要利用对象序列化运行远程主机上的服务,就像在本地机上运行对象时一样。
2)java对象序列化不仅保留一个对象的数据,而且递归保存对象引用的每个对象的数据。可以将整个对象层次写入字节流中,可以保存在文件中或在网络连接上传递。利用对象序列化可以进行对象的“深复制”,即复制对象本身及引用的对象本身。序列化一个对象可能得到整个对象序列。
五、简要描述下Spring的IoC和AOP
IoC就是对象的创建,依赖都有Spring及配置文件控制;而AOP就是统一的给一些类似的方法加上同样的功能,比如日志,事务。
IOC:控制反转,是一种设计模式。
1)控制反转,由传统在程序中控制,变为由容器控制
2)依赖注入:将互相依赖的对象分离,在Spring配置文件中描述他们的依赖关系。他们依赖关系只在使用的时候才建立。
AOP:面向切面。将系统中非核心的业务提取出来,进行单独处理,比如事务、日志和安全等。
实现方式:
- 注解
- xml配置
通知类型:
- @Before:在目标方法被调用之前做增强处理,@Before只需要指定切入点表达式即可;
- @AfterReturning:在目标方法正常完成后做增强,@AfterReturning除了指定切入点表达式后,还可以指定一个返回值形参名returning,代表目标方法的返回值;
- @AfterThrowing:主要用来处理程序中未处理的异常,@AfterThrowing除了指定切入点表达式后,还可以指定一个throwing的返回值形参名,可以通过该形参名来访问目标方法中所抛出的异常对象;@
- @After:在目标方法完成之后做增强,无论目标方法是否成功完成
- @Around:环绕通知,在目标方法完成前后做增强处理,环绕通知是最重要的通知类型,像事务,日志等都是环绕通知,
注意编程中核心是一个ProceedingJoinPoint
1 @Component
2 @Aspect
3 public class Operator {
4
5 @Pointcut("execution(* com.aijava.springcode.service..*.*(..))")
6 public void pointCut(){}
7
8 @Before("pointCut()")
9 public void doBefore(JoinPoint joinPoint){
10 System.out.println("AOP Before Advice...");
11 }
12
13 @After("pointCut()")
14 public void doAfter(JoinPoint joinPoint){
15 System.out.println("AOP After Advice...");
16 }
17
18 @AfterReturning(pointcut="pointCut()",returning="returnVal")
19 public void afterReturn(JoinPoint joinPoint,Object returnVal){
20 System.out.println("AOP AfterReturning Advice:" + returnVal);
21 }
22
23 @AfterThrowing(pointcut="pointCut()",throwing="error")
24 public void afterThrowing(JoinPoint joinPoint,Throwable error){
25 System.out.println("AOP AfterThrowing Advice..." + error);
26 System.out.println("AfterThrowing...");
27 }
28
29 @Around("pointCut()")
30 public void around(ProceedingJoinPoint pjp){
31 System.out.println("AOP Aronud before...");
32 try {
33 pjp.proceed();
34 } catch (Throwable e) {
35 e.printStackTrace();
36 }
37 System.out.println("AOP Aronud after...");
38 }
39
40 }
通知执行的优先级
进入目标方法时,先织入Around,再织入Before,退出目标方法时,先织入Around,再织入AfterReturning,最后才织入After。
注意:Spring AOP的环绕通知会影响到AfterThrowing通知的运行,不要同时使用!同时使用也没啥意义。
原文:https://blog.csdn.net/chinacr07/article/details/78817449
六、说一下转发(forward)和重定向(redirect)的区别
转发(forward) |
|
||
地址栏 | 不变 | 改为重定向的地址 | |
数据共享 | 可以共享转发前request的数据 | 不能共享request的数据 | |
运用 | 一般用于用户登录后转发到相应模块 | 一般用于用户注销后回到首页和跳转到其他网站等 | |
效率 | 高 | 低 |
七、java虚拟机内存的堆、栈、方法区
堆:线程共享,存放数组和实例化对象(GC主要在这里进行垃圾回收)jdk8后把运行时常量池、静态变量也移到堆区进行存储
虚拟机栈:线程私有,随线程创建而创建,存放基本数据类型变量和引用等(操作数栈、方法出口等信息)
本地方法栈:线程私有,与虚拟机用到native方法相关,(JNI)
方法区:常量池
程序计数器:JVM支持多个线程同时运行,每个线程都有自己的程序计数器,运行JVM方法会保存当前指令的地址。(PS:线程执行过程中并不是一口气完成的,执行到一半停止后,下次继续执行时,通过程序计数器就可以知道该从什么地方开始执行)
八、HashMap和HashTable
HashMap | HashTable |
非线程安全 | 线程安全 |
键值允许为null | 不允许为null |
containsValue/containsKey | contains |
继承自Dictionary类 | 是Map接口的实现 |
九、cookie和session 的区别有哪些?
cookie | session |
数据存放在客户端 | 数据存放在服务器 |
通过http协议实现的 | - |
不安全(存放在浏览器) | 相对安全 |
- | 可能影响服务器性能 |
常规信息可放在cookie | 重要信息如登录信息等,放在session |
十、事务的实现方式
(1)编程式事务管理。我们需要在代码中调用beginTransaction()、commit()、rollback()等事务管理相关的方法,这就是编程式事务管理。
(2)基于xml配置, 声明式事务管理(TransactionProxyFactoryBean)
(3)基于注解, @Transactional 的声明式事务管理
(4)基于Aspectj AOP配置事务
十一、java 调用静态方法和构造函数和静态块执行的先后顺序
1 public class ExA { 2 static { 3 System.out.println("父类--静态代码块"); 4 } 5 6 public ExA() { 7 System.out.println("父类--构造函数"); 8 } 9 10 { 11 System.out.println("父类--非静态代码块"); 12 } 13 14 public static void main(String[] args) { 15 new ExB(); 16 } 17 } 18 19 class ExB extends ExA { 20 static { 21 System.out.println("子类--静态代码块"); 22 } 23 { 24 System.out.println("子类--非静态代码块"); 25 } 26 27 public ExB() { 28 System.out.println("子类--构造函数"); 29 } 30 } 31 32 执行结果 33 ===== 34 父类--静态代码块 35 子类--静态代码块 36 父类--非静态代码块 37 父类--构造函数 38 子类--非静态代码块 39 子类--构造函数
十二、java中的sleep()和wait()的区别
sleep()是Thread类中的方法,而wait是Object类提供的方法;
调用sleep()方法的时候,线程不会释放对象锁;
调用wait()方法的时候,线程会释放对象锁,只有对此对象调用notify()方法后,才获取对象锁进入运行状态
原文:https://www.cnblogs.com/hongten/p/hongten_java_sleep_wait.html
十三、sql优化
(一)、索引
以下情况不会命中索引:
- 使用 !=或<>
- 使用 is nul / is not null (解决方案:设置默认值,比如设置默认0)
- 使用 or ( 解决方案:使用union all连接两次查询结果)
- 使用 like %xxxx
- 使用 in / not in (解决方案:使用between 或者 exist)
- 使用 自定义参数 @xxx (解决方案:强制使用索引,如:select id from t with(index(索引名)) where num=@num)
- 使用 函数,如:select id from t where substring(name,1,3)='abc' / select id from t where datediff(day,createdate,'2005-11-30')=0
- 使用 数学运算 如:select id from t where num/2=100
- 如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致。