概述
5目录
目录 1
基础篇 4
一、JDK常用的包 4
二、 Get和Post的区别 4
十一、 Page和PageContext的区别 7
十二、 Ajax总结三、 Java多态的具体体现 4
四、 StringBuffer StringBuilder String 区别 5
五、 Hashtable与HashMap的区别 5
六、 九大隐式对象 6
七、 Forword(请求转发)与Redirect(重定向) 6
八、JQurey总结 6
九、 XML和Json的特点 6
十、 request.getSession()、reqeust.getSession(false)和 request.getSession(true) 7 7
十三、JSP9大隐视对象中四个作用域的大小与作用范围 7
十四、 List,Set,Collection,Collections 8
十五、 java的基本数据类型 8
十六、 冒泡排序 8
十七、二分查找法 9
十八、时间类型转换 9
十九、 阶乘 9
二十、UE和UI的区别 10
二十一、osi七层模型 10
二十二、线程和进程的区别 10
二十三、jvm的内存结构 10
二十四、内存泄露和内存溢出 11
二十五、设计模式 11
一.单例设计模式 11
二.代理设计模式 12
三.工厂设计模式 13
二十六、解析xml文件的几种技术 14
二十七、项目的生命周期 15
二十八、OSCache的判断 15
二十九、经常访问的技术网站 15
三十、项目团队中交流的工具 16
三十一、平时浏览的书籍 16
三十二、java Exception体系结构 16
三十三、session和cookie的区别 17
三十四、字节流与字符流的区别 17
三十五、final,finally,finalize 三者区别 17
三十六、Io流的层次结构 18
三十七、JAVA: 19
三十八、JavaSE JavaEE JavaME区别 19
三十九、JDK JRE JVM的区别: 20
四十、报错的状态码: 20
四十一、协议以及默认的端口号 21
四十二、抽象类与接口的区别 21
四十三、修饰符的作用 21
四十四、onready和onload的区别 21
四十五、switch默认接受的几种数据类型 22
四十六、request 跟session的区别 22
四十七、找到解决svn冲突方法 22
四十八、反射的描述 23
框架篇 23
一、 Struts1的运行原理 23
二、 Struts2的运行原理 23
三、 struts2的体系结构 23
四、 Spring MVC运行原理 24
五、 Struts1.x与Struts2.x的区别 25
六、 Spring MVC、struts1和struts2区别 25
七、 Struts2中result中的type类型 25
八、 Struts2标签 26
九、 SSI整合 26
十、 SSH整合 26
十、 Spring MVC整合 27
十一、Hibernate 中get 和 load的区别 27
十二、 Hibernate、Ibatis、Jdbc三者的区别 28
十三、 Hibernate的运行原理 28
十四、 Hibernate五大核心(类/接口)简述 28
十五、 Hibernate与JDBC的区别 28
十六、Hibernate中的两大配置文件 28
十七、 Hibernate事务处理 29
十八、 Hibernate的三种状态以及状态的转换 29
十九、 分页步骤 29
二十、hibernate缓存概述 29
二十一、Ssh的概述: 30
二十二、防止表单重复提交 30
二十三、JSP标签: 30
二十四、过滤器 31
二十五、拦截器的理解 32
二十六、Spring融入框架 32
二十七、项目的部署方式 32
数据库篇 33
一、 JDBC连接数据库步骤(以MYSQL为例) 33
二、 数据库连接池 33
三、 mysql的数据库导入导出 34
四、 jdbc分段批量提交的时候出现异常怎么处理? 34
五、 jdbc批量处理数据 34
六、 Oracle分页 35
七、 Oracle的基本数据类型 35
八、 id、rowid、rownum的区别 36
九、 主键和唯一索引的区别? 36
十、 Preparedstatement和statement的区别 36
十一、 数据库三范式 36
十二、 视图概述 37
十三、 存储过程概述 37
十四、 索引概述 38
十五、 必背的sql语句 39
十六、ibatis批量 40
业务场景篇 41
一、 Spring的概述 42
二、 事务概述 43
三、 权限概述 44
四、 OSCache业务场景 44
五、 线程概述 44
六、 Ajax请求Session超时问题 45
七:java线程池概述 45
八、 OSCache概述 46
九、 OSCache+autocomplete+单例业务场景 47
十、 缓存概述 47
十一、 实现页面静态化业务场景 48
十二、 servlet线程安全描述 48
十三、 (jbpm4)工作流引擎描述: 48
十四、 JPBM业务场景 49
十五、 Ant描述 49
十六、 FreeMarker描述 50
十七、 webService描述 50
十八、 oracle索引概述 52
十九、 oracle存储过程 52
二十、Junit 业务场景 53
二十一、Apache+Tomcat 实现负载均衡及seesion复制 53
二十二、Ant业务场景 54
二十三、maven业务场景 54
二十四、Servlet的概述: 55
二十五、bugfree的操作步骤 55
二十六、Axis2 的配置 56
二十六、spring定时器 56
二十七、Ext概述 57
二十八、lucene的概述 58
二十九、线程池作用 58
三十、jbpm是如何和spring进行整合 58
三十一、Tomcat优化 58
三十二、memcached的介绍 60
优化篇 61
一、 代码优化 61
二、 业务优化 61
三、 sql优化 61
四、 防sql注入 64
sql注入 64
Sql注入的防护 64
五、数据库中常用术语: 66
深入java虚拟机以及大数据 66
一、jvm的相关概念 66
二、云计算+大数据的具体技术实现方案: 69
基础篇
一、JDK常用的包
java.lang: 这个是系统的基础类,比如String、Math、Integer、System和Thread,提供常用功能。
java.io: 这里面是所有输入输出有关的类,比如文件操作等
java.net: 这里面是与网络有关的类,比如URL,URLConnection等。
java.util : 这个是系统辅助类,特别是集合类Collection,List,Map等。
java.sql: 这个是数据库操作的类,Connection, Statememt,ResultSet等
- Get和Post的区别
- 从HTTP规范上:
get用于获取资源信息,而且应该是安全的和幂等的(获取资源信息,就像数据库查询一样,不会修改,增加数据,不会影响资源的状态。)
安全的是指不作数据修改;幂等的意味同一URL的多个请求应该返回同样的结果;
Post可能修改服务器中的信息。
2.理论上的区别:
- 服务端获取GET请求参数用Request.QueryString,获取POST请求参数用Request.Form。
- URL不存在参数上限的问题,HTTP协议规范没有对URL长度进行限制(get是通过URL提交数据,所以get可提交的数据量跟URL的长度有直接关系)
post是没有大小限制的,HTTP协议规范也没有进行大小限制(post数据没有限制,起限制作用的是服务器的处理程序的处理能力)
- POST的安全性要比GET的安全性高(get提交数据会将信息出现在URL上,登录页面时,可能被浏览器缓存,当其他人查看浏览器的历史纪录时,也会看到数据信息,而且get提交数据时,还可能会造成“跨站请求伪造”攻击。)
- Get是向服务器索取数据的一种请求,post是向服务器提交数据的一种请求(在form中,method默认为get)
GET和POST只是发送机制不同,并不是一个取一个发
- Java多态的具体体现
面向对象编程有四个特征:抽象,封装,继承,多态。
多态有四种体现形式:
1. 接口和接口的继承。
2. 类和类的继承。
3. 重载。
4. 重写。
其中重载和重写为核心。
重载:重载发生在同一个类中,在该类中如果存在多个同名方
法,但是方法的参数类型和个数不一样,那么说明该方法被重
载了。
重写:重写发生在子类继承父类的关系中,父类中的方法被子
类继承,方法名,返回值类型,参数完全一样,但是方法体不
一样,那么说明父类中的该方法被子类重写了。
- StringBuffer StringBuilder String 区别
String 字符串常量 不可变 使用字符串拼接时是不同的2个空间
StringBuffer 字符串变量 可变 线程安全 字符串拼接直接在字符串后追加
StringBuilder 字符串变量 可变 非线程安全 字符串拼接直接在字符串后追加
1.StringBuilder执行效率高于StringBuffer高于String.
2.String是一个常量,是不可变的,所以对于每一次+=赋值都会创建一个新的对象,StringBuffer和SztringBuilder都是可变的,当进行字符串拼接时采用append方法,在原来的基础上进行追加,所以性能比String要高,又因为StringBuffer是线程安全的而StringBuilder是线程非安全的,所以StringBuilder的效率高于 StringBuffer.
3.对于大数据量的字符串的拼接,采用StringBuffer,StringBuilder.
- Hashtable与HashMap的区别
HashMap不是线程安全的,HashTable是线程安全。
HashMap允许空(null)的键和值(key),HashTable则不允许。
HashMap性能优于Hashtable。
Map
1.Map是一个以键值对存储的接口。Map下有两个具体的实现,分别是HashMap和HashTable.
2.HashMap是线程非安全的,HashTable是线程安全的,所以HashMap的效率高于HashTable.
3.HashMap允许键或值为空,而HashTable不允许键或值为空.
- 九大隐式对象
输入/输出对象: request response out
作用域通信对象: session application pageContext
Servlet 对象: page config
错误对象: exception
- Forword(请求转发)与Redirect(重定向)
1、从数据共享上
Forword是一个请求的延续,可以共享request的数据
Redirect开启一个新的请求,不可以共享request的数据
2、从地址栏
Forword转发地址栏不发生变化
Redirect转发地址栏发生变化
八、JQurey总结
jquery是一个轻量级的js框架,具有跨浏览器的特性,兼容性好,
并且封装了很多工具,方便使用。
常用的有: 选择器 ,dom操作 ,ajax(ajax不能跨域) ,特效,工具类
$(document).ready(function() { // 程序代码}) 与 window.onload = function() { // 程序代码 } 虽然在功能上可以互换,但他们之间也有许多区别:
- 执行时间不同:$(document).ready 在页面框架下载完毕后就执行;而window.onload 必须在页面全部加载完毕(包含图片下载)后才能执行。很明显前者的执行效率高于后者。
- 执行数量不同:$(document).ready 可以重复写多个,并且每次执行结果不同;而window.onload尽管可以执行多个,但仅输出最后一个执行结果,无法完成多个结果的输出。
- $(document).ready(function() {}) 可以简写成$(function() {}), 因此下面的代码是等价的。
- XML和Json的特点
Xml特点:
1、有且只有一个根节点;
2、数据传输的载体
3、所有的标签都需要自定义
4、是纯文本文件
Json(JavaScript Object Notation)特点:
json分为两种格式:
json对象(就是在{}中存储键值对,键和值之间用冒号分隔,
键 值 对之间用逗号分隔);
json数组(就是[]中存储多个json对象,json对象之间用逗号分隔)
(两者间可以进行相互嵌套)数据传输的载体之一
区别:
传输同样格式的数据,xml需要使用更多的字符进行描述,
流行的是基于json的数据传输。
xml的层次结构比json更清晰。
共同点:
xml和json都是数据传输的载体,并且具有跨平台跨语言的特性。
- request.getSession()、reqeust.getSession(false)和 request.getSession(true)
getSession()/getSession(true):当session存在时返回该session,否则新建一session并返回该对象
getSession(false):当session存在时返回该session,否则返回null
- Page和PageContext的区别
Page是servlet对象;使用this关键字,它的作用范围是在同一页面。
PageContext是作用域通信对象;通常使用setAttribute()和getAttribute()来设置和获取存放对象的值。
- Ajax总结
AJAX 全称: 异步JavaScript及 XML(Asynchronous JavaScript And XML)
Ajax的核心是JavaScript对象XmlHttpRequest(XHR)。
Ajax的优点:
提高用户体验度(UE)
提高应用程序的性能
进行局部刷新
AJAX不是一种新的编程语言,而是一种用于创建更好更快以及交互性更强的 Web 应用程序的技术。
- 通过 AJAX,我们的 JavaScript 可使用JavaScript的XMLHttpRequest对象来直接与服务器进行通信。通过这个对象,我们的 JavaScript 可在不重载页面的情况与Web服务器交换数据,即可局部刷新。
- AJAX 在浏览器与 Web 服务器之间使用异步数据传输(HTTP 请求),这样就可使网页从服务器请求少量的信息,而不是整个页面,减轻服务器的负担,提升站点的性能。
AJAX 可使因特网应用程序更小、更快,更友好,用户体验(UE)好。
- Ajax是基于标准化并被广泛支持的技术,并且不需要插件和下载小程序
十三、JSP9大隐视对象中四个作用域的大小与作用范围
四个作用域从大到小:appliaction>session>request>page
application:全局作用范围,整个应用程序共享.生命周期为:应用程序启动到停止。
session:会话作用域,当用户首次访问时,产生一个新的会话,以后服务器就可以记住这个会话状态。
request:请求作用域,就是客户端的一次请求。
page:一个JSP页面。
以上作用范围使越来越小, request和page的生命周期都是短暂的,他们之间的区别就是:一个request
可以包含多个page页(include,forward)。
- List,Set,Collection,Collections
- List和Set都是接口,他们都继承于接口Collection,List是一个有序的可重复的集合,而Set的无序的不可重复的集合。Collection是集合的顶层接口,Collections是一个封装了众多关于集合操作的静态方法的工具类,因为构造方法是私有的,所以不能实例化。
- List接口实现类有ArrayList,LinkedList,Vector。ArrayList和Vector是基于数组实现的,所以查询的时候速度快,而在进行增加和删除的时候速度较慢LinkedList是基于链式存储结构,所以在进行查询的时候速度较慢但在进行增加和删除的时候速度较快。又因为Vector是线程安全的,所以他和ArrayList相比而言,查询效率要低。
(vector线程安全 是基于加条件的,)
- java的基本数据类型
数据类型 大小
byte(字节) 1(8位)
shot(短整型) 2(16位)
int(整型) 4(32位)
long(长整型) 8(64位)
float(浮点型) 4(32位)
double(双精度) 8(64位)
char(字符型) 2(16位)
boolean(布尔型) 1位
附加:
String是基本数据类型吗?(String不是基本数据类型)
String的长度是多少,有限制?(长度受内存大小的影响)
- 冒泡排序
public class Sort {
public static void sort() {
Scanner input = new Scanner(System.in);
int sort[] = new int[10];
int temp;
System.out.println("请输入10个排序的数据:");
for (int i = 0; i < sort.length; i++) {
sort[i] = input.nextInt();bv
}
for (int i = 0; i < sort.length - 1; i++) {
for (int j = 0; j < sort.length - i - 1; j++) {
if (sort[j] < sort[j + 1]) {
temp = sort[j];
sort[j] = sort[j + 1];
sort[j + 1] = temp;
}
}
}
System.out.println("排列后的顺序为:");
for(int i=0;i<sort.length;i++){
System.out.print(sort[i]+" ");
}
}
public static void main(String[] args) {
sort();
}
}
- 二分查找法
二分查找法:又称折半查找
优点:比较次数少,查找速度快,平均性能好;
缺点:要求待查表为有序表,且插入删除困难;
十八、时间类型转换
public class DateFormat {
public static void fun() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
String newDate;
try {
newDate = sdf.format(new SimpleDateFormat("yyyyMMdd")
.parse("20121115"));
System.out.println(newDate);
} catch (ParseException e) {
e.printStackTrace();
}
}
public static void main(String args[]) {
fun();
}
}
- 阶乘
public class Multiply {
public static int multiply(int num) {
if (num < 0) {
System.out.println("请输入大于0的数!");
return -1;
} else if (num == 0 || num == 1) {
return 1;
} else {
return multiply(num - 1) * num;
}
}
public static void main(String[] args) {
System.out.println(multiply(10));
}
}
二十、UE和UI的区别
UE 是用户体验度
UI 界面原型(用户界面)(相当于买房时用的模型)
设计UI的作用:
1、帮助程序员工作(界面已由美工设计完成)
2、提前让用户对项目有个宏观的了解,知道效果是什么样子。
二十一、osi七层模型
第一层:物理层
第二层:数据链路层
第三层:网络层
第四层:传输层
第五层:会话层
第六层:表示层
第七层:应用层
二十二:线程和进程的区别
1、功能不同:进程是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。
2、工作原理不同:早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。
二十三、jvm的内存结构
java虚拟机的内存结构分为堆(heap)和栈(stack),堆里面存放是对象实例也就是new出来的对象。栈里面存放的是基本数据类型以及引用数据类型的地址。
对于所谓的常量是存储在方法区的常量池里面。
二十四、内存泄露和内存溢出
内存泄露 (memory leak),是指应用程序在申请内存后,
无法释放已经申请的内存空间.一次内存泄露危害可以忽略,
但如果任其发展最终会导致内存溢出(out of memory).
如读取文件后流要进行及时的关闭以及对数据库连接的释放。
内存溢出(out of memory)是指应用程序在申请内存时,
没有足够的内存空间供其使用。
如我们在项目中对于大批量数据的导入,采用分段批量提交的方式。
二十五、设计模式
一.单例设计模式
单例就是该类只能返回一个实例。
单例所具备的特点:
1.私有化的构造函数
2.私有的静态的全局变量
3.公有的静态的方法
单例分为懒汉式、饿汉式和双重判定锁
饿汉式:
public class Singleton1 {
private Singleton1() {};
private static Singleton1 single = new Singleton1();
public static Singleton1 getInstance() {
return single;
}
}
懒汉式:
public class Singleton2 {
private Singleton2() {}
private static Singleton2 single=null;
public static Singleton2 getInstance() {
if (single == null) {
single = new Singleton2();
}
return single;
}
}
线程安全:(双重判定锁)
public class Singleton3 {
private Singleton3() {}
private static Singleton3 single ;
public static Singleton3 getInstance() {
if(null == single){
synchronized(single ){
if(null == single){
single = new Singleton3();
}
}
}
return single;
}
}
参考:
通过双重判断来保证单列设计模式在多线程中的安全性,
并且它在性能方面提高了很多。
synchronized在方法上加锁 (同步锁)
synchronized在代码块内部加锁 (同步代码块)
synchronized(同步锁)
使用synchronized如何解决线程安全的问题?
1.synchronized在方法上加锁
2.synchronized在代码块内部加锁
- 懒汉 2.饿汉 3.双重判断
二.代理设计模式
接口:
public interface BrandService {
public List<Brand> getBrandList();
}
接口的真实实现:
public class BrandServiceImpl implements BrandService {
public List<Brand> getBrandList() {
brandDao.getBrandList();
}
}
接口的代理实现:
public class BrandServiceProxy implements BrandService {
private BrandService brandService;
public BrandServiceProxy(BrandService brandService) {
this.brandService = brandService;
}
public List<Brand> getBrandList() {
if (cache中不存在) {
List<Brand> brandList = brandService.getBrandList();
cache.put("brandList", brandList);
}
return brandList;
}
}
客户端调用:
public class client {
public static void main(String[] args) {
BrandService brandService = new BrandServiceProxy(new BrandServiceImpl());
brandService.getBrandList();
}
}
三.工厂设计模式
接口:
public interface DataSource {
public Connection getConnection();
}
mysql数据源实现类:
public class MysqlDataSource implements DataSource{
public Connection getConnection() {
return mysql数据库的连接
}
}
oracle数据源实现类:
public class OracleDataSource implements DataSource{
public Connection getConnection() {
return oracle数据库的连接
}
}
数据源工厂:
public class DataSourceFactory {
public static DataSource getDataSource(String dbType) {
if (dbType.equals("mySql")) {
return new MysqlDataSource();
} else if (dbType.equals("oracle")) {
return new OracleDataSource();
} else {
throw new RuntimeException("数据源类型不匹配");
}
}
}
客户端测试类:
public class Client() {
public static void main(String[] args) {
DataSourceFactory.getDataSource("mySql").getConnection();
}
}
二十六、解析xml文件的几种技术
1、 解析xml的几种技术
1.dom4j
2.sax
3.jaxb
4.jdom
5.dom
1.dom4j
dom4j是一个Java的XML API,类似于jdom,用来读写XML文件的。dom4j是一个非常优秀的Java XML API,具有性能优异、功能强大和极端易用使用的特点,同时它也是一个开放源代码的软件。
2.sax
SAX(simple API for XML)是一种XML解析的替代方法。相比于DOM,SAX是一种速度更快,更有效的方法。它逐行扫描文档,一边扫描一边解析。而且相比于DOM,SAX可以在解析文档的任意时刻停止解析,但任何事物都有其相反的一面,对于SAX来说就是操作复杂。
3.jaxb
JAXB(Java Architecture for XML Binding) 是一个业界的标准,是一项可以根据XML Schema产生Java类的技术。该过程中,JAXB也提供了将XML实例文档反向生成Java对象树的方法,并能将Java对象树的内容重新写到XML实例文档。从另一方面来讲,JAXB提供了快速而简便的方法将XML模式绑定到Java表示,从而使得Java开发者在Java应用程序中能方便地结合XML数据和处理函数。
2、dom4j 与 sax 之间的对比:【注:必须掌握!】
dom4j不适合大文件的解析,因为它是一下子将文件加载到内存中,所以有可能出现内存溢出,
sax是基于事件来对xml进行解析的,所以他可以解析大文件的xml
也正是因为如此,所以dom4j可以对xml进行灵活的增删改查和导航,而sax没有这么强的灵活性
所以sax经常是用来解析大型xml文件,而要对xml文件进行一些灵活(crud)操作就用dom4j
二十七、项目的生命周期
1.需求分析
2.概要设计
3.详细设计(用例图,流程图,类图)
4.数据库设计(powerdesigner)
5.代码开发(编写)
6.单元测试(junit 白盒测试)(开发人员)
svn版本管理工具(提交,更新代码,文档)
7.集成测试 (黑盒测试,loadrunner(编写测试脚本)(高级测试))
8.上线试运行 (用户自己体验)
9.压力测试(loadrunner)
10.正式上线
11.维护
二十八、OSCache的判断
Object obj = CacheManager.getInstance().getObj("oaBrandList");
//从缓存中取数据
if (null == obj) {
obj = brandDao.getBrandList();
//如果为空再从数据库获取数据
//获取之后放入缓存中
CacheManager.getInstance().putObj("oaBrandList", obj);
}
return (List<OaBrand>)obj;
二十九、经常访问的技术网站
1.csdn(详细步骤的描述)
2.iteye(详细步骤的描述)
3.oschina(开源中国获取java开源方面的信息技术)
4.java开源大全 www.open-open.com(获取java开源方面的信息技术)
5.infoq(对java,php,.net等这些语言的一些最新消息的报道)
6.推酷
三十、项目团队中交流的工具
飞秋(局域网) qq(局域网,外网)
RTX(局域网,外网) 邮箱(foxmail,outlook) (局域网,外网)
三十一、平时浏览的书籍
实战经验:
*** in action(实战)
*** 深入浅出
*** 入门指南
思想基础:
大话设计模式 重构
三十二、java Exception体系结构
java 异常是程序运行过程中出现的错误。Java把异常当作对象来处理,并定义一个基类java.lang.Throwable作为所有异常的超类。在Java API中定义了许多异常类,分为两大类,错误Error和异常Exception。其中异常类Exception又分为运行时异常(RuntimeException)和非运行时异常(非runtimeException),也称之为不检查异常(Unchecked Exception)和检查异常(Checked Exception)。
1、Error与Exception
Error是程序无法处理的错误,比如OutOfMemoryError、ThreadDeath等。
这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。
Exception是程序本身可以处理的异常,这种异常分两大类运行时异常和非运行时异常。程序中应当尽可能去处理这些异常。
2、运行时异常和非运行时异常
运行时异常: 都是RuntimeException类及其子类异常: IndexOutOfBoundsException 索引越界异常
ArithmeticException:数学计算异常
NullPointerException:空指针异常
ArrayOutOfBoundsException:数组索引越界异常
ClassNotFoundException:类文件未找到异常
ClassCastException:造型异常(类型转换异常)
这些异常是不检查异常(Unchecked Exception),程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的。
非运行时异常:是RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如:
IOException、文件读写异常
FileNotFoundException:文件未找到异常
EOFException:读写文件尾异常
MalformedURLException:URL格式错误异常
SocketException:Socket异常
SQLException:SQL数据库异常
三十三、session和cookie的区别
session是存储在服务器端,cookie是存储在客户端的,所以安全来讲session的安全性要比cookie高,然后我们获取session里的信息是通过存放在会话cookie里的sessionid获取的。又由于session是存放在服务器的内存中,所以session里的东西不断增加会造成服务器的负担,所以会把很重要的信息存储在session中,而把一些次要东西存储在客户端的cookie里,然后cookie确切的说分为两大类分为会话cookie和持久化cookie,会话cookie确切的说是存放在客户端浏览器的内存中,所以说他的生命周期和浏览器是一致的,浏览器关了会话cookie也就消失了,然而持久化cookie是存放在客户端硬盘中,而持久化cookie的生命周期就是我们在设置cookie时候设置的那个保存时间,然后我们考虑一问题当浏览器关闭时session会不会丢失,从上面叙述分析session的信息是通过sessionid获取的,而sessionid是存放在会话cookie当中的,当浏览器关闭的时候会话cookie消失所以我们的sessionid也就消失了,但是session的信息还存在服务器端,这时我们只是查不到所谓的session但它并不是不存在。那么,session在什么情况下丢失,就是在服务器关闭的时候,或者是sessio过期,再或者调用了invalidate()的或者是我们想要session中的某一条数据消失调用session.removeAttribute()方法,然后session在什么时候被创建呢,确切的说是通过调用session.getsession来创建,这就是session与cookie的区别
三十四、字节流与字符流的区别
stream结尾都是字节流,reader和writer结尾都是字符流
两者的区别就是读写的时候一个是按字节读写,一个是按字符。
实际使用通常差不多。
在读写文件需要对内容按行处理,比如比较特定字符,处理某一行数据的时候一般会选择字符流。
只是读写文件,和文件内容无关的,一般选择字节流。
字节流继承于InputStream OutputStream,
字节流在操作时本身不会用到缓冲区(内存),是文件本身直接操作的。(字节流读取单位为byte,byte作为计算机存储最基本单位,可以用字节流来读取很多其他格式的文件,比如图片,视频等等。基于B/S和C/S的文件传输都可以采用字节流的形式。)
字符流继承于InputStreamReader OutputStreamWriter。
字符流在操作时使用了缓冲区,通过缓冲区再操作文件(字符流主要是读取文本文件内容的,可以一个字符一个字符的读取,也可以一行一行的读取文本文件内容)
三十五、final,finally,finalize 三者区别
Final是一个修饰符:
当final修饰一个变量的时候,变量变成一个常量,它不能被二次赋值
当final修饰的变量为静态变量(即由static修饰)时,必须在声明这个变 量的时候给它赋值
当final修饰方法时,该方法不能被重写
当final修饰类时,该类不能被继承
Final不能修饰抽象类,因为抽象类中会有需要子类实现的抽 象方法,(抽 象类中可以有抽象方法,也可以有普通方法,当一个抽象类中没有抽象方 法时,这个抽象类也就没有了它存在的必要)
Final不能修饰接口,因为接口中有需要其实现类来实现的方法
Finally:
Finally只能与try/catch语句结合使用,finally语句块中的语句一定会执行, 并且会在return,continue,break关键字之前执行
finalize:
Finalize是一个方法,属于java.lang.Object类,finalize()方法是GC (garbage collector垃圾回收)运行机制的一部分,finalize()方法是在 GC清理它所从 属的对象时被调用的
三十六、Io流的层次结构
从流的方向
输入流 输出流
从流的类型上
字符流 字节流
inputstream和outputstream都是抽象类
它们下面的实现包括
FileInputStream,BufferedInputStream
FileOutputStream,BufferedOutputStream
reader 和 writer
FileReader,BufferedReader,StringReader
FileWriter,BufferedWriter,StringWriter,PrintWriter
三十七、JAVA:
Java是面向对象的,跨平台的,它通过java虚拟机来进行跨平台操作,它可以进行自动垃圾回收的【c语言是通过人工进行垃圾回收】,java还会进行自动分配内存。【c语言是通过指定进行分配内存的】,只需要new一个对象,这个对象占用了多少空间,不需要我们来管,java虚拟机负责管这些,用完之后也不需要我们来释放,java虚拟机会自动释放
三十八、JavaSE JavaEE JavaME区别
是什么:
Java SE=Java Standard Edition=j2se = java 标准版
Java EE=Java Enterprise Edition=j2ee= java 企业版
Java ME=Java Mobile Edition=j2me = java移动版
特点:
SE主要用于桌面程序(swing),控制台开发(main程序)。
EE企业级开发(JSP,EJB,Spring MVC,Struts,hibernate,ibatis等),
用于企业级软件开发,网络开发,web开发。
ME嵌入式开发(手机,小家电,PDA)。[苹果的ios,黑莓]
三者之间的关系:
Java SE(Java Platform, Standard Edition,Java标准版)就是基于JDK和JRE的。
Java SE为Java EE提供了基础。
Java EE除了基于我们这个所谓的Java SE外,还新加了企业应用所需的类库
三十九、JDK JRE JVM的区别:
Jdk【Java Development ToolKit】就是java开发工具箱, JDK是整个JAVA的核心里边包含了jre,它除了包含jre之外还包含了一些javac的工具类,把java源文件编译成class文件,java命令是用来运行这个程序的,除此之外,里边还包含了java源生的API,java.lang.integer在rt的jar包里边【可以在项目中看到】,通过rt这个jar包来调用我们的这些io流写入写出等
JDK有以下三种版本:
J2SE,standard edition,标准版,是我们通常用的一个版本
J2EE,enterpsise edtion,企业版,使用这种JDK开发J2EE应用程序
J2ME,micro edtion,主要用于移动设备、嵌入式设备上的java应用程序
Jre【Java Runtime Enviromental】是java运行时环境,那么所谓的java运行时环境,就是为了保证java程序能够运行时,所必备的一基础环境,也就是它只是保证java程序运行的,不能用来开发,而jdk才是用来开发的,所有的Java程序都要在JRE下才能运行。
包括JVM和JAVA核心类库和支持文件。与JDK相比,它不包含开发工具——编译器、调试器和其它工具。
Jre里边包含jvm
Jvm:【Java Virtual Mechinal】因为jre是java运行时环境,java运行靠什么运行,而底层就是依赖于jvm,即java虚拟机,java虚拟机用来加载类文件,java中之所以有跨平台的作用,就是因为我们的jvm
关系:
J2se是基于jdk和jre,
JDK是整个JAVA的核心里边包含了jre,
Jre里边包含jvm
四十、报错的状态码:
301 永久重定向
302 临时重定向
304 服务端 未改变
403 访问无权限
200 正常
404 路径
500 内部错误
四十一、协议以及默认的端口号
ftp 21 文件传输协议
Pop3 110 它是因特网 <http://baike.baidu.com/view/1706.htm>电子邮件 <http://baike.baidu.com/view/1524.htm>的第一个离线 <http://baike.baidu.com/view/113466.htm>协议标准
Smtp 25 简单邮件传输协议
http 80 超文本传输协议
oracle 默认端口号1521
mysql默认端口号 3306
Mongodb 默认端口号 27017
Redis 默认端口号 6379
Svn 默认端口号 443
四十二、抽象类与接口的区别
1.一个类只能进行单继承,但可以实现多个接口。
2.有抽象方法的类一定是抽象类,但是抽象类里面不一定有抽象方法;
接口里面所有的方法的默认修饰符为public abstract,接口里的成员变量默认的修饰符为 pulbic static final。
关系
接口和接口 继承
接口和抽象类 抽象类实现接口
类和抽象类 类继承抽象类
类和类 继承
四十三、修饰符的作用
修饰符的作用范围:
private default protected public
同一个类中 可以 可以 可以 可以
同一个包的类中 可以 可以 可以
不同包的子类中 可以 可以
不同包的类中 可以
四十四、onready和onload的区别
1.onready比onload先执行
2.onready是在页面解析完成之后执行,而onload是在页面所有元素加载后执行
3.onload只执行最后一个而onready可以执行多个。
参考:
- 执行时间 window.onload必须等到页面内包括图片的所有元素加载完毕后才能执行。 $(document).ready()是DOM结构绘制完毕后就执行,不必等到加载完毕。 2.编写个数不同 window.onload不能同时编写多个,如果有多个window.onload方法,只会执行一个 $(document).ready()可以同时编写多个,并且都可以得到执行 3.简化写法 window.onload没有简化写法 $(document).ready(function(){})可以简写成$(function(){}); 另外,需要注意一点,由于在$(document).ready() 方法内注册的事件,只要DOM 就绪就会被执行,因此可能此时元素的关联文件未下载完。例如与图片有关的html 下载完毕,并且已经解析为DOM 树了,但很有可能图片还没有加载完毕,所以例如图片的高度和宽度这样的属性此时不一定有效。要解决这个问题,可以使用Jquery 中另一个关于页面加载的方法---load() 方法。Load() 方法会在元素的onload 事件中绑定一个处理函数。如果处理函数绑定给window 对象,则会在所有内容( 包括窗口、框架、对象和图像等) 加载完毕后触发,如果处理函数绑定在元素上,则会在元素的内容加载完毕后触发。 Jquery 代码如下: $(window).load(function (){ // 编写代码 });等价于JavaScript 中的以下代码 Window.onload = function (){ // 编写代码 }
四十五、switch默认接受的几种数据类型
Short, int, byte, char, String(jdk版本1.7以上),enum
四十六、request 跟session的区别
1.他们的生命周期不同,
request对应的是一次请求,request是获取信息,通过用户提交的表单,查询字符串
session对应的是一次会话,session是服务端用来保存一些数据
2.request占用资源比较少,安全性比较高,相对来说缺乏持续性,
而session资源消耗比较大,安全性相对比较低,所以通常使用request来保存信息
四十七、找到解决svn冲突方法
对于svn冲突,可以采用手工处理将冲突的部分进行整合,
之后备份最新整合后的文件,采用覆盖更新的方式处理完
冲突之后,再把最新整合后的文件进行提交。
四十八、反射的描述
通过字符串可以动态创建java对象,并且可以动态访问方法,
属性等。
我们在项目中的时候封装过数据库jdbc的持久层,
其中就利用反射这项
技术来达到通用
和灵活的目的。
框架篇
- Struts2的运行原理
1、tomcat 启动的时候会加载 web.xml 、核心控制器 (老版)FilterDispatcher、(新版)StrutsPrepareAndExecuteFilter会加载并解析 struts.xml
2、客户端会发送一个请求到 action 、StrutsPrepareAndExecuteFilter(新版) 会根据后缀名进行拦截
3、FilterDispatcher根据 struts.xml 的配置文件信息 找到 某个action 对应的某个类里的指定方法
4、执行相关的业务逻辑最后返回 一个String
5、<action/> 里配置 <result/> name的属性值与返回的String 进行匹配,跳转到指定的jsp 页面
- struts2的体系结构
1、客户端向Servlet容器(例如Tomcat)发送一个请求;
2、这个请求经过一系列的过滤器(Filter);
3、接着FilterDispatcher被调用,FilterDispatcher询问ActionMapper来决定这个请求是否需要调用某个Action;
4、如果ActionMapper决定需要调用某个Action,FilterDispatcher把请求的处理交给ActionProxy;
5、ActionProxy通过Configuration Manager询问框架的配置文件,找到需要调用的Action类;
6、ActionProxy创建一个ActionInvocation的实例。
7、ActionInvocation在调用Action的过程前后,涉及到相关拦截器(Intercepter)的调用。
8、一旦Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果。返回结果通常是jsp或者FreeMarker的模版。(体系结构图见下一页)
- Spring MVC运行原理
整个处理过程从一个HTTP请求开始:
1.Tomcat在启动时加载解析web.xml,找到spring mvc的前端总控制器DispatcherServlet,并且通过DispatcherServlet来加载相关的配置文件信息。
2.DispatcherServlet接收到客户端请求,找到对应HandlerMapping,根据映射规则,找到对应的处理器(Handler)。
3.调用相应处理器中的处理方法,处理该请求后,会返回一个ModelAndView。
4.DispatcherServlet根据得到的ModelAndView中的视图对象,找到一个合适的ViewResolver(视图解析器),根据视图解析器的配置,DispatcherServlet将要显示的数据传给对应的视图,最后显示给用户。
1、Springmvc的核心原理 以及常见注解
1.1核心原理
- 用户发送请求给服务器。url:user.do
- 服务器收到请求。发现Dispatchservlet可以处理。于是调用DispatchServlet。
- DispatchServlet内部,通过HandleMapping检查这个url有没有对应的Controller。如果有,则调用Controller。
4、 Control开始执行
- Controller执行完毕后,如果返回字符串,则ViewResolver将字符串转化成相应的视图对象;如果返回ModelAndView对象,该对象本身就包含了视图对象信息。
- DispatchServlet将执视图对象中的数据,输出给服务器。
- 服务器将数据输出给客户端。
1.2、springmvc常见注解:
@Controller控制器定义 ,和Struts1一样,Spring的Controller是Singleton的。这就意味着会被多个请求线程共享。因此,我们将控制器设计成无状态类。
@Autowired按照byType的方式进行注入
@Service声明当前是业务处理层
@Repository声明当前是持久层
@Entity声明当前类是一个实体类
@Table对应实体类的表名
@Id声明主键
@GeneratedValue主键生成策略
@Column设置对应的列名
@RequestMapping 在类前面定义,则将url和类绑定。在方法前面定义,则将url和类的方法绑定
@RequestParam 一般用于将指定的请求参数付给方法中形参
@SessionAttributes 将ModelMap中指定的属性放到session中
@ModelAttribute 这个注解可以跟@SessionAttributes配合在一起用。可以将ModelMap中属性的值通过该注解自动赋给指定变量。
- Struts1.x与Struts2.x的区别
Struts 2以WebWork为核心,
采用拦截器的机制来处理用户的请求,struts1严重依赖于servletAPI,
属于侵入性框架,struts2不严重依赖于servletAPI,属于非侵入型框架。
线程模型方面:
Struts1的Action是单实例的,
一个Action的实例处理所有的请求。
Struts2的Action是一个请求对应一个实例(每次请求时都新new出一个对象),
没有线程安全方面的问题
封装请求参数:
Struts1中强制使用ActionForm对象封装请求的参数。
Struts2可以选择使用POJO类来封装请求的参数,或者直接使用Action的属性。
struts1的前端总控制器(核心总控制器)为ActionServlet,
struts2的前端总控制器(核心总控制器)为FilterDispather(旧版)、StrutsPrepareAndExecuteFilter(新版)
- Spring MVC、struts1和struts2区别
1.spring mvc 单例 非线程安全
struts2线程安全对每个请求都产生一个实例
2.spring mvc的入口是servlet,而struts2是filter
spring 的前端总控制器为 DispatcherServlet
struts2 的前端总控制器为 FilterDispatcher
3. 参数传递:struts是在接受参数的时候,
可以用属性来接受参数,这就说明参数是让多个方法共享的。
springmvc 用方法来接受参数
4.spring mvc是基于方法的设计,而sturts是基于类
- Struts2中result中的type类型
1.dispatcher:它是默认的,用来转向页面,通常处理JSP
2.redirect:将用户重定向到一个已配置好的URL
3.redirectAction:将用户重定向到一个已定义好的action
4.chain:将action和另外一个action链接起来
5.freemarker:呈现Freemarker模板
6.httpheader:返回一个已配置好的HTTP头信息响应
7.stream:向浏览器发送InputSream对象对下载的内容和图片非常有用
8.velocity:呈现Velocity模板
9.xslt :该XML可以通过XSL模板进行转换
10.plaintext:显示原始文件内容,例如文件源代码
- Struts2标签
首先需要引用 <%@taglib prefix="s" uri="/struts-tags"%>
1.<s:if></s:if> 判断标签 后面可跟 <s:else>
2.<s:iterator> </s:iterator> 迭代标签
3.<s:include></s:include> 引入标签 可以把一个JSP页面或者servlet引入一 个页面中
4.<s:property></s:property> 输出标签
5.<s:set></s:set> 标签赋予变量一个特定范围内的值
6.<s:form></s:form> 表单标签
7.<s:testarea></s:textarea> 文本域标签
8.<s:select></s:select> 下拉标签
9.<s:url></s:url> 声明一个url的路径
最常用的是:
判断<s:if></s:if>、<s:elseif></s:elseif>、<s:else></s:else>
循环<s:iterator></s:iterator>
输出<s:property></s:property>
- SSI整合
1、Action继承于Actionsupport
2、引入struts-spring-plugin.jar包,从而完成struts和spring的整合
3、在struts2的action中注入service,保证service的名字和配置文件中的一致, 并生成get,set方法
4、Dao层继承于SqlMapClientDaoSupport
5、在dao层的配置文件中注入sqlMapClient
- SSH整合
1.首先在web.xml中通过ContextLoaderListener来融入spring,
并加载spring的相关配置文件
2.同样配置sturts2的前端总控制器filterDispatcher来过滤相关的
请求并且加载struts.xml
3.action继承ActionSupport,然后通过引入struts-spring-plugin.jar
包并且根据配置文件中service的id生成get,set方法来注入service层。
4.dao层继承于HibernateDaoSupport,并且在dao的配置文件中注入sessionFactory.
5.通过spring中的配置文件加载hibernate.cfg.xml文件从而融入hibernate.
在ssh框架中是怎么整合spring?
首先在web.xml中通过ContextLoaderListener来融入spring,
并加载spring的相关配置文件
在ssh框架中是怎么整合hibernate?
通过spring中的配置文件加载hibernate.cfg.xml文件从而融入hibernate
dao层继承于HibernateDaoSupport,并且在dao的配置文件中注入sessionFactory
在ssh框架中是怎么整合struts2?
配置sturts2的前端总控制器filterDispatcher来过滤相关的
请求并且加载struts.xml
- Spring MVC整合
1.首先,要在web.xml里面配置SpringMVC的核心控制器,DispatcherServlet,对指定的后缀请求进行拦截。
2.Controller层要加 @Controller注解,表明该类是MVC的控制层。
3.创建Service接口,给实现类加上注解 @Component或者 @Service 表明这是Service业务处理层
4.在Controller层声明Service变量(属性),给变量(属性) 加上 @Autowired注解,通过自动绑定机制将Service注入到Controller。 (注:@Autowired默认是ByType,如果想根据属性名注入,那么就再加上注解 @Resource(name="属性名"))
5.在Controller层的方法上加上注解 @RequestMapping("requestAddress") 表明该方法的请求地址
6.Dao层要加上注解 @Repository 表明这是数据库持久层
7.同样将dao实例注入到service层中。
8.配置视图解析器 "InternalResourceViewResolver",对处理后的跳转进行统一配置。
十一、Hibernate 中get 和 load的区别
加载方式:
load为延迟加载(返回的是一个只有id属性的代理,只有使用该对象属性时,才 发出sql语句);
get为立即加载(执行时,会立即向数据库发出sql语句)
返回结果:
load检索不到记录时,会抛ObjectNotFoundException异常
get检索不到记录时,会返回null
- Hibernate、Ibatis、Jdbc三者的区别
Hibernate属于全自动, Ibatis属于半自动,Jdbc属于手动,从开发效率上讲hibernate较高,ibatis居中,jdbc较低,从执行效率上讲hibernate较低,ibatis居中,jdbc较高,因为jdbc是手工写sql语句,程序员对sql的控制能力更大,可以根据业务需要进行优化,而ibatis虽然也可以对sql进行优化,但是他里面将resultset封装为实体的过程中采用了反射机制所以一定程度上影响了性能,而hibernate因为高度封装所以开发效率相对较高,但正因为这个原因,所以程序员在对sql语句的控制和优化方面相对比较弱,而且在将resultset封装成实体的过程中也采用了反射机制,所以在性能方面较低
- Hibernate的运行原理
首先通过configuration去加载hibernate.cfg.xml这个配置文件,根据
配置文件的信息去创建sessionFactory,sessionFactory是线程安全的,
是一个session工厂,用来创建session,session是线程不安全的,相当于
jdbc的connection,最后通过session去进行数据库的各种操作,在进行操作
的时候通过transaction进行事务的控制。
- Hibernate五大核心(类/接口)简述
1 .Configuration接口的作用是对Hibernate进行配置,以及对它进行启动。(加载 hibernate.cfg.xml)并创建一个SessionFactory对象。
2 .SessionFactory接口
SessionFactory接口负责初始化Hibernate。它充当数据存储源的代理,并负责创建 Session对象。SessionFactory是线程安全的。
3 .Session接口
Session(会话)接口是Hibernate应用使用的主要接口。Session接口负责执行被持久化对象的CRUD操作(增删改查)。Session对象是非线程安全的。Session 相当于jdbc的connection
4 .Query与Criteria接口
总之Query和Criteria接口负责执行各种数据库查询。
5 .Transaction接口
Transaction(事务)负责操作相关的事务。
十六、Hibernate中的两大配置文件
*.hbm.xml:主键生成策略,映射关系,一对多,一对一的关系。
Hibernate.cfg.xml:方言(用哪个数据库),数据库连接信息,包含*.hbm.xml内容,映射 文件,也可以配事务。
- Hibernate事务处理
开启事务 session.beginTransaction();
执行相关的操作,如果成功则session.getTransaction().commit();
执行操作失败则 session.getTransaction.rollback();
- Hibernate的三种状态以及状态的转换
Transient(临时)
new 一个初始化对象后,并没有在数据库里保存数据,处于临时状态;
Persistent(持久化)
当执行save()方法,调用session.close()方法之前,内存中的对象与数据库有 对应关系处于持久化状态;
Detached(托管/游离)
当执行session.close()之后,处于托管状态;
状态的转换
处于托管状态下,调用update()方法后,转换为持久化状态;
在持久化状态下,执行delete()方法后,转换为临时状态;
在未初始化对象之前,调用get(),load(),find(),iterate()之后,直接进入持久化 状态。
- 分页步骤
①前台封装一个显示分页的组件
②查询总条数
③后台封装分页工具类,计算开始位置、结束位置、总页数
④后台写支持分页的sql语句
⑤前台包含分页组件,实现分页效果
注意:
查询总条数的where和查询列表信息的where条件要保证一致。
二十、hibernate缓存概述
hibernate分为一级缓存即session缓存也叫事务级别的缓存以及
二级缓存sessionFactory即应用级别的缓存,还有查询缓存即三级缓存.
一级缓存的生命周期和session的生命周期保持一致,
hibernate默认就启用了一级缓存,
不能将其关闭,可以通过session.clear()和session.evict(object)来管理一级缓存。其中get,load,iterate都会使用一级缓存,一级缓存缓存的是对象。
二级缓存的生命周期和sessionFactory的生命周期保持一致,可以跨session,被多个session共享,hibernate3默认开启二级缓存,也可以手动开启并指定缓存插件如ehcache,oscache
等。二级缓存也只能缓存对象。
三级缓存也叫查询缓存,查询缓存是针对普通属性结果集的缓存,
对实体对象的结果集只缓存id。对query.list()起作用,query.iterate不起作用,也就是query.iterate不使用查询缓存
二十一、Ssh的概述:
ssh是web开发中常见的一种框架
s-struts
s-spring
h-hibernate
其中struts在框架中充当控制器,实现MVC,主要用来处理用户的请求,和跳转页面。使项目结构清晰,开发者只需要关注业务逻辑的实现即可。
spring在ssh充当粘合剂,粘合struts-sping-hibernate,主要用来进行事物的控制,
hibernate-充当数据库持久层,主要用它来与数据库交互,提高开发效率,减轻程序员sql控制要求,而且hibernate通过反射机制,有灵活的映射性,还支持各种关系,一对一,一对多,多对多。
在进行ssh整合的时候,我们应该注意:
1. Action继承于ActionSupport
引入struts-spring-plugin.jar包,从而完成struts和spring的整合
在struts2的action中注入service,保证service的名字和配置文件中的一致,并生成get,set方法
Dao层继承于hibernateDaoSupport
在dao层的配置文件中注入sessionFactory
二十二、防止表单重复提交
针对于重复提交的整体解决方案:
1.用redirect来解决重复提交的问题
2.点击一次之后,按钮失效
3.通过loading
4.自定义重复提交过滤器
5.解决struts2重复提交
可以结合s:token标签来解决重复提交问题
利用token的原理:
1.在前端的jsp页面中加入s:token标签,在访问该页面时就会生成
隐藏域,该隐藏域中包含一个随机生成的字符串,并把该字符串
存入session中
2.在struts2的配置文件中加入token拦截器后,当正常访问action
的时候,会从session中取出该字符串,然后和页面隐藏域中提交
字符串做对比,如果一致则正常执行并删除session中存储的字符串。
二十三、JSP标签:
1.JSP include动作
jsp:include 动作
以“<jsp: 动作名 ” 开始,以“</jsp:动作名> ” 结束
比如:<jsp:include page=" Filename" />
2.JSP指令:<%@ include%><%@ %>
以“<%@ ” 开始,以“%> ” 结束。比如:
<%@ include file = " Filename" %>
3.JSP输出表达式:<%= %><%=Java表达式 %>
输出变量的值,后边不能加<%= ; %>
4.JSP Scriptlet【脚本】:<% ;%> <% Java 代码 %>
例子:
<% Calendar now = Calendar.getInstance(); %>
5.JSP声明:<%! %> <%! 函数或者方法 %>
例子:
<%!
String getHello(String name) {
return "Hi," + name + "!";
}
%>
6.迭代标签:<c:foreach>
Jstl中的核心标签(core)
7.JSP注释:
<!-- 这是注释,但客户端可以查看到 -->
<%-- 这也是注释,但客户端不能查看到 --%>
- el表达式:${}
- jsp:include动作是在运行时动态包含。
@include指令是在编译时包含。
它们两个都只能包含本项目的相关文件,不能包含其他项目的。
如果要包含其他项目的文件可以使用c:import
二十四、过滤器
filter的概述:
filter是一个过滤器,用来在请求前和响应后进行数据的处理。
filter的生命周期是:
实例化--->初始化(init)-->进行过滤(doFilter)--->销毁(destroy)-->释放资源
一个Filter必须实现javax.servlet.Filter接口
在项目中我们通常通过filter进行编码转换,
进行安全验证,进行重复提交的判断。
了解(不需要主动说)
filter 相当于 拦截器 相当于Spring AOP
servlet+jsp+javabean+jdbc+filter
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.leopard.filter.EncodingFilter</filter-class>
<init-param>
<param-name>encode</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>*</url-pattern>
</filter-mapping>
二十五、拦截器的理解
Struts2的Interceptor,其拦截的对象是Action代码,可以定义在Action代码之前或者之后执行拦截器的代码。
在项目中,我们经常用来拦截非正常的访问,
Struts2的拦截器和Servlet过滤器类似。在执行Action的execute方法之前,Struts2会首先执行在struts.xml中引用的拦截器,在执行完所有引用的拦截器的intercept方法后,会执行Action的execute方法。
其中intercept方法是拦截器的核心方法,所有安装的拦截器都会调用之个方法。在Struts2中已经在struts-default.xml中预定义了一些自带的拦截器,如timer、params等。如果在<package>标签中继承struts-default,则当前package就会自动拥有struts-default.xml中的所有配置。代码如下:
<package name="demo" extends="struts-default" > ... </package>
拦截器是Struts2框架的核心,它主要完成解析请求参数、将请求参数赋值给Action属性、执行数据校验、文件上传等工作
在struts-default.xml中有一个默认的引用,在默认情况下(也就是<action>中未引用拦截器时)会自动引用一些拦截器。struts2中默认的拦截器是defaultStack.
自定义拦截器需要特别注意的是不要忘记引入struts2默认的拦截器。为了实现某些操作,我们可以自定义拦截器,
自定义拦截器有三种方式定义。分别为实现Interceptor接口,继承抽象类AbstractInterceptor,继承MethodFilterInteceptor类。
拦截器在项目中的运用:
同时可以减轻代码冗余,提高重用率。
如果要求用户密码、权限等的验证,就可以用自定义的拦截器进行密码验证和权限限制。对符合的登入者才跳转到正确页面。
二十六、Spring融入框架
我们通过在web.xml中配置ContextLoaderListener这个监听器也加载
spring的配置文件,从而融入到项目框架中。
二十七、项目的部署方式
1、如果项目单独部署到tomcat中的时候,应该看tomcat中的server.xml;
2、如果和eclipse结合使用进行项目部署的时候,应该看eclipse里面的server.xml.
数据库篇
- JDBC连接数据库步骤(以MYSQL为例)
1、加载JDBC驱动程序:
通过Class类的forName方法实现,并将驱动地址放进去
成功加载后,会将Driver类的实例注册到DriverManager类中。
2、提供JDBC连接的URL 、创建数据库的连接
•要连接数据库,需要向java.sql.DriverManager请求并获得Connection对象,
该对象就代表一个数据库的连接。
•使用DriverManager的getConnectin()方法传入指定的欲连接的数据库的路径、数 据库的用户名和密码。
Connection con=DriverManager.getConnection(url , username , password);
&&&:"jdbc:mysql://localhost/test?user=root&password=123&useUnicode=true&characterEncoding=utf-8”;
3、创建一个Statement
•要执行SQL语句,必须获得java.sql.Statement实例
•执行静态SQL语句。通常通过Statement实例实现。
•执行动态SQL语句。通常通过PreparedStatement实例实现。
String sql = “”;
Statement st = con.createStatement() ;
PreparedStatement pst = con.prepareStatement(sql) ;
4、执行SQL语句
Statement接口提供了executeQuery、executeUpdate、execute三种方法
executeQuery:执行select语句,返回ResultSet结果集
ResultSet rst = pst.executeQuery();
• executeUpdate:执行insert、update、delete语句
pst.executeUpdate();
5、关闭JDBC对象
操作完成以后要把所有使用的JDBC对象全都关闭,以释放JDBC资源。
- 数据库连接池
数据库连接池的优点运行原理:
在我们不使用数据库连接池的时候,每次访问数据库都需要创建连接,
使用完成之后需要释放关闭连接,而这样是很耗费资源的。当我们使用
数据库连接池的时候,在tomcat启动的时候就创建了指定数量的连接,
之后当我们程序使用的时候就直接从连接池里面取,而不需要创建,同理,
当我们使用完的时候也不需要关闭连接,而是将连接返回到连接池中,供
其他请求继续使用。
DBCP:比较稳定。
C3P0: 性能比较高。
- mysql的数据库导入导出
配置:
首先找到mysql的安装目录,进入bin目录下复制路径
将mysql的bin目录粘贴在计算机环境变量的path中
授权:
登录mysql
将某张表的某个权限赋给某个用户
grant [select,insert,update,delete,create,drop] on [databaseName].[tableName] to [userName]@[userIP] identified by [‘连接口令’]
grant select,insert,update,delete,create,drop on oa_ssh.user to root@[IP] identified by 'root';
将所有库的所有权限赋给某个用户
grant all privileges on *.* to [userName]@[userIp] identified by [‘连接口令’]
grant all privileges on *.* to root@[IP] identified by ‘root';
将所有库的所有权限赋给所有用户
grant all privileges on *.* to root@'%' identified by ‘root’;
导出本地数据库:
mysqldump -u 用户名 -p 数据库名 > 磁盘:导出的文件名(加后缀)
远程导出数据库:
mysqldump -h IP -u 用户名 -p 数据库名称 >导出的文件名(加后缀)
远程导出数据表:
mysqldump -u root -p -d --add-drop-table 数据库名称 > 导出文件
名(加后缀)
导入数据:
mysql -u root -p登录成功后 ==》 source 磁盘:导入的文件名(加后缀)
- jdbc分段批量提交的时候出现异常怎么处理?
通过Map来解决性能问题。首先在分段批量提交的时候,我们不采用事务,这样就保证了合法的数据就自动提交,不合法的数据就自己自动进行回滚,为了避免不合法数据影响后续合法数据的提交,采用定义业务规则字典表,实现对数据的验证,将不合法的数据记录下来,供用户进行后续处理,而合法的数据就全部提交。
- jdbc批量处理数据
批量处理数据:(代码优化:提高程序执行性能)
降低了java程序代码(客户端)和数据库之间的 网络通信的次数。
在jdbc中进行批量插入的核心API为 addBatch,executeBatch
大数据量的插入问题:(jdbc,hibernate,ibatis)
1.每次只插入一条和数据库交互多次(很耗时间)
2.批量插入和数据库只交互一次(内存溢出)
3.分段批量插入(推荐)
jdbc批量处理数据是通过PreparedStatement对象的 addbatch(), executebatch() clearbatch()进行和数据库的交互。通常我们使用分段批量处理的方式 这样可以提高程序的性能 ,防止内存溢出。
1.每个sql语句都和数据库交互一次(非批量操作)
2.只和数据库交互一次(批量操作)(内存溢出)
当数据达到一定额度的时候就和数据库进行交互,分多次进行(分段批量操作)
(500或者1000)
pst.addBatch();
if (i > 0 && i%1000 == 0) {
pst.executeBatch();
pst.clearBatch();
}
- Oracle分页
select * from (select * from (select s.*,rownum rn from student s ) where rn<=5) where rn>0
- Oracle的基本数据类型
Oracle的基本数据类型(常用):
1、字符型
Char 固定长度字符串 占2000个字节
Varchar2 可变长度字符串 占4000个字节
Nvarchar2 占2000个字符(最多能存2000个字母/中文)
2、大对象型(lob)
Blob :二进制数据 最大长度4G
Blob 用于存一些图片,视频,文件。
比如:当我们在进行文件上传时,我们一般把上传的文件存在硬盘上,可以不占用 数据库,如果项目迁移时,文件也要跟着迁移。因此我们可以把用blob把它存在数据库中。但这样也增加了数据库的负担。
Clob :字符数据 最大长度4G,可以存大字符串 varchar2和nvarchar2都具有一定的局限性,它们长度有限,但数据库中无论用varchar2或nvarchar2类型,还是用clob,在java端都使用String接收。
3、数值型
Integer 整数类型,小的整数。
Float 浮点数类型。
Real 实数类型。
Number(p,s)包含小数位的数值类型。P表示精度,s表示小数后的位数。
Eg: number(10,2) 表示小数点之前可有8位数字,小数点后有2位。
4、日期类型
Date 日期(日-月-年) DD-MM-YY(HH-MI-SS)
Timestamp 跟date比 它可以精确到微秒。精确范围0~9 默认为6.
- id、rowid、rownum的区别
rowid物理位置的唯一标识。
而id是逻辑上的唯一标识,所以rowid查找速度要快于id,是目前最快的
定位一条记录的方式
rowid和rownum都是"伪数列"
所谓“伪数列”也就是默认隐藏的一个数列。
rownum用于标记结果集中结果顺序的一个字段,
它的特点是按顺序标记,而且是连续的,
换句话说就是只有有rownum = 1的记录,才可能有rownum = 2的记录。
rownum关键字只能和<或者<=直接关联
如果是>或者=则需要给他起个别名
- 主键和唯一索引的区别?
在创建主键的同时会生成对应的唯一索引,主键在保证数据唯一性的同时不允许为空,而唯一可以有一个为空数据项,一个表中只能有一个主键,但是一个主键可以 有多个字段,一个表中可以有多个唯一索引。
- Preparedstatement和statement的区别
用Prepared statement进行开发。Prepared statement是预编译的,而statement不是,在每次执行sql语句的增删改时,如果是一条数据两者没差距,但如果数据量大于1,那么每次执行sql语句statement都要重新编译一次,而Prepared statement不用,Prepared statement的运行效率大于statement;从代码的可维护性和可读性来说,虽然用Prepared statement来代替statement会使代码多出几行,但这样的代码无论从可读性还是可维护性来说,都比直接使用statement的代码高很多档次;最重要的一点,从安全角度来说,使用Prepared statement可以大大提高程序的安全性,因为Prepared statement是用‘?’传参,可以防止sql注入,具有安全性,而statement用的是‘+’字符串拼接,安全性较低。
- 数据库三范式
第一范式:数据库表中的所有字段值都是不可分解的原子值。
第二范式:需要确保数据库表中的每一列都和主键相关,而不能只与主键的某一部 分相关(主要针对联合主键而言)
第三范式:需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关
- 视图概述
视图可以视为“虚拟表”或“存储的查询”
创建视图所依据的表称为“基表”
视图的优点:
提供了另外一种级别的表安全性:隐藏了一些关键的字段
简化的用户的SQL命令
隔离基表结构的改变
- 存储过程概述
存储过程(Stored Procedure)
可以包含逻辑判断的sql语句集合。
是经过预编译,存在于数据库中。
通过调用指定存储过程的名字(可有参,可无参)来执行。
优点:
简化了复杂的业务逻辑,根据需要可重复使用
屏蔽了底层细节,不暴露表信息即可完成操作
降低网络的通信量,多条语句可以封装成一个存储过程来执行
设置访问权限来提高安全性
提高执行效率,因为它是预编译以及存储在数据库中
缺点:
可移植性差,相同的存储过程并不能跨多个数据库进行操作
大量使用存储过程后,首先会使服务器压力增大,而且维护难度逐渐增加
存储过程的语法:
--下面是在oracle数据库下最基本的语法
--仅创建一个名为testProcedure 的无参的存储过程
--IS也可以是AS
--如果已经存在名为 testProcedure 的存储过程,下面的语法会出现 名称已被使用的错误
--解决办法:
--第一句可以写成 create or replace procedure testProcedure
--这样会替换原有的存储过程
--NULL表示任何可以正确执行的sql 语句,但至少一句
create procedure testProcedure
IS
BEGIN
NULL
END;
存储过程的参数的分类:
IN
OUT
INOUT
注意:
存储过程之间可相互调用
存储过程一般修改后,立即生效。
- 索引概述
1、索引的概念
索引就是为了提高数据的检索速度。
数据库的索引类似于书籍的索引。
在书籍中,索引允许用户不必翻阅完整个书就能迅速地找到所需要的信息。
在数据库中,索引也允许数据库程序迅速地找到表中的数据,
而不必扫描整个数据库.
2、索引的优点
1.创建唯一性索引,保证数据库表中每一行数据的唯一性
2.大大加快数据的检索速度,这也是创建索引的最主要的原因
3.减少磁盘IO(向字典一样可以直接定位)
3、索引的缺点
1.创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加
2.索引需要占用额外的物理空间
3.当对表中的数据进行增加、删除和修改的时候,
索引也要动态的维护,降低了数据的维护速度
4、索引的分类
1.普通索引和唯一性索引
普通索引:CREATE INDEX mycolumn_index ON mytable (myclumn)
唯一性索引:保证在索引列中的全部数据是唯一的
CREATE unique INDEX mycolumn_index ON mytable (myclumn)
2. 单个索引和复合索引
单个索引:对单个字段建立索引
复合索引:又叫组合索引,在索引建立语句中同时包含多个字段名,
最多16个字段
CREATE INDEX name_index ON userInfo(firstname,lastname)
3.顺序索引,散列索引,位图索引
- 必背的sql语句
1:oracle 分页
select * from (select t.*, rownum rn from (select * from menu order by id desc) t where rownum < 10) where rn >=5
2: mysql 分页
select * from music where id limi
3:oracle中如何快速将一张表的数据复制到另外一张表中(另外一张表不存在,另外一张 表存在,但数据为空)
- .不存在另一张表时:(表的备份)
create table 新表 as select * from 将要复制的表
- 存在另一张表时:(批量插入)
insert into 新表名 select 字段 from 将要复制的表名
4:音乐专辑
查询出special <app:ds:special>表中的id 专辑名 并下面有多少首歌曲
Select s.id , min(s.sname),count(m.mid) from special s inner
join ms m on s.id=m.id group by s.id
5:快速删除一张表(不可事物回滚,也就是没有日志记录)
TRUNCATE from 表名
6:inner join
select 查找信息 from 表名 1 inner join 表名2 on 表名1.列名 = 表名2.列名
7:left join
左外连接 select 查找信息 from 表名1 left join 表名2 on 表名1.列名 = 表名2.列名
8:right join
右外连接 select 查找信息 from 表名1 right join 表名2 on 表名1.列名 = 表名2.列名
9:oracle中查询遍历树形结构(start with)
select * from extmenu
start with pid=1
connect by prior id = pid
快速删除父节点以及父节点下的所有节点:
Delete from extmenu where id in (
elect * from extmenu
start with pid=1
connect by prior id = pid
)
10:查询出来60-70,80-90,95-100学生的信息
select * from stu where chengji between 60 and 70 or between 80 and 90 or between 95 and 100
select * from stu where chengji > 60 and chengji < 70 or chengji > 80 and chengji < 90 or chengji > 95 and chengji < 100
11:用exists替换in------进行联表查询
select * from dept where exists(select * from emp where emp.deptno=dept.deptno);
或
select * from dept d inner join emp e on d.deptno = e.deptno(只查询出两表共同拥有的字段数据)
12:删除表中的重复数据:
delete from xin a where a.rowid != (
select max(b.rowid) from xin b
where a.name = b.name
);
13:row_number(),rank() over ,dense_rank() over 按工资排序
select sal,
row_number() over(order by sal desc) rank1,
rank() over(order by sal desc) rank,
dense_rank() over(order by sal desc) drank
from emp
14:select * from (select emp.* from(
dense_rank() over(partition by departNo order by sal desc)
rk from emp )
Where rk=4
十六、ibatis批量
this.getSqlMapClientTemplate().execute(
new SqlMapClientCallback() {
public Object doInSqlMapClient(
SqlMapExecutor executor)
throws SQLException {
executor.startBatch();
for (int i = 0, n = list.size(); i < n; i++) {
executor.insert(
"productAttach.insertProductAttach",
list.get(i));
}
executor.executeBatch();
return null;
}
});
ibatis,jdbc,hibernate的分段的实现:
都应该在组装list的时候进行拆分(如:action层加入)
if(list.size() % 1000 == 0)
{
productAttachService.addBatch(list);
list.clear();
}
if (list.size() > 0)
productAttachService.addBatch(list);
业务场景篇
一、MVC框架思想
MVC是一种分层架构的思想,主要是将Web项目结构进行分成controller、Model、View三个层次,在初期设计中,MVC也包含了持久层。Controller层作为控制层,主要是响应用户请求,接收并校验用户参数(因为用户有可能通过某种方式跳过页面的js验证,比如直接在地址栏发起请求),调用业务逻辑层,也就是Mode层完成业务操作,最后根据业务处理的结果,完成页面跳转到View层。在Model层中,主要是完成了各种业务逻辑操作。View层主要负责视图展示,通常不应该设计业务操作,View层常用的技术包括:html、jsp等等。
二、Struts2框架
Struts2框架是一个MVC的实现,它主要有一个核心控制器Str ntext中取出。请求接下来会进入到核心配置文件Strust.xml中,在这个配置文件中主要是完成请求到后台action类的映射。Xml中包含了对package的配置,package只要是统一管理action标签。通常会继承struts-default,struts-default是struts提供的一个默认的package,里面封装了默认的拦截器、resultType等信息。Package还有一个属性namespace,主要是对请求路径的层次划分。Package中有action标签, 其中主要的属性包括:name,class,method。Name就是用户从页面发起的请求名称,可以使用*这样的通配符减少分配置文件的数据量。如果使用统配,则取得通配符的值使用的{下标},比如配置的是*-*,当用户请求的action是User-addUser.action的时候,获得User这个值是通过{1},获得addUser则通过{2}。Class指的是请求对应的action类。Method则指向action类中的方法。Method属性可以配置,也可以不配置。如果不配置,则可以通过动态调用的方式。调用格式是在.action前面加上!方法名。当然,如果没有配置method属性,也没有动态调用方法,则默认会执行execute方法。我在之前的项目一般不会这么使用。完成请求映射后,struts2会通过一个proxy代理类实例化action对象,所以struts2的action是多实例的,也就意味着他是线程安全的。实例化action后,并不是直接进入到action方法中,而是先经过18个默认的拦截器Interceptor。这18个默认拦截器主要是对请求的参数处理、i18n语言乱码处理等等。当然,我们可以根据项目的需要配置自定义拦截器,比如登录拦截器、权限拦截器等等。经过拦截器的时候,需要调用invocation对象的invoke方法,使请求继续往下执行。最后请求就到达了struts的action中,在action类中,接收用户的请求参数,通过属性驱动和模型驱动实现。比如说:在action中定义一个全局的私有变量,并提供他的set和get方法,要求这个变量的名称和请求的参数名称保持一致。如果是以对象的方式传值,则通过对象.属性的方式实现,对象就是后台定义的变量,属性就是这个变量类型中的属性名称。接收到参数后,通常要对参数进行二次校验,最基本的是非空判断。然后调用service业务逻辑层,完成业务操作。最后,请求结束,通过返回一个字符串进行页面跳转。Action类通常可以继承ActionSuport类,这个类中提供了一些常用的返回值常量,比如SUCCESS、ERRO等。这个字符串返回到struts2后,会找到action标签下的result标签,result标签中有两个属性,一个是name,一个是type,这个name就是从action类中返回的字符串。根据这个字符串就完成页面跳转,默认的值是success。Type属性指定了跳转的类型,默认是dispatcher,如果使用重定向,则需要将type设置成redirect。
注意:以上描述中,可能被问到的技术点有
1、interceptor和Filter的区别
Filter:是一个映射过滤器,通常在项目中进行编码转换,进行安全验证,进行重复提交的判断。在web.xml 中配置filter 和 filter-mapping标签,拦截需要拦截的请求。一个类实现filter接口 ,实现它的doFilter 方法放过请求。
Interceptor:
2、转发和重定向的区别
转发:一个请求的延续,可以共享request数据,地址栏不发生改变。
重定向:开启一个新的请求,不可以共享request数据,地址栏发生改变。
3、线程安全的问题。(springMVC关于线程安全的问题)
三、SpringMVC框架
springMVC是一个基于注解型的MVC框架。它的核心控制器是dispatcherServlet,在web.xml中配置。用户发起的请求都会被核心控制器拦截,进入springMVC的核心配置文件spring-servlet.xml。在这个xml中,主要配置的是注解的开启和扫描信息。首先要开启注解模式,annotation-driven。并且指定注解所在的路径,通过component-scan标签的base-package来设置。当请求到达后,springMVC会根据请求的action名称,通过ActionMapping去找到对应的action类中的requestMapping注解,这个注解中有一个value值,需要与请求的名称保持一致。所以请求可以到达action层。当然,springMVC也有自己的拦截器Interceptor。如果需要完成自定义拦截器,则需要写一个类,去继承handlerInterceptor类。重写里面的preHandler方法,在这方法中,通过返回true或者fasle来决定请求继续执行还是被拦截。定义好拦截器类以后,需要将拦截器配置到spring-servlet中去。springMVC中,获得参数的方式很简单,只需要在方法的参数列表中定义相关的局部变量就可以了。也可以通过requestParam的注解对参数进行更具体的配置,比如require为true时,要求这个参数必须传递。如果要求传递而没有传值,则报403错误。除了这样的配置以外,requestMapping也有相关的属性配置,比如requestType可以设置成GET或者是POST,指定请求的格式必须是Get请求还是post请求。如果需要传递的是对象类型参数,直接在参数列表中定义相关的对象,页面的参数名称必须要和类型的属性保持一致。(与strust2的传值方式稍微不同,没有对象.属性的方式)action接收参数后,则调用service业务逻辑层,完成业务操作。最后,springMVC进行页面跳转时,也很灵活,可以通过string返回一个字符串,也可以通过ModelAndView来进行跳转。ModelAndView中有一个setViewName方法,可以指定跳转的路径,还有一个addObject方法,可以将对象或者集合带到页面进行数据展示。如果是通过string类型跳转,则可以通过request.setAttribute()方法往页面传参。获得request的方式可以直接在参数列表中定义request对象获得,当然response也一样。
在spring-servlet中定义了一个试图解析器,统一配置了跳转路径的前缀和后缀名。
注意:可能会被问到的问题
1、常用的注解
2、拦截器的使用
四、Spring框架
我认为spring就是一个框架的集成器,通常使用spring来管理action层和DAO层。Spring本身有很多的组件,比如:MVC、IOC、AOP、DaoSupport等等。IOC本身也就是一个容器,它管理了所有的bean和bean之间的依赖关系。IOC也叫作控制反转,核心是BeanFactory。也就意味着IOC是基于工厂模式设计的,同时这个工厂生产的bean默认是单例的。如果想修改单例变成多实例,则需要修改bean的scope属性,值是prototype。在没有使用IOC以前,程序员需要自己在对应的类中new相关依赖的对象。比如UserAction依赖于UserService完成业务操作,而UserService又依赖于UserDAO完成数据库操作。所以需要在action中new servcie,在service中new DAO。这样的方式,是由程序员来管理了对象的生命周期和他们之间的依赖关系,耦合度很高,不利于程序的拓展。所以通过IOC来管理bean和依赖关系,可以解耦合。我们将所有的action、service和dao等类定义成IOC的一个bean组件,此时类的实例化就交给了IOC的beanFactory,由工厂来负责实例化bean的对象。IOC有三种注入方式,属性注入、构造器注入和接口注入。接口注入只是spring提出的设计,并没有具体的实现,所以常用的注入方式是属性注入。属性注入就是在bean的标签中,配置property标签设定注入其他的bean。要求注入的bean在被注入的bean中要存在一个全局的私有变量,并提供set方法。这样就可以实现了依赖关系的注入。如果需要很多的bean,则直接注入就可以。如此操作会导致bean标签的配置比较冗余复杂,所以spring提供了autowried的自动装配方式,可以byName也可以byType。后续的版本中,spring还提供了annotation的方式,不需要再去定义多余的bean标签,而是直接在对应的类上面声明注解就可以了。常用的注解有:@controller、@Service、@Repository、@Component、@AutoWried、@Resource等。除了IOC以外,项目中通常通过AOP来实现事务控制。AOP就是面向切面编程,一般事务我们会控制在service层,因为一个service有可能会调用到多个DAO层的方法,所以只有当一个service方法执行成功后,再提交或者回滚事务。具体的配置方式是:在applicationContext.xml中,配置aop:config标签,指定事务控制在service层。除此还需要配置事务的管理类transactionManager,将这个transactionManager指定给事务管理的bean,并且配置事务的传播特性、隔离级别、回滚策略以及只读事务read-only等等。Spring默认的传播特性是如果当前上下文中存在事务则支持当前事务,如果没有事务,则开启一个新的事务。还有另外一个传播特性是在项目中经常用的,REQUIRES_NEW这个配置,这个属性指的是总是开启一个新的事务,如果当前上下文中存在一个事务,则将当前的事务挂起后开启新的事务。比如说:在一个本来是只读事务的操作中,想加入写操作的时候,就使用REQUIRES_NEW。关于事务的隔离级别,一般使用默认的配置提交读。也就是说,事务提交以后,才能访问这条数据。除了事务控制以外,我们通常还可以使用AOP去完成一些特殊操作,比如日志控制、安全校验等等。这么做的目的就是将功能操作的代码从实际的业务逻辑操作出分离出来。实现的方式是通过代理模式,真正完成操作的不是实际的业务对象而是代理对象。代理模式有静态代理和动态代理,实现的方案也有两种,一种是基于JDK的Proxy代理类,另外一种则通过CGLIB来实现。实现AOP的方式,主要是在applicationContext中定义一个AOP处理类,这就是一个普通的bean,在类中定义要执行的方法。然后去配置一个aop:config标签,在标签中定义aop:aspect切面,在切面中关联刚才定义好的处理类bean。然后在切面标签中配置aop:pointcut切入点,切入点就指的是在哪个类的哪个方法上加入代理事务,然后配置通知模型。AOP的通知模型中包含:前置通知、后置通知、最终通知、异常通知、环绕通知。这几个通知模型表示在方法执行以前、执行以后、最终执行、当遇到异常时执行以及前后都执行。在执行的AOP切面方法中,可以通过JoinPoint连接点来获得当前被代理的对象以及被代理对象要执行的方法和方法的参数。除了IOC和AOP,我们经常还会使用到spring的DaoSupport。主要是spring提供了对hibernate和myBatis等持久型框架的接口。比如HibernateDaoSupport,和sqlSessionDaoSupport。如果DAO继承了HibernateDaoSupport,则需要在对应的bean中注入sessionFactory。而sessionFactory是通过IOC加载的。
五、关于Hibernate和MyBatis的理解。
Hibernate和MyBatis都是实现了ORM思想的框架。ORM就是对象关联关系映射,主要的目的就是想通过操作对象的方式来操作数据库。其中Hibernate是全自动的ORM框架实现,对JDBC进行高度封装,省去了大量的sql编写和基本的操作语句,提高了开发效率。但是执行效率,相比于jdbc、myBatis要低一些。而MyBatis是半自动的ORM框架,它需要我们手动去编写sql语句,只对返回的结果集进行了处理。这样既保证了执行的效率,又能省去了一部分的编码复杂程度。开发效率相比于hibernate要低一些,效率要高一些。如果是单纯的执行效率来说,JDBC的效率应该是最高的,当然开发效率却是最低的。如果使用hibernate,它也可以通过其他一些手段来提高效率,比如缓存,批量操作等等。如果是使用MyBatis,它可以支持mapping的映射,直接写一个mapper的接口,交给spring来管理,只要接口的方法名和sql的id一致,就可以轻松的调用方法。而且使用MyBatis的时候,可以通过map结构来发挥更简洁的结果集处理。通常会将持久层交给spring来管理,需要去继承相关的类,经常使用到的方法,比如hibernate里边有:saveOrUpdate、get、load、delete等。myBatis中有selectList,selectOne等等。Hibernate有一个延迟加载,这种方法查询数据时,只返回代理对象,并没有直接去访问数据库查询数据。当程序访问对象的属性的时候,才会去发起数据库查询。这样其实也是提高了查询效率,但是也存在一个session关闭的问题。解决的方法也很简单,在web.xml中配置OpenSessionInView这个Filte就可以解决这个问题。在处理对象关系的方面,hibernate提供了one2one,one2many,many2one,many2many这样的关系映射,需要在hbm.xml中进行配置(举个简单的例子:权限控制)。对于MyBatis框架而言,多表操作关键在于SQL。(此处接SQL优化的方案)。
注意:可能被问到的问题
1、ORM的具体理解。(核心技术:反射)
a) 反射是Java提供的一种访问机制。通过反射可以动态的访问类的内部信息,包括类的名称、属性、方法,设置是继承的父类信息。同时还可以动态调用方法(通过invoke方法调用)。
2、Hibernate的描述(核心的API)
3、Hibernate的缓存机制(三级缓存)
4、Hibernate的一对一、一对多等关系的映射配置
5、SQL语句
- Spring的概述
Spring 是完全面向接口的设计,降低程序耦合性,主要是事务控制并创建bean实例对象。在ssh整合时,充当黏合剂的作用。IOC(Inversion of Control) 控制反转/依赖注入,又称DI(Dependency Injection) (依赖注入)
IOC的作用:产生对象实例,所以它是基于工厂设计模式的
Spring IOC的注入
通过属性进行注入,通过构造函数进行注入,
注入对象数组 注入List集合
注入Map集合 注入Properties类型
Spring IOC 自动绑定模式:
可以设置autowire按以下方式进行绑定
按byType只要类型一致会自动寻找,
按byName自动按属性名称进行自动查找匹配.
AOP 面向方面(切面)编程
AOP是OOP的延续,是Aspect Oriented Programming的缩写,
意思是面向方面(切面)编程。
注:OOP(Object-Oriented Programming ) 面向对象编程
AOP 主要应用于日志记录,性能统计,安全控制,事务处理(项目中使用的)等方面。
Spring中实现AOP技术:
在Spring中可以通过代理模式来实现AOP
代理模式分为
静态代理:一个接口,分别有一个真实实现和一个代理实现。
动态代理:通过代理类的代理,接口和实现类之间可以不直接发生联系,而 可以在运行期(Runtime)实现动态关联。
动态代理有两种实现方式,可以通过jdk的动态代理实现也可以通过cglib
来实现而AOP默认是通过jdk的动态代理来实现的。jdk的动态代理必须要有
接口的支持,而cglib不需要,它是基于类的。
Spring AOP事务的描述:
在spring-common.xml里通过<aop:config>里面先设定一个表达式,设定对service里那些方法 如:对add* ,delete*,update*等开头的方法进行事务拦截。我们需要配置事务的传播(propagation="REQUIRED")特性,通常把增,删,改以外的操作需要配置成只读事务(read-only="true").只读事务可以提高性能。之后引入tx:advice,在tx:advice引用 transactionManager(事务管理),在事务管理里再引入sessionFactory,sessionFactory注入 dataSource,最后通过<aop:config> 引入txAdvice。
Spring实现ioc控制反转描述:
原来需要我们自己进行bean的创建以及注入,而现在交给
spring容器去完成bean的创建以及注入。
所谓的“控制反转”就是 对象控制权的转移,
从程序代码本身转移到了外部容器。
官方解释:
控制反转即IoC (Inversion of Control),
它把传统上由程序代码直接操控的对象的调用权交给容器,
通过容器来实现对象组件的装配和管理。
所谓的“控制反转”概念就是对组件对象控制权的转移,
从程序代码本身转移到了外部容器。
- 事务概述
在数据库中,所谓事务是指一组逻辑操作单元即一组sql语句。当这个单元中的一部分操作失败,整个事务回滚,只有全部正确才完成提交。
事务的ACID属性
1. 原子性(Atomicity)
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,
要么都不发生。
2. 一致性(Consistency)
事务必须使数据库从一个一致性状态变换到另外一个一致性状态。(数据不被破坏)
3. 隔离性(Isolation)
事务的隔离性是指一个事务的执行不能被其他事务干扰.
4. 持久性(Durability)
持久性是指一个事务一旦被提交,
它对数据库中数据的改变就是永久性的.
在JDBC中,
事务默认是自动提交的,
每次执行一个 SQL 语句时,如果执行成功,
就会向数据库自动提交,而不能回滚
为了让多个 SQL 语句作为一个事务执行:
(1)执行语句前调用 Connection 对象的 setAutoCommit(false);
以取消自动提交事务
(2)在所有的 SQL 语句都成功执行后,调用 commit(); 方法提交事务
(3)在出现异常时,调用 rollback(); 方法回滚事务。
- 权限概述
权限涉及到5张表:
用户表,角色表,权限表(菜单表),用户角色关联表,角色权限关联表
当用户登录时,根据用户名和密码到用户表验证信息是否合法,如果合法
则获取用户信息,之后根据用户id再到用户角色关联表中得到相关连的角色
id集合,之后根据角色id再到角色权限关联表中获取该角色所拥有的权限id集合,
然后再根据权限id集合到权限表(菜单表)中获取具体的菜单,展现给当前
登录用户,从而达到不同用用户看到不同的菜单权限。
我们通过ZTree来给角色赋权并且通过ZTree来展示菜单,以及通过ZTree来管理菜单即增加和编辑菜单。
我们做的权限控制到url级别,为了防止用户不登录直接输入url访问的这个弊端,通过拦截器进行拦截验证。
- OSCache业务场景
在我以前的项目中,我们考虑了系统性能问题,这个时候我们采用了Oscache缓存,刚开始把这个功能交给了项目组中的另外一个同事来做的,但是他做完的时候他发现缓存中明明已经缓存了数据,但是在取得时候发现没有数据,我们项目经理让我去帮忙看看这个问题,我阅读完他的代码之后,我发现了他每次缓存的时候都是调用一个新的缓存对象的方法,结果出现了明明已经走了缓存的方法而取不到数据的问题,通过我多年的工作经验,我就想到了应该用单例模式去封装一个单例工具类来调用oscache。但是,在后来的测试过程中,发现当并发访问的时候也会出现上述的问题,这个时候我直接采取的DCL(双重判定锁)单例模式封装了工具类,既解决了线程安全问题,相对的性能问题也考虑到了,这个问题才得到了完善的解决。
- 线程概述
线程的状态以及状态之间的相互转换:
1、新建状态(New):新创建了一个线程对象。
2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
(一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。
(二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
(三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
实现线程的两种方式:
是继承Thread类或实现Runnable接口,但不管怎样,当new了这个对象后,线程就已经进入了初始状态
wait和sleep的区别:
线程访问:
锁池状态,之后等待锁释放,然后访问代码
wait
等待队列(释放资源)--->调用notify或者notifyall之后锁池状态--->( 等待锁释放)--->可运行状态--->运行状态---->访问代码
sleep,join
不释放资源-->结束后直接进入可运行状态--->运行状态---->访问代码
一个java控制台程序,默认运行两个线程,一个主线程,一个垃圾回收线程。
线程与进程的区别:
1.线程(Thread)与进程(Process)
进程定义的是应用程序与应用程序之间的边界,通常来说一个进程就代表一个与之对应的应用程序。不同的进程之间不能共享代码和数据空间,而同一进程的不同线程可以共享代码和数据空间。
- 一个进程可以包括若干个线程,同时创建多个线程来完成某项任务,便是多线程。
- 实现线程的两种方式:继承Thread类,实现Runable接口
- Ajax请求Session超时问题
我在做项目时有时会遇到session超时问题,如果session超时,平常请求没有什么问题,通过拦截器可以正确跳到登陆页面,可是你如果用ajax请求的话这就出现问题了,因为ajax是异步的,局部刷新,所以登陆界面不会再全页面中显示,他只会显示到页面的一部分当中。所以根据我这几年的经验找到了我认为比较好的一种方法。因为那我用的框架是和struts2集成的,所以就在拦截器中进行设置:
首先判断session是否为空就是判断session是否超时,如果超时就取出请求的head头信息request.getHeader("x-requested-with"),如果不为空就和XMLHttpRequest(Ajax标识)进行比较 (request.getHeader("x-requested-with").equalsIgnoreCase("XMLHttpRequest"))) 如果相等说明此请求是ajax请求。
如果是ajax请求就可以用response.setHeader("键","值")来设置一个标识来告诉用户这是ajax请求并且session超时时发出的,这样我就可以在回调函数中取出自己设置的那个唯一标识:XMLHttpRequest.getResponseHeader("");如果取出的值是和自己在后台中设置的值一样的话,就证明session已经超时,这样就可以设置window.location.replace("登陆界面"),来跳转到登陆界面了。
这样做虽然解决了问题,但是,会在每个回调函数中写入那些代码,这样的话代码就会显得特别零散,所以就想能不能定义一个全局的设置所以就找到了jqery的ajaxSetUp方法,通过ajaxSetUp对jqery的ajax进行全局的判断(ajaxSetUp就相当于ajax的拦截器),通过设置ajaxSetUp里的complete,它就相当于回调函数,这样那就弥补了上一方法的不足。
我做项目时还用到$(document).ajaxStart(),这是ajax请求时的事件;$(document).ajaxSuccess(),这是AJAX请求成功后的事件。我一般用他们来显示遮罩层和隐藏遮罩层用的加遮罩层是为了不让用户重复提交,更提高了用户体验度,让用户知道已经提交了。
七:java线程池概述
java线程池的工作原理和数据库连接池的差不多,因为每次重新创建线程
都是很耗资源的操作,所以我们可以建立一个线程池,这样当需要用到线程
进行某些操作时,就可以直接去线程池里面找到空闲的线程,这样就可以直接
使用,而不用等到用到的时候再去创建,用完之后可以把该线程重新放入线程池
供其他请求使用从而提高应用程序的性能。
线程池的核心流程:
1.构建一个 ThreadPoolExecutor 并指定默认要创建的线程的数量
2.通过 threadPool.execute()
去添加一个个要执行的线程即实现了Runable接口的java类
3.在实现了Runable接口的java类的run方法中写入具体的业务代码
线程池的业务场景:
我在工作的时候,当时一个同事给我提了一个需求,目前有大量的图片
需要处理生产缩略图并进行加水印,因为按照普通的处理方法一个个的
进行处理太慢了,问我有没有好的解决方案,这个时候我就想到了java中
的线程池,我构建了一个线程数为5个线程池,然后采用分段批量提取的
方式每500条为一组数据进行图片信息的提取,然后再把这些通过Threadpool的
execute方法交给线程池中的线程进行处理,即充分使用了CPU硬件资源又加快
了大数据情况下程序的处理效率。
我当时在工作的过程中,认识一个做电商的朋友,他们当时公司才起步,很多
技术都不成熟,所以就常常和我探讨一些技术问题,有次他向我请教一个问题,
问我如何才能提高网站的性能,我根据自己在项目中的经验以及自己以前阅读的
关于优化方面的资料给他提出了很多建议,如用lucene进行全文检索,用memcached
进行分布式缓存,以及通过spring定时器结合freeMarker模板引擎来生成静态
页面,由于要生成的页面的数量比较多,考虑到程序的性能,我建议他结合
java的线程池进行工作,这样就可以充分使用了CPU硬件资源又加快
了大数据情况下程序的处理效率。
- OSCache概述
oscache是一个高性能的j2ee框架,可以和任何java代码进行集成,并且还可以通过标签对页面内容进行缓存,还以缓存请求。
我们通常将那些频繁访问但是又不是经常改变的数据进行缓存。为了保证缓存数据的有效性,在数据发生改变的时候,我们要刷新缓存,避免脏数据的出现。刷新缓存的策略有两种,一种是定时刷新,一种手动刷新。
缓存数据的时机通常也分为两种,即在tomcat(web容器)启动时候加载数据进行缓存,另外也可以在用户第一次访问数据的时候进行缓存,这个相当于缓存的立即加载和按需加载。
缓存的层次如下:jsp-->action-->service-->dao,缓存越靠前对性能的提升越大
一个action里面可以有多个service,一个service中可以有多个dao或者多个service
任何类之间都可以进行相互调用,可以通过构造函数传参,set,get传参或者是方法传 参将相关的类连接起来。
- OSCache+autocomplete+单例业务场景
在我以前做某项目的过程中,其中我们在做产品列表的查询的时候为了提高用户的体验度,我们使用了autocomplete插件来代替select进行品牌的选择,才开始的时候每次都要根据用户输入的信息去查询数据库进行模糊匹配返回结果,后来我们考虑到系统的性能,因此我们采用了oscache缓存,才开始这个功能是交给我们项目组中的另外一个同事来做的,但是他做完后,我们在使用这个工具类的时候,发现有时缓存中明明已经有时我们需要的数据,但是从缓存里面取的时候,发现没有,之后项目经理让我去帮这个同事看看这个问题,我经过阅读他的代码发现,它里面在使用缓存的时候,针对于每次方法的调用都产生一个新的实例,结果导致了上面的问题,这个时候我想起了可以使用设计模式中的单例模式来解决这个问题,才开始我直接采用了普通的单列模式,但是后来在测试的过程中,发现当用户并发量大的时候还是会出现上面的问题,之后我再次考虑了代码,最后发现是因为没有给单列模式加锁的原因,从而导致了大用户并发的时候,线程安全的问题,之后我便在方法上加上了synchronized关键字,解决上述的问题,但是后来测试人员反馈,觉的这段的性能有问题,我考虑之后便采用在方法体内加锁并结合双重判定的方式解决了上面的问题。我们是将数据在tomcat启动的时候加载到缓存中,之后用户进行查询的时候直接从缓存中获取数据,根据前缀匹配进行查询,将结果返回给用户。这样在提高用户体验度的同时也提高性能。
- 缓存概述
通常情况下缓存是为了提高应用程序的性能,减少数据库的访问次数,缓存的存储介质可以
内存或者硬盘,通常将数据存储在内存里,确切的说是jvm的内存中,缓存是
基于Map这种思想构建的,以键值对的方式进行存取,之所以还可以将
缓存的数据存储在硬盘中,是因为内存资源相当有限和宝贵,所以当内存资源
不足的时候,就可以将其存储到硬盘中,虽然硬盘的存取速度比内存要慢,但是
因为减少了网络通信量,所以还是提高程序的性能。缓存可以分为客户端缓存和
服务器端缓存,所谓的客户端缓存通常指的是IE浏览器的缓存,服务器端缓存指
的web服务器的缓存,通常可以通过第三方组件实现,如ehcache,oscache或者memcache
我们通常将那些频繁访问但是又不是经常改变的数据进行缓存。为了保证缓存数据的
有效性,在数据发生改变的时候,我们要刷新缓存,避免脏数据的出现。刷新缓存的
策略有两种,一种是定时刷新,一种手动刷新。
缓存的层次如下:jsp-->action-->service(通常放置在service)-->dao,
缓存越靠前对性能的提升越大
缓存的策略:(缓存空间不足需要进行清理的时候使用)
LRU:最近最少使用原则.(理解:存储书)
FIFO:先进先出的缓存策略.(理解:排队)
你来说说缓存?说说你对缓存的理解(如果遇到重复的,就可以省略)
我们在项目中使用缓存的目的是为了提高应用程序的性能,减少访问数据库
的次数,从而提高应用程序的吞吐量。我们通常将权限,菜单,组织机构
这些频繁访问但是不经常改变的基础数据进行缓存,其中我在做()某某项目的时候
就通过oscache对ZTree的树形菜单进行了缓存,并且在做的时候和单列设计
模式进行结合,考虑到多线程下的安全问题,还对单例模式加入了双重判定锁
的检查方式。
- 实现页面静态化业务场景
我们在做某项目时,涉及到程序访问的性能问题,这时候我们想到可以通过静态化来提高用户访问时候的性能,所以我们就采用了freemarker模板引擎,考虑到页面也是要有动态的变化的,所以我们采用spring定时器在每天晚上2点钟的时候定时再次生成html静态页面,考虑发布时候的性能问题,我们又采取线程池技术,让多个线程同时发布,从而缩减发布时间。
- servlet线程安全描述
servlet是单列的,对于所有请求都使用一个实例,所以如果有全局变量被多
线程使用的时候,就会出现线程安全问题。
解决这个问题有三种方案:
1.实现singleThreadModel接口,这样对于每次请求都会创建一个新的servlet实例,这样就会消耗服务端内存,降低性能,但是这个接口已经过时,不推荐使用。
2.可以通过加锁(synchroniezd关键字)来避免线程安全问题。这个时候虽然还是单列,但是对于多线程的访问,每次只能有一个请求进行方法体内执行,只有执行完毕后,其他线程才允许访问,降低吞吐量。
3.避免使用全局变量,使用局部变量可以避免线程安全问题,强烈推荐使用此方法来解决servlet线程安全的问题。
- (jbpm4)工作流引擎描述:
JPBM是JBOSS旗下的一个开源的基于hibernate的工作流引擎。工作流就是在日常生活中,我们一些常见的如请假流程、采购流程、入职流程,通俗的来讲就是一些在现实生活中的流程以信息化以程序的方式实现。
一个工作流首先需要进行流程定义,流程定义是由节点和跳转组成的,节点又可以称为环节、活动节点、活动环节,并且节点也可以分为两大类型:人工节点和自动节点,人工节点有start开始节点、end结束节点、task任务节点,自动节点有decision判断节点、fork分支节点、join聚合节点和state状态节点,并且一个流程有且只有一个开始节点,但可以有多个结束节点。
流程定义是静止的,它在运行状态时会转换成流程实例,一个流程定义可以对应多个流程实例。流程运行后,会产生两个文件,*.jdpl.xml文件和*.png图片文件,也会生成18张数据库表,常用且核心的表有JBPM4_LOB 存储表,主要存储xml文件和png图片、JBPM4_TASK 任务表、JBPM4_EXECUTION 流程实例表、JBPM4_VARIABLE变量表。
图形化的灵活定制(主动说)
可以根据需求进行流程图的改变的,即定义的流程图是可以根据需要改变的,而不是死的。
可以进行图形化的监控(主动说)
输出图片
获取活动节点的坐标
进行叠加
判断节点:(主动说,也可以了解)
实现implements DecisionHandler接口并重写decide方法,
返回的字符串要和xml中配置的transition的name保持一致。
分支判定节点
JBPM有五大核心类:
ProcessEngine:主要获取各种的Service
RepositoryService:主要发布流程定义
ExecutionService:主要操作流程实例
TaskService:主要操作人工服务
HistoryService:主要操作历史服务。
核心方法:
读取jbpm定义的文件生成zip包存到lob表中:createDeployment()
获取流程定义列表:createProcessDefinitionQuery
根据定义的key或id来启动流程实例:startProcessInstanceByKey(id)
获取待办任务列表:findPersonalTasks(userName)
完成指定任务列表:completeTask(*.getActivityId())
获取历史任务列表:createHistoryTaskQuery()
获取流程实例的ID:task.getExecutionId()
(了解的表)
JBPM4_HIST_ACTINST 流程活动(节点) 实例表
JBPM4_HIST_DETAIL 流程历史详细表
JBPM4_HIST_PROCINST 流程实例历史表
JBPM4_HIST_TASK 流程任务实例历史表
JBPM4_HIST_VAR 流程变量( 上下文) 历史表
- ACTIVITI业务场景
首先进行请假的流程定义,我们流程的定义是(员工提交请假单---》经理审批---》总监审批---》总经理审批---》结束),通过repositoryService将其发布部署到jbpm4_lob表中,
之后获取流程定义列表,选中请假的流程定义,员工开始进行请假单的填写,保存并通过executionService开启流程实例,然后用taskService获取经理的待办任务列表,选中待办任务,进行审批,通过调用taskService.completeTask()进入到总监审批环节,然后用总监进行登录,同样获取待办任务列表,然后调用taskService.completeTask()进入总经理审批环节,总经理审批之后,结束流程。在这个过程中我们还可以根据historyService查看当前登录人已办的任务列表。
- Ant描述
Ant是apache旗下的对项目进行自动打包、编译、部署的构建工具,他主要具有 轻量级并且跨平台的特性,而且基于jvm,默认文件名为build.xml
Ant主要的标签:
Project 根标签,
target 任务标签,
property 属性标签,自定义键/值 供多次使用,
java 执行编译后的java文件
javac 编译java文件
war 打成war包
其它标签:copy,delete,mkdir,move,echo等。
- FreeMarker描述
FreeMarker是一个用Java语言编写的模板引擎,它是基于模板来生成文本输出的通用工具。Freemarker可以生成HTML, XML,JSP或Java等多种文本输出。
工作原理:定义模板文件,嵌入数据源,通过模板显示准备的数据
(数据 + 模板 = 输出)
我们在使用模板中发现freemarker具有许多优点,它彻底的分离表现层和业务逻辑,模板只负责数据在页面中的表现,不涉及任何的逻辑代码,所以使得开发过程中的人员分工更加明确,作为界面开发人员,只需专心创建HTML文件、图像以及Web页面的其他可视化方面,不用理会数据;而程序开发人员则专注于系统实现,负责为页面准备要显示的数据。
如果使用jsp来展示,开发阶段进行功能调适时,需要频繁的修改JSP,每次修改都要编译和转换,浪费了大量时间,FreeMarker模板技术不存在编译和转换的问题,在开发过程中,我们在不必在等待界面设计开发人员完成页面原型后再来开发程序。由此使用freemarker还可以大大提高开发效率。
- webService描述
(主动说)
webservice是SOA(面向服务编程)的一种实现,
主要是用来实现异构平台通信也就
是不同平台不同项目之间的数据传输,从而避免了信息孤岛的问题,
它之所以能够
进行异构平台通信是因为它是完全基于xml的,
所以说,webService是跨平台,
跨语言,跨框架的,在java中通常有三种技术框架分别是xfire,cxf,axis2。
我们为了保证
webservice的安全性,采用了基于
WS-Security标准的安全验证(使用回调函数)。
(没必要主动说)
webservice的三要素分别是:
wsdl(webservice description language)
用来描述发布的接口(服务)
soap(simple object access protocol)
是xml和http的结合,是webservice数据通信的协议
uddi 用来管理,查询webService的服务
(没必要主动说)
webservice的具体三种实现方式(框架)或者三种实现框架的区别
1. Axis2:可以用多种语言开发,
是一个重量级框架,功能非常强大,
但是它的性能比较低。
2. Xfire:它相比Axis2来说是一个轻量级框架,
它的性能要比Axis2高。
3. cxf:是Xfire的升级版,就好比是,
struts2是webwork的升级,
然后cxf和spring集成起来非常方便,简易,
性能方面也要比Xfire高。
【注】jdk6 自带的webservice jws
(主动说)
业务场景
我在以前做项目的时候,其中遇到一个功能,
需要进行两个项目之间的数据的传输,
项目经理让我去完成这个任务,我根据以往的项目经验,
想到两种解决方案,第一种
就是开放另外一个项目的数据库的权限给我,
然后我直接通过访问另外一个项目的数据
库,来得到需要的信息,但后来我分析了下,觉的这种方式不安全,
而且因为当时
这个项目是另外一家公司负责在做,所以数据库里面的表结构,
以及以后牵涉
到的责任问题都很多,所以我就采用了第二种方案,
即通过webservices的方式,进行
异构系统之间数据信息的传递,webservices的具体实现,
有xfire,cxf,axis2,
我根据以往的项目经验,了解到cxf是xfire的升级版本,适用于java语言,
xfire/cxf 性能比axis2要高,并且和spring整合起来也比较方便,
而axis2支持更多的语言,
性能相对于cxf要低,通过上面分析,
结合我们目前的两个项目都是基于java
语言的,所以我采用cxf这种方式实现了两个项目之间数据的传递,
我们为了保证
webservice的安全性我们采用了基于
WS-Security标准的安全验证(使用CXF回调函数)。
(没必要主动说)
webservice服务端配置流程
首先在web.xml中引入cxfServlet核心类,
指定对以/cxf开头的url路径提供webservice服务,
之后我们在要发布成webservice接口上添加@Webservice 注解,
而且还要在实现类上添加同样的webservice注解并且要说明实现了哪个接口,
之后在spring-webservice.xml中发布webservice服务,
通过jaxws:endpoint这个标签,
并且在标签配置implementor和address来表明实现服务的类,
以及发布的地址,
最后在浏览器中输入相关的webservice地址?wsdl来验证服务是否发布成功。
(没必要主动说)
webservice客户端的配置
首先通过wsdl2java根据发布的webservice服务端地址的wsdl
生成客户端调用的中间桥梁java类,
将生成的java类拷贝到客户端项目中,
配置spring-client.xml文件,
通过jaxws:client定义一个bean,
并通过address属性指明要访问的webservice的服务地址,
通过serviceClass指明充当中间桥梁的服务类,之后获取该bean,
就可以通过它来访问发布的webservice接口中的方法。
- oracle索引概述
索引呢 是与表相关的一个可选结构,可以提高sql语句的检索效率,相当于我们的字典目录 ,可以快速进行定位 ,所以可以减少磁盘I/O, 但是因为索引在物理与逻辑上都是独立于表的数据 它会占用一定的物理空间(额外磁盘空间) 所以并不是索引越多越好,而我们应该根据业务需求去创建索引,而且进行增删改操作时 oracle又要自动维护索引 所以在一定程度上也降低了维护速度,而且我们在创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加,我们一般创建索引呢 是这样创建的 create index 索引名 on 表名(字段),索引又分为普通索引 唯一索引(unique) 单个索引 复合索引(又叫组合索引,在索引建立语句中同时可包含多个字段名),顺序索引,散列索引,位图索引。
- oracle存储过程
存储过程就是封装一些sql的集合,也就是一条条的sql语句,过程的优点就是简化了sql命令加上它是预编译的,所以它的执行效率和性能较高,再者,如果不调用过程的话就要和数据库发生多次交互,调用过程只需传一个命令所有的那些执行逻辑都在数据库端执行,所以说它降低了网络的通信量,其次,存储过程大大提高了安全性,这就是优点
缺点呢,就是不同的数据库对过程支持的关键字支持的关键字都是不一样的,所以它的移植性是非常差的,再者,它的维护性难度也比较大,因为它没有专业的调试和维护工具,所以说它维护起来比较麻烦,这就是存储过程的基本概述.
二十、Junit 业务场景
在我们开发项目的时候为了提高代码的性能和保证逻辑正确性,在我们编写代码后往往都要进行单元测试,来验证代码,当时我们公司开发人员全部使用的main方法来进行验证,但是使用mian的最大缺点就是不能将多个类同时进行验证,验证的结果不直观,测试复杂(每个类都要写main方法,单个运行),一定程度上浪费时间,所有我和项目经理提议使用专业测试工具Junit来进行测试,因为Junit是一个Java语言的单元测试框架 ,测试简单,不仅可以提供工作效率和代码的质量,也提高团队的合作能力,我提议后我们进行了Junit的培训使用Junit4加注解的方式来测试。
二十一、Apache+Tomcat 实现负载均衡及seesion复制
当我们tomcat访问量大,线程连接数不够时,我们考虑到了tomcat的负载均衡来分担过多的访问.性能方面负载均衡也能利用多台tomcat来增大内存量,
流程,准备工作apache,Jk_mod,tomcat,在apache的conf/httpd.conf文件中 使用include 标签引入我们自定义的一个mod_jk.conf,在modules中引入下载的k_mod-apache-X.X.XX.so文件,在其中引入我们的.so,及work.properties文件,及指定负载分配控制器controller,在work.properties文件中worker.list=controller,tomcat1,tomcat2指定service,worker.tomcat1.port Ajp端口号,type 是ajp,host为指定ip,lbfactor 指定分配权重值越大分担请求越多,worker.controller.type=lbworker.controller.balanced_workers=tomcat1,tomcat2 指定分担请求的tomcat Session的复制在tomcat中service.xml中Engine标签加入 jvmRoute 值为work,properties中指定的tomcat名称,然后打开<Cluster标签的注释,最后在应用中程序的web.xml文件中增加<distributable/>。
我们在做这个项目时,我们考虑到服务器性能的问题,我们最开始想到使用纵向扩展,来增加硬件的配置提高其性能,但这样做比较耗费资金,而且服务器内存空间也是有限的;所以后来就想到使用横向扩展来达到这一目的
当时我们的apache是通过jk借助于ajp协议与tomcat进行通信的,在我们不进行负载均衡之前,那所有的请求都由一台tomcat进行处理,这样会使我们的tomcat所承受的压力增大,而我们进行负载均衡之后,同样数量的请求经过apache和jk将其分发到多台tomcat进行处理,从而降低每台tomcat所承受的压力,而且当其中一台机器宕机时,其他机器还可以继续提供服务,保证服务不间断,同时我们考虑到单台apache可能出现单点故障,所以我们就
在前面增加一个f5来避免这个问题。
在这个过程中,我们遇到了session问题,然后我此昂到用session复制来解决这个问题;
在apache的配置文件中增加session粘带特性:
worker.lb.sticky_session=1
worker.lb.sticky_session_force=0
Tomcat的配置
修改server.xml文件:
<Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat2">
增加jvmRoute=”tomcat2” *. jvmRoute赋的值为worker.properties中配置的相应的server名一致
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/> 将此配置的注释去掉
修改应用的web.xml文件
在应用中的web.xml文件中增加<distributable/>。
如果这样做,当第一次访问的时候,会把所以数据全部缓存到第一台服务器上,通过web配置文件,会把第一台缓存的数据全部复制到第二胎服务器上,这样做就加大网路通信量,导致阻塞,所以我们就想到了可以通过memcached分布式缓存来存取session从而解决上述问题。
二十二、Ant业务场景
Ant是基于java语言编写的,因此具有跨平台的特性,此外还具有简洁方便,灵活
配置的特性,因此我就在XX项目中使用ant进行项目的编译,打包,部署操作。使用ant
之后,如果我们在客户那里修改代码后,就可以直接使用ant进行编译,打包,部署,而不需要为了编译,打包,部署专门在客户那里安装eclipse.此外使用ant也可以直接和svn进行交互,下载源码的同时进行编译,打包,部署。
二十三、maven业务场景
maven业务场景
前段时间在研究maven,知道maven是一个项目管理工具,其核心特点就是通过
maven可以进行包的依赖管理,保证jar包版本的一致性,以及可以使多个项目共享
jar包,从而能够在开发大型j2ee应用的时候,减小项目的大小,并且和ant
比起来,maven根据“约定优于配置”的特性,可以对其项目的编译打包部署进行了
更为抽象的封装,使得自己不需要像ant那样进行详细配置文件的编写,直接使用
系统预定好的mvn clean,compile,test,package等命令进行项目的操作。于是我就
在XX项目中采用了maven,为了保证团队中的成员能够节省下载jar包所需要的时间,
于是我就采用nexus搭建了在局域网内的maven私服,然后通过配置settings.xml中
建立mirror镜像,将所有下载jar包的请求都转发到maven私服上,之后通过在pom.xml
即(project object model)中配置项目所依赖的jar包,从而达到在构建项目的时候,
先从本地仓库中查找,如果不存在从内部私服查找,如果不存在最后再从外网central
服务器查找的机制,达到了节省下载带宽,提高开发效率,以及jar包重用的目的。
ant业务场景
ant是基于java语言编写的,因此具有跨平台的特性,此外还具有简洁方便,灵活
配置的特性,因此我就在XX项目中使用ant进行项目的编译,打包,部署操作。使用ant
之后,如果我们在客户那里修改代码后,就可以直接使用ant进行编译,打包,部署,
而不需要为了编译,打包,部署专门在客户那里安装eclipse.此外使用ant也可以
直接和svn进行交互,下载源码的同时进行编译,打包,部署。
maven的常用命令
mvn eclipse:clean eclipse:eclipse -Dwtpversion=2.0
mvn clean package
maven的生命周期是独立的,但是生命周期下的阶段是相互关联并且延续的。
maven的生命周期
clean(清理):clean;default(默认):compile,test,packageinstall;site(站点)
二十四、Servlet的概述:
Servlet是一个web容器,我们通常用的servlet是继承httpservlet,而httpservlet又是继承于genericservlet,而genericservlet又实现了servlet接口
servlet的生命周期是 :先进行实例化,然后是初始化,然后是提供服务,然后销毁,最后不可用,在这五个生命周期,其中,初始化是调用的init方法,这个方法只有一个,而提供服务的时候调用的是service方法,而我们具体在我们所写的这个方法中,因为我们继承了httpservlet,其实就是对应了doGet(),doPost(),这种方法,然后据我了解,servlet是单例的。
非线程安全的,我们通常有一下几种方案来解决:
第一种,实现SingleThreadModel接口但是这样每次都会创建一个新的servlet实例,但这样消耗服务器的内存,降低了性能,并且这个接口现在已经过时了,不推荐使用。
第二种:我们尽量避免使用全局变量,就我个人而言,我比较喜欢使用这种方法。
第三种,我们可以通过使用ThreadLocal, 内部结构是一个Map结构,用当前线程作为key,他会创建多个副本。get,set方法
第四种,我们当然还可以来加锁,进行解决线程问题。
而且我还知道,向我们这种常用的MVC框架,struts1,spring这些MVC框架,都是基于servlet发展而来的,就比如struts1 的核心总控制器是ActionServlet,而springMVC的前端总控制器是dispatchServlet,在项目我们曾经用serlet来生成 图片验证码的,防止用户进行暴力破解
(别人问了,再回答)
servlet的配置文件 web.xml
<servlet>
<servlet-name>ImageCodeServlet</servlet-name> <servlet-class>org.leopard.code.ImageCodeServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ImageCodeServlet</servlet-name>
<url-pattern>/d</url-pattern>
</servlet-mapping>
描述:
我在web.xml中,我首先需要写一个servlet标签,servlet标签中有两个子标签,一个叫servlet-name,这个name可以随便起,但是要保证唯一性,除此之外,在这个servlet-name下有一个servlet-class,这个servlet-class对应的就是我后台提高服务的servlet,除此之外还有一个servlet-mapping,这个里边首先有一个servl-name。,这个servl-name首先要保证和上边的servlet-name保持一致,除此之外还有一个url-pattern,这是一个虚拟路径,是用来发送请求的url地址
二十五、bugfree的操作步骤
我们在使用bugfree的时候我们首先登陆的时候是以测试员的身份登陆的,也就是系统管理员用户;测试员在登陆后首先应该给要测试的项目的相关负责人,每人创建一个账号(也就是在登陆后的页面的后台管理中创建用户),用户都新建完成之后就新建组,把要测试的项目的用户添加到组中。最后就新建项目并且新建该项目的模块。新建完项目之后就是开始测试程序,在程序中遇到bug以后就把错误截图,在到bugfree中新建bug填写相关的信息和要指派的人(出错模块的负责人)和把刚才的错误截图作为附件一并传送过去。
开发人员每天早上上班的第一件事就是用自己的用户登录bugfree,然后输入查询条件看看前一天有没有指派给自己的bug需要解决的如果有就进行解决。
开发人员把对应的bug解决之后就去bugfree上把bug对应的状态改成已解决状态,然后进行保存提交,这样bug的状态就变成已解决状态。测试人员上线查看已解决状态的bug并再次进行测试,如果经过测试bug的问题已解决,就可以把bug关闭;如果经过测试,发现仍然存在bug,就把bug激活;这样等开发人员再次登录的时候就可以再次看到这个未解决的bug,再次进行解决,如此反复直到bug全部解决,因为bugfree对bug的修改都有保留,所有我们可以看到bug的一步步的完善,直到最后把bug关闭。
Bug的三种状态:未解决(Active)(测试人员)、已解决(Resolved)(开发人员)、关闭(Closed)(测试人员)
二十六、Axis2 的配置
axis2服务端配置流程
1.引入相关的jar包并且在web.xml中配置axis2的核心控制器 axisServlet
2.在web-inf下建立相关的三层文件夹结构:
services-->自定义文件夹名-->META-INF-->servies.xml
3.在servies.xml中配置service的name以及对应的springBeanName
4.在浏览器中输入webservice的服务端地址并加上?wsdl来进行测试,看
是否发布成功
axis2客户端配置流程
1.通过wsdl2java根据webservice服务端的url生成客户端代码
- 将代码引入项目的文件夹中进行正常访问
二十六、spring定时器
每隔固定的时间执行
1.建立一个triggers触发器集合
2.建立SimpleTriggerBean并且指定每次间隔的时间以及执行的次数以及要执行的目标
3.通过 targetObject以及targetMethod找到要执行的具体类的具体方法
目标对象是一个普通的java类
每到指定的时间执行
1.建立一个triggers触发器集合.
2.建立CronTriggerBean指定cron表达式以及要执行的目标
3.通过 targetObject以及targetMethod找到要执行的具体类的具体方法
目标对象是一个普通的java类
二十七、Ext概述
据我了解Ext是一个用js编写RIA框架,它可以和各种后台语言结合使用。
我在项目中用Ext来完成的模块大概情况是这个样子,首先我通过layout
等于border的这种方式来进行布局,分为上下左右中,然后在左边用ext
tree来进行菜单的展示,之后在中间区域通过tabs来加入选项卡,而在
选项卡中就是一个个的grid以及form,其中我在做grid的时候,首先通过
store来存取后台返回的符合model格式数据集,store是通过proxy和后台的
contoller进行交互,之后把store赋值给grid的store属性并且通过renderTO
在指定的位置进行渲染展示。
Grid问题:
当时我在做grid的时候,发现数据没有展示出来,
我通过f12进行跟踪,发现压根就没有发送请求,后来我分析了下,发现因为
没有调用store的loadPage方法,所以导致了这个问题。除此之外在我们做
项目的过程中,我手底下带的一个人同样在负责grid的时候,数据可以正常展示,但
分页信息没有展示,通过跟踪他的代码发现是因为他没有把store属性赋值给
分页工具条,所以才导致了这个问题。
tabs选项卡:
当我在做tab选项卡这一模块的时候,
我首先在加载页面的时候用TabPanel创建了一个
tab页面,让它展示在中间位置,
然后点击左边Tree菜单调用add方法动态添加一个个
的tab选项卡,但是做的过程中出现了相同的选项卡会重复添加的问题,
我查了一些相关资料,最后通过tab的id或者一个唯一标识判断tab是否选中,
如果选中则调用setActiveTab来激活该选项卡,让它选中,
否则就添加一个tab。
最后达到了tab不存在就添加,存在就选中的效果。
了解:
Ext4.0也支持前端的MVC开发模式.
为啥没采用mvc的开发模式?
我们当时因为时间方面的原因,项目经理就决定用普通的这种
开发模式进行开发,并没有采用Ext4.0这种mvc模式的特性。但
我认为他们的核心操作流程是一致的所以对我来说去学习和使用
这种方式并没有什么难度。
二十八、lucene的概述
lucene是一个全文检索引擎,在进行模糊匹配的时候,他可以
用来替代数据库中的like,从而在匹配准确性以及性能进行大幅度
的提高。我在做XX项目的XX模块的时候,就是用lucene来进行全文检索
用IK分词器来进行分词。从而实现了高亮显示关键词,分页,排序,
多字段,多条件的高性能搜索。在从数据中取数据生成索引的时候,
因为表中的数据量比较大,防止一次取出所导致内存溢出问题,我采用了
分段批量提取的方式进行,除此之外我们对后续增加的数据根据优先级的
不同采取不同的策略,对于那些需要及时显示的数据我们通过spring
定时器 在短时间内(30分钟)进行增量索引的生成,对于那些不需要
及时展示的数据,我们通过spring定时器在每天晚上凌晨的时候进行索
引的重新生成。
二十九、线程池作用
1.减少了创建和销毁线程的次数,
每个线程都可以被重复利用,
可执行多个任务。
2.可以根据系统的承受能力,
调整线程池中线程的数目,
防止因为消耗过多的内存,
而导致服务器宕机
(每个线程需要大约1MB内存,线程开的越多,
消耗的内存也就越大,最后宕机)。
通常我们使用的线程池是实现了ExecutorService的
ThreadPoolExecutor。
三十、jbpm是如何和spring进行整合
1.通过在spring-common.xml配置文件中配置springHelper,通过springHelper创建
processEngine,再通过processEngine获取各种工作流的Service,
如repositoryService,executionService,historyService,taskService
- 在src根目录下新建jbpm.cfg.xml文件
三十一、Tomcat优化
增大内存(堆,持久代)并开启server模式
我在做XXX项目时,用到了poi导入和导出数据,由于公司的业务比较繁多,数据量很大,测试时报内存溢出,经过我的分析再结合上网查阅资料,发现可能是tomcat内存不足,需要增大,修改配置文件后测试不再报错.
tomcat增大内存的方式通过修改tomcat配置文件
window下, 在bin/catalina.bat文件中最前面添加:
set JAVA_OPTS=-XX:PermSize=64M -XX:MaxPermSize=128m –Xms1024m -Xmx1024m
linux下,在catalina.sh最前面增加:
JAVA_OPTS="-XX:PermSize=64M -XX:MaxPermSize=128m –Xms1024m -Xmx1024m "
-client
–server
当我们在cmd中运行-java时,黑窗口会出现-client -service这两参数.其作用是设置虚拟机运行模式;client模式启动比较快,但运行时性能和内存管理效率不如server模式,通常用于客户端应用程序。server模式启动比client慢,但可获得更高的运行性能。Windows默认为client,如果要使用server模式,就需要在启动虚拟机时加-server参数,以获得更高性能,对服务器端应用,推荐采用server模式,尤其是多个CPU的系统。在Linux,Solaris上,默认值为server模式.
JDK版本
影响虚拟机内存大小的有1.操作系统的位数2.系统的可用内存3.JDK的位数
还有JDK的版本,JDK分为32位,64位两种版本,32位装在32位系统,64位系统可以装32位和64位JDK.
64位JDK性能优于32位JDK.
测试的命 ion 报错,配置大小失败,反之成功
增加Tomcat最大连接数
使用场景
我在做完一个XXX项目后,测试时发现并发数量增加到一定程度就会很卡,于是我想到了是不是tomcat最大连接数设置有限制.果不其然,配置文件中最大值才500,于是我更改了最大连接数,根据业务我修改了连接数为1500,完美的解决了这个问题;
修改方法在conf/server.xml中默认值
<Connector port="8080" maxHttpHeaderSize="8192" maxThreads="1500"
minSpareThreads="30" maxSpareThreads="75" enableLookups="false"
redirectPort="8443" acceptCount="100" connectionTimeout="20000"
disableUploadTimeout="true" />,修改maxthreads的值即可
tomcat进行gzip压缩从而降低网络传输量,优化响应时间
tomcat 压缩设置tomcat压缩gzip启用
HTTP 压缩可以大大提高浏览网站的速度,它的原理是,
在客户端请求服务器对应资源后,从服务器端将资源文件压缩,
再输出到客户端,由客户端的浏览器负责解压缩并浏览。
相对于普通的浏览过程HTML ,CSS,Javascript , Text ,
它可以节省60%左右的流量。更为重要的是,它可以对动态生成的,
包括CGI、PHP , JSP , ASP , Servlet,SHTML等输出的网页也能进行压缩,
压缩效率也很高。
启用tomcat 的gzip压缩
要使用gzip压缩功能,你需要在Connector节点中加上如下属性
记住来源:http://www.qi788.com/info-42.html
http://www.blogjava.net/freeman1984/archive/2010/09/15/332121.html
compression="on" 打开tomcat压缩功能
compressionMinSize="10" 当超过最小数据大小才进行压缩 单位是KB
noCompressionUserAgents="gozilla, traviata" 对于以逗号隔开配置的浏览器类型不进行gzip压缩
compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain" 那些类型是需要压缩的
<Connector port="80" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" executor="tomcatThreadPool" URIEncoding="utf-8"
compression="on"
compressionMinSize="50" noCompressionUserAgents="gozilla, traviata"
compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain" />
三十二、memcached的介绍
memcached是一个用C语言开发的分布式的缓存,内部基于类似hashMap的结构。
它的优点是协议简单,内置内存存储,并且他的
分布式算法是在客户端完成的,不需要服务器端进行
通信,我们当时在做项目的时候因为考虑到项目
的高可用性高扩展性,因此在服务器部署方面采用
了apache+jk+tomcat这种负载均衡的方式,但是也带来了一个问题
就是session共享的问题,虽然可以通过session复制来解决这个
问题,但是在性能方面存在缺陷,所以最后我们采用了
用memcached+cookie的方式来替代session,这样既解决了session共享
问题,也解决了session复制那种方式所产生的性能问题。
了解(不必主动说,但别人问的话一定要知道)
memcached是以KEY-VALUE的方式进行数据存储的,
KEY的大小限制:Key(max)<=250个字符;
VALUE在存储时有限制:Value(max)<= 1M;
根据最近最少使用原则删除对象即LRU.
memcached默认过期时间:ExpiresTime(max)= 30(days)
Memcached是通过hash一致性的算法完成数据的存取的.
Memcached在存储自定义对象时候需要序列化即实现Serializable接口
优化篇
- 代码优化
代码结构层次的优化(目的:更加方便代码的维护--可维护性,可读性)
1.代码注释(代码规范)
2.工具类的封装(方便代码的维护,使代码结构更加清晰不臃肿,保证团队里代码 质量一致性)
3.公共部分的提取
代码性能的优化(目的:使程序的性能最优化)
1.使用一些性能比较高的类(bufferInputStream)
2.缓冲区块的大小(4k或者8k)
3.公共部分的提取
4.通常要用stringbuffer替代string加号拼接
- 业务优化
我们做项目的时候业务优化这方面最主要是从用户体验度角度进行考虑,减少用户操 作的步骤提高工作效率,通常有以下几种:
1.可以通过tabindex属性来改变tab键盘的操作顺序
2.可以通过回车键来进行搜索或者提交操作
3.对于单选按钮和复选按钮可以通过操作后面的文本来选择前面的单选按钮以及复选 按钮
4.添加的信息要按照id倒序进行排列
5.进行搜索操作时加入js loading操作(不仅告诉用户所进行的请求正在被处理,而 且防止用户多次点击提交操作)
6.当进行删除操作的时候要弹出提示框,警告用户要进行删除操作,是否确认。
7.根据returnURL在用户登录成功后直接跳到想要访问的资源。
8.进行删除操作时通过confirm提示用户是否确认删除操作,操作完后提示操作是否 成功。
9.减少用户操作的步骤
10.使用autocomplete插件快速进行搜索
必背,必做:
1.可以通过回车键来进行搜索或者提交操作
2.添加的信息要按照id倒序进行排列
3.进行搜索操作时加入js loading操作(不仅告诉用户所进行的请求正在被处理,而且防止用户多次点击提交操作)
4.当进行删除操作的时候要弹出提示框,警告用户要进行删除操作,是否确认,如果删除成功则弹出提示框告诉用户。
5.减少用户操作的步骤
6.通过ztree,以及kindeiditor来提高用户的体验度
- sql优化
1、SELECT子句中避免使用 *, 尽量应该根据业务需求按字段进行查询
2、尽量多使用COMMIT如对大数据量的分段批量提交释放了资源,减轻了服务器压力
3、在写sql语句的话,尽量保持每次查询的sql语句字段用大写,因为oracle总是先解析 sql语句,把小写的字母转换成大写的再执行
4、用UNION-ALL 替换UNION,因为UNION-ALL不会过滤重复数据,所执行效率 要快于UNION,并且UNION可以自动排序,而UNION-ALL不会
5、避免在索引列上使用计算和函数,这样索引就不能使用
Sql优化精简版:
1.(重点)(必须说) SELECT语句中避免使用 *,
尽量应该根据业务需求按字段进行查询
举例:如果表中有个字段用的是clob或者是blob这种大数据字段的话,
他们的查询应该根据业务需要来进行指定字段的查询,切记勿直接用*
2.(重点) 删除重复记录(oracle):
最高效的删除重复记录方法 ( 因为使用了ROWID)例子:
DELETE FROM EMP E WHERE E.ROWID > (SELECT MIN(X.ROWID)
FROM EMP X WHERE X.EMP_NO = E.EMP_NO);
3. 用>=替换>
如一个表有100万记录,一个数值型字段A,
A=0时,有30万条;
A=1时,有30万条;
A=2时,有39万条;
A=3时,有1万记录。
那么执行 A>2 与 A>=3 的效果就有很大的区别了,因为 A>2 时,
ORACLE会先找出为2的记录索引再进行比较,
而A>=3时ORACLE则直接找到=3的记录索引。
4.(重点)尽量多使用COMMIT
如对大数据量的分段批量提交
5. (重点)用NOT EXISTS 或(外连接+判断为空)方案 替换 NOT IN操作符
此操作是强列推荐不使用的,因为它不能应用表的索引。
推荐方案:用NOT EXISTS 或(外连接+判断为空)方案代替
6.(重点 必须说)LIKE操作符(大数据的全文检索使用luncene)(solr)
因为使用like不当,会导致性能问题,原因是like在左右两边都有
%的时候,不会使用索引。
如LIKE '%5400%' 这种查询不会引用索引,
而LIKE 'X5400%' 则会引用范围索引。
一个实际例子:
查询营业编号 YY_BH LIKE '%5400%' 这个条件会产生全表扫描,
如果改成 YY_BH LIKE 'X5400%' OR YY_BH LIKE 'B5400%'
则会利用 YY_BH 的索引进行两个范围的查询,性能肯定大大提高。
7.(重点,必须说)避免在索引列上使用计算和函数,这样索引就不能使用
举例:
低效:
SELECT … FROM DEPT WHERE SAL * 12 > 25000;
高效:
SELECT … FROM DEPT WHERE SAL > 25000/12;
8.(重点 必须说)用UNION-ALL 替换UNION,
因为UNION-ALL不会过滤重复数据而且不会自动排序,
所执行效率要快于UNION。
9. (优化,重点,3个方面 a.缓存 b.分段批量 c.存储过程)减少访问数据库的次数
举例:如果批量删除多条数据,可以用 delete from tableName where id
in (1,2,3)
而不要用多条delete语句进行删除
10.(重点 必须说)用TRUNCATE替代DELETE
TRUNCATE不记录日志,DELETE记录日志,所以TRUNCATE要快于DELETE
但是一旦用TRUNCATE进行删除就不能进行恢复,TRUNCATE是删除整张表的数据
不能加where条件。
==================================================================
mysql,sqlserver中如果
id为自增类型,那么如果用TRUNCATE删除,则id字段再插入数据时从1开始,
如果delete删除的话,则从删除之前的id的值继续增长。
- 防sql注入
针对防sql注入,我们通常是这样做的:
首先在前台页面对用户输入信息进行js验证,对一些特殊字符进行屏蔽,
比如:or ,单引号,--,= ,还有就是限制用户名输入的长度,我们一般
将其限制在6---13位。另外,对于用户的敏感信息我们进行Md5加密,还有
,为了增加用户体验度和用户友好度,为了不使用户看到一些详细的异常信息
我们会进行错误信息页面的定制,像404,500错误。另一个我层面讲,这样做
也是为了保护我们的一些重要信息。此外,我们会给特定的人分配定定的权限
,而不是给其分配管理员权限!
sql注入
所谓SQL注入,就是通过一些含有特殊字符的sql语句发送到服务器欺骗服务器并进行攻击。(特殊字符:or, 单引号,--,空格)
Sql注入的防护
1.永远不要信任用户的输入。对用户的输入进行校验,可以通过正则表达式(js正则或者java后台正则),或限制长度;对单引号和双"-"进行转换等。
2.永远不要使用动态拼装sql,使用参数化的sql。(永远不要使用+号拼接sql字符串,而是使用?传参的方式进行)
3.不要给用户太高的权限而根据需求进行赋权
4.对敏感信息进行加密 如md5(单向加密不可逆转)。
5.自定义错误页面。目的是为了不把我们的程序的bug暴露在别有用心的人的面前。而去不会让用户看到报错的页面,也提高了用户的体验度。
SQL注入防范
使用参数化的过滤性语句
要防御SQL注入,用户的输入就绝对不能直接被嵌入到SQL语句中。恰恰相反,用户的输入必须进行过滤,或者使用参数化的语句。参数化的语句使用参数而不是将用户输入嵌入到语句中。在多数情况中,SQL语句就得以修正。然后,用户输入就被限于一个参数。
输入验证
检查用户输入的合法性,确信输入的内容只包含合法的数据。数据检查应当在客户端和服务器端(java代码)都执行之所以要执行服务器端验证,是为了弥补客户端验证机制脆弱的安全性。
在客户端,攻击者完全有可能获得网页的源代码,修改验证合法性的脚本(或者直接删除脚本),然后将非法内容通过修改后的表单提交给服务器。因此,要保证验证操作确实已经执行,唯一的办法就是在服务器端也执行验证。你可以使用许多内建的验证对象,例如Regular Expression Validator,它们能够自动生成验证用的客户端脚本,当然你也可以插入服务器端的方法调用。如果找不到现成的验证对象,你可以通过Custom Validator自己创建一个。
错误消息处理
防范SQL注入,还要避免出现一些详细的错误消息,因为黑客们可以利用这些消息。要使用一种标准的输入确认机制来验证所有的输入数据的长度、类型、语句、企业规则等。
加密处理
将用户登录名称、密码等数据加密保存。加密用户输入的数据,然后再将它与数据库中保存的数据比较,这相当于对用户输入的数据进行了“消毒”处理,用户输入的数据不再对数据库有任何特殊的意义,从而也就防止了攻击者注入SQL命令。
存储过程来执行所有的查询
SQL参数的传递方式将防止攻击者利用单引号和连字符实施攻击。此外,它还使得数据库权限可以限制到只允许特定的存储过程执行,所有的用户输入必须遵从被调用的存储过程的安全上下文,这样就很难再发生注入式攻击了。
使用专业的漏洞扫描工具
攻击者们目前正在自动搜索攻击目标并实施攻击,其技术甚至可以轻易地被应用于其它的Web架构中的漏洞。企业应当投资于一些专业的漏洞扫描工具,如大名鼎鼎的Acunetix的Web漏洞扫描程序等。一个完善的漏洞扫描程序不同于网络扫描程序,它专门查找网站上的SQL注入式漏洞。最新的漏洞扫描程序可以查找最新发现的漏洞。
确保数据库安全
锁定你的数据库的安全,只给访问数据库的web应用功能所需的最低的权限,撤销不必要的公共许可,使用强大的加密技术来保护敏感数据并维护审查跟踪。如果web应用不需要访问某些表,那么确认它没有访问这些表的权限。如果web应用只需要只读的权限,那么就禁止它对此表的 drop 、insert、update、delete 的权限,并确保数据库打了最新补丁。
安全审评
在部署应用系统前,始终要做安全审评。建立一个正式的安全过程,并且每次做更新时,要对所有的编码做审评。开发队伍在正式上线前会做很详细的安全审评,然后在几周或几个月之后他们做一些很小的更新时,他们会跳过安全审评这关, “就是一个小小的更新,我们以后再做编码审评好了”。请始终坚持做安全审评。
五、数据库中常用术语:
ddl:数据定义语言 Create Drop Alter
dml:数据操纵语言 insert update delete select
dcl:数据控制语言 grant revoke
tcl:事务控制语言 commit rollback
面试常见43个问题
1. 说说你对缓存的理解?
2. 你对Maven是怎么理解的?
3. Spring MVC中的注解你都用过哪些,SpringMVC的运行原理是什么?
4. SSH整合的流程/SSM整合流程/SSI整合流程?
5. 谈谈你对Spring的理解?
6. HashMap和HashTable的区别?
7. StringBuffer和StringBuilder和String的区别?
8. Set,List,Collection,Collections的区别?
9. 系统管理的描述?
10. 抽象类和接口的区别?
11. 事务的概述?
12. 索引的概述?
13. 三层分页的SQL语句/单例设计模式?(手写)
14. 数据库三范式?
15. id,rowid,rownum的区别?
16. 主键和唯一索引的区别?
17. Preparedstatement和statement的区别?
18. oracle的基本数据类型?
19. 数据库连接池?
20. jdbc,ibatis,hibernate的区别?
21. SQL优化/java代码优化/Hibernate优化?
22. 给我谈谈jquery的认识?
23. 你对ajax是怎么理解的(融入ajax跨域的解决方案)?
24. 你对多线程是怎么理解的?
25. 你对webservice是怎么理解的?
26. 你对xml和json是怎么理解的?
27. 你对负载均衡这块有认识吗?
28. 你在linux上怎么部署项目?
29. 你对linux中的常用命令都了解哪些?
30. 你上家公司在哪?你在哪住?你去公司怎么坐车?
31. 你期望薪资多少?你上家工资多少?税后拿到手的有多少?扣了多少钱的税?
32. 你哪个学校毕业的?学的什么专业?你们学校校长叫什么名字?你们学校还有啥其他专业吗?你大学都学了什么?
33. 你今年多大了?属相是啥?
34. 你为啥从上家公司离职?
35. 你交社保了吗?为啥没交?
36. 你的优缺点是啥?
37. 你的五年规划是啥?
38. 你啥时候上的大学?哪年毕业的?从火车站怎么到你们学校?你们学校周围都有啥?
39. 你知道五险一金都是啥吗?
40. 你们公司有多少人?你们公司有哪几个部门?
41. 说说你对redis的理解?
42. 说说你对jms的理解?
43. 悲观锁乐观锁?
1:说说你对缓存的理解?
通常情况下缓存是为了提高应用程序的性能,减少数据库的访问次数,缓存的存储介质可以
内存或者硬盘,通常将数据存储在内存里,确切的说是jvm的内存中,缓存是
基于Map这种思想构建的,以键值对的方式进行存取,之所以还可以将
缓存的数据存储在硬盘中,是因为内存资源相当有限和宝贵,所以当内存资源
不足的时候,就可以将其存储到硬盘中,虽然硬盘的存取速度比内存要慢,但是
因为减少了网络通信量,所以还是提高程序的性能。缓存可以分为客户端缓存和
服务器端缓存,所谓的客户端缓存通常指的是IE浏览器的缓存,服务器端缓存指
的web服务器的缓存,通常可以通过第三方组件实现,如ehcache,oscache或者memcache
我们通常将那些频繁访问但是又不是经常改变的数据进行缓存。为了保证缓存数据的
有效性,在数据发生改变的时候,我们要刷新缓存,避免脏数据的出现。刷新缓存的
策略有两种,一种是定时刷新,一种手动刷新。
缓存的层次如下:jsp-->action-->service(通常放置在service)-->dao,
缓存越靠前对性能的提升越大
缓存的策略:(缓存空间不足需要进行清理的时候使用)
LRU:最近最少使用原则.(理解:存储书)
FIFO:先进先出的缓存策略.(理解:排队)
你来说说缓存?说说你对缓存的理解(如果遇到重复的,就可以省略)
我们在项目中使用缓存的目的是为了提高应用程序的性能,减少访问数据库
的次数,从而提高应用程序的吞吐量。我们通常将权限,菜单,组织机构
这些频繁访问但是不经常改变的基础数据进行缓存,其中我在做()某某项目的时候
就通过oscache对ZTree的树形菜单进行了缓存,并且在做的时候和单列设计
模式进行结合,考虑到多线程下的安全问题,还对单例模式加入了双重判定锁
的检查方式。
2:你对maven是怎么理解的?
接触到正式工作不久接触到了maven,我花费了一些时间研究了一下maven,知道maven是一个项目管理工具,其核心特点就是通过maven可以进行jar包的依赖管理,保证jar包版本的一致性,以及可以使多个项目共享jar包,从而能够在开发大型java应用的时候,减小项目的大小,maven根据“约定优于配置”的特性,可以对其项目的编译打包部署进行了更为抽象的封装,我们可以直接使用系统预定好的mvn clean,package等命令进行项目的操作。于是我就在网上购物商城系统中采用了maven,为了保证团队中的成员能够节省下载jar包所需要的时间,于是我就搭建了在局域网内的maven私服,然后通过配置settings.xml中建立mirror镜像,将所有下载jar包的请求都转发到maven私服上,之后通过在pom.xml即(project object model)中配置项目所依赖的jar包,从而达到在构建项目的时候,先从本地仓库中查找,如果不存在从内部私服查找,如果不存在最后再从外网服务器查找的机制,达到了节省下载带宽,提高开发效率,以及jar包重用的目的。
3:Springmvc中的注解你都用过哪些,springmvc的运行原理是什么?
Spring MVC概述:
我们通常使用Spring MVC来充当我们项目中的控制层,我们控制层的作用就是接受前台传递的参数,调用业务逻辑层进行业务处理以及将返回的结果集返回前台进行展示,首先我们要在web.xml中配置Spring MVC的前端总控制器DispatcherServlet并加载Spring MVC的配置文件,我们在Controller层上加上@Controller注解,使其充当控制层,并且要在Spring mvc的配置文件中通过component-scan对Controller层进行扫描从而使类中的@Controller注解生效,Spring mvc用方法来接收参数,所以我们说Spring mvc是基于方法的设计,我们也可以通过@PathVariable冲路径中获取信息,我们通常通过@Resource这个注解来进行Bean注入,他是java中的注解,而不是Spring中的,默认是按照属性名进行注入,我们也可以通过设置name属性的值,让其只能按照属性名进行注入,我们也可以用@Autowired注解来进行Bean的注入,他默认是按照类型进行注入,如果要按属性名进行注入我们需要结合@Qualifier注解使其按照名字进行注入,我们可以将返回值的类型改为ModelAndView并在配置文件中配置视图解析器的前缀和后缀,以此来给我们前台页面传递数据,也可以在方法中通过ModelMap进行返回数据。也可以通过@ResponseBody将返回的实体类或者实体类的集合转换为指定的格式进行前台页面交互。并且要在配置文件中进行相关的配置。@RequestMapping是将Url映射到具体的方法上。文件上传时我们通过@RequestParam来接收前台上传的文件。以上就是我对Spring MVC的理解和运用。
MyBatis和Spring整合:
我们通过在Dao层只创建接口这种方式来实现MyBatis,其中有一个*.xml的配置文件,其中mapper标签中的namespace必须和接口的类路径保持一致,相当于dao接口的实现类,并且可以进行Sql的编写来对数据进行增删改查的操作如果有条件判断可以通过if标签进行判断,其中还有foreach标签可以对集合进行循环遍历来进行操作,我们的批量删除时就用到了这个标签,我们和Spring整合的时候要在Spring的配置文件中通过SqlSessionFactoryBean对象下的属性进行数据库的连接,为所有的实体bean自动创建别名以及自动查找mybatis的xml配置文件,并且通过 MapperScannerConfigurer自动扫描指定包中的所有的接口,并且把sqlSession与接口结合
4:ssh的整合?ssm的整合?ssi的整合,spring MVC整合?
Ssh:
1.首先在web.xml中通过ContextLoaderListener来融入spring,
并加载spring的相关配置文件
2.同样配置sturts2的前端总控制器filterDispatcher来过滤相关的
请求并且加载struts.xml
3.action继承ActionSupport,然后通过引入struts-spring-plugin.jar
包并且根据配置文件中service的id生成get,set方法来注入service层。
4.dao层继承于HibernateDaoSupport,并且在dao的配置文件中注入sessionFactory.
5.通过spring中的配置文件加载hibernate.cfg.xml文件从而融入hibernate.
在ssh框架中是怎么整合spring?
首先在web.xml中通过ContextLoaderListener来融入spring,
并加载spring的相关配置文件
在ssh框架中是怎么整合hibernate?
通过spring中的配置文件加载hibernate.cfg.xml文件从而融入hibernate
dao层继承于HibernateDaoSupport,并且在dao的配置文件中注入sessionFactory
在ssh框架中是怎么整合struts2?
配置sturts2的前端总控制器filterDispatcher来过滤相关的
请求并且加载struts.xml
Spring MVC:
1.首先,要在web.xml里面配置SpringMVC的核心控制器,DispatcherServlet,对指定的后缀请求进行拦截。
2.Controller层要加 @Controller注解,表明该类是MVC的控制层。
3.创建Service接口,给实现类加上注解 @Component或者 @Service 表明这是Service业务处理层
4.在Controller层声明Service变量(属性),给变量(属性) 加上 @Autowired注解,通过自动绑定机制将Service注入到Controller。 (注:@Autowired默认是ByType,如果想根据属性名注入,那么就再加上注解 @Resource(name="属性名"))
5.在Controller层的方法上加上注解 @RequestMapping("requestAddress") 表明该方法的请求地址
6.Dao层要加上注解 @Repository 表明这是数据库持久层
7.同样将dao实例注入到service层中。
8.配置视图解析器 "InternalResourceViewResolver",对处理后的跳转进行统一配置。
5:谈谈你对spring的理解?
Spring是完全面向接口的设计,降低程序耦合性,主要是事务控制并创建bean实例对象。在ssh整合时,充当粘合剂的作用。IOC(Inversion of Control)控制反转/依赖注入,又称DI(依赖注入)
IOC的作用:产生对象实例,所以它是基于工厂设计模式的。
Spring IOC的注入:通过属性进行注入,通过构造函数进行注入
Spring IOC自动绑定模式:按byType只要类型一致就会自动寻找
按byName自动按属性名称进行自动查找匹配
AOP面向切面编程,OOP面向对象编程
AOP主要应用于日志记录,性能统计,安全控制,事务处理等方面。
在Spring中可以通过代理模式来实现AOP,代理模式分为
静态代理:一个接口,分别有一个真实实现和一个代理实现。
动态代理:通过代理类的代理,接口和实现类之间可以不直接发生联系,而可以在运行期(Runtime)实现动态关联。
动态代理有两种实现方式,可以通过jdk的动态代理实现也可以通过cglib来实现,而AOP默认是通过jdk的动态代理来实现的。jdk的动态代理必须要有接口支持,而cglib不需要,它是基于类的。
Spring实现IOC控制反转描述:
原来需要我们自己进行bean的创建以及注入,而现在交给Spring容器去完成bean的创建及其注入。
官方解释:
控制反转即IOC,它把传统上由程序代码直接操控的对象的调用权交给容器,通过容器来实现对象组件的装配和管理。所谓的“控制反转”概念就是对组件对象控制权的转移,从程序代码本身转移到了外部容器。
6:hashMap和hashTable的区别?
HashMap不是线程安全的,HashTable是线程安全。
HashMap允许空(null)的键和值(key),HashTable则不允许。
HashMap性能优于Hashtable。
Map
1.Map是一个以键值对存储的接口。Map下有两个具体的实现,分别是HashMap和HashTable.
2.HashMap是线程非安全的,HashTable是线程安全的,所以HashMap的效率高于HashTable.
3.HashMap允许键或值为空,而HashTable不允许键或值为空.
7:StringBuffer和StringBuilder和String的区别?
String 字符串常量 不可变 使用字符串拼接时是不同的2个空间
StringBuffer 字符串变量 可变 线程安全 字符串拼接直接在字符串后追加
StringBuilder 字符串变量 可变 非线程安全 字符串拼接直接在字符串后追加
1.StringBuilder执行效率高于StringBuffer高于String.
2.String是一个常量,是不可变的,所以对于每一次+=赋值都会创建一个新的对象,StringBuffer和StringBuilder都是可变的,当进行字符串拼接时采用append方法,在原来的基础上进行追加,所以性能比String要高,又因为StringBuffer 是线程安全的而StringBuilder是线程非安全的,所以StringBuilder的效率高于StringBuffer.
3.对于大数据量的字符串的拼接,采用StringBuffer,StringBuilder.
8:Set,List,Conllection,Collections的区别?
1.List和Set都是接口,他们都继承于接口Collection,List是一个有序的可重复的集合,而Set的无序的不可重复的集合。Collection是集合的顶层接口,Collections是一个封装了众多关于集合操作的静态方法的工具类,因为构造方法是私有的,所以不能实例化。
2.List接口实现类有ArrayList,LinkedList,Vector。ArrayList和Vector是基于数组实现的,所以查询的时候速度快,而在进行增加和删除的时候速度较慢LinkedList是基于链式存储结构,所以在进行查询的时候速度较慢但在进行增加和删除的时候速度较快。又因为Vector是线程安全的,所以他和ArrayList相比而言,查询效率要低。
9:系统管理的描述?
系统管理:
我负责项目中的系统管理模块,其中包含 用户管理,角色管理,
菜单管理以及给用户赋角色,给角色赋权限;涉及到的表有用户表,
角色表,用户角色关联表,菜单表,用户菜单关联表。
在菜单管理模块采用ztree进行菜单的增删改查操作,
为了将权限控制到按钮级别我们在进行菜单管理时会设置该菜单是属于按钮级别还是普通菜单,
通过在数据库中增加一个type类型的字段来实现,如type为1则是普通菜单,
type为2则是按钮菜单。
这样用户在登陆的时候根据用户名和密码到用户表验证信息是否合法,
如果合法 则获取用户信息,
之后根据用户id再到用户角色关联表中得到相关连的角色id集合,
之后根据角色id再到角色权限关联表中获取该角色所拥有的权限id集合,
然后再根据权限id集合到权限表(菜单表)中获取具体的菜单,
展现给当前登录用户,从而达到不同用用户看到不同的菜单权限。
为了防止用户不登录而直接访问后台资源我通过在项目中加入LoginInterceptor拦截器对未经认证的用户进行拦截,
如果未经认证则跳转到登陆页面使用户再次登陆,
除此之外还加入了PermissionInterceptor拦截器来对用户无权访问的资源进行拦截,
如果无权限访问则跳转到没有访问权限页面。
再者考虑到性能将用户登陆后所拥有的资源权限通过oscache结合单例设计模式
将数据存储到了缓存中,这样可以避免每次操作都需要重新进行查询的代价,
减少和数据库的交互次数,提高系统性能。
考虑到单列设计模式在多线程环境中会出现线程安全的问题,
所以就给单例加了双重判断锁,从而避免该问题的发生。
10:抽象类和接口的区别?
1.一个类只能进行单继承,但可以实现多个接口。
2.有抽象方法的类一定是抽象类,但是抽象类里面不一定有抽象方法;
接口里面所有的方法的默认修饰符为public abstract,接口里的成员变量默认的修饰符为 pulbic static final。
关系
接口和接口 继承
接口和抽象类 抽象类实现接口
类和抽象类 类继承抽象类
类和类 继承
11:事务的概述?
在数据库中,所谓事务是指一组逻辑操作单元即一组sql语句。当这个单元中的一部分操作失败,整个事务回滚,只有全部正确才完成提交。
事务的ACID属性
1. 原子性(Atomicity)
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,
要么都不发生。
2. 一致性(Consistency)
事务必须使数据库从一个一致性状态变换到另外一个一致性状态。(数据不被破坏)
3. 隔离性(Isolation)
事务的隔离性是指一个事务的执行不能被其他事务干扰.
4. 持久性(Durability)
持久性是指一个事务一旦被提交,
它对数据库中数据的改变就是永久性的.
在JDBC中,
事务默认是自动提交的,
每次执行一个 SQL 语句时,如果执行成功,
就会向数据库自动提交,而不能回滚
为了让多个 SQL 语句作为一个事务执行:
(1)执行语句前调用 Connection 对象的 setAutoCommit(false);
以取消自动提交事务
(2)在所有的 SQL 语句都成功执行后,调用 commit(); 方法提交事务
(3)在出现异常时,调用 rollback(); 方法回滚事务。
12:索引的概述?
1、索引的概念
索引就是为了提高数据的检索速度。
数据库的索引类似于书籍的索引。
在书籍中,索引允许用户不必翻阅完整个书就能迅速地找到所需要的信息。
在数据库中,索引也允许数据库程序迅速地找到表中的数据,而不必扫描整个数据库.
2、索引的优点
1.创建唯一性索引,保证数据库表中每一行数据的唯一性
2.大大加快数据的检索速度,这也是创建索引的最主要的原因
3.减少磁盘IO(向字典一样可以直接定位)
3、索引的缺点
1.创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加
2.索引需要占用额外的物理空间
3.当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,降低了数据的维护速度
4、索引的分类
1.普通索引和唯一性索引
普通索引:CREATE INDEX mycolumn_index ON mytable (myclumn)
唯一性索引:保证在索引列中的全部数据是唯一的
CREATE unique INDEX mycolumn_index ON mytable (myclumn)
2. 单个索引和复合索引
单个索引:对单个字段建立索引
复合索引:又叫组合索引,在索引建立语句中同时包含多个字段名, 最多16个字段
CREATE INDEX name_index ON userInfo(firstname,lastname)
3.顺序索引,散列索引,位图索引
13:三层分页以及三种单例设计模式?
select * from (select t.*, rownum rn from (select * from menu order by id desc) t where rownum < 10) where rn >=5
14:数据库的三范式?
第一范式:数据库表中的所有字段值都是不可分解的原子值。
第二范式:需要确保数据库表中的每一列都和主键相关,而不能只与主键的某一部 分相关(主要针对联合主键而言)
第三范式:需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关
15:id,rowis,rowunm的区别?
rowid物理位置的唯一标识。
而id是逻辑上的唯一标识,所以rowid查找速度要快于id,是目前最快的
定位一条记录的方式
rowid和rownum都是"伪数列"
所谓“伪数列”也就是默认隐藏的一个数列。
rownum用于标记结果集中结果顺序的一个字段,
它的特点是按顺序标记,而且是连续的,
换句话说就是只有有rownum=1的记录,才可能有rownum=2的记录。
rownum关键字只能和<或者<=直接关联
如果是>或者=则需要给他起个别名
16:主键和唯一索引的区别?
在创建主键的同时会生成对应的唯一索引,主键在保证数据唯一性的同时不允许为空,而唯一可以有一个为空数据项,一个表中只能有一个主键,但是一个主键可以有多个字段,一个表中可以有多个唯一索引。
17:PreparedStatement和statement的区别?
用Prepared statement进行开发。Prepared statement是预编译的,而statement不是,在每次执行sql语句的增删改时,如果是一条数据两者没差距,但如果数据量大于1,那么每次执行sql语句statement都要重新编译一次,而Prepared statement不用,Prepared statement的运行效率大于statement;从代码的可维护性和可读性来说,虽然用Prepared statement来代替statement会使代码多出几行,但这样的代码无论从可读性还是可维护性来说,都比直接使用statement的代码高很多档次;最重要的一点,从安全角度来说,使用Prepared statement可以大大提高程序的安全性,因为Prepared statement是用‘?’传参,可以防止sql注入,具有安全性,而statement用的是‘+’字符串拼接,安全性较低。18:oracle的基本数据类型?
1、字符型
Char 固定长度字符串 占2000个字节
Varchar2 可变长度字符串 占4000个字节
Nvarchar2 占2000个字符(最多能存2000个字母/中文)
2、大对象型(lob)
Blob :二进制数据 最大长度4G
Blob 用于存一些图片,视频,文件。
比如:当我们在进行文件上传时,我们一般把上传的文件存在硬盘上,可以不占用 数据库,如果项目迁移时,文件也要跟着迁移。因此我们可以把用blob把它存在数据库中。但这样也增加了数据库的负担。
Clob :字符数据 最大长度4G,可以存大字符串 varchar2和nvarchar2都具有一定的局限性,它们长度有限,但数据库中无论用varchar2或nvarchar2类型,还是用clob,在java端都使用String接收。
3、数值型
Integer 整数类型,小的整数。
Float 浮点数类型。
Real 实数类型。
Number(p,s)包含小数位的数值类型。P表示精度,s表示小数后的位数。
Eg: number(10,2) 表示小数点之前可有8位数字,小数点后有2位。
4、日期类型
Date 日期(日-月-年) DD-MM-YY(HH-MI-SS)
Timestamp 跟date比 它可以精确到微秒。精确范围0~9 默认为6.
19数据库的连接池?
数据库连接池的优点运行原理:
在我们不使用数据库连接池的时候,每次访问数据库都需要创建连接,
使用完成之后需要释放关闭连接,而这样是很耗费资源的。当我们使用
数据库连接池的时候,在tomcat启动的时候就创建了指定数量的连接,
之后当我们程序使用的时候就直接从连接池里面取,而不需要创建,同理,
当我们使用完的时候也不需要关闭连接,而是将连接返回到连接池中,供
其他请求继续使用。
DBCP:比较稳定。
C3P0: 性能比较高。
20:jdbc,ibatis,hibernate的区别?
Hibernate属于全自动,Ibatis数据半自动,jdbc数据纯手工,从开发效率上来讲hibernate较高,ibatis居中,jdbc较低,从执行效率上来讲hibernate较低,ibatis居中,jdbc较高,因为jdbc是手工写sql语句程序员对sql的控制能力更大,可以根据业务需要进行优化,而ibatis虽然也可以对sql进行优化,但是他里面将result封装为实体的过程中采用了反射机制所以一定程度上影响了性能,而hibernate因为高度封装所以开发效率相对较高,但正是因为这个原因,程序员对sql语句的控制和优化方面相对比较弱,在将result封装成实体的过程中也采用了反射机制,所以在性能方面比较弱
21:SQL优化,java代码优化,hibernate的优化?
SELECT语句中避免使用’*’,这样可以减少oracle解析sql语句的时间
oracle外键必须加索引。使用表的别名(Alias),这样可以减少解析的时间并且避免列名相同所引发的错误。
用NOT EXISTS 或者(外连接+判断为空)替换 NOT IN 操作符。通过rowid删除重复记录
java代码优化:避免在循环条件中使用复杂表达式。在finally块中关闭流,断开连接,释放资源。单线程应尽量使用 HashMap, ArrayList,因为HashTable,Vector使用了同步机制,降低了性能。对于大批量字符串的拼接使用stringbuffer或者stringbuilder代替string进行+拼接。解析大文件的xml数据使用sax替代dom4j,使用分段批量提交来完成大数据量的插入。 根据业务情况使用缓存减少对数据库的访问。
Hibernate优化:在处理大数据量时,会有大量的数据缓冲保存在Session的一级缓存中,这缓存大太时会严重显示性能,所以在使用Hibernate处理大数据量的,可以使用session. clear()或者session. evict(Object) ,在处理过程中,清除全部的缓存或者清除某个对象。 通过使用Hibernate的一级缓存,二级缓存,查询缓存等来提高性能 。Hibernate可以通过设置hibernate.jdbc.fetch_size,hibernate.jdbc.batch_size等属性,对Hibernate进行优化。
22:给我谈谈jquery的认识?
Jquery是一个js框架,拥有跨浏览器的特性,可以兼容各种浏览器,可以使用它的append方法、remove方法、insertAfter方法操作文档对象、通过id选择器$("#id")以及类选择器$(".class")还有标签选择器$("标签名")可以选择DOM元素、通过fadeIn以及fadeOut制作淡入淡出的动画效果、通过bind来对指定元素绑定事件、通过$.get,$.post以及$.ajax发送ajax异步请求。
23:你对ajax怎么理解的?
AJAX 全称: 异步JavaScript及 XML。
Ajax的核心是JavaScript中的XmlHttpRequest(XHR)。
使用ajax可以提高用户的体验度,进行异步数据传输从而提高性能。ajax不能跨域。所谓的不能跨域就是不能跨多个网站(多个域名),不能跨多个项目。可以通过jsonp来解决ajax跨域的问题,而jsonp的实质就是通过动态添加script标签来实现的。
24:你对多线程是怎么理解的?
1、新建状态(New):新创建了一个线程对象。
2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
(一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。
(二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
(三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
实现线程的两种方式:
是继承Thread类或实现Runnable接口,但不管怎样,当new了这个对象后,线程就已经进入了初始状态
wait和sleep的区别:
线程访问:
锁池状态,之后等待锁释放,然后访问代码
wait
等待队列(释放资源)--->调用notify或者notifyall之后锁池状态--->( 等待锁释放)--->可运行状态--->运行状态---->访问代码
sleep,join
不释放资源-->结束后直接进入可运行状态--->运行状态---->访问代码
一个java控制台程序,默认运行两个线程,一个主线程,一个垃圾回收线程。
线程与进程的区别:
1.线程(Thread)与进程(Process)
进程定义的是应用程序与应用程序之间的边界,通常来说一个进程就代表一个与之对应 的应用程序。不同的进程之间不能共享代码和数据空间,而同一进程的不同线程可以共 享代码和数据空间。
2.一个进程可以包括若干个线程,同时创建多个线程来完成某项任务,便是多线程。
25:你对webservice是怎么理解的?
26:你对xml和json是怎么理解的?
xml(可扩展标记语言)的特点:
1. 有且只有一个根节点
2. 它是数据传输的载体
3. 它的所有标签都需要自定义,标签是成对出现的
4. 它是纯文本文件
xml里几个关键的术语 :
1. 根节点(rootElement)
2. 元素(Element)(开始节点,结束节点/开始元素,结束元素)有<>号的就是元素
3. 属性(Attribute)(属性名,属性值)在开始节点中的东西就是属性
4. 文本(text)
例子:
<students(根节点也叫根元素)>
<student id="1"(id属性名,1是属性值)>
<userName>小花(文本)</userName>
<age>20(文本)</age>
</student(子节点也叫根节点)>
</students>
json(全称:JavaScriptObjectNotation(javascript对象表式))
json对象分为两种格式:
json对象(就是在{}中存储键值对,健和值之间用冒号分割,键值对之间用逗号分割)
json数组:(就是在[]中存储多个json对象,json对象之间用逗号分割(两者之间可以进行相互嵌套)数据传输的载体之一)
27:你对负载均衡这块有认识么?
我们在做这个项目时,考虑到服务器性能的问题,最开始想到使用纵向扩展,来增加硬件的配置提高其性能,但这样做比较耗费资金,而且服务器内存空间也是有限的;所以后来就使用横向扩展来达到这一目的.
当时我们使用nginx+3个tomcat进行负载均衡,在我们不进行负载均衡之前,那所有的请求都由一台tomcat进行处理,这样会使我们的tomcat所承受的压力增大,而我们进行负载均衡之后,同样数量的请求经过nginx将其分发到多台tomcat进行处理,从而降低每台tomcat所承受的压力,而且当其中一台机器宕机时,其他机器还可以继续提供服务,保证服务不间断。
当时项目在部署完成后,遇到这么个问题,用户登录输入验证码的时候,明明验证码输入的正确,但总是提醒说验证码不正确从而不能正常登录,经过分析后发现有可能第一次请求被发送到t1上,那么放在session中的验证码就被放到了t1上,当用户输入验证码点击登录时,新发送的请求有可能被发送到t2上面,这样在进行对比时就肯定会不一致从而提示验证码输入错误,后来我就考虑使用ip_hash这种负载均衡策略来代替默认的轮询策略,虽然解决了验证码错误问题,但是在后续的测试中发现如果用户在使用过程中突然一台服务器宕机了,那么因为session在这台服务器上存储着,所以就会提示用户重新登录,这样使用户的体验度非常不好,最后就通过将session信息保存到redis服务器中从而在多台web服务器中实现session共享,这样就解决了上面所说的那些问题。
同事我们为了避免ngnix的单点故障,就是在ngnix前面加上一个f5,分给多个ngnix,再通过ngnix分发给多个tomcat
28:你再linux上怎么部署项目?
1.Linux上安装jdk,配置环境变量
2.通过SSH将tomcat压缩包上传到Linux服务器,通过unzip解压缩
3.开启防火墙中的8080端口号
4.通过修改conf/server.xml部署项目
5.通过chmod +x *.sh开启执行权限
6.通过./startup.sh启动tomcat访问项目,
并通过tail -f catalina.out来查看启动日志。
29:你对linux中的常用命令都了解那些?
1. ifconfig:查看IP地址
2. java -version:查看jdk的版本
3. rpm -qa | grep 软件的名称:查找和指定名称相关的软件
4. rpm -ivh 软件名称:安装指定的软件
5. rpm -e --nodeps 软件名称:卸载指定的软件
6. uname -a:查看linux系统的基本信息(计算机名,操作的位数,版本号)
7. ll(L):用来查看当前目录下的所有文件夹
8. mkdirs 目录名:创建文件夹
9. vi 文件名:对指定的文件名进行编辑 wq!:保存并退出 。q!:不保存并退出
10. pwd:查看当前目录的完整路径
11. unzip 文件名.zip:解压后缀名为zip的压缩文件
12. mv 源文件名 目标件名:重命名的作用
13. rm -rf 文件夹名:递归强制删除文件夹及其下面的所有子文件夹
14. service iptables stop:禁用防火墙
15. chmod +x*.sh:使所有后缀名为sh的文件,拥有可执行权限
16. 在bin目录下 ./startup.sh启动tomcat
17. 在bin目录下通过tail -f ../logs/catalina.out来查看启动日志
18. ps -f | grep 进程名:查看指定进程是否启动
19. kill -9 进程号 :强制杀死进程
Redis 的理解?
redis是一个基于key,value的支持多种数据类型的可进行持久化的
内存数据库。
我们在项目中使用的时候为了保证redis服务器的安全,通常会在
redis.conf配置文件中绑定具体的ip地址,这样只有该ip地址才能
访问redis服务器,并且设置长度为20位左右的密码,从而保证
只有进行了密码授权才能进行相关的操作。
为了保证redis不会因为占用内存过大而导致系统宕机,通常在
将redis当做缓存服务器使用时,设置存储数据的过期时间,并且
通过设置maxmemory【最大内存】和maxmemory-policy【数据清除策略】
为allkeys-lru来达到预期的效果。
我们在项目中通常使用redis来充当缓存服务器来缓存分类列表,
品牌列表,热销商品,推荐商品以及该商品的关联商品等等。
使用了jedis作为客户端,并考虑到性能问题使用了jedis连接池。
考虑到redis服务器的高可用性,我们做了redis的主从复制,并且
通过加入哨兵来使redis主服务器宕机时,从服务器自动转换为主服务器
继续提供服务
42:说说你对jms的理解?
JMS是java消息服务的简称,定义了一系列的接口,是一个消息服务的标准。
ActiveMQ被称为一个JMS Provider,实现了JMS所定义的一系列接口。
MQ是消息队列的简称,分为很多种类,ActiveMQ就是其中之一。
MQ在项目中最主要的作用是用来进行项目之间的解耦,异步处理以及
应对高并发进行缓冲,可以起到削峰填谷的作用。它有两种工作模式
P2P以及发布订阅。P2P的模式相当于一对一,也就是一条消息只能被
一个消费者消费,一旦被消费后就会从消息队列中删除;支持消费者离线,
也就是说生产者发送消息到消息队列时即便消费者未启动也不影响,
这条消息会在消息队列中存放着,等到消费者启动后再获取该消息进行消费。
发布订阅模式相当于一对多,也就是一条消息可以同时被多个订阅者消费。
我们在用户注册成功后给用户发邮件这块就使用了ActiveMQ的P2P模式,
常规情况下用户注册后数据插入数据库,并调用邮件发送接口发送邮件,
成功后跳转到注册成功页面,但在发送邮件时如果因为网络等某些原因
导致发送速度过慢那么就会造成用户长久等待从而降低用户的体验度,
所以我们就使用了ActiveMQ来进行异步处理,当将数据插入数据库后,
直接将消息发送到消息队列中,然后马上跳转到注册成功页面,
之后再通过另外一个应用去读取消息队列中的消息进行邮件的发送。
我们的电商平台考虑到扩展性和高可用性的特性采用了分布式的架构,将电商平台分为
订单系统,积分系统,库存系统,短信系统等等。当时在订单系统中下订单时需要同时
向积分系统增加积分,库存系统中对该商品减库存以及通过短息系统来给用户发送短息。
考虑到如果使用webservice这种同步远程调用技术的话,如果这些系统中有一个有问题则
会产生连锁反应导致用户下订单都不能成功。所以我就采用ActiveMQ来充当消息中间件
完成各个系统间解耦。在用户下订单后将相关的订单信息发送到消息队列中,而其他各个系统
充当消费者接收消息队列中的消息并完成后续的减库存,发短信,增加积分的功能。
43: 悲观锁和乐观锁?
悲观锁(Pessimistic Lock), 每次去查询数据的时候都认为别人会修改,
所以每次在查询数据的时候都会上锁,
这样别人想拿这个数据就会阻塞直到它拿到锁。
传统的关系型数据库里边就用到了这种锁机制,
比如通过select ....for update进行数据锁定。
乐观锁(Optimistic Lock), 每次去查询数据的时候都认为别人不会修改,
所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新
这个数据,可以使用版本号,时间戳等机制。乐观锁适用于读比较多的
应用类型,这样可以提高吞吐量。
44、你对ajax的理解:
AJAX 全称: 异步JavaScript及 XML。
Ajax的核心是JavaScript中的XmlHttpRequest(XHR)。
使用ajax可以提高用户的体验度,进行异步数据传输从而
提高性能。ajax不能跨域。所谓的不能
跨域就是不能跨多个网站(多个域名),不能跨多个项目。
可以通过jsonp来解决ajax跨域的
问题,而jsonp的实质就是通过动态添加script标签来实现的。
45、你对jquery的理解:
Jquery是一个js框架,拥有跨浏览器的特性,可以兼容各种浏览器,
可以使用它的append方法、remove方法、insertAfter方法操作文档对象、
通过id选择器$("#id")以及类选择器$(".class")还有标签选择器$("标签名")
可以选择DOM元素、通过fadeIn以及fadeOut制作淡入淡出的动画效果、
通过bind来对指定元素绑定事件、通过$.get,$.post以及$.ajax发送ajax
异步请求。
46、tomcat调优
1.使用64位的tomcat和jdk
2.开启server模式
3.通过-Xms和-Xmx设置初始堆大小和最大堆大小,通常将两个值设置为一样,避免堆空间不断增大和缩小所带来的性能损耗
4.启用gzip压缩
5.通过maxThreads增加tomcat的最大并发数,将其设置为500
47、bootstrap?
bootstrap是Twitter(推特)推出的一个应用于前端开发的开源工具包,能够在各种设备上构建出响应式页面布局。
bootstarp自身提供了很多组件,例如:导航条,面盘,菜单,form表单,还有栅格系统等。而且都是支持响应式的,在各种设备上都能进行完美的展现。
这里面我感觉最有价值的就是栅格系统它将整个页面分为12列,而且可以根据屏幕的大小进行自适应,这是响应式的关键所在,在使用的时候注意;最外层的样式是container,里面为row,row里面包含列,列里面填充各种组件。
我们在项目中使用bootstrap大概是这样的,首先使用它的导航栏,并且将其固定在顶部,为了防止拖拉时不至于看不到,之后在导航栏下面我们采用了栅格系统将其分为两部分,左边使用它的panel -group和accordion来完成二级菜单的手风琴效果,右边进行上下分切,上面采用它的这个form表单组件,下面使用它的响应式表格以及分页组件,在进行增加修改时使用了第三方的bootbox弹出框从而完成页面的大致布局。
48、easyUI?
我们在 项目中通常用easyui来充当展示层,它是一个ria富客户端框架,自身提供了许多插件,可以提高用户体验度,而且也可以有助于后台开发人员直接是使用,提高开发效率,使我们后天程序员在没有前台美工的参与下也能写出较为美观的功能丰富的页面。
在项目中常用到的组件有:layout布局、easyUI的tree、tab页、datagrid、form表单、dialog对话框、message提示信息、还有accordion手风琴效果、progress进度条等;
在项目中的通常生成easyui的tree的方式有两种:
一种是通过一条sql语句直接查询出所有的菜单信息,然后再java代码中通过这个递归的方式将其拼装成制定的json格式的数据,这种方式适合数据量较小的情况,也减少了与数据库的访问次数,所以也太提高了性能。
另一种是通过ajax异步加载的方式,没点击一下树节点就向后台发送一次请求,从而来获取该节点下的子节点。这种方式适合用于大数据量,因为如果在数据量过大时再采取递归的方式很可能会出现内存溢出的问题。
我们在项目中使用easyui的tree是通过第一种方式也就是递归方式来拼装成制定的josn格式的数据来生成我们得菜单的。因为考虑大菜单信息的数据量不是很大,所以也不会出现内存溢出的问题
我们在项目中对于菜单这方面呢,考虑到菜单的数据量并不是很大,所以就采用了第一种方式也就是递归的方式拼装指定格式的json数据来生成的菜单,这样同时也提高了项目的性能,又考虑到菜单信息不是经常改变的所以又结合oscache缓存以及双重判定所的单例模式的特性来将其缓存到内存中,进一步提高了项目的性能,这呢就是我对easyui的tree的使用的概述。
在点击树形菜单动态添加tab页时一定要注意,为了避免每次点击都添加一个新的tab页,我们得做法是当点击时间发生时,先判断当前选中的菜单所对应的tab页在这里用的是selected方法,是否已经存在,如果存在就激活选中;如果没有,就创建新的tab页。在这个后续的开发中呢,还遇到这样一个问题,如果存在多个tab页,我们对一个tab页上的信息进行了变动,其他的tab页的数据它可能不同步,这挺尴尬了,当时我想到的解决方法是在这个tab页上加上一个刷新按钮,通过点击刷新按钮给所对应的tab页所对应的iframe的src属性重新赋值,来达到刷新tab页的目的。
datagrid也是我们在 项目中经常使用到的,在使用时应注意的是在拼装json数据中,需要有total和rows两个属性,其中tatal为总条数,rows为当前页的数据列表,在前台页面中需要保证columns中的filed和rows中的属性名相对应,否则数据展示不出来,而且在进行图片等指定格式的数据展示时,需要用这个formatter进行格式化,否则展示不出来。最后呢在进行数据的相关操作时是调用datagrid的reload方法进行刷新的。
49、dom4j如何生成xml文件?
· 创建一个document对象
· 根据document对象创建element根标签
· 根据根标签创建字标签element
· 设置属性attribute
· 设置值
50、sax解析xml核心:
startDocument():
startElement():
characters()
endElement():
endDocument():
51、POI如何导出excel?
· 获取符合条件的数据
· 转换为指定的格式(POI如何生成excel?)
o 创建一个workbook
o 根据workbook创建sheet
o 根据sheet创建row
o 根据row创建cell
o 设置cell的值
· 下载
52、springMVC ?
我们在项目中通常是这样使用springMVC的,springMVC通常作为控制层,通常我们以前是结合spring ibatis使用的,通常我们首先在web.xml里面通过dispatcherservlet springMVC的前端总控制器来加载springMVC的配置文件,从而融入咱们的springMVC,同时也在web.xml里面通过contextLoadLIstener 来加载spring的相关配置文件把spring融入进来,springMVC充当控制层,所以在使用过程中会建立一个普通的java类,然后通过@controller注解让java类充当控制层,然后我们为了能接收前台请求调用具体的方法,我们会使用@requestMaping注解,从而把具体的请求和方法利用起来,我们接收前台表单传递过来的参数用的是在Controller层方法里面传递的参数从而接收前台表单的信息。在这呢,controller层的返回值有这么几种类型,要么是string对应的是一个具体的页面,当然,这需要配合配置文件里的内容视图解析器来结合使用,从而跳转到一个具体的页面,跳转有2中方式forword跳转和redirect跳转(重定向),之后除了返回string类型之外呢,还可以返回modelAndView类型,他的作用是不仅可以跳转到一个具体的视图页面,而且可以带着数据跳转到前台页面进行展示,在这除了返回string和ModelAndView,如果是ajax的话,我们可以返回void类型,然后我们用自己封装的outJson方法可以将json传到前台,还可以结合springMVC里的@responbody注解但是@responbody也是需要在配置文件里面做相关的配置,responbady将实体类转换为json格式的过程中用的是jackJson这个第三方工具类,我们除了这些注解意外,还记得当初在做springMVC上传的时候,用到了一个@requestParam这个注解吗,同样在配置文件里要配置和上传相关的一些类,还有一些信息,从而使springMVC进行上传。再者还记得在项目中用的这样的一个注解@patvalue。它的作用就是获取路径里面的变量信息来充当参数进行查询就是咱们通过${product.id}.jhtml。这样通过路径里面来获取id的唯一编号从而来获取相关的信息,这是我们控制层目前的注解,当然我们控制层因为要注入service层,我们不会用到@resouse或者@autowired,就我所知@resouce它是默认按照名字进行注入的,然后当找不到的情况下。他会自动转换为按bytype进行注入,但是我们在项目中,通常会强制限制他用byName的方式进行注入,我们这样做就是在他后面再加上name等于我们想要访问的bean的id从而让他强制按照byName来诸如。
@autowired他默认按照bytype注入,也可以结合@qualifier按照我们指定的明智进行相关注入,这个@resource 是java提供的一个注解,不需要引用第三方的包,而@autowired是spring框架里面提供的一个包,这是控制层,通常我们在service层的实现类上加上@service的注解,并且为了注入的时候方便我们还会在@service里面写上一个name值,给他指定名字,dao层因为我们使用的是ssi框架,dao层继承sqlMapClientDaoSupport,并且在上面加@Repository注解,您也知道,dao层要和数据库进行交互,如果用ibatis的话,他的核心类是sqlMapClient ,我们是在配置文件中进行配置的,并且我们配置文件的上面加一个好像叫做default autowired byName让他根据名字自动把sqlMapClient注入到dao层的实现类中,从而能够跟咱们的数据库进行交互,这就是我们在项目中是怎么使用springMVC的,包括spring和ibatis的一个整合的一个过程,其他的也没有什么,这就是我哦对他的一个认识。
53、Linux上部署项目:
1.Linux上安装jdk,配置环境变量
2.通过SSH将tomcat压缩包上传到Linux服务器,通过unzip解压缩
3.开启防火墙中的8080端口号
4.通过修改conf/server.xml部署项目
5.通过chmod +x *.sh开启执行权限
6.通过./startup.sh启动tomcat访问项目,
并通过tail -f catalina.out来查看启动日志。
54、maven?
知道maven是一个项目管理工具,其核心特点就是通过maven可以进行包的依赖管理,保证jar包版本的一致性,以及可以使多个项目共享jar包,从而能够在开发大型j2ee应用的时候,减小项目的大小,并且和ant比起来,maven根据“约定优于配置”的特性,可以对其项目的编译打包部署进行了更为抽象的封装,使得自己不需要像ant那样进行详细配置文件的编写,直接使用系统预定好的mvn clean,compile,test,package等命令进行项目的操作。于是我就在上个项目中采用了maven,为了保证团队中的成员能够节省下载jar包所需要的时间,于是我就采用nexus搭建了在局域网内的maven私服,然后通过配置settings.xml中建立mirror镜像,将所有下载jar包的请求都转发到maven私服上,之后通过在pom.xml即(project object model)中配置项目所依赖的jar包,从而达到在构建项目的时候,先从本地仓库中查找,如果不存在从内部私服查找,如果不存在最后再从外网central服务器查找的机制,达到了节省下载带宽,提高开发效率,以及jar包重用的目的。
55、简历个人资料准备
姓名:xxx
年龄:26
出生年月:1990-10-22
学历:
大学 :2008年9月——2012年7月
高中 : 2005年9月——2008年6月
初中 : 2002年9月——2005年6月
河北科技大学 校长:孙鹤旭 专业:计算机科学与技术
专业课程:
微机原理及应用、数据结构、数据库原理及应用、编译原理、操作系统、计算机组成 原理、软件工程、离散数学、计算机网络、多媒体技术等。
学校地址:河北省石家庄市东岗路116号 邮编:050031
从家到学校的路径:
中校区地址:河北省石家庄市裕华东路70号 邮编:050018
东校区地址:河北省石家庄市东岗路116号 邮编:050031
西校区地址:现已拆除
理工学院校区:石家庄市东开发区湘江道58号 邮政编码:050035
新校区地址:石家庄市裕翔街666号。裕华区南栗村以南,规划中的南三环路以北,体育大街以西,建设大街以东,与规划中的体育中心、会展中心遥相呼应。
身份证号:130434199010223918
上家公司:北京创原天地有限公司
公司地址:朝阳区安定路33号化信大厦A座12层
怎么去公司的:育新花园---8号线-----北土城换乘10号线-------安贞门站下车----走北土城东路走上200米左右到安定路辅路再走上200米左右就到了。
第一个项目:河北三佳购物网上商城
第二个项目:
为什么需要四五天入职:回家,换房子。
57、商品管理模块的开发,这个模块包含
商品管理,商品分类管理,商品参数管理,商品属性管理,规格管理,品牌管理;
在数据库中涉及到的表有产品表,产品图片表,品牌表,分类表,产品参数组表,产品参数表,产品参数值表等。
通常先在商品分类管理模块中建立产品的分类,该分类是一个树形表结构,我通过全部取出数据并对取出的数据进行递归算法生成树形结构的数据,并将其数据渲染为具有层次嵌套关系的下拉列表以及表格控件从而在页面进行使用,因为在后台的很多地方都使用到分类信息,而分类信息又不容易发生变化,所以我就结合oscache将分类信息进行缓存,并封装到BaseController中,从而方便在其他Controller中直接调用该方法。
建立分类后可以在商品参数管理模块中建立和指定分类绑定的参数组以及参数信息,之后在添加产品时就可以选择产品所属的分类然后就会出现和该分类绑定的参数组以及参数信息供公司内部业务人员填写,保存后将参数信息保存到参数值表中,普通用户可以在 产品详情页 的 商品参数 中找到。
规格管理,属性管理的操作流程和上述流程类似。
58、统计分析模块:
统计模块由 会员统计,店铺统计,销量统计等构成,
其中会员统计可以按天、周、月通过echarts的
折线图对新增的会员进行统计分析,通过echarts的饼状图
对会员所在区域进行统计,并且可以根据会员
的下单量,下单商品件数,下单金额分别进行统计分析
计算出买家排行top10,并支持将这会员信息通过poi导出
excel结合javamail发送到指定人的邮箱中。
59、购物车及订单支付:
在详情页点击“加入购物车”或者“查看购物车”的时候我们做了一个判断,假如用户没有登录,我们会将用户加入到购物车中的商品信息存入到cookie中,然后在用户点击"去结算"时提醒用户进行登陆,并将购物车中的信息和用户关联起来存入到redis中,如果用户已经登陆则用户将商品加入购物车时直接将信息存入redis中,并设置过期时间,这样做的目的是为了防止用户频繁的对购物车进行操作增加系统压力,而redis可以看做是一个持久化的缓存。之后用户填写完收获地址信息以及选择支付方式,点击“提交订单”时会将reidis中的信息取出来插入到数据库的订单表中,并清除reidis中该购物车的信息,同时将订单表中该条记录的状态设置为1,接下来用户可以选择进行支付,支付成功后会将订单的状态改为2,并将财付通返回的交易信息存入数据库的交易记录表中,同时调用webservice接口发送短信通知用户支付成功,此时的订单已进入后台去处理,前台再查看订单时,我们会根据状态显示不同的信息,比如:状态1是未支付订单,状态为2的是支付成功等待审核状态,当后台审核通过状态会改为3,当发货时状态会改为4,这时客户就可以确认收货,确认收货后状态改为5。
注意:订单支付采用的是 财付通(腾讯),支付宝(淘宝)。
商品评论模块:
为了控制商品评论,我们设置了只有订单状态为5,也就是交易成功收货后才能评论,并在评论成功后将订单状态改为6。
在存储商品评论时我们采用了Mongodb,MongoDB是NoSQL的非关系型数据库,易于扩展,可以进行分布式文件存储,适用于大数据量、高并发、弱事务的互联网应用,因此我在项目中使用它来存储电商产品详情页的评论信息(评论id,商品id,标题,评分,内容,评论人信息,评论的发布时间,评论标签组),并且为了提高可用性和高并发用了3台服务器做了mongodb的副本集,其中一台作为主节点,另外两台作为副本节点,这样在任何一台mongodb服务器宕机时就会自动进行故障转移,不会影响应用程序对mongodb的操作,为了减轻主节点的读写压力过大的问题,我还对mongodb副本集做了读写分离,使写操作在主节点进行,读取操作在副本节点进行。
雪崩是指平时正常调用和被调用的A系统和B系统,突然A系统对B系统的访问超出B系统的承受能力,造成B系统崩溃。
注意将雪崩效应与拒绝服务攻击相区别:两者都是因为B系统过载导致崩溃,但后者是人为的蓄意攻击。
注意将雪崩效应与访问量激增区别:如果A系统直接面对用户,那么激增的用户将直接带来A系统和B系统的流量激增,不过这种case可以通过预估而对A系统和B系统做扩容应对。
一种雪崩case
如果A系统的访问量很平稳,那么是什么造成B的访问量激增呢?造成雪崩的原因很多,本文给出一个比较典型的case。首先先来看一个被称为火柴棍的体系结构:
A系统依赖B系统读服务,A系统是60台机器组成的集群,B系统是6台机器组成的集群,之所以6台机器能够扛住60台机器的访问,是因为A系统并不是每次都访问B,而是首先请求缓存,只有缓存的相应数据失效时才会请求B。
这正是cache存在的意义,它让B系统节省了大量机器,如果没有cache,B系统不得不组成60台机器的集群,如果A也同时依赖C系统呢?C系统也要60台机器,放大的流量将很快耗尽公司的资源。
然而这也不是十全十美,这个结构就像一个火柴棍顶住了上面的一辆大卡车,原因并不是火柴棍有多牛逼,而是因为大卡车上绑满了氢气球,而氢气球就是cache。我想你应该看出问题来了,如果气球破了不就傻逼了?没错,这就是雪崩的一种方式!
回到A和B的架构,造成雪崩的原因至少有下面三种:
1、B系统的前置代理发生故障或者其他原因造成B系统暂时不可用,等B系统系统服务恢复时,A系统流量将铺天盖地打过来。
2、Cache系统故障,A系统的访问将铺天盖地的打到B系统。
3、Cache故障恢复,但这时Cache为空,Cache瞬间命中率为0,相当于Cache被击穿
第一个原因不太好理解,为什么B系统恢复后流量会猛增呢?主要原因就是缓存的超时时间。当有数据超时的时候,A系统会访问B系统,但是这时候B系统偏偏故 障不可用,那么这个数据只好超时,等发现B系统恢复时,B系统发现缓存里的数据已经都超时了,都成了旧数据,这时当然所有的请求就打到了A。
也许你很快就给出解决方案:既然引入Cache会有这么多雪崩隐患,那么去掉Cache,让B系统服务能力大于或等于A系统不就得了。
这种方案确实能消除雪崩,但却不是一条正确路子!这句话解释起来有点麻烦,而是举一个栗子说明:众所周知资本主义对于封建社会是一种制度进步,但是资本主 义本身也会有各种各样的新问题,这些问题在封建社会中可能并不存在,如果想解决资本主义的这些问题难道要退回到封建社会么?
雪崩的预防
Client端的方案
所谓Client端指的就是上文结构中的A系统,相对于B系统,A系统就是B系统的Client。
针对造成雪崩的三个原因:B系统故障恢复、Cache故障、Cache故障恢复,看看A系统有哪些方案可以应对。
合理使用Cache应对B系统宕机
cache的每个Key除了对应Value,还对应一个过期时间T,在T内,get操作直接在Cache中拿到Key对应Value并返回。但是在T到达时,get操作主要有五种模式:
1、基于超时的简单(stupid)模式
在T到达时,任何线程get操作发现Cache中的Key和对应Value将被清除或标记为不可用,get操作将发起调用远程服务获取key对应的Value,并更新写回Cache;
2、基于超时的常规模式
在T到达时,Cache中的Key和对应Value将被清除或标记为不可用,get操作将调用远程服务获取key对应的Value,并更新写回Cache;
此时,如果另一个线程发现key和Value已经不可用,get操作还需要判断有没有其他线程发起了远程调用,如果有,那么自己就等待,直到那个线程远程获取操作成功,Cache中得key变得可用,自己直接从Cache中获取就完事了。
为了便于理解,打个比方:5个工人(线程)去港口取同样key的货(get),发现货已经过期被扔掉了,那么只需派出一个人去对岸取货,其他四个人在港口 等待即可,而不用5个人全去。而基于超时的简单模式中,就相当于5个工人去港口取同样key的货,发现没有,则5个工人都去远方港口取货,5个工人彼此完 全无沟通,视彼此不存在。
实现基于超时的常规模式就需要用到经典的Java Synchronized-double-check惯用法了。
3、基于刷新的简单(stupid)模式
在T到达时,Cache中的key和相应Value不动,但是如果有线程调用get操作,将触发refresh操作,根据get和refresh的同步关系,又分为两种模式:
同步模式:任何线程发现key过期,都触发一次refresh操作,get操作等待refresh操作结束,refresh结束后,get操作返回当前 Cache中key对应的Value,注意refresh操作结束并不意味着refresh成功,还可能抛了异常,没有更新Cache,但是get操作不 管,get操作返回的值可能是旧值。
异步模式:任何线程发现key过期,都触发一次refresh操作,get操作触发refresh操作,不等refresh完成,直接返回Cache中的旧值。
4、基于刷新的常规模式
在T到达时,Cache中的key和相应Value不动,但是如果有线程调用get操作,将触发refresh操作,根据get和refresh的同步关系,又分为两种模式:
同步模式:get操作等待refresh操作结束,refresh结束后,get操作返回当前Cache中key对应的Value,注意refresh操 作结束并不意味着refresh成功,还可能抛了异常,没有更新Cache,但是get操作不管,get操作返回的值可能是旧值。如果其他线程进行get 操作,key已经过期,并且发现有线程触发了refresh操作,则自己不等refresh完成直接返回旧值。异步模式:get操作触发refresh操 作,不等refresh完成,直接返回Cache中的旧值。那么显然,如果其他线程进行get操作,key已经过期,并且发现有线程触发了refresh 操作,则自己不等refresh完成直接返回旧值。
再举上面码头工人的例子说明基于刷新:这次还是5工人去港口取货,这时货都在,虽然已经旧了,这时5个工人有两种选择:
派一个人去远方港口取新货,其余哥四个拿着旧货先回(同步模式);
通知一个雇佣工去远方取新货,哥五个都拿着旧货先回(异步模式);
基于刷新的简单模式和基于刷新的常规模式的区别可以参考基于超时的简单模式和基于超时的常规模式的区别,不再赘述。
5、基于刷新的续费模式
该模式和基于刷新的常规模式唯一的区别在于refresh操作超时或失败的处理上。在基于刷新的常规模式中,refresh操作超时或失败时抛出异常,Cache中的相应key-Value还是旧值,这样下一个get操作到来时又会触发一次refresh操作。
在基于刷新的续费模式中,如果refresh操作失败,那么refresh将把旧值当成新值返回,这样就相当于旧值又被续费了T时间,后续T时间内get操作将取到这个续费的旧值而不会触发refresh操作。
基于刷新的续费模式也像常规模式那样分为同步模式和异步模式,不再赘述。
下面讨论这5种cache get模式在雪崩发生时的表现,首先假设如下:
假设A系统的访问量为每分钟M次
假设cache能存Key为C个,并且key空间有N个
假设正常状态下,B系统访问量为每分钟w次,显然w=未命中数+过期数这时因为某种原因,比如B长时间故障,造成cache中得key全部过期,B系统这时从故障中恢复,五种get模式分析表现分析如下:
1、在基于超时和刷新的简单模式中,B系统的瞬间流量将达到和A的瞬时流量M大体等同,相当于cache被击穿。这就发生了雪崩,这时刚刚恢复的B系统将肯定会被拍死。
2、在基于超时和刷新的常规模式中,B系统的瞬间流量将和cache中key空间N大体等同。这时是否发生雪崩,B系统能否扛得住就要看key空间N是否超过B系统的流量上限了。
3、在基于刷新的续费模式中,B系统的瞬间流量为w,和正常情况相同而不会发生雪崩!实际上,在基于刷新的续费模式中,不存在cache key全部过期的情况,就算把B系统永久性的干掉,A系统的cache也会基于旧值长久的平稳运行!
从B系统的角度看,能够抵抗雪崩的基于刷新的续费模式完胜。
从A系统的角度看,由于一般情况下A系统是一个高访问量的在线web应用,这种应用最讨厌的一个词就是“线程等待”,因此基于刷新的各种异步模式较优。
综合考虑,基于刷新的异步续费模式是首选。
然而凡是有利就有弊,有两点需要注意的地方:
1、基于刷新的模式最大的缺点是key-Value一旦放入cache就不会被清除,每次更新也是新值覆盖旧值,GC永远无法对其进行垃圾收集。而 基于超时的模式中,key-Value超时后如果新的访问没有到来,内存是可以被GC垃圾回收的。所以如果你使用的是寸土寸金的本地内存做cache就要 小心了。
2、基于刷新的续费模式需要做好监控,不然有可能发生cache中得值已经和真实的值相差很远了,应用还以为是新值,而你也不知道。
关于具体的cache,来自Google的Guava本地缓存库支持上文的第二种、第四种和第五种get操作模式。
但是对于Redis等分布式缓存,只提供原始的get、set方法,而提供的get仅仅是获取,与上文提到的五种get操作模式不是一个概念。开发者想用这五种get操作模式的话不得不自己封装和实现。
五种get操作模式中,基于超时和刷新的简单模式是实现起来最简单的模式,但遗憾的是这两种模式对雪崩完全无免疫力,这可能也是雪崩在大量依赖缓存的系统中频繁发生的一个重要原因吧。
应对分布式cache宕机
如果是Cache直接挂了,那么上面的基于刷新的异步续费模式也就然并卵了。这时A系统铁定无法对Cache进行存取操作,只能将流量完全打到B系统,B系统面对雪崩在劫难逃...
本节讨论的预防Cache宕机仅限于分布式Cache,因为本地Cache一般和A系统应用共享内存和进程,本地Cache挂了A系统也挂了,不会出现本地Cache挂了而A系统应用正常的情况。
首先,A系统请求线程检查分布式cache状态,如果无应答等说明分布式Cache挂了,则转向请求B系统,这样一来雪崩将压垮B系统。这时可选的方案如下:
1、A系统的当前线程不请求B系统,而是打个日志并设置一个默认值。
2、A系统的当前线程按照一定概率决定是否请求B系统。
3、A系统的当前线程检查B系统运行情况,如果良好则请求B系统。
方案1最简单,A系统知道如果没有Cache,B系统可能扛不住自己的全部流量,索性不请求B系统,等待Cache恢复。但这时B系统利用率为0,显然不是最优方案,而且当请求不容易设置默认值时,这个方案就不行了。
方案2可以让一部分线程请求B系统,这部分请求肯定能被B系统hold住。可以保守的设置这个概率u=B系统的平均流量/A系统的峰值流量
方案3是一种更为智能的方案,如果B系统运行良好,当前线程请求,如果B系统过载,则不请求,这样A系统将让B系统处于一种宕机与不宕机的临界状 态,最大限度挖掘B系统性能。这种方案要求B系统提供一个性能评估接口返回yes和no,yes表示B系统良好,可以请求;no表示B系统情况不妙,不要 请求。这个接口将被频繁调用,必须高效。
方案3的关键在于如何评估一个系统的运行状况。一个系统中当前主机的性能参数有cpu负载、内存使用率、swap使用率、gc频率和gc时间、各个 接口平均响应时间等,性能评估接口需要根据这些参数返回yes或者no,是不是机器学习里的二分类问题?关于这个问题已经可以单独写篇文章讨论了,再这里 就不展开了,你可以想一个比较简单傻瓜的保守策略,结果无非是让A系统无法很好的逼近B系统的性能极限。
综合以上分析,方案2比较靠谱。如果选择方案3,建议由专门团队负责研究并提供统一的系统性能实时评估方案和工具。
应对分布式cache宕机后的恢复
不要以为成功hold住分布式cache宕机就万事大吉了,真正的考验是分布式Cache从宕机过程恢复之后,这时分布式Cache中什么都没有。
即使是上文中提到了基于刷新的异步续费策略这时也没有什么卵用,因为分布式Cache为空,无论如何都要请求B系统。这时B系统的最大流量是Key的空间取值数量。
如果Key的取值空间数量很少,则相安无事;如果Key的取值空间数量大于B系统的流量上限,雪崩依然在所难免。
这种情况A系统很难处理,关键原因是A系统请求cache返回key对应value为空,A系统无法知道是因为当前Cache是刚刚初始化,所有内容都为空;还是因为仅仅是自己请求的那个key没在Cache里。
如果是前者,那么当前线程就要像处理cache宕机那样进行某种策略的回避;如果是后者,直接请求B系统即可,因为这是正常的cache使用流程。
对于cache宕机的恢复,A系统真的无能为力,只能寄希望于B系统的方案了。
Server端的方案
相当于client端需要应对各种复杂问题,Server端需要应对的问题非常简单,就是如何从容应对过载的问题。无论是雪崩也要,还是拒绝服务攻击也罢,对于Server端来说都是过载保护的问题。对于过载保护,主要有两种现实方案和一种超现实方案。
流量控制
流量控制就是B系统实时监控当前流量,如果超过预设的值或者系统承受能力,则直接拒绝掉一部分请求,以实现对系统的保护。
流量控制根据基于的数据不同,可分为两种:
1、基于流量阈值的流控:流量阈值是每个主机的流量上限,流量超过该阈值主机将进入不稳定状态。阈值提前进行设定,如果主机当前流量超过阈值,则拒绝掉一部分流量,使得实际被处理流量始终低于阈值。
2、基于主机状态的流控:每个接受每个请求之前先判断当前主机状态,如果主机状况不佳,则拒绝当前请求。
基于阈值的流控实现简单,但是最大的问题是需要提前设置阈值,而且随着业务逻辑越来越复杂,接口越来越多,主机的服务能力实际应该是下降的,这样就需要不断下调阈值,增加了维护成本,而且万一忘记调整的话,呵呵……
主机的阈值可以通过压力测试确定,选择的时候可以保守些。
基于主机状态的流控免去了人为控制,但是其最大的确定上文已经提到:如何根据当前主机各个参数判断主机状态呢?想较完美的回答这个问题目测并不容易,因此在没有太好答案之前,我推荐基于阈值的流控。
流量控制基于实现位置的不同,又可以分为两种:
1、反向代理实现流控:在反向代理如Nginx上基于各种策略进行流量控制。这种一般针对Http服务。
2、借助服务治理系统:如果Server端是RMI、RPC等服务,可以构建专门的服务治理系统进行负载均衡、流控等服务。
3、服务容器实现流控:在业务逻辑之前实现流量控制。
第三种在服务器的容器(如Java容器)中实现流控并不推荐,因为流控和业务代码混在一起容易乱;其次实际上流量已经全量进入到了业务代码里,这时的流控只是阻止其进入真正的业务逻辑,所以流控效果将打折;再次,如果流量策略经常变动,系统将不得不为此经常更改。
因此,推荐前两种方式。
最后提一个注意点:当因为流控而拒绝请求时,务必在返回的数据中带上相关信息(比如“当前请求因为超出流量而被禁止访问”),如果返回值什么都没有 将是一个大坑。因为造成调用方请求没有被响应的原因很多,可能是调用方Bug,也可能是服务方Bug,还可能是网络不稳定,这样一来很可能在排查一整天后 发现是流控搞的鬼。。。
服务降级
服务降级一般由人为触发,属于雪崩恢复时的策略,但为了和流控对比,将其放到这里。
流量控制本质上是减小访问量,而服务处理能力不变;而服务降级本质上是增强服务处理能力,而访问量不变。
服务降级是指在服务过载时关闭不重要的接口(直接拒绝处理请求),而保留重要的接口。比如服务由10个接口,服务降级时关闭了其中五个,保留五个,这时这个主机的服务处理能力将增强到二倍左右。
然而,雪崩到来时动辄就超出系统处理能力10倍,而服务降级能使主机服务处理能力提高10倍么?显然很困难,看来服务降级并不能代替流控成为过载保护的主要策略。
动态扩展
动态扩展指的是在流量超过系统服务能力时,自动触发集群扩容,自动部署并上线运行;当流量过去后又自动回收多余机器,完全弹性。
这个方案是不是感觉很不错。但是就算是前两年云计算泡沫吹的最天花乱坠的时候也没见哪个国内大公司真正用起来。据说亚马逊、谷歌可以,但是天朝不让用,自研的话还是任重道远呐!
雪崩的恢复
雪崩发生时需要运维控制流量,等后台系统启动完毕后循序渐进的放开流量,主要目的是让Cache慢慢预热。流量控制刚开始可以为10%,然后 20%,然后50%,然后80%,最后全量,当然具体的比例,尤其是初始比例,还要看后端承受能力和前端流量的比例,各个系统并不相同。
如果后端系统有专门的工具进行Cache预热,则省去了运维的工作,等Cache热起来再发布后台系统即可。但是如果Cache中的key空间很大,开发预热工具将比较困难。
结论
“防患于未然”放在雪崩的应对上也适合,预防为主,补救为辅。综合上文分析,具体的预防要点如下:
1、调用方(A系统)采用基于刷新的异步续费模式使用Cache,或者至少不能使用基于超时或刷新的简单(stupid)模式。
2、调用方(A系统)每次请求Cache时检查Cache是否可用(available),如果不可用则按照一个保守的概率访问后端,而不是无所顾忌的直接访问后端。
3、服务方(B系统)在反向代理处设置流量控制进行过载保护,阈值需要通过压测获得。
深入java虚拟机以及大数据
一、jvm的相关概念
当List放了大量的数据超过jvm中所能容纳的内存后,就会发生堆溢出。
当递归调用没有临界退出条件就会出现 栈溢出。
当批量导入大量数据或者用dom4j解析大的xml文件的时候,
会出现 堆溢出,这个时候可以通过分段批量提交以及用
sax代替dom4j来解决问题。
heap(堆),stack(栈)
jvm的结构细分及其概述?
Java 虚拟机有一个堆,堆是运行时数据区域,
所有类实例和数组的内存均从此处分配。
堆是在 Java 虚拟机启动时创建的。”
“在JVM中堆之外的内存称为非堆内存(Non-heap memory)”。
可以看出JVM主要管理两种类型的内存:堆和非堆。
简单来说堆就是Java代码可及的内存,是留给开发人员使用的;
非堆就是JVM留给自己用的。
jvm 内存结构?
堆: 逻辑上是连续,物理上可以处于不连续的内存空间中,
里面存储的是对象实例以及数组。可以细分为新生代,老生代。
通过-Xmx和-Xms控制大小。
栈:基本数据类型,对象引用(地址,指针)。
本地方法栈(了解):它与虚拟机栈发挥的作用差不多,区别在于虚拟机栈为java方法
的执行提供服务,而本地方法栈为虚拟机使用到的Native(本地)方法服务。
方法区:放了所加载的类的信息(名称、修饰符等)、类中的静态变量、
类中定义为final类型的常量、类中的Field信息、类中的方法信息
在Sun JDK中这块区域对应的为PermanetGeneration,又称为持久代,
默认为64M,可通过-XX:PermSize以及-XX:MaxPermSize来指定其大小
在服务器启动的时候报内存溢出是因为方法区太小,也就相当于持久代的内存太小。
通过-XX:PermSize以及-XX:MaxPermSize来指定其大小,可以解决这个问题。
常量池是方法区的一部分,用来存储常量信息。如String就存储在
常量池中。
计数器(了解):通过该计数器的值来选取下一条要执行的字节码指令。
GC是什么,为什么要有GC?
GC就是垃圾回收,java这种语言是动态分配内存大小的,并且依靠
垃圾回收机制来完成对分配内存空间的回收,从而来避免内存溢出的问题,
也在一定程度上降低了程序员工作的复杂度。
jvm中的GC采用了generation(分代回收)算法,
因为大多数的对象存活的时间比较短,
而少部分的对象才能够长时间存活。
因此,jvm将堆内存划分为年轻代(young generation)和
年老代(old generation)。
年轻代中的对象通常建立时间不久,且大部分生命周期也很短;
年老代中的对象则已经创建比较久了,
其声明周期也相对年轻代比较长。
按照上面的划分,jvm在做GC时也进行了区别对待,
对年轻代GC会相对比较频繁,且采用了copying(复制)算法;
年老代的GC相对比较少,且采用的是tracing算法的一种,
是标记-清除-压缩。
JVM内存限制(最大值)
JVM内存的最大值跟操作系统有很大的关系。
简单的说就32位处理器虽然 可控内存空间有4GB,
但是具体的操作系统会给一个限制,
这个限制一般是2GB-3GB
(一般来说Windows系统下为1.5G-2G,Linux系统 下为2G-3G),
而64bit以上的处理器就不会有限制了。
Java 监视和管理控制台:
JConsole 使您能够在运行时监视各种 JVM 资源统计信息。
这种特性特别适用于检测死锁、内存泄漏。
它可以连接到一个本地或远程 JVM 并可用来进行监视:
线程状态(包括相关的锁)
内存使用情况
垃圾收集
运行时信息
JVM 信息
jvm的调优?
开启-Server模式,增大堆的大小,以及持久代的大小,从而
提高程序的运行效率,并且将初始堆大小和最大堆大小设置为
一样的值从而避免了堆增长会带来额外压力。持久代大小的设置
同理,也设置为初始大小和最大大小一样大。
jvm的类加载机制? jvm中类的生命周期?
生命周期:加载、连接、初始化,使用,卸载
对象基本上都是在jvm的堆区中创建,在创建对象之前,
会触发类加载(加载、连接、初始化),
当类初始化完成后,
根据类信息在堆中实例化类对象,
初始化非静态变量、非静态代码以及默认构造方法,
当对象使用完之后会在合适的时候被jvm垃圾收集器回收。
要经过三步:加载(Load),链接(Link),初始化(Initializ)。
其中链接又可分为校验(Verify),准备(Prepare),解析(Resolve)三步。
ClassLoader就是用来装载的。通过指定的className,找到二进制码,
生成Class实例,放到JVM中。
ClassLoader从顶向下分为 :
Bootstrap ClassLoader:引导类加载器,
它负责加载Java的核心类(如rt.jar)
Extension ClassLoader:扩展类加载器,
它负责加载JRE的扩展目录
(JAVA_HOME/jre/lib/ext)中的JAR包
System ClassLoader:系统(也称为应用)类加载器,
它负责在JVM被启动时加载来自在命令java中的-classpath
中的JAR包
User-Defined ClassLoader:用户自定义的类加载器
linux中的命令:
ps -ef | grep :查看进程信息
vi:文件编辑命令
more:分页查看命令
top:常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况
ifconfig:显示或配置网络设备的命令
ping:它通常用来测试与目标主机的连通性
rsync、scp:文件同步命令
二、云计算+大数据的具体技术实现方案:
Hadoop是一个能够对大量数据进行分布式处理的软件框架。
它以并行的方式工作,通过并行处理加快处理速度,
维护多个工作数据副本,
具有可伸缩性,能够处理 PB 级数据.
hadoop 由许多元素构成。其最底部是HDFS,
它存储 Hadoop 集群中所有存储节点上的文件。
HDFS的上一层是MapReduce 引擎.
hadoop下的子项目:
HDFS:Hadoop分布式文件系统
MapReduce:并行计算框架(建立在HDFS上的)
HBase: 类似Google BigTable的分布式NoSQL 列数据库
Hive:数据仓库工具
Zookeeper:分布式锁设施
Pig: 大数据分析平台,为用户提供多种接口
行业知识(了解):
存储容量:是该存储设备上可以存储数据的最大数量,通常使用千字节(kb kilobyte)、兆字节(MB megabyte)、吉字节(GB, gigabyte)、太字节(TB ,terabyte)和PB(Petabyte)、EB(Exabyte)等来衡量。
1KB=2(10)B=1024B; 括号中的数字为2的指数(即多少次方)
1MB=2(10)KB=1024KB=2(20)B;
1GB=2(10)MB=1024MB=2(30)B。
1TB=2(10) GB=1024GB=2(40)B
1PB=2(10) TB=1024TB=2(50)B
1EB=2(10) PB=1024PB=2(60)B
1Byte(相當於一個英文字母,您的名字相當6Bytes(6B)。
Kilobyte(KB)=1024B相當於一則短篇故事的內容。
Megabyte(MB)=l024KB相當於一則短篇小說的文字內容。
Gigabyte(GB)=1024MB相當於貝多芬第五樂章交響曲的樂譜內容。
Terabyte(TB)=1024GB相當於一家大型醫院中所有的X光圖片資訊量。
Petabyte(PB)=l024TB相當於50%的全美學術研究圖書館藏書資訊內容。
Exabyte (EB)=1024PB;5EB相當於至今全世界人類所講過的話語。
Zettabyte(ZB)=1024EB如同全世界海灘上的沙子數量總和。
Yottabyte(YB)=1024ZB相當於7000位人類體內的微細胞總和。
https://blog.csdn.net/thinkwon/category_9731418.ht
最后
以上就是俊逸小猫咪为你收集整理的java统一复习5目录基础篇框架篇 数据库篇业务场景篇 优化篇的全部内容,希望文章能够帮你解决java统一复习5目录基础篇框架篇 数据库篇业务场景篇 优化篇所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复