2018年上海后半年JAVA软件工程师面试真题

版权声明:技术交流群:758191639 https://blog.csdn.net/u014131617/article/details/85333028
  1. 查询超过该部门员工大于3个的部门

SELECT deptid FROM table1

GROUP BY deptid

HAVING COUNT(deptid) > 3

 

 

如果员工名称重复的部门id

SELECT deptid FROM table1

GROUP BY deptid

HAVING COUNT(empname) > 3

 

 

  1. 单例模式
  1. package com.day01;
  2.  
  3. public class 单例模式 {
  4.  
  5.  
  6. private final static 单例模式 INSTANCE = new 单例模式();
  7.  
  8. private 单例模式(){
  9.    
  10. }
  11.  
  12. public static 单例模式  getInstance(){
  13.     return INSTANCE;
  14. }
  15. }

 

1 需要写个类

2 在类里面new 一个自己,private final static

3 私有无参构造

4 static 方法,返回自己

 

 

 

 

 

 

3.冒泡排序

package com.day01;

 

public class 冒泡排序 {

  

   public static void main(String[] args) {

     

     

      int[] arr = {3,2,1};

     

      for (int i = 0; i < arr.length - i; i++) {

        

         for (int j = 0; j < arr.length - 1 - i; j++) {

           

            if(arr[j] > arr[j+1]){

               

                int temp = arr[j];

               

                arr[j] = arr[j+1];

               

                arr[j+1] = temp;

            }

         }

        

      }

     

      System.out.println(arr[0]);

      System.out.println(arr[1]);

      System.out.println(arr[2]);

     

   }

 

}

 

 

 

 

 

 

 

 

 

4.找关键字次数

package com.day01;

 

import java.io.BufferedReader;

import java.io.File;

import java.io.FileInputStream;

import java.io.InputStreamReader;

 

public class 找关键字次数 {

  

   public static void main(String[] args) throws Exception {

     

      StringBuilder sBuilder = new StringBuilder();   // 创建拼接字符串

     

      File file = new File("F:\\get.txt");   // 读取文件

     

      //创建IO留独处文件

       InputStreamReader isr = new InputStreamReader(new FileInputStream(file), "utf-8");

       BufferedReader br = new BufferedReader(isr);

       //用于暂时保存文本内容

       String lineTxt = null;

       //循环读取

       while ((lineTxt = br.readLine()) != null) {

         sBuilder.append(lineTxt);

       }

       br.close();

      

      String srcText = sBuilder.toString();

      String findText = "d";

     

      int num = appearNumber(srcText,findText);

      System.out.println(num);

   }

  

  

   public static int appearNumber(String srcText,String findText){

      int count = 0;

      int index = 0;

      while((index = srcText.indexOf(findText,index)) != -1){

         index = index + findText.length();

         count++;

      }

      return count;

   }

 

}

 

 

 

5.四个访问修饰符

// 访问权限      子类  其他包

public String aString;

  

   // 继承的类可以访问以及和private一样的权限

   protected String bString;

  

   // 包访问权限,即在整个包内均可被访问

   /*default*/ String cString;

  

   //除类型创建者和类型的内部方法之外的任何人都不能访问的元素

   private String dString;

 

 

 

 

 

6.Spring开启注解的方式

在Spring的配置文件中

  <!-- 使用annotation定义事务 -->

事务:

<tx:annotation-driven transaction-manager="defaultTransactionManager" proxy-target-class="true" />

 

<contextcomponent-scan base-package =“xx.test.*”use-default-filters =“false”>

 

 

 

 

 

7.数据库索引

第一,通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。

第二,可以大大加快数据的检索速度,这也是创建索引的最主要的原因。

第三,可以加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义。

第四,在使用分组和排序子句进行数据检索时,同样可以显著减少查询中分组和排序的时间。

第五,通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。

 

索引类型

普通索引,唯一索引,主键索引,组合索引

索引的创建

创建索引

CREATE INDEX indexName ON mytable(username(length)); 
如果是CHARVARCHAR类型,length可以小于字段实际长度;如果是BLOBTEXT类型,必须指定 length,下同。

修改表结构

ALTER mytable ADD INDEX [indexName] ON (username(length))

创建表的时候直接指定

CREATE TABLE mytable( ID INT NOT NULL, username VARCHAR(16) NOT NULL, 
INDEX [indexName] (username(length)) );
删除索引的语法:

DROP INDEX [indexName] ON mytable;

 

 

 

 

8.线程中run和start的区别

每个线程都有要执行的任务。线程的任务处理逻辑可以在Tread类的run实例方法中直接实现或通过该方法进行调用,因此

run()相当于线程的任务处理逻辑的入口方法,它由Java虚拟机在运行相应线程时直接调用,而不是由应用代码进行调用。

start()作用是启动相应的线程。启动一个线程实际是请求Java虚拟机运行相应的线程,而这个线程何时能够运行是由线程调度器决定的。start()调用结束并不表示相应线程已经开始运行,这个线程可能稍后运行,也可能永远也不会运行。

 

 

 

9.线程状态有哪些

第一是创建状态。在生成线程对象,并没有调用该对象的start方法,这是线程处于创建状态。
  第二是就绪状态。当调用了线程对象的start方法之后,该线程就进入了就绪状态,但是此时线程调度程序还没有把该线程设置为当前线程,此时处于就绪状态。在线程运行之后,从等待或者睡眠中回来之后,也会处于就绪状态。
  第三是运行状态。线程调度程序将处于就绪状态的线程设置为当前线程,此时线程就进入了运行状态,开始运行run函数当中的代码。
  第四是阻塞状态。线程正在运行的时候,被暂停,通常是为了等待某个事件的发生(比如说某项资源就绪)之后再继续运行。sleepsuspendwait等方法都可以导致线程阻塞。
  第五是死亡状态。如果一个线程的run方法执行结束或者调用stop方法后,该线程就会死亡。对于已经死亡的线程,无法再使用start方法令其进入就绪。

 

 

 

10.索引的优缺点

创建索引可以大大提高系统的性能:
第一,通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。
第二,可以大大加快数据的检索速度,这也是创建索引的最主要的原因。
第三,可以加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义。
第四,在使用分组和排序 子句进行数据检索时,同样可以显著减少查询中分组和排序的时间。
第五,通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。

增加索引也有许多不利的方面:
第一,创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。
第二,索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,那么需要的空间就会更大。
第三,当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,这样就降低了数据的维护速度。

 

 

 

 

11.JVM内存模型

 

 

 

 

 

 

 

12.跨域的出现和解决

跨域是指a页面想获取b页面资源,如果ab页面的协议、域名、端口、子域名不同,或是a页面为ip地址,b页面为域名地址,所进行的访问行动都是跨域的,而浏览器为了安全问题一般都限制了跨域访问,也就是不允许跨域请求资源。

 

解决跨域:

  

Jsonp,把请求路径转成jsonp的格式,再进行请求

 

在Contoller中加:

       // 设置:Access-Control-Allow-Origin头,处理Session问题

               response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));

               response.setHeader("Access-Control-Allow-Credentials", "true");

               response.setHeader("P3P", "CP=CAO PSA OUR");

               if (request.getHeader("Access-Control-Request-Method") != null && "OPTIONS".equals(request.getMethod())) {

                   response.addHeader("Access-Control-Allow-Methods", "POST,GET,TRACE,OPTIONS");

                   response.addHeader("Access-Control-Allow-Headers", "Content-Type,Origin,Accept");

                   response.addHeader("Access-Control-Max-Age", "120");

               }

SpringBoot跨域解决方案:

在Controller上加@CrossOrigin

 

 

 

 

13.GC回收垃圾的时候System.gc()和 finalize()的区别

收集并删除未引用的对象。可以通过调用"System.gc()"来触发垃圾回收,但并不保证会确实进行垃圾回收。

JVM的垃圾回收只收集哪些由new关键字创建的对象。所以,如果不是用new创建的对象,你可以使用finalize函数来执行清理。

 

 

 

 

 

 

14.MyBatis中的$和#有什么区别?

1 #是将传入的值当做字符串的形式,eg:select id,name,age from student where id =#{id},当前端把id1,传入到后台的时候,就相当于 select id,name,age from student where id ='1'.

 2 $是将传入的数据直接显示生成sql语句,eg:select id,name,age from student where id =${id},当前端把id1,传入到后台的时候,就相当于 select id,name,age from student where id = 1.

 3 使用#可以很大程度上防止sql注入。(语句的拼接)

 4 但是如果使用在order by 中就需要使用 $.

 5 在大多数情况下还是经常使用#,但在不同情况下必须使用$. 

 

 

 

15.SpringBean的生命周期

 

 

 

16.List中如何去重

方式一,利用HashSet不能添加重复数据的特性 由于HashSet不能保证添加顺序,所以只能作为判断条件:

private static void removeDuplicate(List<String> list) {

    HashSet<String> set = new HashSet<String>(list.size());

    List<String> result = new ArrayList<String>(list.size());

    for (String str : list) {

        if (set.add(str)) {

            result.add(str);

        }

    }

    list.clear();

    list.addAll(result);

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

方式二,利用LinkedHashSet不能添加重复数据并能保证添加顺序的特性 :

private static void removeDuplicate(List<String> list) {

    LinkedHashSet<String> set = new LinkedHashSet<String>(list.size());

    set.addAll(list);

    list.clear();

    list.addAll(set);

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

方式三,利用List的contains方法循环遍历:

private static void removeDuplicate(List<String> list) {

    List<String> result = new ArrayList<String>(list.size());

    for (String str : list) {

        if (!result.contains(str)) {

            result.add(str);

        }

    }

    list.clear();

    list.addAll(result);

}

--------------------- 作者:灰色流连 来源:CSDN 原文:https://blog.csdn.net/u012156163/article/details/78338574?utm_source=copy 版权声明:本文为博主原创文章,转载请附上博文链接!

17.Redis存储机制

Redis存储机制分成两种SnapshotAOF。无论是那种机制,Redis都是将数据存储在内存中。

 

 

 

 

  1. WebService实现方式

方式1

HttpClient:可以用来调用webservie服务,也可以抓取网页数据

版本1HttpClient3.0.x

版本2HttpClient4.x.x(目前最新4.5.2)

2个版本的使用方式不一样;变动较大

方式2:纯Java(自带API)      jws

方式3cxf框架

方式4axis2框架

 

准备工作:

1.了解wsimport        java自带的一个命令(建议使用jdk7,稳定支持)

作用:将wsdl文件生成本地代理(java代码),方便调用

语法  wsimport [opations] <wsdl_uri>
    - wsdl_uri:wsdl
的统一资源标识符
    - d  :指定要输出的文件的位置
    - s  :表示要解析java的源码 ,默认解析出的是class字节码 
    - p  
指定输出的包名
 
1.
获取服务路径:比如
  http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx?WSDL


2.
获取wsdl文件.建议使用JDK1.6以上的版本的wsimport命令


进入dos的桌面:
方式1wsimport http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx?WSDL
这种默认只会生成class,且会生成默认的包
方式2:生成源码,指定包和路径
wsimport -s ./ -p cn.aa http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx?WSDL


3.
可以把class文件打成jar jar cvf  test.jar  打包目录

4.放在你的项目中进行调用:

[html] view plain copy

  1. public static void main(String[] args) {  
  2.         MobileCodeWS mobileCodeWs=new MobileCodeWS();  
  3.         MobileCodeWSSoap mobileCodeWSSoap=mobileCodeWs.getMobileCodeWSSoap();  
  4.         String tel=mobileCodeWSSoap.getMobileCodeInfo("183735xxxx",null);  
  5.         System.out.println(tel);  
  6.     }  

 

 

 

  1. 如何实现线程通信
  1. 用volatile关键字
  2. 用redis实现

 

 

  1. volatile和synchronized
  1. volatile主要用在多个线程感知实例变量被更改了场合,从而使得各个线程获得最新的值。它强制线程每次从主内存中讲到变量,而不是从线程的私有内存中读取变量,从而保证了数据的可见性。
  2.  
  3. 关于synchronized,可参考:JAVA多线程之Synchronized关键字--对象锁的特点
  4.  
  5. 比较:
  6.  
  7. ①volatile轻量级,只能修饰变量。synchronized重量级,还可修饰方法
  8.  
  9. ②volatile只能保证数据的可见性,不能用来同步,因为多个线程并发访问volatile修饰的变量不会阻塞。
  10.  
  11. synchronized不仅保证可见性,而且还保证原子性,因为,只有获得了锁的线程才能进入临界区,从而保证临界区中的所有语句都全部执行。多个线程争抢synchronized锁对象时,会出现阻塞。
  12.  
  13.  
  14.  
  15. 四,线程安全性
  16.  
  17. 线程安全性包括两个方面,①可见性。②原子性。
  18.  
  19. 从上面自增的例子中可以看出:仅仅使用volatile并不能保证线程安全性。而synchronized则可实现线程的安全性。

 

 

21.QuartZ如何实现

scheduler(调度器):将jobtrigger绑定在一起

job(任务)              :配置具体哪个类实现定时任务

trigger(触发器)     :配置定时器参数,如:多久执行一次,执行多上次等

 

开启注解

 

 

 

 

22.HashMap的底层原理

https://blog.csdn.net/a2524289/article/details/78888480

 

 

 

 

23.Dubbo的配置文件

 

 

 

 

24.Dubbo底层协议是什么

NIO方式的TCP协议

 

25.redis底层协议是什么

TCP

 

 

 

 

 

26.线程的活锁和死锁

https://blog.csdn.net/qq_29924795/article/details/72772251

 

 

 

27.MQ消息的丢失和中途宕机解决

服务端丢失

把内存中的消息持久化,重启mq的时候进行从持久化文件中读取

客户端丢失

用户发出的消息还依旧在缓存中,mq可以重新进行read

消息确认

RabbitMQ引入了消息确认机制,当消息处理完成后,给Server端发送一个确认消息,来告诉服务端可以删除该消息了,如果连接断开的时候,Server端没有收到消费者发出的确认信息,则会把消息转发给其他保持在线的消费者。

 

 

28.MQ一般用在哪里

 

 

 

29.Shiro实现方法

1 配置 applicationContext-shiro.xml 激活注解

<!-- 开启shiro注解模式  -->
    <bean
        class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
        depends-on="lifecycleBeanPostProcessor" >
        <property name="proxyTargetClass" value="true" />
    </bean>
        
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
         <property name="securityManager" ref="securityManager"/>
    </bean>

2.业务层使用 shiro 注解

@RequiresPermissions("")

3.使用方法注解进行权限控制, 当权限不足时,代理对象抛出一个异常
org.apache.shiro.authz.UnauthorizedException: Subject does not
have permission [courier_add]

 

 

 

 

 

30.Shiro数据库表设计

 

 

31.请求类型方式有几种

GET 请求指定的页面信息,并返回实体主体。 
HEAD 只请求页面的首部。 
POST 请求服务器接受所指定的文档作为对所标识的URI的新的从属实体。 
PUT 从客户端向服务器传送的数据取代指定的文档的内容。 
DELETE 请求服务器删除指定的页面。 
OPTIONS 允许客户端查看服务器的性能。 
TRACE 请求服务器在响应中的实体主体部分返回所得到的内容。 
PATCH 实体中包含一个表,表中说明与该URI所表示的原内容的区别。 
MOVE 请求服务器将指定的页面移至另一个网络地址。 
COPY 请求服务器将指定的页面拷贝至另一个网络地址。 
LINK 请求服务器建立链接关系。 
UNLINK 断开链接关系。 
WRAPPED 允许客户端发送经过封装的请求。 

 

 

 

 

32.线程池如何实现

https://blog.csdn.net/douunderstand/article/details/72190330

 

 

33.redis持久化如何实现

RDB持久化

AOF持久化

3、二者优缺点

RDB存在哪些优势呢?

1). 一旦采用该方式,那么你的整个Redis数据库将只包含一个文件,这对于文件备份而言是非常完美的。比如,你可能打算每个小时归档一次最近24小时的数 据,同时还要每天归档一次最近30天的数据。通过这样的备份策略,一旦系统出现灾难性故障,我们可以非常容易的进行恢复。

2). 对于灾难恢复而言,RDB是非常不错的选择。因为我们可以非常轻松的将一个单独的文件压缩后再转移到其它存储介质上。

3). 性能最大化。对于Redis的服务进程而言,在开始持久化时,它唯一需要做的只是fork出子进程,之后再由子进程完成这些持久化的工作,这样就可以极大的避免服务进程执行IO操作了。

4). 相比于AOF机制,如果数据集很大,RDB的启动效率会更高。

RDB又存在哪些劣势呢?

1). 如果你想保证数据的高可用性,即最大限度的避免数据丢失,那么RDB将不是一个很好的选择。因为系统一旦在定时持久化之前出现宕机现象,此前没有来得及写入磁盘的数据都将丢失。

2). 由于RDB是通过fork子进程来协助完成数据持久化工作的,因此,如果当数据集较大时,可能会导致整个服务器停止服务几百毫秒,甚至是1秒钟。

AOF的优势有哪些呢?

1). 该机制可以带来更高的数据安全性,即数据持久性。Redis中提供了3中同步策略,即每秒同步、每修改同步和不同步。事实上,每秒同步也是异步完成的,其 效率也是非常高的,所差的是一旦系统出现宕机现象,那么这一秒钟之内修改的数据将会丢失。而每修改同步,我们可以将其视为同步持久化,即每次发生的数据变 化都会被立即记录到磁盘中。可以预见,这种方式在效率上是最低的。至于无同步,无需多言,我想大家都能正确的理解它。

2). 由于该机制对日志文件的写入操作采用的是append模式,因此在写入过程中即使出现宕机现象,也不会破坏日志文件中已经存在的内容。然而如果我们本次操 作只是写入了一半数据就出现了系统崩溃问题,不用担心,在Redis下一次启动之前,我们可以通过redis-check-aof工具来帮助我们解决数据 一致性的问题。

3). 如果日志过大,Redis可以自动启用rewrite机制。即Redisappend模式不断的将修改数据写入到老的磁盘文件中,同时Redis还会创 建一个新的文件用于记录此期间有哪些修改命令被执行。因此在进行rewrite切换时可以更好的保证数据安全性。

4). AOF包含一个格式清晰、易于理解的日志文件用于记录所有的修改操作。事实上,我们也可以通过该文件完成数据的重建。

AOF的劣势有哪些呢?

1). 对于相同数量的数据集而言,AOF文件通常要大于RDB文件。RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。

2). 根据同步策略的不同,AOF在运行效率上往往会慢于RDB。总之,每秒同步策略的效率是比较高的,同步禁用策略的效率和RDB一样高效。

二者选择的标准,就是看系统是愿意牺牲一些性能,换取更高的缓存一致性(aof),还是愿意写操作频繁的时候,不启用备份来换取更高的性能,待手动运行save的时候,再做备份(rdb)。rdb这个就更有些 eventually consistent的意思了。

4、常用配置

RDB持久化配置

Redis会将数据集的快照dumpdump.rdb文件中。此外,我们也可以通过配置文件来修改Redis服务器dump快照的频率,在打开6379.conf文件之后,我们搜索save,可以看到下面的配置信息:

save 900 1              #900(15分钟)之后,如果至少有1key发生变化,则dump内存快照。

save 300 10            #300(5分钟)之后,如果至少有10key发生变化,则dump内存快照。

save 60 10000        #60(1分钟)之后,如果至少有10000key发生变化,则dump内存快照。

AOF持久化配置

Redis的配置文件中存在三种同步方式,它们分别是:

appendfsync always     #每次有数据修改发生时都会写入AOF文件。

appendfsync everysec  #每秒钟同步一次,该策略为AOF的缺省策略。

appendfsync no          #从不同步。高效但是数据不会被持久化。

 

 

 

34.Spring传播机制是如何实现的

https://www.cnblogs.com/softidea/p/5962612.html

 

https://blog.csdn.net/weixin_38070406/article/details/78157603

 

 

 

 

 

35.打印菱形

Scanner input = new Scanner(System.in);

        // TODO 自动生成的方法存根

       System.out.println("请输入行数:");

       int m=input.nextInt();

       //确定打印菱形的行数,可以自行设置

       int n=m/2+1;

       int t=m/2;

       for(int i=1;i<=n;i++)//打印菱形的上半部分

       {

           for(int j=(n-i);j>0;j--)

               System.out.print(" ");

             //打印空格数 

           for(int k=2*i-1;k>0;k--)

               System.out.print("*");

              //打印*

           System.out.println();

           //一行打印结束,换行

       }

       for(int i=t;i>0;i--)//打印菱形的下半部分

       {

           for (int b=(n-i);b>0;b--)

               System.out.print(" ");

               //打印空格数

           for (int a=(2*i-1);a>0;a--)

               System.out.print("*");

               //打印*

           System.out.println();

           //一行打印结束,换行

       }

        }

 

 

36.编写程序,对输入的年、月、日,给出该天是该年的第多少天?

       System.out.println("43 编写程序,对输入的年、月、日,给出该天是该年的第多少天?");

             // 编写程序,对输入的年、月、日,给出该天是该年的第多少天?

             SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");

             Date date;

             Date date1;

             Scanner sc = new Scanner(System.in);

             while (true){

                 System.out.print("请输入一个日期\n【格式】yyyy-MM-dd\n请输入:");

                 String temp = sc.next();

                 try {

                     date = format.parse(temp);

                     date1 = format.parse(temp.split("-")[0]+"-01-01");

                     break;

                 } catch (ParseException e) {

//                       e.printStackTrace();

                     System.out.println("输入有误!");

                 }

             }//getTime() 方法可返回距 1970 1 1 日之间的毫秒数。

             long l = date.getTime() - date1.getTime();

             //毫秒 / 1000 = / 60 = 分钟 / 60 = 小时 / 24 = + 1 = 第几天

             System.out.println("该天是该年的第"+((l)/1000/60/60/24+1)+"");

        

 

 

37.原生JDBC实现CRUD

 

 

38.GC垃圾回收算法

 1.标记-清除算法

  等待被回收对象的标记过程在上文已经提到过,如果在被标记后直接对对象进行清除,会带来另一个新的问题——内存碎片化。如果下次有比较大的对象实例需要在堆上分配较大的内存空间时,可能会出现无法找到足够的连续内存而不得不再次触发垃圾回收。

  2.复制算法(Java堆中新生代的垃圾回收算法)

GC算法实际上解决了标记-清除算法带来的内存碎片化问题。首先还是先标记处待回收内存和不用回收的内存,下一步将不用回收的内存复制到新的内存区域,这样旧的内存区域就可以全部回收,而新的内存区域则是连续的。它的缺点就是会损失掉部分系统内存,因为你总要腾出一部分内存用于复制。

  在上文JVM入门——运行时数据区》提到过在Java堆中被分为了新生代和老年代,这样的划分是方便GCJava堆中的新生代就使用了GC复制算法。在新生代中又分为了三个区域:Eden 空间、To Survivor空间、From Survivor空间。不妨将注意力回到这张图的左边新生代部分:

https://img-blog.csdnimg.cn/20181229001921440

  新的对象实例被创建的时候通常在Eden空间,发生在Eden空间上的GC称为Minor GC当在新生代发生一次GC,会将Eden和其中一个Survivor空间的内存复制到另外一个Survivor,如果反复几次有对象一直存活,此时内存对象将会被移至老年代。可以看到新生代中Eden占了大部分,而两个Survivor实际上占了很小一部分。这是因为大部分的对象被创建过后很快就会被GC(这里也许运用了是二八原则)。

  3.标记-压缩算法(或称为标记-整理算法,Java堆中老年代的垃圾回收算法)

  对于新生代,大部分对象都不会存活,所以在新生代中使用复制算法较为高效,而对于老年代来讲,大部分对象可能会继续存活下去,如果此时还是利用复制算法,效率则会降低。标记-压缩算法首先还是“标记”,标记过后,将不用回收的内存对象压缩到内存一端,此时即可直接清除边界处的内存,这样就能避免复制算法带来的效率问题,同时也能避免内存碎片化的问题。老年代的垃圾回收称为“Major GC”。

 

 

 

 

39.Maven和Git的常用命令

开发中常用的git命令:

    查看有变动的文件

    git status

    将当前目录下所有有变化目录进行添加

    git add .

    将添加的变动提交到本地仓库,提交后git status将看不到有变化文件啦

    git commit  -m 'mengshanshan update'

    将git上最新代码拉到本地

    git pull origin master

    将本地仓库代码提交到master

    git push origin master

    将本地仓库代码提交到master的streaming分支,页面可以替交merge

    git push origin master:streaming

    退回到上一版本

    git reset —hard  

5. maven命令:

    下载本地依赖:

    mvn -DskipTests clean package install

    下载缺失包:

     mvn compile

    下载编译项目

    mvn clean && mvn install -U

    生成lib目录,将相关包移进lib下

    mvn dependency:copy-dependencies -DoutputDirectory=lib

    tar包:

    mvn assembly:assembly -DskipTests -P{p}

    class包,产生class等,部署项目使用~

    mvn -DskipTests -Pcdn_online clean package

    查找依赖

    mvn dependency:tree

 

40.java如何实现秒杀商品

把商品数量存入redis中,当某一个用户购买成功后,同步缓存,数量就为0,别的用户就不能购买。

41.redis缓存同步的方法

1:读取数据的时候先从redis里面查,若没有,再去数据库查,同时写到redis里面,并且要设置失效时间。
2:存数据的时候要具体情况具体分析,可以选择同时插到数据库和redis(要是存放到redis中,最好设置失效时间),也可以选择直接插到数据库里面,少考虑一些问题。

42.单元测试,性能测试方法

Junit

·@Before

       public void befores(){

              System.out.println("前--");

       }

      

       @Test

       public void test1(){

              System.out.println("中间--");

       }

      

       @After

       public void afters(){

              System.out.println("后--");

       }

jMeter详解:

https://blog.csdn.net/jek123456/article/details/79027037

43.ajax传递对象,controller如何接收

requestBody详解

https://www.cnblogs.com/qiankun-site/p/5774300.html

 

44.事务是如何实现的,项目中怎么使用的事务

实现方式共有两种:编码方式;声明式事务管理方式。

基于AOP技术实现的声明式事务管理,实质就是:在方法执行前后进行拦截,然后在目标方法开始之前创建并加入事务,执行完目标方法后根据执行情况提交或回滚事务。

声明式事务管理又有两种方式:基于XML配置文件的方式;另一个是在业务方法上进行@Transactional注解,将事务规则应用到业务逻辑中。

事务的4个特性:

  • 原子性:一个事务中所有对数据库的操作是一个不可分割的操作序列,要么全做,要么全部做。
  • 一致性:数据不会因为事务的执行而遭到破坏。
  • 隔离性:一个事务的执行,不受其他事务(进程)的干扰。既并发执行的个事务之间互不干扰。
  • 持久性:一个事务一旦提交,它对数据库的改变将是永久的。

 

45.java队列

https://www.cnblogs.com/Dylansuns/archive/2017/04/30/6789161.html

46.java内存

http://www.cnblogs.com/xwdreamer/archive/2012/04/01/2428857.html

47.序列化框架

https://blog.csdn.net/zlfprogram/article/details/74066841

48.jdk1.8新特性

1. 速度更快红黑树
2. 代码更少Lambda
3.
强大的Stream API – Stream
4.
便于并行Parallel
5.
最大化减少空指针异常Optional

49.concurrentHashMap底层原理

在ConcurrentHashMap中,无论是读操作还是写操作都能保证很高的性能:在进行读操作时(几乎)不需要加锁,而在写操作时通过锁分段技术只对所操作的段加锁而不影响客户端对其它段的访问。特别地,在理想状态下,ConcurrentHashMap 可以支持 16 个线程执行并发写操作(如果并发级别设为16),及任意数量线程的读操作。

ConcurrentHashMap本质上是一个Segment数组,而一个Segment实例又包含若干个桶,每个桶中都包含一条由若干个 HashEntry 对象链接起来的链表。总的来说,ConcurrentHashMap的高效并发机制是通过以下三方面来保证的(具体细节见后文阐述):

通过锁分段技术保证并发环境下的写操作;

通过 HashEntry的不变性、Volatile变量的内存可见性和加锁重读机制保证高效、安全的读操作;

通过不加锁和加锁两种方案控制跨段操作的的安全性。

 

50.springAop、springIoc的实现原理

springAop:

实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法创建方面,从而使得编译器可以在编译期间织入有关方面的代码。

springIoc的实现原理:

反射与工厂模式

51.mybatis与hibernate中的区别

2.1 开发方面

        在项目开发过程当中,就速度而言:

            hibernate开发中,sql语句已经被封装,直接可以使用,加快系统开发;

            Mybatis 属于半自动化,sql需要手工完成,稍微繁琐;

        但是,凡事都不是绝对的,如果对于庞大复杂的系统项目来说,发杂语句较多,选择hibernate 就不是一个好方案。

    2.2 sql优化方面

        Hibernate 自动生成sql,有些语句较为繁琐,会多消耗一些性能;

        Mybatis 手动编写sql,可以避免不需要的查询,提高系统性能;

    2.3 对象管理比对

        Hibernate 是完整的对象-关系映射的框架,开发工程中,无需过多关注底层实现,只要去管理对象即可;

        Mybatis 需要自行管理 映射关系;

    2.4 缓存方面    

相同点:

Hibernate和Mybatis的二级缓存除了采用系统默认的缓存机制外,都可以通过实现你自己的缓存或为其他第三方缓    存方案,创建适配器来完全覆盖缓存行为。

不同点:

Hibernate的二级缓存配置在SessionFactory生成的配置文件中进行详细配置,然后再在具体的表-对象映射中配置是那种缓存。

MyBatis的二级缓存配置都是在每个具体的表-对象映射中进行详细配置,这样针对不同的表可以自定义不同的缓存机制。并且Mybatis可以在命名空间中共享相同的缓存配置和实例,通过Cache-ref来实现。

 

 

比较:

          Hibernate 具有良好的管理机制,用户不需要关注SQL,如果二级缓存出现脏数据,系统会保存,;

           Mybatis 在使用的时候要谨慎,避免缓存CAche 的使用。

 

52.struts2与springmvc的区别

区别1:

 

Struts2 的核心是基于一个Filter即StrutsPreparedAndExcuteFilter

SpringMvc的核心是基于一个Servlet即DispatcherServlet(前端控制器)

 

区别2:

 

Struts2是基于类开发的,传递的参数是通过类的属性传递(属性驱动和模型驱动),所以只能设计成多例prototype

 

SpringMvc是基于类中的方法开发的,也就是一个url对应一个方法,传递参数是传到方法的形参上面,所以既可以是单例模式也可以是多例模式singiton

 

区别3:

 

Struts2采用的是值栈存储请求以及响应数据,OGNL存取数据

 

SpringMvc采用request来解析请求内容,然后由其内部的getParameter给方法中形参赋值,再把后台处理过的数据通过ModelAndView对象存储,Model存储数据,View存储返回的页面,再把对象通过request传输到页面去。

---------------------

作者:爪哇小明

来源:CSDN

原文:https://blog.csdn.net/weixin_38429587/article/details/79050550?utm_source=copy

版权声明:本文为博主原创文章,转载请附上博文链接!

53.jsp的内置对象的作用

1、pageContext 表示页容器 –>EL、标签、上传
2、request 服务器端取得客户端的信息:头信息、Cookie、请求参数、MVC设计模式
3、response 服务器端回应给客户端信息:Cookie、重定向
4、session 表示每一个用户,用于登录验证上
5、application 表示整个服务器,getRealPath()
6、config 去的初始化参数,初始化参数在web.xml中配置
7、exception 表示的是错误页的处理操作
8、page 如同this一样,表示整个JSP页面
9、out 输出,但是尽量使用表达式输出

54.简述uml中include跟extend的区别

https://www.cnblogs.com/wangyaning/p/3740899.html

55.简述软件复用与重用的区别

软件复用是指在构造新的软件系统的过程中,对已存在的软件人工制品的使用技术。
如果是在一个系统中多次使用一个相同的软件成分,这叫软件共享。
对一个软件进行修改,使它能运行于新的软、硬件平台这称作软件移植。

56.servlet的五个加载方法

init():在Servlet实例化后,Servlet容器会调用init()方法,来初始化该对象,主要是为了让Servlet对象在处理客户请求前可以完成一些初始化的工作。例如:建立数据库连接,获取配置信息等。对于每一个Servlet实例,init()方法只能被调用一次。init()方法有一个类型为ServletConfig的参数,Servlet容器通过这个参数向Servlet传递配置信息。Servlet使用ServletConfig对象从Web应用程序的配置信息中获取以名-值对形式提供的初始化参数。另外,在Servlet中,还可以通过ServletConfig对象获取描述Servlet运行环境的ServletContext对象,使用该对象,Servlet可以和它的Servlet容器进行通信。

 

service():容器调用service()方法来处理客户端的请求。要注意的是,在service()方法被容器调用之前,必须确保init()方法正确完成。容器会构造一个表示客户端请求信息的请求对象(类型为ServletRequest)和一个用于对客户端进行响应的响应对象(类型为ServletResponse)作为参数传递给service()方法。在service()方法中,Servlet对象通过ServletRequest对象得到客户端的相关信息和请求信息,在对请求进行处理后,调用ServletResponse对象的方法设置响应信息。

 

destroy():当容器检测到一个Servlet对象应该从服务中被移除的时候,容器会调用该对象的destroy()方法,以便让Servlet对象可以释放它所使用的资源,保存数据到持久存储设备中。例如:将内存中的数据保存到数据库中,关闭数据库的连接等。当需要释放内存或容器关闭时,容器就会调用Servlet对象的destroy()方法。在Servlet容器调用destroy()方法前,如果还有其他的线程正在service()方法中执行,容器会等待这些线程执行完毕或等待服务器设定的超时值到达。一旦Servlet对象的destroy()方法被调用,容器不会再把其他的请求发送给该对象。如果需要该Servlet再次为客户端服务,容器会重新产生一个Servlet对象来处理客户端的请求。在destroy()方法调用后,容器会释放这个Servlet对象,在随后的时候内,该对象会被Java的垃圾收集器所回收。

 

getServletConfig():该方法返回容器调用init()方法时传递给Servlet对象的ServletConfig对象,ServletConfig对象包含了Servlet的初始化参数。

 

getServletInfo():返回一个String类型的字符串,其中包括了关于Servlet的信息,例如,作者、版本和版权。该方法返回的应该是纯文本字符串,而不是任何类型的标记(HTML,XML等)。

 

57.Oracle常用函数

ASCII(X)

返回字符XASCII

CONCAT(X,Y)

连接字符串XY

INSTR(X,STR[,START][,N)

X中查找str,可以指定从start开始,也可以指定从n开始

LENGTH(X)

返回X的长度

LOWER(X)

X转换成小写

UPPER(X)

X转换成大写

LTRIM(X[,TRIM_STR])

X的左边截去trim_str字符串,缺省截去空格

RTRIM(X[,TRIM_STR])

X的右边截去trim_str字符串,缺省截去空格

TRIM([TRIM_STR  FROM]X)

X的两边截去trim_str字符串,缺省截去空格

REPLACE(X,old,new)

X中查找old,并替换成new

SUBSTR(X,start[,length])

返回X的字串,从start处开始,截取length个字符,缺省length,默认到结尾

 

58.springmvc的加载顺序

1、客户端发起请求、前端控制器统一(org.springframework.web.servlet.DispatcherServlet)进行拦截。调用其中的doService(..)方法。再调用其中的doDispatch(..)方法

 

2、根据请求查询请求的处理器映射器。返回HandlerExecutionChain处理器执行链。

mappedHandler = getHandler(processedRequest, false);

3、获取处理器适配器。

HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

4、由获取到的处理器适配器执行处理器方法。

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

5、处理器执行完成后返回modelAndView

6、将modelandview 传递给视图解析器。进行数据绑定。

7、响应给客户端

59.解析json数据的方式

https://www.cnblogs.com/zhujiabin/p/5498272.html

60.ajax的数据类型

"xml": 返回 XML 文档,可用 jQuery 处理。"html": 返回纯文本 HTML 信息;包含的 script 标签会在插入 dom 时执行。"script": 返回纯文本 JavaScript 代码。不会自动缓存结果。除非设置了 "cache" 参数。注意:在远程请求时(不在同一个域下),所有 POST 请求都将转为 GET 请求。(因为将使用 DOM 的 script标签来加载)"json": 返回 JSON 数据 。"jsonp": JSONP 格式。使用 JSONP 形式调用函数时,如 "myurl?callback=?" jQuery 将自动替换 ? 为正确的函数名,以执行回调函数。"text": 返回纯文本字符串

 

 

61.springAop一般用在那些地方

事务

缓存

异常

日志

62List Set Map 的区别

       https://www.cnblogs.com/IvesHe/p/6108933.html

63 MySql 事务的原理

    一个事务会涉及到大量的cpu计算和IO操作,这些操作被打包成一个执行单元,要么同时都完成,要么同时都不完成.

     事务是一组原子性的sql命令或者说是一个独立的工作单元,如果数据库引擎能够成功的对数据库应用该组的全部sql语句,那么就执行该组命令

     如果其中有任何一条语句因为崩溃或者其它原因无法执行,那么该组中所有的sql语句都不会执行

     如果没有显示启动事务,数据库会根据autocommit的值.默认每条sql操作都会自动提交.

   原子性

        一个事务中的所有操作,要么都完成,要么都不执行.对于一个事务来说,不可能只执行其中的一部分.

   一致性

       数据库总是从一个一致性的状态转换到另外一个一致性状态.

   隔离性

       一个事务所做的修改在最终提交以前,对其它事务是不可见的.多个事务之间的操作相互不影响. 每降低一个事务隔离级别都能提高数据库的并发

       1.读未提交          其它事务未提交就可以读
       2.读已提交          其它事务只有提交了才能读
       3.可重复读          只管自己启动事务时候的状态,不受其它事务的影响(mysql默认)
       4.
事务串行          按照顺序提交事务保证了数据的安全性,但无法实现并发

   持久性

          一旦一个事务已经提交了,就算服务器崩溃,仍然需要在下次启动的时候自动恢复.

         结合事务日志完成:

                事务日志写入磁盘的时候是顺序IO,写数据文件的时候是随机IO

         一旦事务提交了,必须立即执行一个IO操作,确保此事务立即写入磁盘.

                

事务的状态

      活动

      部分提交

      失败

      中止

      提交

            事务一旦成功提交,便无法再撤销.

 

 

事务并发访问控制方式:

   

    时间

    多版本和快照隔离(mvcc)

    MVCC是行级锁的一个变种,但是它在很多情况下避免了加锁操作,因此开销更低,虽然实现机制有所不同,但大都实现了非阻塞的读操作,写操作也只锁定必要的行.

    MVCC的实现是通过保存数据在某个时间点的快照来实现的,也就是说,不管需要执行多长时间,只要事务开始时间相同,每个事务看到的数据都是一致的,事务开始的时间不同时,每个事务对同一张表,同一时刻看到的数据可能是不一样的(因为不同的时间点可能数据就已经产生了不同的快照版本,而每个事务在默认的RR隔离级别下只能看到事务开始时的数据快照)

 innoddmvcc是通过在每行记录后面保存两个隐藏列来实现的.这两个列,一个保存了行的创建时间.一个保存了行的过期时间(删除时间).列里面存储的并不是实际的时间值.而是系统版本号.每开启一个新的事务,系统版本号都会自动递增.

  一个事务在开启的时刻的系统版本号作为当前事务的版本号,用来和查询到的每行记录的版本号做对比.mvcc具体操作如下:

  select:

   A:过滤创建版本

      innodb只查找版本早于当前事务版本的数据行(也就是,行的系统版本号小于或等于事务的系统版本号),这样可以确保事务读取的行,要么是在事务开始之前已经存在,要么是事务自身插入或修改的数据.

  B: 过滤删除版本

       行的删除版本要么未定义,要么大于当前事务版本号,这可以确保事务读取到的行,在事务开始之前未被删除(即,这样做的目的是为了事务不会读取到被真正删除的行,删除版本号小于当前事务版本号的表示操作删除记录的事务已经提交--数据已经被删除,删除版本号大于当前事务版本号的表示这个事务是在当前事务之后开始的--当前事务开始时这些记录是还存在的,根据事务的隔离性,一致性要求,之后开始的事务操作的记录并提交,对当前事务不可见,所以还需要当前事务能够查询这些记录--只能够查询,不能够修改和删除)

只有满足以上两个条件的才可以返回作为查询结果

 

insert

      innodb为新插入的每一行保存当前系统版本号作为行版本号

 

delete

      innodb为删除的每一行保存当前系统版本号作为行删除标识

 

update

     innodb为插入一行新记录,保存当前系统版本号作为行版本号

     修改表中原来行把当前系统版本号更新到原来的行作为行删除版本号

 

保存这两个额外的系统版本号,使大多数读操作都可以不用加锁,这样设计使得读数据操作很简单,性能很好,并且也能保证只会读取到符合标准的行,不足之处是每行记录都需要额外的存储空间,需要做更多的行检查工作,以及一些额外的维护工作。

MVCC只在repeatable-readread-committed两个隔离级别下才工作,其他两个隔离级别都和MVCC不兼容,因为read uncommitted总是读取最新的数据行,而不是符合当前事务版本的数据行,而serializeble则会对所有读取的行都加锁。

另外要注意:MVCCRRRC隔离级别下的区别,在RR隔离级别下,一个事务只能读取到事务开始的那个时刻的数据快照,即,别的事务修改并提交的数据在自身没有提交之前一般读取不到(加for update语句的select除外,因为这个语句要对数据加X锁必须读取最新的数据快照),在RC隔离级别下,事务总是读取数据行的最新快照,即会产生不可重复读的问题。

      

死锁

    两个或者多个事务在同一资源上相互占用,并请求锁定对方占用的资源的状态

 

 

事务提交

    自动提交

     mysql默认采用自动提交(AUTOCOMMIT)模式.如果没有显示的开始一个事务,那么每条sql语句都会被当作一个事务执行提交的操作

     AUTOCOMMIT=0的时候所有的sql语句都是在一个事务中,直到显示的执行COMMITROLLBACK回滚该事务结束.同时又开始了另外一个新的事务.

 

    显式提交

     开启事务

            START TRANSACTION 

     结束事务

          (1)  COMMIT:         提交 

          (2)  ROLLBACK:    回滚 

   

     注意:一旦一个事务成功提交,将无法回滚.一个事务要想回滚,只能在没有提交成功之前执行回滚

                只有事务型存储引擎中的DML语句方能支持此类操作

     建议:显式请求和提交事务,而不要使用自动提交功能 set autocommit={1|0}

     事务支持保存点 savepoint

            SAVEPOINT identifier

            ROLLBACK [WORK] TO [SAVEPOINT]  identifier

            RELEASE SAVEPOINT identifier 

 

事务日志

     事务要保证ACID的完整性必须依靠事务日志做跟踪,每一个操作在真正写入数据数据库之前,先写入到日志文件中
     如要删除一行数据会先在日志文件中将此行标记为删除,但是数据库中的数据文件并没有发生变化.

     只有在(包含多个sql语句)整个事务提交后,再把整个事务中的sql语句批量同步到磁盘上的数据库文件

     在事务引擎上的每一次写操作都需要执行两遍:
        1.
先写入日志文件中
                写入日志文件中的仅仅是操作过程,而不是操作数据本身,所以速度比写数据库文件速度要快很多.
        2.
然后再写入数据库文件中

                写入数据库文件的操作是重做事务日志中已提交的事务操作的记录.

    日志组
         一般不止设置一个日志文件,一个文件写满之后使用另外一个日志文件提高服务器效率.

    日志文件的日志同步到磁盘后空间会自动释放,单个日志文件不宜设置过大     如果日志文件过大mysql进程在把日志同步到数据文件的时候可能会崩溃

  事务日志的用途

      事务日志可以帮助提高事务的效率,使用事务日志,存储引擎在修改表的数据的时候只需要修改其内存拷贝,再把该行为记录到持久在磁盘的事务日志中.而不用每次都将修改的数据本身持久到磁盘.事务日志采用的是追加方式,因此写日志的操作是磁盘上一小块区域的顺序IO,而不像随机IO需要磁盘在多个地方移动.所以采用事务日志的方式相对来说要快的多,事务日志持久后,内存中的修改在后台慢慢的刷回磁盘.期间如果系统发生崩溃,存储引擎在重启的时候依靠事务日志自动恢复这部分被修改数据

 

64 一个方法里面 有一个String参数 这个参数里有符号字符汉字数字  要求将参数里的数字挑选出来  进行相加  求和

       答: 需要使用 String 类中提供的 toCharArray() 方法进行 分割操作 分割成单个字符  将每个字符进行int转换  转换成功 就相加  否则不相加

65 一个方法里面 有一个Object参数  这个参数可能是  List,Map,Set  这三种类型如何判断该方法传入的参数是何种类型

使用Java 反射   Object   obj.getClass().getTypeName()  即可判断

 

66 说出你使用的第三方API 如:支付宝,微信

微信支付,微信登录,支付宝支付,支付宝登录,QQ登录,微博登录,百度地图,高的地图,腾讯短信,阿里云短信,

67 redis 的常用方法 

get

set

hset

hget

lset

lget

ldel

hdel

del

exist

hxistes

  1. EXPIRE 将key的生存时间设置为ttl秒
  2. PEXPIRE 将key的生成时间设置为ttl毫秒
  3. EXPIREAT 将key的过期时间设置为timestamp所代表的的秒数的时间戳
  4. PEXPIREAT 将key的过期时间设置为timestamp所代表的的毫秒数的时间戳

 

68 Mybatis Mapper配置文件里的常用标签

 

69 Spring 运行流程(tomcat启动时Spring在干什么)

70 zookeeper节点类型

PERSISTENT                持久化节点

 

PERSISTENT_SEQUENTIAL     顺序自动编号持久化节点,这种节点会根据当前已存在的节点数自动加 1

 

EPHEMERAL                 临时节点, 客户端session超时这类节点就会被自动删除

 

EPHEMERAL_SEQUENTIAL      临时自动编号节点

 

71 Redis   缓存雪崩,穿透

 

缓存雪崩:由于原有的缓存过期失效,新的缓存还没有缓存进来,有一只请求缓存请求不到,导致所有请求都跑去了数据库,导致数据库IO、内存和CPU眼里过大,甚至导致宕机,使得整个系统崩溃。

解决思路:
1,采用加锁计数,或者使用合理的队列数量来避免缓存失效时对数据库造成太大的压力。这种办法虽然能缓解数据库的压力,但是同时又降低了系统的吞吐量。
2,分析用户行为,尽量让失效时间点均匀分布。避免缓存雪崩的出现。
3,如果是因为某台缓存服务器宕机,可以考虑做主备,比如:redis主备,但是双缓存涉及到更新事务的问题,update可能读到脏数据,需要好好解决。

加锁:加锁排队只是为了减轻数据库的压力,并没有提高系统吞吐量。假设在高并发下,缓存重建期间key是锁着的,这是过来1000个请求999个都在阻塞的。同样会导致用户等待超时,这是个治标不治本的方法。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

public class CacheDemo

{

    public object GetCacheDataList()

        {

            const int cacheTime = 60;   

            const string lockKey = cacheKey;

            const string cacheKey = "datainfolist";      

            var cacheValue = CacheHelper.Get(cacheKey);

            if (cacheValue != null)

            {

                return cacheValue;

            }

            else

            {

                lock (lockKey)

                {

                    cacheValue = CacheHelper.Get(cacheKey);

                    if (cacheValue != null)

                    {

                        return cacheValue;

                    }

                    else

                    {

                        cacheValue = GetDataBaseInfo();              

                        CacheHelper.Add(cacheKey, cacheValue, cacheTime);

                    }                   

                }

                return cacheValue;

            }

        }

}

  标记失效缓存:

缓存标记:记录缓存数据是否过期,如果过期会触发通知另外的线程在后台去更新实际key的缓存。

  缓存数据:它的过期时间比缓存标记的时间延长1倍,例:标记缓存时间30分钟,数据缓存设置为60分钟。 这样,当缓存标记key过期后,实际缓存还能把旧数据返回给调用端,直到另外的线程在后台更新完成后,才会返回新缓存。

  这样做后,就可以一定程度上提高系统吞吐量。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

public object GetProductListNew()

        {

            const int cacheTime = 30;

            const string cacheKey = "product_list";

            //缓存标记。

            const string cacheSign = cacheKey + "_sign";

             

            var sign = CacheHelper.Get(cacheSign);

            //获取缓存值

            var cacheValue = CacheHelper.Get(cacheKey);

            if (sign != null)

            {

                return cacheValue; //未过期,直接返回。

            }

            else

            {

                CacheHelper.Add(cacheSign, "1", cacheTime);

                ThreadPool.QueueUserWorkItem((arg) =>

                {

                    cacheValue = GetProductListFromDB(); //这里一般是 sql查询数据。

                    CacheHelper.Add(cacheKey, cacheValue, cacheTime*2); //日期设缓存时间的2倍,用于脏读。               

                });

                 

                return cacheValue;

            }

        }

  缓存穿透:

缓存穿透是指用户查询数据,在数据库没有,自然在缓存中也不会有。这样就导致用户查询的时候,在缓存中找不到,每次都要去数据库再查询一遍,然后返回空。这样请求就绕过缓存直接查数据库,这也是经常提的缓存命中率问题。

  解决的办法就是:如果查询数据库也为空,直接设置一个默认值存放到缓存,这样第二次到缓冲中获取就有值了,而不会继续访问数据库,这种办法最简单粗暴。

缓存穿透是指用户查询数据,在数据库没有,自然在缓存中也不会有。这样就导致用户查询的时候,在缓存中找不到,每次都要去数据库中查询。

解决思路:

1,如果查询数据库也为空,直接设置一个默认值存放到缓存,这样第二次到缓冲中获取就有值了,而不会继续访问数据库,这种办法最简单粗暴。

2,根据缓存数据Key的规则。例如我们公司是做机顶盒的,缓存数据以MacKeyMac是有规则,如果不符合规则就过滤掉,这样可以过滤一部分查询。在做缓存规划的时候,Key有一定规则的话,可以采取这种办法。这种办法只能缓解一部分的压力,过滤和系统无关的查询,但是无法根治。

3,采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的BitSet中,不存在的数据将会被拦截掉,从而避免了对底层存储系统的查询压力。关于布隆过滤器,详情查看:基于BitSet的布隆过滤器(Bloom Filter) 

大并发的缓存穿透会导致缓存雪崩。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

public object GetProductListNew()

        {

            const int cacheTime = 30;

            const string cacheKey = "product_list";

 

            var cacheValue = CacheHelper.Get(cacheKey);

            if (cacheValue != null)

                return cacheValue;

                 

            cacheValue = CacheHelper.Get(cacheKey);

            if (cacheValue != null)

            {

                return cacheValue;

            }

            else

            {

                cacheValue = GetProductListFromDB(); //数据库查询不到,为空。

                 

                if (cacheValue == null)

                {

                    cacheValue = string.Empty; //如果发现为空,设置个默认值,也缓存起来。               

                }

                CacheHelper.Add(cacheKey, cacheValue, cacheTime);

                 

                return cacheValue;

            }

        }

  把空结果,也给缓存起来,这样下次同样的请求就可以直接返回空了,即可以避免当查询的值为空时引起的缓存穿透。同时也可以单独设置个缓存区域存储空值,对要查询的key进行预先校验,然后再放行给后面的正常缓存处理逻辑。

缓存预热

  缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样避免,用户请求的时候,再去加载相关的数据。

  解决思路:

    1,直接写个缓存刷新页面,上线时手工操作下。

    2,数据量不大,可以在WEB系统启动的时候加载。

    3,定时刷新缓存,

 

缓存更新

  缓存淘汰的策略有两种:

    (1) 定时去清理过期的缓存。

    (2)当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存。 

  两者各有优劣,第一种的缺点是维护大量缓存的key是比较麻烦的,第二种的缺点就是每次用户请求过来都要判断缓存失效,逻辑相对比较复杂,具体用哪种方案,大家可以根据自己的应用场景来权衡。1. 预估失效时间 2. 版本号(必须单调递增,时间戳是最好的选择)3. 提供手动清理缓存的接口。

 

72  jst

理解JWT的使用场景和优劣

 

淘楼小能手

百家号04-2816:20

经过前面两篇文章《JSON Web Token - Web应用间安全地传递信息》《八幅漫画理解使用JSON Web Token设计单点登录系统》的科普,相信大家应该已经知道了 JWT 协议是什么了。至少看到

1

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJxaWFubWlJZCI6InFtMTAzNTNzaEQiLCJpc3MiOiJhcHBfcW0xMDM1M3NoRCIsInBsYXRmb3JtIjoiYXBwIn0.cMNwyDTFVYMLL4e7ts50GFHTvlSJLDpePtHXzu7z9j4

这样形如 A.B.C 的字符串时能敏感地认出这是使用了 jwt。发了这两篇文章后,有不少读者在文末留言,表达了对 jwt 使用方式的一些疑惑,以及到底哪些场景适合使用 jwt。我并不是 jwt 方面的专家,和不少读者一样,起初研究时我也存在相同疑惑,甚至在逐渐接触后产生了更大的疑惑,经过这段时间项目中的使用和一些自己思考,把个人的总结整理成此文。

编码,签名,加密

这些基础知识简单地介绍下,千万别搞混了三个概念。在 jwt 中恰好同时涉及了这三个概念,笔者用大白话来做下通俗的讲解(非严谨定义,供个人理解)

编码(encode)和解码(decode)

一般是编码解码是为了方便以字节的方式表示数据,便于存储和网络传输。整个 jwt 串会被置于 http Header 或者 url 中,为了不出现乱码解析错误等意外,编码是有必要的。在 jwt 中以.分割的三个部分都经过 base64 编码(secret 部分是否进行 base64 编码是可选的,header payload 则是必须进行 base64 编码)。注意,编码的一个特点:编码和解码的整个过程是可逆的。得知编码方式后,整个 jwt 串便是明文了,随意找个网站验证下解码后的内容:

 

base64

所以注意一点,payload 是一定不能够携带敏感数据如密码等信息的。

签名(signature)

签名的目的主要是为了验证我是jwt 中常用的签名算法是 HS256,可能大多数人对这个签名算法不熟悉,但 md5,sha 这样的签名算法肯定是为人熟知的,签名算法共同的特点是整个过程是不可逆的。由于签名之前的主体内容(header,payload)会携带在 jwt 字符串中,所以需要使用带有密钥(yuè)的签名算法,密钥是服务器和签发者共享的。header 部分和 payload 部分如果被篡改,由于篡改者不知道密钥是什么,也无法生成新的 signature 部分,服务端也就无法通过,在 jwt 中,消息体是透明的,使用签名可以保证消息不被篡改。

前面转载的文章中,原作者将 HS256 称之为加密算法,不太严谨。

加密(encryption)

加密是将明文信息改变为难以读取的密文内容,使之不可读。只有拥有解密方法的对象,经由解密过程,才能将密文还原为正常可读的内容。加密算法通常按照加密方式的不同分为对称加密( AES)和非对称加密( RSA)。你可能会疑惑:“jwt 中哪儿涉及加密算法了?,其实 jwt 第一部分(header) 中的 alg 参数便可以指定不同的算法来生成第三部分(signature),大部分支持 jwt 的框架至少都内置 rsa 这种非对称加密方式。这里诞生了第一个疑问

疑问:一提到 rsa,大多数人第一想到的是非对称加密算法,而 jwt 的第三部分明确的英文定义是 signature,这不是矛盾吗?

划重点!

rsa 加密和rsa 签名是两个概念!(吓得我都换行了)

这两个用法很好理解:

既然是加密,自然是不希望别人知道我的消息,只有我自己才能解密,所以公钥负责加密,私钥负责解密。这是大多数的使用场景,使用 rsa 来加密。

既然是签名,自然是希望别人不能冒充我发消息,只有我才能发布签名,所以私钥负责签名,公钥负责验证。

所以,在客户端使用 rsa 算法生成 jwt 串时,是使用私钥来加密的,而公钥是公开的,谁都可以解密,内容也无法变更(篡改者无法得知私钥)。

所以,在 jwt 中并没有纯粹的加密过程,而是使加密之虚,行签名之实。

什么场景该适合使用jwt

来聊聊几个场景,注意,以下的几个场景不是都和jwt贴合。

一次性验证

比如用户注册后需要发一封邮件让其激活账户,通常邮件中需要有一个链接,这个链接需要具备以下的特性:能够标识用户,该链接具有时效性(通常只允许几小时之内激活),不能被篡改以激活其他可能的账户这种场景就和 jwt 的特性非常贴近,jwt payload 中固定的参数:iss 签发者和 exp 过期时间正是为其做准备的。

restful api的无状态认证

使用 jwt 来做 restful api 的身份认证也是值得推崇的一种使用方案。客户端和服务端共享 secret;过期时间由服务端校验,客户端定时刷新;签名信息不可被修改…spring security oauth jwt 提供了一套完整的 jwt 认证体系,以笔者的经验来看:使用 oauth2 jwt 来做 restful api 的认证都没有大问题,oauth2 功能更多,支持的场景更丰富,后者实现简单。

使用 jwt 做单点登录+会话管理(不推荐)

在《八幅漫画理解使用JSON Web Token设计单点登录系统》一文中提及了使用 jwt 来完成单点登录,本文接下来的内容主要就是围绕这一点来进行讨论。如果你正在考虑使用 jwt+cookie 代替 session+cookie ,我强力不推荐你这么做。

首先明确一点:使用 jwt 来设计单点登录系统是一个不太严谨的说法。首先 cookie+jwt 的方案前提是非跨域的单点登录(cookie 无法被自动携带至其他域名),其次单点登录系统包含了很多技术细节,至少包含了身份认证和会话管理,这还不涉及到权限管理。如果觉得比较抽象,不妨用传统的 session+cookie 单点登录方案来做类比,通常我们可以选择 spring security(身份认证和权限管理的安全框架)和 spring sessionsession 共享)来构建,而选择用 jwt 设计单点登录系统需要解决很多传统方案中同样存在和本不存在的问题,以下一一详细罗列。

jwt token泄露了怎么办?

前面的文章下有不少人留言提到这个问题,我则认为这不是问题。传统的 session+cookie 方案,如果泄露了 sessionId,别人同样可以盗用你的身份。扬汤止沸不如釜底抽薪,不妨来追根溯源一下,什么场景会导致你的 jwt 泄露。

遵循如下的实践可以尽可能保护你的 jwt 不被泄露:使用 https 加密你的应用,返回 jwt 给客户端时设置 httpOnly=true 并且使用 cookie 而不是 LocalStorage 存储 jwt,这样可以防止 XSS 攻击和 CSRF 攻击(对这两种攻击感兴趣的童鞋可以看下 spring security 中对他们的介绍CSRF,XSS

你要是正在使用 jwt 访问一个接口,这个时候你的同事跑过来把你的 jwt 抄走了,这种泄露,恕在下无力

secret如何设计

jwt 唯一存储在服务端的只有一个 secret,个人认为这个 secret 应该设计成和用户相关的,而不是一个所有用户公用的统一值。这样可以有效的避免一些注销和修改密码时遇到的窘境。

注销和修改密码

传统的 session+cookie 方案用户点击注销,服务端清空 session 即可,因为状态保存在服务端。但 jwt 的方案就比较难办了,因为 jwt 是无状态的,服务端通过计算来校验有效性。没有存储起来,所以即使客户端删除了 jwt,但是该 jwt 还是在有效期内,只不过处于一个游离状态。分析下痛点:注销变得复杂的原因在于 jwt 的无状态。我提供几个方案,视具体的业务来决定能不能接受。

仅仅清空客户端的 cookie,这样用户访问时就不会携带 jwt,服务端就认为用户需要重新登录。这是一个典型的假注销,对于用户表现出退出的行为,实际上这个时候携带对应的 jwt 依旧可以访问系统。

清空或修改服务端的用户对应的 secret,这样在用户注销后,jwt 本身不变,但是由于 secret 不存在或改变,则无法完成校验。这也是为什么将 secret 设计成和用户相关的原因。

借助第三方存储自己管理 jwt 的状态,可以以 jwt key,实现去 redis 一类的缓存中间件中去校验存在性。方案设计并不难,但是引入 redis 之后,就把无状态的 jwt 硬生生变成了有状态了,违背了 jwt 的初衷。实际上这个方案和 session 都差不多了。

修改密码则略微有些不同,假设号被到了,修改密码(是用户密码,不是 jwt secret)之后,盗号者在原 jwt 有效期之内依旧可以继续访问系统,所以仅仅清空 cookie 自然是不够的,这时,需要强制性的修改 secret。在我的实践中就是这样做的。

续签问题

续签问题可以说是我抵制使用 jwt 来代替传统 session 的最大原因,因为 jwt 的设计中我就没有发现它将续签认为是自身的一个特性。传统的 cookie 续签方案一般都是框架自带的,session 有效期 30 分钟,30 分钟内如果有访问,session 有效期被刷新至 30 分钟。而 jwt 本身的 payload 之中也有一个 exp 过期时间参数,来代表一个 jwt 的时效性,而 jwt 想延期这个 exp 就有点身不由己了,因为 payload 是参与签名的,一旦过期时间被修改,整个 jwt 串就变了,jwt 的特性天然不支持续签!

如果你一定要使用 jwt 做会话管理(payload 中存储会话信息),也不是没有解决方案,但个人认为都不是很令人满意

每次请求刷新 jwt

jwt 修改 payload 中的 exp 后整个 jwt 串就会发生改变,那就让它变好了,每次请求都返回一个新的 jwt 给客户端。太暴力了,不用我赘述这样做是多么的不优雅,以及带来的性能问题。

但,至少这是最简单的解决方案。

只要快要过期的时候刷新 jwt

一个上述方案的改造点是,只在最后的几分钟返回给客户端一个新的 jwt。这样做,触发刷新 jwt 基本就要看运气了,如果用户恰巧在最后几分钟访问了服务器,触发了刷新,万事大吉;如果用户连续操作了 27 分钟,只有最后的 3 分钟没有操作,导致未刷新 jwt,无疑会令用户抓狂。

完善 refreshToken

借鉴 oauth2 的设计,返回给客户端一个 refreshToken,允许客户端主动刷新 jwt。一般而言,jwt 的过期时间可以设置为数小时,而 refreshToken 的过期时间设置为数天。

我认为该方案并可行性是存在的,但是为了解决 jwt 的续签把整个流程改变了,为什么不考虑下 oauth2 password 模式和 client 模式呢?

使用 redis 记录独立的过期时间

实际上我的项目中由于历史遗留问题,就是使用 jwt 来做登录和会话管理的,为了解决续签问题,我们在 redis 中单独会每个 jwt 设置了过期时间,每次访问时刷新 jwt 的过期时间,若 jwt 不存在与 redis 中则认为过期。

tips:精确控制 redis 的过期时间不是件容易的事,可以参考我最近的一篇借助于 spring session 讲解 redis 过期时间的排坑记录。

同样改变了 jwt 的流程,不过嘛,世间安得两全法。我只能奉劝各位还未使用 jwt 做会话管理的朋友,尽量还是选用传统的 session+cookie 方案,有很多成熟的分布式 session 框架和安全框架供你开箱即用。

jwt,oauth2,session千丝万缕的联系

具体的对比不在此文介绍,就一位读者的留言回复下它的提问

这么长一个字符串,还不如我把数据存到数据库,给一个长的很难碰撞的key来映射,也就是专用token

这位兄弟认为 jwt 太长了,是不是可以考虑使用和 oauth2 一样的 uuid 来映射。这里面自然是有问题的,jwt 不仅仅是作为身份的认证(验证签名是否正确,签发者是否存在,有限期是否过期),还在其 payload 中存储着会话信息,这是 jwt session 的最大区别,一个在客户端携带会话信息,一个在服务端存储会话信息。如果真的是要将 jwt 的信息置于在共享存储中,那再找不到任何使用 jwt 的意义了。

jwt oauth2 都可以用于 restful 的认证,就我个人的使用经验来看,spring security oauth2 可以很好的使用多种认证模式:client 模式,password 模式,implicit 模式(authorization code 模式不算单纯的接口认证模式),也可以很方便的实现权限控制,什么样的 api 需要什么样的权限,什么样的资源需要什么样的 scope… jwt 我只用它来实现过身份认证,功能较为单一(可能是我没发现更多用法)。

总结

web 应用中,使用 jwt 代替 session 存在不小的风险,你至少得解决本文中提及的那些问题,绝大多数情况下,传统的 cookie-session 机制工作得更好。jwt 适合做简单的 restful api 认证,颁发一个固定有效期的 jwt,降低 jwt 暴露的风险,不要对 jwt 做服务端的状态管理,这样才能体现出 jwt 无状态的优势。

可能对 jwt 的使用场景还有一些地方未被我察觉,后续会研究下 spring security oauth jwt 的源码,不知到时会不会有新发现。

 

 

 

73 前后端  分离

 

前端页面  和后台数据处理  分割开  

好处

 

前端可以使用任意框架 最终使用Ajax和Json进行交互

后端只需要注意实现 并且后期可以进行优化

 

74 jQuery 和JavaScript  获取标签元素的区别

一个是DOM对象一个是jQuery对象。对于所有的浏览器,最最最底层都支持javascript,可能在某些浏览器支持一些对象和属性,一些浏览器不支持,所以才会出现了jQuery,解决了浏览器各个兼容的问题。
如果是DOM对象,可能可以使用浏览器底层的方法来使用,比如document.getElementById('A').src='aaa.png';
如果是jQuery对象,就只能使用jQuery自己提供的方法和属性了;
所以问题来了,有使用需要使用DOM,有时候需要使用jQuery对象,两者可能会相互转换的。
使用jQuery选取的对象一般都是数组,你只要使用[0]选取第一个元素,就是DOM对象了,

 

 

75 SQL语句优化

76开发中遇到内存溢出怎么办

引起内存溢出的原因有很多种,常见的有以下几种:
  1.内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
  2.集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;
  3.代码中存在死循环或循环产生过多重复的对象实体;
  4.使用的第三方软件中的BUG
  5.启动参数内存值设定的过小;

内存溢出的解决方案
      第一步,修改JVM启动参数,直接增加内存。(-Xms-Xmx参数一定不要忘记加。)

  第二步,检查错误日志,查看“OutOfMemory”错误前是否有其它异常或错误。

  第三步,对代码进行走查和分析,找出可能发生内存溢出的位置。

77String、StringBuffer、StringBuilder的区别

  1. 可变与不可变

String类中使用字符数组保存字符串,因为String类是final类型的,所以String对象是不可变的

StringBuffer和StringBuilder都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串,这两种对象都是可变的

  1. 线程安全性

String中的对象是不可变的,也就可以理解为常量,显然线程安全

StringBuffer对方法加了同步锁或者调用的方法加了同步锁,所以是线程安全的

StringBuilder并没有对方法加同步锁,所以是非线程安全的

78ArrayList和LinkedList的区别

  1. ArrayList是基于数组开发的,查询效率很高,但是修改数据的效率会低一些
  2. LinkedList是基于链表开发的,两端插入的时候效率很高,但是查询效率会低一些

79Java中如何遍历Map对象,至少写出两种方法

方式一 这是最常见的并且在大多数情况下也是最可取的遍历方式。在键值都需要时使用。

1

2

3

4

Map<Integer, Integer> map = new HashMap<Integer, Integer>();

for (Map.Entry<Integer, Integer> entry : map.entrySet()) {

  System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());

}

方法二 for-each循环中遍历keysvalues

如果只需要map中的键或者值,你可以通过keySetvalues来实现遍历,而不是用entrySet

1

2

3

4

5

6

7

8

9

Map<Integer, Integer> map = new HashMap<Integer, Integer>();

//遍历map中的键

for (Integer key : map.keySet()) {

  System.out.println("Key = " + key);

}

//遍历map中的值

for (Integer value : map.values()) {

  System.out.println("Value = " + value);

}

该方法比entrySet遍历在性能上稍好(快了10%),而且代码更加干净。

80.Map接口的实现类是否存在有序存储值的,它们是如何保证有序的【LinkedHashMap】

package cn.itcast_03;
import java.util.LinkedHashMap;import java.util.Set;
/*
 * LinkedHashMap:Map接口的哈希表和链接列表实现,具有可预知的迭代顺序。
 * 由哈希表保证键的唯一性
 * 由链表保证键盘的有序(存储和取出的顺序一致)
 */public class LinkedHashMapDemo {
    public static void main(String[] args) {
        // 创建集合对象
        LinkedHashMap<String, String> hm = new LinkedHashMap<String, String>();
 
        // 创建并添加元素
        hm.put("2345", "hello");
        hm.put("1234", "world");
        hm.put("3456", "java");
        hm.put("1234", "javaee");
        hm.put("3456", "android");
 
        // 遍历
        Set<String> set = hm.keySet();
        for (String key : set) {
            String value = hm.get(key);
            System.out.println(key + "---" + value);
        }
    }
}
  1. 讲讲Java的反射机制

 Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制

  1. 缓存溢出

https://blog.csdn.net/penglaozi/article/details/53147302

  1. log4j日志的级别

OFF > FATAL > ERROR > WARN > INFO > DEBUG > ALL

84.Servlet的生命周期

加载和实例化、初始化、处理请求、服务结束

初始化的方法init()、处理客户请求的方法service()、服务结束的方法destory()

85.jsp与servlet的区别

jsp是html中内嵌java代码;servlet把html代码和JAVA代码分离开;
jsp侧重与显示;servlet侧重与控制逻辑

86左连接,右连接 内连接,外连接 区别

左外连接是返回主表的所有信息,如果从表没有主表信息显示为空
而内连接是以从表为主如没数据那么就不显示
举个例子
如有2张表 student grade
student
sid,sname两列
有3条数据 1,呵呵 2,嘿嘿 3,嘻嘻
grade
有score,sid
有2条数据 90,1 80,2
比如我们要查哪个人考了多少分呢么左连接就是
呵呵 90
嘿嘿 80
嘻嘻 null
内连接就是
呵呵 90
嘿嘿 80

87 什么情况下使用子查询,左连接,右连接

当一个查询是另一个查询的条件时使用子查询。

当两张表只需要获取左边表的数据时,使用左连接查询

当两张表只需要获取右边表的数据时,使用右连接查询

 

88 怎么使用jquery提交表单

获取表单的id 使用.submit()方法

 

89 springCloud怎么配置网关

@RestController

public class NewsController {

 

    private static final String REST_URL_PREFIX = "http://TOMCATGO-PROVIDE-NEWS";

 

    /**

     * 提供了多种便捷访问的http模板

     */

    @Autowired

    private RestTemplate restTemplate;

    //(url,requestMap,ResponseBean.class)

 

    @GetMapping(value="/news/list")

    public Object index(HttpServletResponse response){

        response.setHeader("Access-Control-Allow-Origin", "*");

        System.out.println("1111111111111111111111");

        return restTemplate.getForObject(REST_URL_PREFIX+"/listAll",Object.class);

    }

 

 

    @GetMapping(value="/news/{id}")

    public Object getNewsInfo(HttpServletResponse response,@PathVariable("id") Integer id){

        response.setHeader("Access-Control-Allow-Origin", "*");

        return restTemplate.getForObject(REST_URL_PREFIX+"/news/"+id,Object.class);

    }

 

    @RequestMapping(value="/index",method = RequestMethod.GET)

    public Object indexx(HttpServletResponse response){

        response.setHeader("Access-Control-Allow-Origin", "*");

        return restTemplate.getForObject(REST_URL_PREFIX+"/index",Object.class);

    }

 

}

90 redis怎么设置过期时间

Jedis调用expire方法设置过期时间

91 数据库触发器

触发器

  其是一种特殊的存储过程。一般的存储过程是通过存储过程名直接调用,而触发器主要是

  通过事件(增、删、改)进行触发而被执行的。其在表中数据发生变化时自动强制执行。

  常见的触发器有两种:after(for)instead of,用于insertupdatedelete事件。

  after(for)        表示执行代码后,执行触发器

  instead of        表示执行代码前,用已经写好的触发器代替你的操作

 

触发器语法:

  create trigger 触发器的名字   on 操作表

  for|after         instead of

  update|insert|delete

  as

  SQL语句

 

触发器实现原理图

https://img-blog.csdnimg.cn/20181229001921494

92 redis和其他缓存技术有啥区别

1RedisMemcache都是将数据存放在内存中,都是内存数据库。不过memcache还可用于缓存其他东西,例如图片、视频等等;

2Redis不仅仅支持简单的k/v类型的数据,同时还提供listsethash等数据结构的存储;

3虚拟内存--Redis当物理内存用完时,可以将一些很久没用到的value 交换到磁盘;

4、过期策略--memcacheset时就指定,例如set key1 0 0 8,即永不过期。Redis可以通过例如expire 设定,例如expire name 10

5、分布式--设定memcache集群,利用magent做一主多从;redis可以做一主多从。都可以一主一从;

6、存储数据安全--memcache挂掉后,数据没了;redis可以定期保存到磁盘(持久化);

7灾难恢复--memcache挂掉后,数据不可恢复; redis数据丢失后可以通过aof恢复;

8Redis支持数据的备份,即master-slave模式的数据备份;

 

93 autowired和 resource的区别

@Resource装配顺序
  1. 如果同时指定了nametype,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常
  2. 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常
  3. 如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常
  4. 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配;

@Autowired @Resource的区别:

 

1 @Autowired@Resource都可以用来装配bean. 都可以写在字段上,或写在setter方法上。

2 @Autowired默认按类型装配(这个注解是属业spring的),默认情况下必须要求依赖对象必须存在,如果要允许null值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用,如下:

1

2

@Autowired()@Qualifier("baseDao")

privateBaseDao baseDao;

3@Resource(这个注解属于J2EE的),默认按照名称进行装配,名称可以通过name属性进行指定,如果没有指定name属性,当注解写在字段上时,默认取字段名进行安装名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。

1

2

@Resource(name="baseDao")

privateBaseDao baseDao;

推荐使用:@Resource注解在字段上,这样就不用写setter方法了,并且这个注解是属于J2EE的,减少了与spring的耦合。这样代码看起就比较优雅。

 

spring @Qualifier注解

 

@Autowired是根据类型进行自动装配的。如果当Spring上下文中存在不止一个UserDao类型的bean时,就会抛出BeanCreationException异常;如果Spring上下文中不存在UserDao类型的bean,也会抛出BeanCreationException异常。我们可以使用@Qualifier配合@Autowired来解决这些问题.

94responseBody作用

@ResponseBody是作用在方法上的,@ResponseBody 表示该方法的返回结果直接写入 HTTP response body 中,一般在异步获取数据时使用【也就是AJAX】,在使用 @RequestMapping后,返回值通常解析为跳转路径,但是加上 @ResponseBody 后返回结果不会被解析为跳转路径,而是直接写入 HTTP response body 中。 比如异步获取 json 数据,加上 @ResponseBody 后,会直接返回 json 数据。@RequestBody 将 HTTP 请求正文插入方法中,使用适合的 HttpMessageConverter 将请求体写入某个对象。

 

猜你喜欢

转载自blog.csdn.net/u014131617/article/details/85333028