存储过程(procedure)是为了完成特定功能的SQL 语句集,存储在数据库中,经过第一次编译后再次调用不需要再次编译。存储过程里边可以定义变量、写if判断、写循环。他不能return返回,但是可以通过参数返回,有三种参数(in输入、our输出 还有 inout 即可输入又可输出。因为存储过程只在创造时进行编译,以后每次执行存储过程都不需再重新编译,而一般SQL语句每执行一次就编译一次,所以使用存储过程可提高数据库执行速度。当对数据库进行复杂操作时(如对多个表进行Update,Insert,Query,Delete时),可将此复杂操作用存储过程封装起来与数据库提供的事务处理结合一起使用。.存储过程可以重复使用,可减少数据库开发人员的工作量。安全性高,可设定只有某此用户才具有对指定存储过程的使用权。
项目周期
接口和抽象类的区别是什么?
什么是值传递和引用传递?
Java反射机制?
算法复杂度
&和&&的区别
Overload和Override的区别
字节流与字符流的区别 字节流是按字节读取或写入设备,但字符流是以字符为单位读取或写入设备。 如果是二进制文件,需要用字节流读取。一般来说,字符流只处理文本文件(txt、word、excel等)。在设备中,大多数情况是以字节形式存储数据的,因此字符流通过需要传入字节流当参数。 XML和Json的特点
悲观锁:就是考虑问题很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁; 实现:sql语句后边加上for update 例子:Select id,nam from biao for update 乐观锁:就是考虑问题很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁 实现:在表里边加一个vesion 例子: Select max(nub) ,version from biao Update biao set nub=nub+1,version=vsersion+1 where id=id and version =version 方法锁: 方法锁主要包括:synchronized锁和lock锁 区别: 1)Lock是一个接口,而synchronized是Java中的关键字; 2)synchronized当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,不能够响应中断,(释放:1执行完线程自动释放2发生异常jvm让线程释放)((比如调用sleep方法)),这样的好处是不会导致死锁现象发生。 Lock锁,可以不让等待的线程一直无期限地等待下去,比如只等待一定的时间或者响应中断。 但Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时必须在try{}catch{}块中进行,需要在finally块中释放锁; 3)通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。 在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。 在并发量比较小的情况下,使用synchronized是个不错的选择,但是在并发量比较高的情况下,其性能下降很严重,此时ReentrantLock(可重入锁,唯一实现了Lock接口的类)是个不错的方案。
java中的事务主要有两种,JDBC事务(本地事物)和JTA(Java Transaction API)事务(分布式事物);事务有四大特性ACID原子性、一致性、隔离性和持久性。框架中,我们一般把事物交给spring来管理。spring配置事务的方式一般有两种,一个是声明式事务,一个是注解式事务。注解事务,比较简单灵活,在spring配置文件中配置一个<tx:annotation-driven>的注解,然后在需要的方法上加@Transactional注解就可以了。声明式事务,切点一般是扫描service层实现类,通过方法名匹配配置传播特性,决定哪些方法上加事务,哪些不需要事物。事务主要有五大隔离级别和7种传播特性;五大隔离级别由低到高:主要控制是否出现脏读,不可重复读和幻觉读;7种传播特性主要决定是新建事务,还是取当前事务;
脏读: 指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据, 那么另外一个事务读到的这个数据是脏数据,依据脏数据所做的操作可能是不正确的。 不可重复读: 指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。 幻觉读: 指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户
mybatis一级缓存是SqlSession级别的缓存,默认支持一级缓存,不需要在配置文件去配置。 mybaits的二级缓存是mapper范围级别,,除了在SqlMapConfig.xml设置二级缓存的总开关 ,还要在具体的mapper.xml中开启二级缓存: useCache配置禁用二级缓存(默认情况是true,即该sql使用二级缓存。) mybatis刷新缓存(就是清空缓存flushCache='true' 属性,默认情况下为true即刷新缓存,如果改成false则不会刷新)
代替分号 ,因为分号在存储过程里边要用;创建好以后, 在用delimiter ;恢复; 我之前写过一些存储过程,但最近接触的比较多的还是电商类的互联网项目,这类项目中,存储过程并不建议使用。最近一次存储过程记得是,为测试人员写的一个创建测试数据的存储过程。当时测试人员为了进行报表统计功能的测试,需要经常重复性的生成大量的数据,还设计到了很多张表。如果手动一条条加数据特别麻烦,于是找我想个办法,我就帮他写了一个存储过程;好久没写了,手比较生,当时也是一边找资料,一边写的。很简单的东西,写了近2个小时;
函数(function)与存储过程的结构类似,但是存储过程中不能用return返回值,函数必须有一个return子句,用于返回函数值;当然函数也可以用类似存储过程的参数返回;函数除了直接调用以外,可以直接放在select语句中使用,存储过程不行,存储过程定义关键字用procedure,函数定义用function。一般情况下如果要求有且有一个返回值,用函数,否则,用存储过程。 触发器(trigger)一般是执行sql语句的增删改查时使用,比如修改A表的一条数据时,让他自动触发一个操作,在B表里边新增一条记录;触发器因为需要绑定在表上,所以有数量限制,他里边的语法和存储过程、函数类似。
视图(View)实际上是多张表之间进行关联查询,把查询结果虚拟成一张表,就叫视图。创建视图也非常简单,create view viewname as + 查询语句,就可以了。视图的使用和表的使用完全一样,但不要在视图上进行增删改的操作,因为他本身就是一个查询结果的集合。利用视图,可以使我们把复杂的查询简单化,可以增强数据的安全性,屏蔽用户对基表的操作;他一般无法提高查询效率。 |
|
|
java中的web服务器,主要是与JSP/Servlet兼容的Web服务器,比较常用的有Tomcat、jetty、JBoss、WebSphere 和 WebLogic 等;目前最流行的还是tomcat,适合于中小型项目,大型项目也可以借助其他其他工具用集群方式部署;jetty是相对于tomcat的一个更轻量级的框架,在小型项目或者云台托管的项目更高效简单。而JBoss、WebSphere、WebLogic是主要运用于大型的企业级web服务,这三个里边只有Jboss是开源免费的,WebSphere和WebLogic都是商业web服务,一套都有上万元以上。他们功能强大,性能稳定,支持分布式部署,有很好的售后支持。WebLogic应该是商业版本里边目前市场占有率最高的服务器,配置简单功能强大。WebSphere是IBM的产品,功能强大,而且有IBM的开发工具相配套,开发Web程序十分方便 |
1、内存溢出,可以调节tomcat内存解决。还要找出代码中内存溢出的漏洞,一般是流没有关闭或者死循环;
2、数据库连接超出最大连接量:修改mysql配置文件的最大连接数;
3、代码编译失败、缓存:清理Eclipse编译缓存、清理tomcat缓存、清理浏览器缓存。
4、tomcat启动端口号被占用:打开任务管理器,杀进程;
5、tomcat启动超时:修改启动时间;
处理高并发一般有分流和队列两种分式。分流可以用集群实现。队列可以用activeMQ消息队列或者线程锁。集群我搭建过nginx+tomcat+redis的集群;消息队列就是把所有的线程消息集中管理,排成对一个一个的进行。具体的实现方式,我没有去转入的研究过。线程锁的方式在生成订单编号时用到过,就是在生成编号的方法上加上syn开头的同步锁。对了我们做项目中有个同事提到了用redis可以实现,说redis是单线程的。如果把库存放到redis里边,就可以防止负库存的实现。还有就是我们项目中有时候会用到线程池的技术,比如做数据的批量导入时,如果使用单线程方式,速度会很慢,如果引入线程池,同时开启n个线程,同时进行就可以增加很大的效率。 |
什么是FastDFS FastDFS 是用 c 语言编写的一款开源的分布式文件系统。FastDFS 为互联网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用、高性能等指标,使用 FastDFS很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。 FastDFS 架构包括 Tracker server 和 Storage server。客户端请求 Tracker server 进行文件上传、下载,通过 Tracker server 调度最终由 Storage server 完成文件上传和下载。 Tracker server 作用是负载均衡和调度,通过 Tracker server 在文件上传时可以根据一些策略找到 Storage server 提供文件上传服务。可以将 tracker 称为追踪服务器或调度服务器。 Storage server 作用是文件存储,客户端上传的文件最终存储在 Storage 服务器上,Storageserver 没有实现自己的文件系统而是利用操作系统 的文件系统来管理文件。可以将storage称为存储服务器。 上传流程:
文件下载流程
|
常用的接口开发方式主要有Httpclient、Webservice、hession、ESB、dubbo几种。(restful风格接口的一种规范,(把所有的请求归纳为4大类、get(查)、post(改)、detete(删)、put(增))
a) 非REST风格的url:localhost:8080/springmvc?name=小白&password=123; b) REST风格的url:localhost:8080/springmvc/小白/123;(参数用@PathVariable接收) 2、Webservice 是传统开发中最常用的接口调用方式,(有四种实现工具:cxf、xfire、axis、axis2)。我用过cxf方式,因为cxf方式和spring集成比较简单。开始webservice是基于soap协议的,数据格式是xml,现在也支持restful。 3、hession我也就简单了解了一下,他是采用二进制协议的。相比WebService,Hessian更简单、快捷。使用方法当时网上看过,也做过个例子,但现在忘了。 4、ESB是企业服务总线,是企业内部多个项目之间接口调用比较频繁时的一种解决方案; 所有项目都和ESB交互。不需要知道谁提供的服务(数据转换,调用监控) ESB帮忙管理服务,进行数据转换 ESB处理高并发时,容易出现问题。ActiveMq(消息队列MQ) 开源:mule收费:IBM ESB 5、Dubbo一般叫服务注册,是分布式立体架构中做的一种治理服务的方案。不负责数据转换,只管中间搭线。(支持负载均衡和分布式) dubbo更适合于一些访问量大,高并发的项目,比如电商;
|
webservice是一种跨平台,跨语言,跨框架的接口开发和调用技术。在java中通常有四种技术框架分别是xfire,cxf, axis,axis2。我们用的是cxf,因为cxf使用简单,并且可以和spring无缝集成.webservice使用wsdl(web service definition language - web service定义语言)语言,soap(简单对象)协议、xml数据格式; webservice服务端配置流程(没必要主动说)
首先在web.xml中引入cxfServlet核心类,指定对以/cxf开头的url路径提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接口中的方法。 restful风格的webService 最近随着restful的流程,webService也支持了restful风格,用起来比较简单,主要是基于cxf框架,使用注解的方式;直接面向对象,不需要关注xml和对象的转换; webService主要分为服务端和客户端,因为都是内部调用,所以服务端和客户端都是我们自己开发,发布服务只需要引入jar包和他的xml文件,在Sevice层接口加上WebService注解,接口实现类上家Path、get(post、dete、put)注解;实体类上需要加注解@XmlBootElement; 调用服务时,映入jar包和配置文件后,在controller里边可以直接用@Resource注入要调用的服务;就想调用本地服务一样;应用的配置都在xml文件中,通过jaxs标签应用 |
HttpClient字面理解就是http请求的客户端,他是Apache开发的一套HTTP协议的客户端编程工具包。是纯Java语音编写的,支持https协议,实现了http的get、post、delete、put等全部方法,但我们一般只用get和post方法,用httpclient可以直接跨域请求http协议的url,并拦截返回的响应结果。 我们项目里边有写好的工具类,里边封装好了他的get和post方法,传入url和参数,返回String类型数据(一般是json字符串或xml字符串),然后我们进行解析就可以了。 他的调用步骤是: 1. 创建HttpClient对象。 2. 创建请求方法的实例,并指定请求URL。如果需要发送GET请求,创建HttpGet对象;如果需要发送POST请求,创建HttpPost对象。 3. 如果需要发送请求参数,可调用HttpGet、HttpPost共同的setParams(HetpParams params)方法来添加请求参数;对于HttpPost对象而言,也可调用setEntity(HttpEntity entity)方法来设置请求参数。 4. 调用HttpClient对象的execute(HttpUriRequest request)发送请求,该方法返回一个HttpResponse。 5. 调用HttpResponse的getAllHeaders()、getHeaders(String name)等方法可获取服务器的响应头;调用HttpResponse的getEntity()方法可获取HttpEntity对象,该对象包装了服务器的响应内容。程序可通过该对象获取服务器的响应内容。 6. 释放连接。无论执行方法是否成功,都必须释放连接
|
我负责系统管理模块,系统管理主要是对用户角色和权限的管理,不同角色的人登录应该看到不同的权限和内容,权限通常有3,5,7张表甚至更多来完成,我们当时用了五张,包括员工信息表、角色信息表、权限信息表和两张中间关系表:一张员工角色关系表,一张角色权限关系表。可以对用户赋角色,然后角色赋权限,权限表里存着不同的权限的url,当用户登录时,从session中获取用户id,通过用户id获取用户的所有角色id,再通过角色id获取所有权限url。把当前用户的权限信息用ztree以树形结构展示,就实现了不同的人登录展示不同的权限树。(当然,在这里可能有一些安全问题,就是用户直接在浏览器输入非法权限的url执行非法操作的问题,我们采用filter拦截器在用户执行操作时先判断该用户有无该权限的url,在有权限的情况下才放行,否则提示非法操作,并且终止该操作)。 |
Apache Shiro是Java的一个安全框架。对比Spring Security,它相当简单,可能没有Spring Security做的功能强大,但是在实际工作时可能并不需要那么复杂的东西,所以使用小而简单的Shiro就足够了。 Shiro主要包含登录认证、授权、会话管理三大功能,另外还提供了加密、缓存、web基础等功能。但他不会去维护用户和权限,这个还需要我们自己完成。他的工作流程是:首先,我们通过SecurityUtils获取Subject,用它来进行认证和授权,然后通过安全管理器securityMananager来管理所有的subject。给Shiro的SecurityManager中注入Realm,通过realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作。框架中使用一般是,先导入相关jar包,web.xml中加shiro-filter过滤器,配置shiro.xml,里边配置过滤器和安全管理器,还可以自定义form 认证过滤器、自定义realm(领域)、加密等。他提供了多种授权方式;编程授权、注解授权、jsp标签授权。 |
我们的支付是直接和农业银行对接的。首先银行为我们创建一个总账号(众陶联),然后在这个总账号下可以开设多个子账号,子账户和普通账号类似,子账号只能进行充值,不能提现,转账也只能是子账号直接互相转;企业在我们平台注册时,开始只是注册基本信息,想要交易时,就需要进行资质认证;企业把需要认证的信息上传到平台,运营后台进行审核,审核通过后,直接调用农业银行提供的开设子账号接口,开设子账号,然后把子账号线上反馈给用户(采购商或供应商); 采购商支付主要有几种情况:1、缴纳保证金;2、交易支付;3、支付委托金; 平台除了有一个总账号以外,也有一个平台自己的子账号; 银行接口:
接口调用方式:
注意:
|
1、Mysql数据库的引擎有很多种,最重要的就是他的事物型引擎和非事务型引擎,我们一般都采用他的事务型引擎,因为增删改操作必须要用到事务;非事务引擎,可以在做主从复制、读写分离后的从数据库上使用,因为从数据库一般仅仅是读的操作,不需要事务,这样会更快。 |
Keepalived是一个基于VRRP协议来实现的服务高可用方案,可以利用其来避免IP单点故障,类似的工具还有heartbeat、corosync、pacemaker。但是它一般不会单独出现,而是与
其它负载均衡技术(如lvs、haproxy、nginx)一起工作来达到集群的高可用。 Nginx+Keepalived一般是一主一备,正常情况下都有主机的nginx绑定公网虚拟ip,提供负载均衡服务。主备机器上都有一个keepalive通过脚本监控着nginx。当主机nginx挂掉以后,主机keepalive检测到异常后,就进行自杀,这样备机的keepalive就会接收到信息,备机的keepalive就会通知备机的nginx监管主机的工作。当主机恢复以后,备机keepalive同样会收到消息,就会把主动权又交还给主机。 |
Redis是一个继memcached后的又一个第三方缓存数据库,他比memcached强大很多,支持更多的数据类型(String、list、set、sort set、hash),支持持久化,支持集群;Redis虽然支持持久化,但是他并不适合持久化的保存数据。因为他不是很稳定。但是由于他是保存在内存中,读取速度非常快,所以在项目中一般都用它作为数据库和应用程序直接的中间层来使用,已减轻数据库压力,提高运 行效率。 我们项目中很多地方用到了redis;比如商品的三级分类、省市县、关于我们、联系我们、友情链接,常见问题等经常查询但是不经常改变的数据.redis还可以在tomcat集群里边实现session的共享。由于他的单线程的,所以在电商平台里边也经常用他做“防止超卖”, 生成规则的商品编号等。还有就是购物车也用到了redis 代码中,我们一般都通过spring整合redis官方提供的jedis工具包来操作redis。可以是单机版,也可以是集群。Redis本身就支持集群操作redis_cluster,另外redis还支持主从复杂,还有他独特的哨兵模式,在主服务器宕机时,从服务器可以自动转换为主服务器。另外,他也有他的分片机制,就像mysql的水平分表设计,数据量大时,可以把数据存储到不同的库中,来减轻redis负担。 Redis的持久化方式主要有2种,RDB和AOF,RDB是一种快照方式,默认每隔5分钟创建一个快照副本,这种方式占用空间大,而且会丢失间隔时间5分钟之内的数据,但是他适合做备份,恢复时,可以根据需要恢复任意间隔时间点的数据。AOF是一种日志的持久化记录方式,每秒钟,都把redis中新增的数据记录到日志文件中,这种方式只有一个文件,占用空间少,最多丢失1秒内的数据。相对比较好,但是如果想要恢复5分钟或10分钟前某个时间点的数据,就不行了。所以实际项目中,我们一般会两种方式同时使用。如果搭建集群的话,还可以通过集群互相备份数据,只要集群不同时挂掉,单个redis就可以从集群中的其他服务器获取到最新数据。 还有就是,由于redis不是很稳定,有时候会发生“穿透”和“雪崩”; redis,都是按照key去缓存查询,如果不存在对应的value,就应该去数据库查找。如果key对应的value是一定不存在的,并且对该key并发请求量很大,就会对后端系统造成很大的压力。这就叫做缓存穿透。当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如DB)带来很大压力,这就叫“缓存雪崩”。解决办法是,对查询结果为空的情况也进行缓存,并且给缓存设置不同的有效期。当然redis容灾的最有效的方法还是搭建集群。 |
Java中,一般用poi来操作Excel,用itext来操作word或PDF; 导出Excel需要注意的地方主要有: 他的2003版本和2007版本用的类是不一样的。 2003 HSSFWorkbook 2007 XSSFWorkbook 并且poi3.8版本后,提供了一个超大数据量导出的类;SXSSFWorkbook 导出的步骤主要就是:
|
spring boot 我理解就是把 spring,spring-mvc,spring-data-jpa 等等的一些常用的常用的基础框架组合起来,提供默认的可插拔的设计配置。spring boot对spring的配置进行简化,几乎零配置。同时对spring 需要的jar 也进行了整合,解决jar冲突的问题。可以理解为springMvc的升级版。提供了spring-boot-starter-parent的整合工具包。集成了他以后,项目中大部分jar包就不需要再引用了。内置了Tomcat插件,可以直接用main方法运行; spring_cloud是基于spring_boot的微服务分布式架构,他主要包括:
|
我们的项目,为了安全,一般都部署在内外环境中,只有Nginx服务器会绑定域名,放到公网环境上。当用户从公网发送一个请求过来时,首先请求到Nginx,然后通过Nginx再反向代理到其他内外服务器上。 我们的服务器一般都采用centos7,因为Nginx是c语言开发的,所以Nginx环境的安装,需要一些gcc环境的支持,如果用rpm安装,他的默认路径是/etc/nginx;会自动绑定到服务上,直接用systemctl start Nginx就可以。 Nginx的核心配置文件是conf下的Nginx.xml,他主要有两大功能,一是反向代理,通过配置文件中的server和location来实现,可以配置多个server,制定不同的端口或同一个端口,不同的域名来实现代理,也可以通过一个server里边配置多个location来实现代理。二是负载均衡,负载均衡一般通过配置upstream来实现,常用的负载机制有轮循、权重和ip_hash三种;轮循就是各个服务器按顺序轮着进行;权重就是通过配置weight权重,来分配url的访问次数。Ip_hash是根据客户请求的sessionid,把同一个用户的请求,锁定到同一个url上。 Nginx除了他的反向代理和负载均衡以外,他本身也是一个静态服务器,可以用它发布html、css、img等一系列静态资源。我们项目中做静态化生成的静态页面就放在了Nginx服务器上。 |
首先微信提供了一个官方的微信公众号平台,我们申请成功以后,可以在这个平台上直接进行配置。比如消息回复,有消息自动回复,被关注时被动回复、关键字回复等。回复的消息包括文字消息、图片消息、语音消息、视频消息等。还有就是自定义菜单,最多可以配置3*5个菜单,点击菜单可以回复消息,跳转链接,跳转小程序。如果想要进一步使用更复杂的功能,就需要成为开发者,调用他的api接口文档了。微信公众号的接口都是restful风格的接口,我们可以用httpclient直接调用,接口有些是json数据格式,有些事xml数据格式。调用接口文档首先需要接入测试和网页授权。接入时,必须使用域名和80端口,在公众号平台配置好url,发送请求,平台url里边接收参数,按照要求生成签名,与传过来的签名进行比较,验证签名,如果成功返回一个字符串。这样就接入成功了。Json和xml的解析我们当时封装了工具类。解析方法网上都有,很简单。 |
|
AngularJS 是一个 JavaScript 框架。它是一个以 JavaScript 编写的库。和他类似的还有vue.js, AngularJS它更适用于开发CRUD应用,即数据操作比较多的应用。为了实现这些,AngularJs引入了一些非常棒的特性,包括模板机制、数据绑定、指令、依赖注入、路由等。数据绑定可能是AngularJS最酷最实用的特性。通过数据与模板的绑定,能够让我们摆脱繁琐的DOM操作,而将注意力集中在业务逻辑上。AngularJS内置了很多指令用来控制模板,如ng-repeat,ng-class等,也有很多指令来帮我们完成业务逻辑。还有AngularJS服务啊路由之类的功能也都是非常实用的。AngularJS不依赖(也不妨碍)任何其他的框架,我们甚至可以基于其它的框架来开发AngularJS应用。在我们之前的一个项目中也用到了AngularJS,通过AngularJS,我们实现了前后端分离开发,前端使用路由,页面的性能会有很大的提升,同时也会减少后端的压力,页面跳转可以不需要经过后端,后端只负责提供数据做为展示。 |
工作流就是我们日常工作中的一些需要一组人完成的一个流程。比如请假流程、报销流程、入职流程、离职流程等,首先需要有人发起流程-然后经过一系列审批,最后结束流程。这些流程如果比较固定、比较单一,那么我们通过状态判断一般就可以实现。但是在OA系统中,很多工作需要大家共同协同办公,这种流程特别多。并且很多流程中间的审核环节不是固定的,经常变动,如果仅仅使用状态通过代码判断实现就比较麻烦了。这时候用这种比较成熟的工作流插件就会特别方便。他的核心思想就是,把流程的审核过程和流程定义进行分离,把流程归纳为三部分:“发起”“审批”“结束”;这样就可以把创建流程和流程结束的代码固定下来,中间流程审批的代码使用递归的方式实现。这样多个流程就可以共用一套代码,也不怕流程审核过程变更了。目前常用的工作流就是jbpm5和activity5。jbpm是基于hibernate的,activity是基于mybatis的。我们项目用的是mybatis,所以为了方便集成,就选择了activity。 activity开发步骤:
学习activity工作流的要点:
在Eclipse中安装Activity插件,就可以在Eclipse中绘制Activity工作流图
|
查看tomcat进程:ps –ef|grep tomcat 杀进程:kill -9 进程id 查看Tomcat日志: tail -300f Tomcat目录/logs/catalina.out 重启mysql:systemctl restart mysql 环境变量配置文件位置: /etc/profile 防火墙配置:/etc/sysconfig/iptables Nginx配置:/etc/nginx/nginx.cnf 解压缩命令:tar –zxvf 文件名 创建一级目录:mkdir 目录 创建多级目录:mkdir -p 目录 复制文件及文件夹:cp -rf 原文件(夹) 目标文件(夹) 远程复制:scp 用户名@ip:远程文件(夹) 当前文件(夹) 删除文件及文件夹:rm -rf 文件(夹)(可用*做通配符) 移动文件及文件夹:mv 原文件(夹) 目标文件(夹) 卸载:rpm -e --nodeps 包名 安装:rpm -ivh 包名 检查jdk是否安装:rpm -qa|grep -i jdk 赋予权限:chmod 777 文件 查看端口号占用情况: netstat -apn | grep 8080(根据端口号查询进程id) ps -aux|grep 进程id(根据进程id查看进程) shell脚本就是一个.sh的文件,里边放了多条shell命令(我们常用的Linux命令都是shell命令),还要if判断、for循环等;shell脚本一般都以#!/bin/bash开头,就是一个声明,没有太大意义。可以参考tomcat下的stratup.sh 项目部署:
|
JRE:Java运行时环境(JRE),是将要执行Java程序的Java虚拟机。 JDK:Java开发工具包(JDK),是完整的Java软件开发包,包含了JRE,编译器和其他的工具(比如:JavaDoc,Java调试器),可以让开发者开发、编译、执行Java应用程序。
|
JVM内存结构
程序计数器
是最小的一块内存区域,可以看作是当前线程所执行的字节码的行号指示器。是唯一一个在java虚拟机规范中没有规定任何“内存溢出错误”的区域 |
Java虚拟机栈
主要用于存放局部变量、对象引用、操作数栈等,空间比较小,规定 两种异常:StackOverflowError和OutOfMemoryErroy异常; |
本地方法栈
和虚拟机栈的作用是非常相似的,区别是虚拟机栈为虚拟机执行java方法服务,而本地方法栈则是虚拟机执行native方法服务; |
Java堆(heap)
是java虚拟机内存中最大的一块区域,在jvm启动时创建,存放了对象实例和数组(所有new 的对象),其大小通过-Xms(最小值)和-Xmx(最大值)参数设置,为了避免运行过程中频繁调整heap的大小,通常这两个值设置成一样; |
Java堆是垃圾收集器管理的主要区域,一般采用分代收集算法回收。堆被分为新生代和老年代,新生代主要存储新创建的对象和尚未进入老年代的对象,老年代存储经过多次新生代GC(回收),仍然存活的对象;新生带可通过-Xmn参数来设置; 新生的对象有可能直接进入老年代,比如:启动时大小直接超过了新生代的分配; |
方法区
存储已被加载的类信息、常量、静态变量、编译后的代码等数据;可以通过-XX:PermiSize和-XX:MaxPermiSize来设置方法区的大小;在jdk1.7以后,逐步改为采用native memory来实现方法区; |
运行常量池
是方法区的一部分; |
垃圾回收
目前大部分JVM都采用分代收集算法,把堆分为新生代和老年代,新生代的特点是每次回收都有大量对象需要被回收,老年代每次只有少量对象被回收。所以根据他们的特点,新生代一般采用copying算法进行回收,就是创建2块相同的内存,把存活的对象复杂到另外一块上,然后把旧的一起清楚;老年代一般采用“标记-整理算法”,先把需要回收的对象进行标记,然后把存活的对象都向一段移动,然后把边界以外的清楚; |
常用的垃圾回收器有:并行收集器、窜行收集器、并发收集器 |
JVM的类加载机制和双亲委派模型
(1) Bootstrap ClassLoader : 将存放于 (2) Extension ClassLoader : 将 (3) Application ClassLoader : 负责加载用户类路径(ClassPath)上所指定的类库,开发者可直接使用。
双亲委派模型
工作过程:如果一个类加载器接收到了类加载的请求,它首先把这个请求委托给他的父类加载器去完成,每个层次的类加载器都是如此,因此所有的加载请求都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它在搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。 好处:java类随着它的类加载器一起具备了一种带有优先级的层次关系。例如类java.lang.Object,它存放在rt.jar中,无论哪个类加载器要加载这个类,最终都会委派给启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都是同一个类。相反,如果用户自己写了一个名为java.lang.Object的类,并放在程序的Classpath中,那系统中将会出现多个不同的Object类,java类型体系中最基础的行为也无法保证,应用程序也会变得一片混乱。 |
JVM调优
因为我们的项目,web服务器中间件都用的是Tomcat,所以jvm的调优一般都是针对Tomcat的调优。调优工具可以可以借用jdk自带的VisualVM工具,到JDK安装目录/bin目录下,双击jvisualvm.exe文件,直接启动,就可以查看到当前的堆空间大小分配情况、线程监控情况和垃圾回收监控等。根据这些信息,再进行相应的调整。
我觉的Jvm调优的重点就是垃圾回收(gc,garbage collection)和内存管理。垃圾回收的时候会导致,整个虚拟机暂停服务。因此,应该尽可能地缩短垃圾回收的处理时间。 Java中垃圾回收机制是jvm自动完成的,我们不用操作,但可以通过修改一些配置对他进行影响;
2、针对JVM堆的设置,JVM初始堆内存分配有-Xms指定,默认是物理内存的1/64; 最大分配堆内存有-Xmx指定,默认是物理内存的1/4;当堆内存小于40%时,JVM就会自动增加,直到最大值。当空余堆内存大于70%时,JVM就会自动减少,直到最小值。因此,为了防止垃圾收集器在最小、最大之间收缩堆而产生额外的时间,我们通常把最大、最小设置为相同的值; 3、配置年轻代(Xmn)的值,持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。 4、设置个线程的堆栈大小Xss,每个线程默认堆栈大小为1M,可根据应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数也是有限制的,不能无限生成,经验值在3000~5000左右。 5、回收器的选择,JVM给了三种选择:串行收集器、并行收集器、并发收集器,JVM会根据当前系统配置进行判断,自动选择。但我们可以对一些参数进行设置。串行收集器只适用于小数据量的情况,一般不用处理;可以配置年轻代使用并发收集器,老年代使用并行收集器;如果响应时间有限,就选择并发收集器,尽可能设大年轻代; 如果吞吐量优先就选择并行收集器,也尽可能设大年轻代; 6、禁用Tomcat的DNS查询。(当 Web 应用程序记录客户端的信息时,它也会记录客户端的 IP 地址或者通过域名服务器查找机器名转换为 IP 地址。 DNS 查询需要占用网络,并且可能从很多很远的服务器或者不起作用的服务器上去获取对应的 IP, 这样会消耗一定的时间。为了消除 DNS 查询对性能的影响,可以关闭 DNS 查询。方法是修改 server.xml 文件中的 enableLookups 参数值。) 7、线程数配置:Tomcat连接数过大可能引起的死机。所以,可以根据并发量在Tomcat的server.xml中修改他的最大线程数、初始化线程数等参数。(一般也就估计这配置,小了就配大点。也没有具体评估过) 我们在项目中,一般也就是项目出现问题以后,再去优化。Tomcat在年老代溢出(java.lang.OutOfMemoryError: Java heap space)、持久代溢出(java.lang.OutOfMemoryError: PermGen space)、堆栈溢出(java.lang.StackOverflowError)、线程溢出(Fatal: Stack size too smal)、内存溢出(java.lang.OutOfMemoryError: unable to create new native thread)是会抛出不同溢出。根据溢出去进行修改。 |
线程是进程的一个子集;每个进程可以有多个线程 |
多线程主要是为了提高cpu或者内存的利用率,但是如果用不好了,可能起到相反的效果;多线程可以使我们并行的去处理一些事情。
|
使用线程池的好处在于:在项目启动时,可以给我们初始化多个线程,使用时只需要从线程时拿取一个线程就Ok了;用完以后线程池会帮我们自动回收;
|
|
Start是方法被用来启动新创建的线程;start里边调用了run方法; 所以一般启动一个线程用start方法;run方法列表用来写要执行的方法体; |
Wait是等待,必须通过notify进行唤醒,才能继续运行; Sleep是休眠,休眠时间到了会自动醒来; |
用join方法,可以使线程按顺序执行; 本地线程threadlocal |
Java里边主要有两种集合,collection接口和map接口,其中collection下又包含list和set两个子接口; List子接口:有序,可以有重复元素。和数组类似,List可以动态增长,查找元素效率高,相对的插入删除元素效率低,因为会引起其他元素位置改变。 Set子接口:无序,不允许重复。检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。set集合中的元素不按特定方式排序,只是简单的把对象加入集合中,就像往口袋里放东西。 List接口下有三个实现类ArrayList 、LinkedList和Vector Vector是一种老的动态数组,是线程同步的,效率很低,一般不赞成使用 ArrayList 实现一个动态数组,它的规模可变并且能像链表一样被访问。它提供的功能类似Vector类但不同步,它是以Array方式实现的List,允许快速随机存取。特点是读快改慢;
LinkedList实现一个链表,提供最佳顺序存取,适合插入和移除元素。由这个类定义的链表也可以像栈或队列一样被使用。提供最佳顺序存取,适合插入和移除元素。特点是改快读慢 Set接口有HashSet 和TreeSet 两个实现类 HashSet 能够快速定位一个元素, 要注意的是:存入HashSet中的对象必须实现HashCode()方法; TreeSet 将放入其中的元素按序存放。它实现的是SortedSet接口,也就是加入了对象比较的方法。通过对集中的对象迭代,我们可以得到一个升序的对象集合。 Map接口的实现类主要有HashMap 、HashTable和TreeMap; 当元素的顺序很重要时选用TreeMap,当元素不必以特定的顺序进行存储时,使用HashMap。HashMap不是同步的,Hashtable是同步的,但Hashtable不推荐使用,因为HashMap提供了所有类似的功能,并且速度更快。当需要在多线程环境下使用时,HashMap也可以转换为同步的。HashMap没法保证映射的顺序一直不变,但是作为HashMap的子类LinkedHashMap可以。 HashMap可以通过Map m = Collections.synchronizedMap(hashMap)来达到同步的效果。 |
|
我们用的比较多List包括ArrayList和LinkedList,这两者的区别也很明显,从其名称上就可以看出。ArrayList的底层的通过数组实现,所以其随机访问的速度比较快,但是对于需要频繁的增删的情况,效率就比较低了。而对于LinkedList,底层通过链表来实现,所以增删操作比较容易完成,但是对于随机访问的效率比较低。 |
一般可以直接使用LinkedList完成,从上述类图也可以看出,LinkedList继承自Deque,所以LinkedList具有双端队列的功能。PriorityQueue的特点是为每个元素提供一个优先级,优先级高的元素会优先出队列。 |
Set与List的主要区别是Set是不允许元素重复的,而List则可以允许元素重复的。判断元素的重复需要根据对象的hash方法和equals方法来决定。这也是我们通常要为集合中的元素类重写hashCode方法和equals方法的原因。
元素加入List的时候,不执行额外的操作,并且可以重复。而加入Set之前需要先执行hashCode方法,如果返回的值在集合中已存在,则要继续执行equals方法,如果equals方法返回的结果也为真,则证明该元素已经存在,会将新的元素覆盖老的元素,如果返回hashCode值不同,则直接加入集合。这里记住一点,对于集合中元素,hashCode值不同的元素一定不相等,但是不相等的元素,hashCode值可能相同。 HashSet和LinkedHashSet的区别在于后者可以保证元素插入集合的元素顺序与输出顺序保持一致。而TresSet的区别在于其排序是按照Comparator来进行排序的,默认情况下按照字符的自然顺序进行升序排列。
|
到Collection类继承自Iterable,该接口的作用是提供元素遍历的功能,也就是说所有的集合类(除Map相关的类)都提供元素遍历的功能。Iterable里面包含了Iterator的迭代器 |
Map类型的集合最大的优点在于其查找效率比较高,理想情况下可以实现O(1)的时间复杂度。Map中最常用的是HashMap,LinkedHashMap与HashMap的区别在于前者能够保证插入集合的元素顺序与输出顺序一致。这两者与TreeMap的区别在于TreeMap是根据键值进行排序的,当然其底层的实现也有本质的区别,如HashMap底层是一个哈希表,而TreeMap的底层数据结构是一棵树。 |
TreeMap和LinkedHashMap的区别,前者是按字符串排序进行输出的,而后者是根据插入顺序进行输出的。HashMap与TreeMap的区别,与之前提到的HashSet与TreeSet的区别是一致的,在后续进行源码分析的时候,我们可以看到HashSet和TreeSet本质上分别是通过HashMap和TreeMap来实现的,所以它们的区别自然也是相同的。HashTable现在已经很少使用了,与HashMap的主要区别是HashTable是线程安全的,不过由于其效率比较低,所以通常使用HashMap,在多线程环境下,通常用CurrentHashMap来代替。 |
HashMap基于哈希表的 Map 接口的实现。允许使用 null 值和 null 键。值得注意的是HashMap不是线程安全的,如果想要线程安全的HashMap,可以通过Collections类的静态方法synchronizedMap获得线程安全的HashMap。
|
HashMap的底层主要是基于数组和链表来实现的,它之所以有相当快的查询速度主要是因为它是通过计算散列码来决定存储的位置。HashMap中主要是通过key的hashCode来计算hash值的,只要hashCode相同,计算出来的hash值就一样。如果存储的对象对多了,就有可能不同的对象所算出来的hash值是相同的,这就出现了所谓的hash冲突。学过数据结构的同学都知道,解决hash冲突的方法有很多,HashMap底层是通过链表来解决hash冲突的。 |
HashMap其实就是一个Entry数组,Entry对象中包含了键和值,其中next也是一个Entry对象,它就是用来处理hash冲突的,形成一个链表。 |
我们一般对哈希表的散列很自然地会想到用hash值对length取模(即除法散列法),Hashtable中也是这样实现的,这种方法基本能保证元素在哈希表中散列的比较均匀,但取模会用到除法运算,效率很低,HashMap中则通过h&(length-1)的方法来代替取模,同样实现了均匀的散列,但效率要高很多,这也是HashMap对Hashtable的一个改进。 |
ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长,在new的时候,不会开辟空间,而是在第一次放入值时,开辟一个10个长度的空间;当放入值超过10个时,会自动进行扩容,扩大到之前的1.5倍;ArrayList不是线程安全的,只能用在单线程环境下,多线程环境下可以考虑用Collections.synchronizedList(List l)函数返回一个线程安全的ArrayList类; |
LinkedList 和 ArrayList 一样,都实现了 List 接口,但其内部的数据结构有本质的不同。LinkedList 是基于链表实现的(通过名字也能区分开来),所以它的插入和删除操作比 ArrayList 更加高效。但也是由于其为基于链表的,所以随机访问的效率要比 ArrayList 差。LinkedList不是线程安全的,所以在多线程的环境下使用LinedList需要注意LinkedList类型变量的线程同步问题。当然,有一种方式可以创建一个线程安全的LinkedList:
LinkedList是基于双向列表的,有first(从前向后)和last(从后向前)两条列组成; 当给Linkedlist添加1个值时, First:首先把这个值new 成一个node对象;然后把这个node对象,放到firt里边最后一个对象的next里边;。 Last:把last值,给node的next;然后把node整个给last |
LinkedList 和 ArrayList 一样,都实现了 List 接口,但其内部的数据结构有本质的不同。 ArrayList底层是基于数组实现的;当添加第一个值时,开辟空间,默认开辟10个长度的数组;当里边数值超过10时,会自动扩容,每次扩容到它的1.5倍;由于他的扩容机制是创建1个新的数组,然后把就数组的值给新数组,效率比较低。所以我们在做ArrayList的优化时,可以根据预估数据量给他一次性开辟好空间,避免自动扩容。另外由于数组有下标,所以查找时非常方便;但插入时,可能导致下标重新指定。所以效率可能较低; LinkedList是基于双向列表的结构;当给里边添加值时,首先把当前值创建成一个node对象;然后把当前对象赋给first列最后1个值的next中;同时,把last列直接给到node的next中。然后把当前node再赋给last中;这样就形成了一个双向列表; 再插入值时,只需要把插入节点的next先拿出来,然后放到当前node的next中。然后把当前node再给到插入节点的next中就可以了 |
hashMap底层是一个数组+Entry对象的单向列表组成的;在创建是默认不开辟空间;在添加第一个值的时候开辟空间,默认数组长度为16;Entry里边主要包含key、value、next和hash4个值;当给hashmap添加1个值时,首先先把当前值new 成一个entry对象;hash是根据key计算出来的hashcode值。把这个值和数组的长度求余,根据这个余数来确定entry存放在数组的位置;找到位置以后,首先把位置中原先的值,拿出来,放到当前entry的next里边;最后在把当前entry放到这个位置;hashmap的默认扩容因子是0.75;当map中的值超过3/4时,会自动扩容,扩容到原理的2倍; |
1:集合 Collection(单列集合) List(有序,可重复) ArrayList 底层数据结构是数组,查询快,增删慢 线程不安全,效率高 Vector 底层数据结构是数组,查询快,增删慢 线程安全,效率低 LinkedList 底层数据结构是链表,查询慢,增删快 线程不安全,效率高 Set(无序,唯一) HashSet 底层数据结构是哈希表。 哈希表依赖两个方法:hashCode()和equals() 执行顺序: 首先判断hashCode()值是否相同 是:继续执行equals(),看其返回值 是true:说明元素重复,不添加 是false:就直接添加到集合 否:就直接添加到集合 最终: 自动生成hashCode()和equals()即可
LinkedHashSet 底层数据结构由链表和哈希表组成。 由链表保证元素有序。 由哈希表保证元素唯一。 TreeSet 底层数据结构是红黑树。(是一种自平衡的二叉树) 如何保证元素唯一性呢? 根据比较的返回值是否是0来决定 如何保证元素的排序呢? 两种方式 自然排序(元素具备比较性) 让元素所属的类实现Comparable接口 比较器排序(集合具备比较性) 让集合接收一个Comparator的实现类对象 Map(双列集合) A:Map集合的数据结构仅仅针对键有效,与值无关。 B:存储的是键值对形式的元素,键唯一,值可重复。
HashMap 底层数据结构是哈希表。线程不安全,效率高 哈希表依赖两个方法:hashCode()和equals() 执行顺序: 首先判断hashCode()值是否相同 是:继续执行equals(),看其返回值 是true:说明元素重复,不添加 是false:就直接添加到集合 否:就直接添加到集合 最终: 自动生成hashCode()和equals()即可 LinkedHashMap 底层数据结构由链表和哈希表组成。 由链表保证元素有序。 由哈希表保证元素唯一。 Hashtable 底层数据结构是哈希表。线程安全,效率低 哈希表依赖两个方法:hashCode()和equals() 执行顺序: 首先判断hashCode()值是否相同 是:继续执行equals(),看其返回值 是true:说明元素重复,不添加 是false:就直接添加到集合 否:就直接添加到集合 最终: 自动生成hashCode()和equals()即可 TreeMap 底层数据结构是红黑树。(是一种自平衡的二叉树) 如何保证元素唯一性呢? 根据比较的返回值是否是0来决定 如何保证元素的排序呢? 两种方式 自然排序(元素具备比较性) 让元素所属的类实现Comparable接口 比较器排序(集合具备比较性) 让集合接收一个Comparator的实现类对象
2.关于集合选取原则
是否是键值对象形式: 是:Map 键是否需要排序: 是:TreeMap 否:HashMap 不知道,就使用HashMap。
否:Collection 元素是否唯一: 是:Set 元素是否需要排序: 是:TreeSet 否:HashSet 不知道,就使用HashSet
否:List 要安全吗: 是:Vector 否:ArrayList或者LinkedList 增删多:LinkedList 查询多:ArrayList 不知道,就使用ArrayList 不知道,就使用ArrayList
3:集合的常见方法及遍历方式 Collection: add() remove() contains() iterator() size()
遍历: 增强for 迭代器
|--List get()
遍历: 普通for |--Set
Map: put() remove() containskey(),containsValue() keySet() get() value() entrySet() size() 遍历: 根据键找值 根据键值对对象分别找键和值 |
|
|