
java相关技术专家
能力说明:
精通JVM运行机制,包括类生命、内存模型、垃圾回收及JVM常见参数;能够熟练使用Runnable接口创建线程和使用ExecutorService并发执行任务、识别潜在的死锁线程问题;能够使用Synchronized关键字和atomic包控制线程的执行顺序,使用并行Fork/Join框架;能过开发使用原始版本函数式接口的代码。
阿里云技能认证
详细说明java中我们想要实现多线程常用的有两种方法,继承Thread 类和实现Runnable 接口,有经验的程序员都会选择实现Runnable接口 ,其主要原因有以下两点: 首先,java只能单继承,因此如果是采用继承Thread的方法,那么在以后进行代码重构的时候可能会遇到问题,因为你无法继承别的类了。 其次,如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。 通过下面的实例可以清晰的看出两种方式的区别所在。 1.继承Thread 类 package test; /** * 继承thread类 * @author hongxin * */ public class testThread extends Thread{ private int count=5; private String name; public testThread(String name) { this.name=name; } public void run() { for (int i = 0; i < 5; i++) { System.out.println(name + "运行 count= " + count--); try { sleep((int) Math.random() * 10); } catch (InterruptedException e) { e.printStackTrace(); } } } } package test; public class main1 { public static void main(String[] args) { testThread mTh1=new testThread("A"); testThread mTh2=new testThread("B"); mTh1.start(); mTh2.start(); } } 运行结果 代码分析 线程1和线程2之间的变量是不能共享的,每次count--都有各自的变量和结果。 2.Runnable 接口 package test; /** * 实现runnable接口 * @author hongxin * */ public class testRunnable implements Runnable{ private int count=15; @Override public void run() { for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + "运行 count= " + count--); try { Thread.sleep((int) Math.random() * 10); } catch (InterruptedException e) { e.printStackTrace(); } } } } package test; public class main2 { public static void main(String[] args) { testRunnable mTh = new testRunnable(); new Thread(mTh, "C").start();//同一个mTh,但是在Thread中就不可以,如果用同一个实例化对象mt,就会出现异常 new Thread(mTh, "D").start(); new Thread(mTh, "E").start(); } } 运行结果 代码分析 三个不同的线程之间的变量是共享的,每次count--得到的记过都是再上一个线程运行结果之上得到的。
最近公司有微信小程序的项目,微信小程序调用接口需要HTTPS协议。HTTPS(全称:Hyper Text Transfer Protocol over Secure Socket Layer),是以安全为目标的HTTP通道,简单讲是HTTP的安全版。即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL。 它是一个URI scheme(抽象标识符体系),句法类同http:体系。用于安全的HTTP数据传输。https:URL表明它使用了HTTP,但HTTPS存在不同于HTTP的默认端口及一个加密/身份验证层(在HTTP与TCP之间)。这个系统的最初研发由网景公司(Netscape)进行,并内置于其浏览器Netscape Navigator中,提供了身份验证与加密通讯方法。 HTTPS的安全基础是SSL,因此tomcat需要配置SSL后才能使用HTTPS协议。配置过程如下: (1)进入到jdk下的bin目录 (2)输入如下指令keytool -v -genkey -alias tomcat -keyalg RSA -keystore d:/tomcat.keystore d:/tomcat.keystore是将生成的tomcat.keystore放到d盘根目录下。注意若要放到c盘,在win7系统下,需要以管理员身份进入到命令行中进行操作,否则是无法创建tomcat.keystore的。本例放到d盘下。 (3)输入keystore密码 密码任意,此处以123456为例,要记住这个密码,之后在进行server.xml配置时需要使用。 (4)输入名字、组织单位、组织、市、省、国家等信息 (5)输入之后会出现确认的提示 此时输入y,并回车。此时创建完成keystore。进入到D盘根目录下可以看到已经生成的tomcat.xml (6)输入tomcat的主密码 可以直接回车,默认为同keystore的密码一样。之后,会显示正在存储即完成。 (7)进入tomcat文件夹 找到conf目录下的sever.xml并进行编辑修改内容如下 <Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true" maxThreads="150" scheme="https" secure="true" clientAuth="false" sslProtocol="TLS" keystoreFile="D:/keys/wsriakey" keystorePass="wsria.com" /> (10)启动成功后,使用https://127.0.0.1:8443 访问页面 页面成功打开即tomcat下的https配置成功。
1.部署多台tomcat 简单的在一台服务器上部署多台tomcat最简单不过了,只需要将tomcat压缩包解压到多个目录,然后更改/conf/server.xml配置文件中的三个端口: a.Server port=”8[X]05″ shutdown=”SHUTDOWN” b.Connector port=”8[X]80″ maxHttpHeaderSize=”8192″ … c.Connector port=”8[X]09″ enableLookups=”false” 这里用‘X’代替第几台tomcat,默认的分别为8005,8080,8009。每增加一台tomcat只需要将这三个数字递增就可以。这样就可以根据IP+8[X]80来区别不同的程序了。 2.用nginx绑定域名与不同的tomcat端口 nginx配置文件位于conf/nginx.conf,它最主要的部分是http部分,这里最重要的两个配置项是upstream,server,这两个项都可以有多个配置。 在http{}内插入下面代码。 upstream home.console.xinyi8090.cn { server 60.205.149.58:8082; } upstream home.vendor.xinyi8090.cn { server 60.205.149.58:8080; } server { listen 80; server_name home.console.xinyi8090.cn; location / { index index.html index.jsp; proxy_pass http://home.console.xinyi8090.cn; proxy_set_header X-Real-IP $remote_addr; client_max_body_size 100m; } } server { listen 80; server_name home.vendor.xinyi8090.cn; location / { index index.html index.jsp; proxy_pass http://home.vendor.xinyi8090.cn; proxy_set_header X-Real-IP $remote_addr; client_max_body_size 100m; } } 大功告成,亲测有效。3.扩展修改tomcat默认页 很多时候我们想输入域名之后就可以访问主页,这时候就需要配置tomcat的默认页面。 首先,修改$tomcat/conf/server.xml文件。在<host></host>标签之间添加上:<Context path="" docBase="mypr" debug="0" reloadable="true" />path是说明虚拟目录的名字,如果你要只输入ip地址就显示主页,则该键值留为空;docBase是虚拟目录的路径,它默认的是$tomcat/webapps/ROOT目录,现在我在webapps目录下建了一个mypro项目,让该项目文件作为我的默认目录。 然后,修改$tomcat/conf/web.xml文件。在web.xml文件中,有一段如下:<welcome-file-list><welcome-file>index.html</welcome-file><welcome-file>index.htm</welcome-file><welcome-file>index.jsp</welcome-file></welcome-file-list> 改成mypro项目中需要直接显示的jsp或者html即可。
使用Jedis连接redis跟我们使用jdbc连接数据库特别向,话不多说,直接上代码。 需要引入的jar包 这里我建的是maven工程,pom坐标配置如下 <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.7.0</version> </dependency> 代码 package com.taotao.rest.jedis; import java.util.HashSet; import javax.swing.Spring; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.taotao.rest.dao.JedisClient; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisCluster; import redis.clients.jedis.JedisPool; public class JedisTest { //单实例链接测试 @Test public void testJedisSingel() { //创建jedis对象 Jedis jedis=new Jedis("192.168.154.128",6379); //调用jedis对象方法,方法名和Jedis命令一致 jedis.set("key1", "jedis test"); String string=jedis.get("key1"); System.out.println(string); } //使用连接池连接测试 @Test public void testJedisPool() { //创建Jedis链接池 JedisPool pool=new JedisPool("192.168.154.128",6379); //从连接池中获得Jedis对象 Jedis jedis=pool.getResource(); String string=jedis.get("key1"); System.out.println(string); jedis.close(); pool.close(); } //集群版链接测试 @Test public void testJedisCluster() { HashSet<HostAndPort> nodes=new HashSet<>(); nodes.add(new HostAndPort("192.168.154.128",6379)); nodes.add(new HostAndPort("192.168.154.128",6380)); nodes.add(new HostAndPort("192.168.154.128",6381)); nodes.add(new HostAndPort("192.168.154.128",6382)); nodes.add(new HostAndPort("192.168.154.128",6383)); nodes.add(new HostAndPort("192.168.154.128",6384)); JedisCluster cluster=new JedisCluster(nodes); cluster.set("key1", "test"); String string=cluster.get("key1"); System.out.println(string); cluster.close(); } //spring整合单机版测试 @Test public void testSpringJedisSingle(){ ApplicationContext applicationContext=new ClassPathXmlApplicationContext("classpath:/spring/applicationContext-*.xml"); JedisPool pool=(JedisPool) applicationContext.getBean("redisClient"); Jedis jedis=pool.getResource(); String string=jedis.get("key1"); System.out.println(string); jedis.close(); pool.close(); } //spring整合集群版测试 @Test public void testSpringJedisCluster() { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring/applicationContext-*.xml"); JedisCluster jedisCluster = (JedisCluster) applicationContext.getBean("redisClient"); String string = jedisCluster.get("key1"); System.out.println(string); jedisCluster.close(); } }
<一> 基础数据类型(Value type)直接在栈(stack)空间分配,方法的形式参数,直接在栈空间分配,当方法调用完成后从栈空间回收。引用数据类型,需要用new来创建,既在栈空间分配一个地址空间(reference),又在堆空间分配对象的类变量(object) 。方法的引用参数,在栈空间分配一个地址空间,并指向堆空间的对象区,当方法调用完成后从栈空间回收。局部变量 new 出来时,在栈空间和堆空间中分配空间,当局部变量生命周期结束后,栈空间立刻被回收,堆空间区域等待GC回收。 方法调用时传入的 literal 参数,先在栈空间分配,在方法调用完成后从栈空间分配。字符串常量在 DATA 区域分配 ,this 在堆空间分配。数组既在栈空间分配数组名称, 又在堆空间分配数组实际的大小!哦 对了,补充一下static在DATA区域分配。从Java的这种分配机制来看,堆栈又可以这样理解:堆栈(Stack)是操作系统在建立某个进程时或者线程(在支持多线程的操作系统中是线程)为这个线程建立的存储区域,该区域具有先进后出的特性。每一个Java应用都唯一对应一个JVM实例,每一个实例唯一对应一个堆。应用程序在运行中所创建的所有类实例或数组都放在这个堆中,并由应用所有的线程共享.跟C/C++不同,Java中分配堆内存是自动初始化的。Java中所有对象的存储空间都是在堆中分配的,但是这个对象的引用却是在堆栈中分配,也就是说在建立一个对象时从两个地方都分配内存,在堆中分配的内存实际建立这个对象,而在堆栈中分配的内存只是一个指向这个堆对象的指针(引用)而已。<二> 参考自深入浅出JVM这本书,对了解JAVA的底层和运行机制有比较大的帮助。废话不想讲了.入主题:先了解具体的概念:JAVA的JVM的内存可分为3个区:堆(heap)、栈(stack)和方法区(method)堆区:1.存储的全部是对象,每个对象都包含一个与之对应的class的信息。(class的目的是得到操作指令)2.jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身栈区:1.每个线程包含一个栈区,栈中只保存基础数据类型的对象和自定义对象的引用(不是对象),对象都存放在堆区中2.每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。3.栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。方法区:1.又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。2.方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。为了更清楚地搞明白发生在运行时数据区里的黑幕,我们来准备2个小道具(2个非常简单的小程序)。AppMain.javaJava代码public class AppMain //运行时, jvm 把appmain的信息都放入方法区 { public static void main(String[] args) //main 方法本身放入方法区。 { Sample test1 = new Sample( " 测试1 " ); //test1是引用,所以放到栈区里, Sample是自定义对象应该放到堆里面 Sample test2 = new Sample( " 测试2 " ); test1.printName(); test2.printName(); } } Sample.java public class Sample //运行时, jvm 把appmain的信息都放入方法区 { private name; //new Sample实例后, name 引用放入栈区里, name 对象放入堆里 public Sample(String name) { this .name = name; } public void printName() //print方法本身放入 方法区里。 { System.out.println(name); } } OK,让我们开始行动吧,出发指令就是:“java AppMain”,包包里带好我们的行动向导图,Let’s GO! 系统收到了我们发出的指令,启动了一个Java虚拟机进程,这个进程首先从classpath中找到AppMain.class文件,读取这个文件中的二进制数据,然后把Appmain类的类信息存放到运行时数据区的方法区中。这一过程称为AppMain类的加载过程。接着,Java虚拟机定位到方法区中AppMain类的Main()方法的字节码,开始执行它的指令。这个main()方法的第一条语句就是:Sample test1=new Sample("测试1");语句很简单啦,就是让java虚拟机创建一个Sample实例,并且呢,使引用变量test1引用这个实例。貌似小case一桩哦,就让我们来跟踪一下Java虚拟机,看看它究竟是怎么来执行这个任务的:1、 Java虚拟机一看,不就是建立一个Sample实例吗,简单,于是就直奔方法区而去,先找到Sample类的类型信息再说。结果呢,嘿嘿,没找到@@,这会儿的方法区里还没有Sample类呢。可Java虚拟机也不是一根筋的笨蛋,于是,它发扬“自己动手,丰衣足食”的作风,立马加载了Sample类,把Sample类的类型信息存放在方法区里。2、 好啦,资料找到了,下面就开始干活啦。Java虚拟机做的第一件事情就是在堆区中为一个新的Sample实例分配内存, 这个Sample实例持有着指向方法区的Sample类的类型信息的引用。这里所说的引用,实际上指的是Sample类的类型信息在方法区中的内存地址,其实,就是有点类似于C语言里的指针啦~~,而这个地址呢,就存放了在Sample实例的数据区里。3、在JAVA虚拟机进程中,每个线程都会拥有一个方法调用栈,用来跟踪线程运行中一系列的方法调用过程,栈中的每一个元素就被称为栈帧,每当线程调用一个方法的时候就会向方法栈压入一个新帧。这里的帧用来存储方法的参数、局部变量和运算过程中的临时数据。OK,原理讲完了,就让我们来继续我们的跟踪行动!位于“=”前的Test1是一个在main()方法中定义的变量,可见,它是一个局部变量,因此,它被会添加到了执行main()方法的主线程的JAVA方法调用栈中。而“=”将把这个test1变量指向堆区中的Sample实例,也就是说,它持有指向Sample实例的引用。OK,到这里为止呢,JAVA虚拟机就完成了这个简单语句的执行任务。参考我们的行动向导图,我们终于初步摸清了JAVA虚拟机的一点点底细了,COOL!接下来,JAVA虚拟机将继续执行后续指令,在堆区里继续创建另一个Sample实例,然后依次执行它们的printName()方法。当JAVA虚拟机执行test1.printName()方法时,JAVA虚拟机根据局部变量test1持有的引用,定位到堆区中的Sample实例,再根据Sample实例持有的引用,定位到方法去中Sample类的类型信息,从而获得printName()方法的字节码,接着执行printName()方法包含的指令。 <三> 在windows中使用taskmanager查看java进程使用的内存时,发现有时候会超过 -Xmx制定的内存大小, -Xmx指定的是java heap,java还要分配内存做其他的事情,包括为每个线程建立栈。VM的每个线程都有自己的栈空间,栈空间的大小限制vm的线程数量,太大了,实用的线程数减少,太小容易抛出java.lang.StackOverflowError异常。windows默认为1M,linux必须运行ulimit -s 2048。在C语言里堆(heap)和栈(stack)里的区别简单的可以理解为:heap:是由malloc之类函数分配的空间所在地。地址是由低向高增长的。stack:是自动分配变量,以及函数调用的时候所使用的一些空间。地址是由高向低减少。一个由c/C++编译的程序占用的内存分为以下几个部分1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。2、在Java语言里堆(heap)和栈(stack)里的区别 1)栈(stack)与堆(heap)都是Java用来在Ram中存放数据的地方。与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆。 2) 栈的优势是,存取速度比堆要快,仅次于直接位于CPU中的寄存器。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。另外,栈数据可以共享,详见第3点。堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢。 3)Java中的数据类型有两种。 一种是基本类型(primitive types), 共有8种,即int, short, long, byte, float, double, boolean, char(注意,并没有string的基本类型)。这种类型的定义是通过诸如int a = 3; long b = 255L;的形式来定义的,称为自动变量。值得注意的是,自动变量存的是字面值,不是类的实例,即不是类的引用,这里并没有类的存在。如int a = 3; 这里的a是一个指向int类型的引用,指向3这个字面值。这些字面值的数据,由于大小可知,生存期可知(这些字面值固定定义在某个程序块里面,程序块退出后,字段值就消失了),出于追求速度的原因,就存在于栈中。 另外,栈有一个很重要的特殊性,就是存在栈中的数据可以共享。假设我们同时定义 int a = 3; int b = 3; 编译器先处理int a = 3;首先它会在栈中创建一个变量为a的引用,然后查找有没有字面值为3的地址,没找到,就开辟一个存放3这个字面值的地址,然后将a指向3的地址。接着处理int b = 3;在创建完b的引用变量后,由于在栈中已经有3这个字面值,便将b直接指向3的地址。这样,就出现了a与b同时均指向3的情况。 特别注意的是,这种字面值的引用与类对象的引用不同。假定两个类对象的引用同时指向一个对象,如果一个对象引用变量修改了这个对象的内部状态,那么另一个对象引用变量也即刻反映出这个变化。相反,通过字面值的引用来修改其值,不会导致另一个指向此字面值的引用的值也跟着改变的情况。如上例,我们定义完a与 b的值后,再令a=4;那么,b不会等于4,还是等于3。在编译器内部,遇到a=4;时,它就会重新搜索栈中是否有4的字面值,如果没有,重新开辟地址存放4的值;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。 另一种是包装类数据,如Integer, String, Double等将相应的基本数据类型包装起来的类。这些类数据全部存在于堆中,Java用new()语句来显示地告诉编译器,在运行时才根据需要动态创建,因此比较灵活,但缺点是要占用更多的时间。 4)每个JVM的线程都有自己的私有的栈空间,随线程创建而创建,java的stack存放的是frames ,java的stack和c的不同,只是存放本地变量,返回值和调用方法,不允许直接push和pop frames ,因为frames 可能是有heap分配的,所以j为ava的stack分配的内存不需要是连续的。java的heap是所有线程共享的,堆存放所有 runtime data ,里面是所有的对象实例和数组,heap是JVM启动时创建。 5)String是一个特殊的包装类数据。即可以用String str = new String("abc");的形式来创建,也可以用String str = "abc";的形式来创建(作为对比,在JDK 5.0之前,你从未见过Integer i = 3;的表达式,因为类与字面值是不能通用的,除了String。而在JDK 5.0中,这种表达式是可以的!因为编译器在后台进行Integer i = new Integer(3)的转换)。前者是规范的类的创建过程,即在Java中,一切都是对象,而对象是类的实例,全部通过new()的形式来创建。Java 中的有些类,如DateFormat类,可以通过该类的getInstance()方法来返回一个新创建的类,似乎违反了此原则。其实不然。该类运用了单例模式来返回类的实例,只不过这个实例是在该类内部通过new()来创建的,而getInstance()向外部隐藏了此细节。那为什么在String str = "abc";中,并没有通过new()来创建实例,是不是违反了上述原则?其实没有。 6)关于String str = "abc"的内部工作。Java内部将此语句转化为以下几个步骤: (1)先定义一个名为str的对String类的对象引用变量:String str; (2)在栈中查找有没有存放值为"abc"的地址,如果没有,则开辟一个存放字面值为"abc"的地址,接着创建一个新的String类的对象o,并将o 的字符串值指向这个地址,而且在栈中这个地址旁边记下这个引用的对象o。如果已经有了值为"abc"的地址,则查找对象o,并返回o的地址。 (3)将str指向对象o的地址。 值得注意的是,一般String类中字符串值都是直接存值的。但像String str = "abc";这种场合下,其字符串值却是保存了一个指向存在栈中数据的引用!为了更好地说明这个问题,我们可以通过以下的几个代码进行验证。 String str1 = "abc"; String str2 = "abc"; System.out.println(str1==str2); //true 注意,我们这里并不用str1.equals(str2);的方式,因为这将比较两个字符串的值是否相等。==号,根据JDK的说明,只有在两个引用都指向了同一个对象时才返回真值。而我们在这里要看的是,str1与str2是否都指向了同一个对象。 结果说明,JVM创建了两个引用str1和str2,但只创建了一个对象,而且两个引用都指向了这个对象。 我们再来更进一步,将以上代码改成: String str1 = "abc"; String str2 = "abc"; str1 = "bcd"; System.out.println(str1 + "," + str2); //bcd, abc System.out.println(str1==str2); //false 这就是说,赋值的变化导致了类对象引用的变化,str1指向了另外一个新对象!而str2仍旧指向原来的对象。上例中,当我们将str1的值改为"bcd"时,JVM发现在栈中没有存放该值的地址,便开辟了这个地址,并创建了一个新的对象,其字符串的值指向这个地址。 事实上,String类被设计成为不可改变(immutable)的类。如果你要改变其值,可以,但JVM在运行时根据新值悄悄创建了一个新对象,然后将这个对象的地址返回给原来类的引用。这个创建过程虽说是完全自动进行的,但它毕竟占用了更多的时间。在对时间要求比较敏感的环境中,会带有一定的不良影响。 再修改原来代码: String str1 = "abc"; String str2 = "abc"; str1 = "bcd"; String str3 = str1; System.out.println(str3); //bcd String str4 = "bcd"; System.out.println(str1 == str4); //true str3 这个对象的引用直接指向str1所指向的对象(注意,str3并没有创建新对象)。当str1改完其值后,再创建一个String的引用str4,并指向因str1修改值而创建的新的对象。可以发现,这回str4也没有创建新的对象,从而再次实现栈中数据的共享。 我们再接着看以下的代码。 String str1 = new String("abc"); String str2 = "abc"; System.out.println(str1==str2); //false 创建了两个引用。创建了两个对象。两个引用分别指向不同的两个对象。 String str1 = "abc"; String str2 = new String("abc"); System.out.println(str1==str2); //false 创建了两个引用。创建了两个对象。两个引用分别指向不同的两个对象。 以上两段代码说明,只要是用new()来新建对象的,都会在堆中创建,而且其字符串是单独存值的,即使与栈中的数据相同,也不会与栈中的数据共享。 7) 数据类型包装类的值不可修改。不仅仅是String类的值不可修改,所有的数据类型包装类都不能更改其内部的值。 8) 结论与建议: (1)我们在使用诸如String str = "abc";的格式定义类时,总是想当然地认为,我们创建了String类的对象str。担心陷阱!对象可能并没有被创建!唯一可以肯定的是,指向 String类的引用被创建了。至于这个引用到底是否指向了一个新的对象,必须根据上下文来考虑,除非你通过new()方法来显要地创建一个新的对象。因此,更为准确的说法是,我们创建了一个指向String类的对象的引用变量str,这个对象引用变量指向了某个值为"abc"的String类。清醒地认识到这一点对排除程序中难以发现的bug是很有帮助的。 (2)使用String str = "abc";的方式,可以在一定程度上提高程序的运行速度,因为JVM会自动根据栈中数据的实际情况来决定是否有必要创建新对象。而对于String str = new String("abc");的代码,则一概在堆中创建新对象,而不管其字符串值是否相等,是否有必要创建新对象,从而加重了程序的负担。这个思想应该是享元模式的思想,但JDK的内部在这里实现是否应用了这个模式,不得而知。 (3)当比较包装类里面的数值是否相等时,用equals()方法;当测试两个包装类的引用是否指向同一个对象时,用==。 (4)由于String类的immutable性质,当String变量需要经常变换其值时,应该考虑使用StringBuffer类,以提高程序效率。 转载文章,原文地址http://blog.sina.com.cn/s/blog_65ca444f01011q14.html
什么是httpclient HTTP 协议可能是现在 Internet 上使用得最多、最重要的协议了,越来越多的 Java 应用程序需要直接通过 HTTP 协议来访问网络资源。虽然在 JDK 的 java net包中已经提供了访问 HTTP 协议的基本功能,但是对于大部分应用程序来说,JDK 库本身提供的功能还不够丰富和灵活。HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。下载地址: http://hc.apache.org/功能介绍 以下列出的是 HttpClient 提供的主要的功能,要知道更多详细的功能可以参见 HttpClient 的主页。 (1)实现了所有 HTTP 的方法(GET,POST,PUT,HEAD 等) (2)支持自动转向 (3)支持 HTTPS 协议(4)支持代理服务器等 导入依赖 执行GET请求 public class DoGET { public static void main(String[] args) throws Exception { // 创建Httpclient对象 CloseableHttpClient httpclient = HttpClients.createDefault(); // 创建http GET请求 HttpGet httpGet = new HttpGet("http://www.baidu.com/"); CloseableHttpResponse response = null; try { // 执行请求 response = httpclient.execute(httpGet); // 判断返回状态是否为200 if (response.getStatusLine().getStatusCode() == 200) { String content = EntityUtils.toString(response.getEntity(), "UTF-8"); System.out.println("内容长度:" + content.length()); // FileUtils.writeStringToFile(new File("C:\\baidu.html"), content); } } finally { if (response != null) { response.close(); } httpclient.close(); } } } 执行GET带参数 public class DoGETParam { public static void main(String[] args) throws Exception { // 创建Httpclient对象 CloseableHttpClient httpclient = HttpClients.createDefault(); // 定义请求的参数 URI uri = new URIBuilder("http://www.baidu.com/s").setParameter("wd", "java").build(); System.out.println(uri); // 创建http GET请求 HttpGet httpGet = new HttpGet(uri); CloseableHttpResponse response = null; try { // 执行请求 response = httpclient.execute(httpGet); // 判断返回状态是否为200 if (response.getStatusLine().getStatusCode() == 200) { String content = EntityUtils.toString(response.getEntity(), "UTF-8"); System.out.println(content); } } finally { if (response != null) { response.close(); } httpclient.close(); } } } 执行post请求 public class DoPOST { public static void main(String[] args) throws Exception { // 创建Httpclient对象 CloseableHttpClient httpclient = HttpClients.createDefault(); // 创建http POST请求 HttpPost httpPost = new HttpPost("http://www.oschina.net/"); CloseableHttpResponse response = null; try { // 执行请求 response = httpclient.execute(httpPost); // 判断返回状态是否为200 if (response.getStatusLine().getStatusCode() == 200) { String content = EntityUtils.toString(response.getEntity(), "UTF-8"); System.out.println(content); } } finally { if (response != null) { response.close(); } httpclient.close(); } } } 带参数的post请求 public class DoPOSTParam { public static void main(String[] args) throws Exception { // 创建Httpclient对象 CloseableHttpClient httpclient = HttpClients.createDefault(); // 创建http POST请求 HttpPost httpPost = new HttpPost("http://www.oschina.net/search"); // 设置2个post参数,一个是scope、一个是q List<NameValuePair> parameters = new ArrayList<NameValuePair>(0); parameters.add(new BasicNameValuePair("scope", "project")); parameters.add(new BasicNameValuePair("q", "java")); // 构造一个form表单式的实体 UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(parameters); // 将请求实体设置到httpPost对象中 httpPost.setEntity(formEntity); CloseableHttpResponse response = null; try { // 执行请求 response = httpclient.execute(httpPost); // 判断返回状态是否为200 if (response.getStatusLine().getStatusCode() == 200) { String content = EntityUtils.toString(response.getEntity(), "UTF-8"); System.out.println(content); } } finally { if (response != null) { response.close(); } httpclient.close(); } } } 封装HttpClient通用工具类 public class HttpClientUtil { public static String doGet(String url, Map<String, String> param) { // 创建Httpclient对象 CloseableHttpClient httpclient = HttpClients.createDefault(); String resultString = ""; CloseableHttpResponse response = null; try { // 创建uri URIBuilder builder = new URIBuilder(url); if (param != null) { for (String key : param.keySet()) { builder.addParameter(key, param.get(key)); } } URI uri = builder.build(); // 创建http GET请求 HttpGet httpGet = new HttpGet(uri); // 执行请求 response = httpclient.execute(httpGet); // 判断返回状态是否为200 if (response.getStatusLine().getStatusCode() == 200) { resultString = EntityUtils.toString(response.getEntity(), "UTF-8"); } } catch (Exception e) { e.printStackTrace(); } finally { try { if (response != null) { response.close(); } httpclient.close(); } catch (IOException e) { e.printStackTrace(); } } return resultString; } public static String doGet(String url) { return doGet(url, null); } public static String doPost(String url, Map<String, String> param) { // 创建Httpclient对象 CloseableHttpClient httpClient = HttpClients.createDefault(); CloseableHttpResponse response = null; String resultString = ""; try { // 创建Http Post请求 HttpPost httpPost = new HttpPost(url); // 创建参数列表 if (param != null) { List<NameValuePair> paramList = new ArrayList<>(); for (String key : param.keySet()) { paramList.add(new BasicNameValuePair(key, param.get(key))); } // 模拟表单 UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList); httpPost.setEntity(entity); } // 执行http请求 response = httpClient.execute(httpPost); resultString = EntityUtils.toString(response.getEntity(), "utf-8"); } catch (Exception e) { e.printStackTrace(); } finally { try { response.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return resultString; } public static String doPost(String url) { return doPost(url, null); } }
Nginx安装环境 Nginx是C语言开发,建议在linux上运行,本教程使用Centos6.5作为安装环境。gcc 安装nginx需要先将官网下载的源码进行编译,编译依赖gcc环境,如果没有gcc环境,需要安装gcc:yum install gcc-c++ PCRE PCRE(Perl Compatible Regular Expressions)是一个Perl库,包括 perl 兼容的正则表达式库。nginx的http模块使用pcre来解析正则表达式,所以需要在linux上安装pcre库。yum install -y pcre pcre-devel注:pcre-devel是使用pcre开发的一个二次开发库。nginx也需要此库。zlib zlib库提供了很多种压缩和解压缩的方式,nginx使用zlib对http包的内容进行gzip,所以需要在linux上安装zlib库。 yum install -y zlib zlib-develOpenssl OpenSSL 是一个强大的安全套接字层密码库,囊括主要的密码算法、常用的密钥和证书封装管理功能及SSL协议,并提供丰富的应用程序供测试或其它目的使用。 nginx不仅支持http协议,还支持https(即在ssl协议上传输http),所以需要在linux安装openssl库。 yum install -y openssl openssl-devel编译安装 将nginx-1.8.0.tar.gz拷贝至linux服务器。 拷贝方法参考http://jingyan.baidu.com/article/647f01159b86e17f2148a891.html解压: tar -zxvf nginx-1.8.0.tar.gz cd nginx-1.8.0configure ./configure --help查询详细参数(参考本教程附录部分:nginx编译参数)参数设置如下:./configure \--prefix=/usr/local/nginx \--pid-path=/var/run/nginx/nginx.pid \--lock-path=/var/lock/nginx.lock \--error-log-path=/var/log/nginx/error.log \--http-log-path=/var/log/nginx/access.log \--with-http_gzip_static_module \--http-client-body-temp-path=/var/temp/nginx/client \--http-proxy-temp-path=/var/temp/nginx/proxy \--http-fastcgi-temp-path=/var/temp/nginx/fastcgi \--http-uwsgi-temp-path=/var/temp/nginx/uwsgi \--http-scgi-temp-path=/var/temp/nginx/scgi 注意:上边将临时文件目录指定为/var/temp/nginx,需要在/var下创建temp及nginx目录编译安装 make make install 安装成功查看安装目录 : 启动nginx cd /usr/local/nginx/sbin/ ./nginx 查询nginx进程: 2791是nginx主进程的进程id,2794是nginx工作进程的进程id 注意:执行./nginx启动nginx,这里可以-c指定加载的nginx配置文件,如下: ./nginx -c /usr/local/nginx/conf/nginx.conf 如果不指定-c,nginx在启动时默认加载conf/nginx.conf文件,此文件的地址也可以在编译安装nginx时指定./configure的参数(--conf-path= 指向配置文件(nginx.conf))停止nginx 方式1,快速停止: cd /usr/local/nginx/sbin ./nginx -s stop 此方式相当于先查出nginx进程id再使用kill命令强制杀掉进程。方式2,完整停止(建议使用):cd /usr/local/nginx/sbin./nginx -s quit此方式停止步骤是待nginx进程处理任务完毕进行停止。重启nginx 方式1,先停止再启动(建议使用): 对nginx进行重启相当于先停止nginx再启动nginx,即先执行停止命令再执行启动命令。 如下: ./nginx -s quit ./nginx 方式2,重新加载配置文件: 当nginx的配置文件nginx.conf修改后,要想让配置生效需要重启nginx,使用-s reload不用先停止nginx再启动nginx即可将配置信息在nginx中生效,如下: ./nginx -s reload测试 nginx安装成功,启动nginx,即可访问虚拟机上的nginx:
简介 Solr是一个高性能,采用Java5开发,Solr基于Lucene的全文搜索服务器。同时对其进行了扩展,提供了比Lucene更为丰富的查询语言,同时实现了可配置、可扩展并对查询性能进行了优化,并且提供了一个完善的功能管理界面,是一款非常优秀的全文搜索引擎。 工作方式 文档通过Http利用XML 加到一个搜索集合中。Solr 查询该集合也是通过http收到一个XML/JSON响应来实现。它的主要特性包括:高效、灵活的缓存功能,垂直搜索功能,高亮显示搜索结果,通过索引复制来提高可用性,提供一套强大Data Schema来定义字段,类型和设置文本分析,提供基于Web的管理界面等。 Solr与Lucene的关系 1. Solr 是什么? Solr 是一个开源的企业级搜索服务器,底层使用易于扩展和修改的Java 来实现。服务器通信使用标准的HTTP 和XML,所以如果使用Solr 了解Java 技术会有用却不是必须的要求。Solr 主要特性有:强大的全文检索功能,高亮显示检索结果,动态集群,数据库接口和电子文档(Word,PDF 等)的处理。而且Solr 具有高度的可扩展,支持分布搜索和索引的复制。2. Lucene 是什么? Lucene 是一个基于 Java 的全文信息检索工具包,它不是一个完整的搜索应用程序,而是为你的应用程序提供索引和搜索功能。Lucene 目前是 Apache Jakarta 家族中的一个开源项目。也是目前最为流行的基于 Java 开源全文检索工具包。目前已经有很多应用程序的搜索功能是基于 Lucene ,比如 Eclipse 帮助系统的搜索功能。Lucene 能够为文本类型的数据建立索引,所以你只要把你要索引的数据格式转化的文本格式,Lucene 就能对你的文档进行索引和搜索。3. Solr VS Lucene Solr 与Lucene 并不是竞争对立关系,恰恰相反Solr 依存于Lucene,因为Solr 底层的核心技术是使用Apache Lucene 来实现的,简单的说Solr 是Lucene 的服务器化。需要注意的是Solr 并不是简单的对Lucene 进行封装,它所提供的大部分功能都区别于Lucene。 Lucene是一套信息检索工具包,但并不包含搜索引擎系统,它包含了索引结构、读写索引工具、相关性工具、排序等功能,因此在使用Lucene时你仍需要关注搜索引擎系统,例如数据获取、解析、分词等方面的东西。 首先Solr是基于Lucene做的 , Solr的目标是打造一款企业级的搜索引擎系统,因此它更接近于我们认识到的搜索引擎系统,它是一个搜索引擎服务,通过各种API可以让你的应用使用搜索 服务,而不需要将搜索逻辑耦合在应用中。而且Solr可以根据配置文件定义数据解析的方式,更像是一个搜索框架,它也支持主从、热换库等操作。还添加了高亮、facet等搜索引擎常见功能的支持
简单介绍: 全文检索是一种将文件中所有文本与检索项匹配的文字资料检索方法。全文检索系统是按照全文检索理论建立起来的用于提供全文检索服务的软件系统。 像我们平时用的百度谷歌搜索引擎,当我们在输入框输入任意内容后点击搜索,搜索引擎就会把与我们搜索内容相关的内容全部检索到,并按照一定的排序规则呈现给我们。 Lucene是apache软件基金会4 jakarta项目组的一个子项目,是一个开放源代码的全文检索引擎工具包,但它不是一个完整的全文检索引擎,而是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎,部分文本分析引擎(英文与德文两种西方语言)。Lucene的目的是为软件开发人员提供一个简单易用的工具包,以方便的在目标系统中实现全文检索的功能,或者是以此为基础建立起完整的全文检索引擎。Lucene是一套用于全文检索和搜寻的开源程式库,由Apache软件基金会支持和提供。Lucene提供了一个简单却强大的应用程式接口,能够做全文索引和搜寻。在Java开发环境里Lucene是一个成熟的免费开源工具。就其本身而言,Lucene是当前以及最近几年最受欢迎的免费Java信息检索程序库。我们经常提到信息检索程序库,虽然与搜索引擎有关,但不应该将信息检索程序库与搜索引擎相混淆。 下面我们就实现一个简单的例子,结合实例给大家讲解Lucene的工作机制。 代码结构: 整个实例的实现思路: 1.创建一个article对象,把该对象放到索引库中 1、创建一个article对象 2、把article对象转化成document对象 3、创建一个IndexWriter对象 4、把document对象加入到索引库中 2.从索引库中把article对象检索出来 1、创建一个IndexSearch对象 2、创建一个QueryParser对象 3、将document对象转换为article对象 代码: package com.tgb.lucene.bean; public class Article { private Long id; private String title; private String content; public Long getId() { return id; } public void setId(Long i) { this.id = i; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } } package com.tgb.lucene.helloworld; import java.io.File; import java.util.ArrayList; import java.util.Dictionary; import java.util.List; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.document.Field.Index; import org.apache.lucene.document.Field.Store; import org.apache.lucene.document.Fieldable; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.IndexWriter.MaxFieldLength; import org.apache.lucene.queryParser.QueryParser; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; import org.apache.lucene.search.ScoreDoc; import org.apache.lucene.search.TopDocs; import org.apache.lucene.store.Directory; import org.apache.lucene.store.FSDirectory; import org.apache.lucene.util.Version; import org.junit.Test; import com.tgb.lucene.bean.Article; /** * 1.创建一个article对象,把该对象放到索引库中 * 2.从索引库中把article对象检索出来 * @author hongxin * */ public class HelloWorld { /** * 创建索引 */ @Test public void testCreateIndex() throws Exception{ /** * 1、创建一个article对象 * 2、把article对象转化成document对象 * 3、创建一个IndexWriter对象 * 4、把document对象加入到索引库中 */ Article article = new Article(); article.setId(1L); article.setTitle("lucene可以做搜索引擎"); article.setContent("baidu,google都是很好的搜索引擎"); Document document = new Document(); /** * 第一个参数 * 放入到索引库中的name的名称"id" * 第二个参数 * 放入到索引库中的value:id的值 * 第三个参数 * Store * YES 该字段向内容库中存储 * no 该字段不向内容库中存储 * 第四个字段 * Index * no 不向目录库中存储 * NOT_ANALYZED 存储,但是不分词 * ANALYZED 存储,分词 */ Field idField = new Field("id", article.getId().toString(), Store.YES, Index.NOT_ANALYZED); Field titleField = new Field("title", article.getTitle(),Store.NO,Index.ANALYZED); Field contentField = new Field("content", article.getContent(), Store.YES, Index.ANALYZED); document.add(idField); document.add(titleField); document.add(contentField); //索引库 Directory directory=FSDirectory.open(new File("./indexDir")); //分词器 Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_30); /** * directory指向了索引库的路径 "./indexDir" * analyzer 分词器 把title,content的内容分词后的内容放入到目录中 * MaxFieldLength 限制每一个字段往索引库中存放的大小 */ IndexWriter indexWriter = new IndexWriter(directory,analyzer, MaxFieldLength.LIMITED); indexWriter.addDocument(document); indexWriter.commit(); indexWriter.close(); } /* * 搜索的过程 */ @Test public void testSearchIndex() throws Exception{ //索引库 Directory directory = FSDirectory.open(new File("./indexDir")); /** * 创建一个IndexSearch对象 */ IndexSearcher indexSearcher = new IndexSearcher(directory); Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_30); /** * 第一个参数 * 版本号 * 第二个参数 * 在哪个字段中进行检索 * 第三个参数 * 为分词器,在调用queryParser的parse方法的时候,需要对参数再次进行分词 */ QueryParser queryParser = new QueryParser(Version.LUCENE_30, "title", analyzer); Query query = queryParser.parse("lucene"); /** * query参数 * 包含了一个分词器 * 包含了一个关键词 * n 查找前n条记录 * TopDocs==Top Documents 前一些Documents */ TopDocs topDocs = indexSearcher.search(query, 1); int count = topDocs.totalHits; // 根据关键词计算出来的总的记录数 //ScoreDoc= Score Document ScoreDoc[] scoreDocs = topDocs.scoreDocs; List<Article> articles = new ArrayList<Article>(); for (ScoreDoc scoreDoc : scoreDocs) { float score = scoreDoc.score;//相关度得分 //scoreDoc.doc 根据关键词找到的索引 Document document = indexSearcher.doc(scoreDoc.doc); //Document转化成article的过程 Article article = new Article(); article.setId(Long.parseLong(document.get("id"))); article.setTitle(document.get("title")); article.setContent(document.get("content")); articles.add(article); } for (Article article : articles) { System.out.println(article.getId()); System.out.println(article.getTitle()); System.out.println(article.getContent()); } } }
JVM全称是Java Virtual Machine(Java虚拟机),他通过模拟一个计算机来达到一个计算机所具有的计算功能。 Java语言的一个非常重要的特点就是与平台的无关性。而使用Java虚拟机是实现这一特点的关键。一般的高级语言如果要在不同的平台上运行,至少需要编译成不同的目标代码。而引入Java语言虚拟机后,Java语言在不同平台上运行时不需要重新编译。Java语言使用Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。Java虚拟机在执行字节码时,把字节码解释成具体平台上的机器指令执行。这就是Java的能够“一次编译,到处运行”的原因。 体系结构 JVM的体系结构基本上由4部分组成:类加载器、执行引擎、内存区以及本地方法调用。结构图如下 工作机制 JVM执行程序的过程 :I.加载.class文件 II.管理并分配内存 III.执行垃圾收集 JRE(java运行时环境)包含JVM的java程序的运行环境 JVM是Java程序运行的容器,但是他同时也是操作系统的一个进程,因此他也有他自己的运行的生命周期,也有自己的代码和数据空间。 JVM在整个jdk中处于最底层,负责与操作系统的交互,用来屏蔽操作系统环境,提供一个完整的Java运行环境,因此也就虚拟计算机.操作系统装入JVM是通过jdk中Java.exe来完成,通过下面4步来完成JVM环境。 1.创建JVM装载环境和配置 2.装载JVM.dll 3.初始化JVM.dll并挂接到JNIENV(JNI调用接口)实例 4.调用JNIEnv实例装载并处理class类
互联网的产生带来了机器间通讯的需求,而互联通讯的双方需要采用约定的协议,序列化和反序列化属于通讯协议的一部分。通讯协议往往采用分层模型,不同模型每层的功能定义以及颗粒度不同,例如:TCP/IP协议是一个四层协议,而OSI模型却是七层协议模型。在OSI七层协议模型中展现层(Presentation Layer)的主要功能是把应用层的对象转换成一段连续的二进制串,或者反过来,把二进制串转换成应用层的对象--这两个功能就是序列化和反序列化。一般而言,TCP/IP协议的应用层对应与OSI七层协议模型的应用层,展示层和会话层,所以序列化协议属于TCP/IP协议应用层的一部分。本文对序列化协议的讲解主要基于OSI七层协议模型。 序列化: 将数据结构或对象转换成二进制串的过程 反序列化:将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程 下面以Java序列化、Hessian序列化、JSON序列化和XML序列化为例简单分析序列化与反序列化的过程。 Person类 /** * */ package com.http.testserialization; import java.util.Date; /** * @Description: 人的属性集合 * @Author chenkangxian * @Date 2013-6-25 下午4:45:59 * @Copyright: 2012 chenkangxian, All rights reserved. **/ class Person implements java.io.Serializable{ private static final long serialVersionUID = 1L; private String name; private int age; private String address; private Date birth; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public Date getBirth() { return birth; } public void setBirth(Date birth) { this.birth = birth; } } Java序列化 /** * */ package com.http.testserialization; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Date; /** * @Description: java序列化 * @Author chenkangxian * @Date 2013-6-25 下午3:43:08 * @Copyright: 2012 chenkangxian, All rights reserved. **/ public class TestJavaSerialization { public static void main(String[] args) throws IOException, ClassNotFoundException{ Person zhansan = new Person(); zhansan.setAddress("hangzhou"); zhansan.setAge(30); zhansan.setBirth(new Date()); zhansan.setName("zhansan"); //定义一个字节数组输出流 ByteArrayOutputStream os = new ByteArrayOutputStream(); //对象输出流 ObjectOutputStream out = new ObjectOutputStream(os); //将对象写入到字节数组输出,进行序列化 out.writeObject(zhansan); byte[] zhansanByte = os.toByteArray(); //字节数组输入流 ByteArrayInputStream is = new ByteArrayInputStream(zhansanByte); //执行反序列化,从流中读取对象 ObjectInputStream in = new ObjectInputStream(is); Person person = (Person)in.readObject(); System.out.println("name : " + person.getName() + ", age : " + person.getAge()); } } Hessian序列化 /** * */ package com.http.testserialization; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Date; import com.caucho.hessian.io.HessianInput; import com.caucho.hessian.io.HessianOutput; /** * @Description: hessian序列化 * @Author chenkangxian * @Date 2013-6-25 下午3:57:53 * @Copyright: 2012 chenkangxian, All rights reserved. **/ public class TestHessianSerialization { public static void main(String[] args) throws IOException { Person zhansan = new Person(); zhansan.setAddress("hangzhou"); zhansan.setAge(30); zhansan.setBirth(new Date()); zhansan.setName("zhansan"); ByteArrayOutputStream os = new ByteArrayOutputStream(); //hessian的序列化输出 HessianOutput ho = new HessianOutput(os); ho.writeObject(zhansan); byte[] zhansanByte = os.toByteArray(); ByteArrayInputStream is = new ByteArrayInputStream(zhansanByte); //hessian的反序列化读取对象 HessianInput hi = new HessianInput(is); Person person = (Person)hi.readObject(); System.out.println("name : " + person.getName() + ", age : " + person.getAge()); } } JSON序列化 /** * */ package com.http.testserialization; import java.io.IOException; import java.io.StringWriter; import java.util.Date; import org.codehaus.jackson.JsonFactory; import org.codehaus.jackson.JsonGenerator; import org.codehaus.jackson.map.ObjectMapper; /** * @Description: 使用json进行序列化 * @Author chenkangxian * @Date 2013-7-3 下午8:52:34 * @Copyright: 2012 chenkangxian, All rights reserved. **/ public class TestJSONSerialization { public static void main(String[] args) throws IOException{ Person person = new Person(); person.setAddress("hangzhou,china"); person.setAge(18); person.setBirth(new Date()); person.setName("zhangsan"); //json对象序列化 String personJson = null; ObjectMapper mapper = new ObjectMapper(); StringWriter sw = new StringWriter(); JsonGenerator gen = new JsonFactory().createJsonGenerator(sw); mapper.writeValue(gen, person); gen.close(); personJson = sw.toString(); //json对象反序列化 Person zhangsan = (Person)mapper.readValue(personJson, Person.class); System.out.println(personJson); System.out.println(zhangsan.getName()); } } XML序列化 /** * */ package com.http.testserialization; import java.util.Date; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.io.xml.DomDriver; /** * @Description: 使用xml进行对象序列化 * @Author chenkangxian * @Date 2013-7-3 下午10:10:18 * @Copyright: 2012 chenkangxian, All rights reserved. **/ public class TestXMLSerialization { /** * @param args */ public static void main(String[] args) { Person person = new Person(); person.setAddress("hangzhou,china"); person.setAge(18); person.setBirth(new Date()); person.setName("zhangsan"); //将person对象序列化为XML XStream xStream = new XStream(new DomDriver()); //设置Person类的别名 xStream.alias("person", Person.class); String personXML = xStream.toXML(person); //将XML反序列化还原为person对象 Person zhangsan = (Person)xStream.fromXML(personXML); System.out.println(personXML); System.out.println(zhangsan.getBirth()); } }
AOP(Aspect-Oriented Programming,面向方面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。 当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此。这种 散布在各处的无关的代码被称为横切(cross-cutting)代码,在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。 而AOP技术则恰恰相反,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为 “Aspect”,即方面。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。AOP代表的是一个横向的关系,如果说“对象”是一个空心的圆柱体,其中封装的是对象的属性和行为; 那么面向方面编程的方法,就仿佛一把利刃,将这些空心圆柱体剖开,以获得其内部的消息。而剖开的切面,也就是所谓的“方面”了。然后它又以巧夺天功的妙手 将这些剖开的切面复原,不留痕迹。就像下面的效果图,业务线走业务线,切面走切面,他们完好的结合在一起。 图解: 业务线:图中的紫色竖条。 服务类:图中的绿色横条。 服务类:像日志,权限,缓冲等跟具体的业务流程没有关系,没有这些服务,系统也可以正常运转,但是可以切入到每条业务线的某一个部分,增强业务线的功能,例如加入 日志后,可以分析日志,优化系统,或者通过日志发现系统运行时错误。
简介 Hibernate对数据库结构提供了较为完整的封装,Hibernate的O/R Mapping实现了POJO (POJO Plain Ordinary Java Object 简单的Java对象,实际就是普通JavaBeans,是为了避免和EJB混淆所创造的简称)和数据库表之间的映射,以及SQL 的自动生成和执行。程序员往往只需定义好了POJO 到数据库表的映射关系,即可通过Hibernate 提供的方法完成持久层操作。程序员甚至不需要对SQL 的熟练掌握, Hibernate/OJB 会根据制定的存储逻辑,自动生成对应的SQL 并调用JDBC 接口加以执行。 MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAO)。iBATIS 的着力点,则在于POJO 与SQL之间的映射关系。然后通过映射配置文件,将SQL所需的参数,以及返回的结果字段映射到指定POJO。 相对Hibernate“O/R”而言,iBATIS 是一种“Sql Mapping”的ORM实现。 Hibernate 优点: Hibernate的DAO层开发比MyBatis简单,Mybatis需要维护SQL和结果映射。 Hibernate对对象的维护和缓存要比MyBatis好,对增删改查的对象的维护要方便。 Hibernate数据库移植性很好,MyBatis的数据库移植性不好,不同的数据库需要写不同SQL。 Hibernate有更好的二级缓存机制,可以使用第三方缓存。MyBatis本身提供的缓存机制不佳。 Hibernate功能强大,数据库无关性好,O/R映射能力强,如果你对Hibernate相当精通,而且对Hibernate进行了适当的封装,那么你的项目整个持久层代码会相当简单,需要写的代码很少,开发速度很快,非常爽。 缺点: Hibernate的缺点就是学习门槛不低,要精通门槛更高,而且怎么设计O/R映射,在性能和对象模型之间如何权衡取得平衡,以及怎样用好Hibernate方面需要你的经验和能力都很强才行。 MyBatis 优点: MyBatis可以进行更为细致的SQL优化,可以减少查询字段。 MyBATIS入门简单,即学即用,提供了数据库查询的自动对象绑定功能,而且延续了很好的SQL使用经验,对于没有那么高的对象模型要求的项目来说,相当完美。 MyBatis容易掌握,而Hibernate门槛较高。 缺点: MyBATIS的缺点就是框架还是比较简陋,功能尚有缺失,虽然简化了数据绑定代码,但是整个底层数据库查询实际还是要自己写的,工作量也比较大,而且不太容易适应快速数据库修改。 两者相同点: Hibernate与MyBatis都可以是通过SessionFactoryBuider由XML配置文件生成SessionFactory,然后由SessionFactory 生成Session,最后由Session来开启执行事务和SQL语句。其中SessionFactoryBuider,SessionFactory,Session的生命周期都是差不多的。 Hibernate和MyBatis都支持JDBC和JTA事务处理。
上一篇文章介绍到redis的安装和配置,有时候我们需要考虑负载均衡或者容灾机制,在某台机器宕机的时候不能影响程序的正常工作,因此今天和大家共同搭建一些redis集群。 首先声明这篇文章紧密联系上一篇,一些路径以及压缩包都是上一篇文章下载的。这里我们以六个redis组成的集群为例(三主三从)。需要设置6个同样的redis服务,redis集群采用了数据分片的技术。我们可以是6个虚拟机,6个IP,也可以是1个虚拟机,同一个IP,不同的端口号。实例中采用的是一个虚拟机,不同端口的方式。 一、配置子节点 1.新建一个文件夹(myredis,可以自己定义)然后定位的该路径 2.在当前路径下新建六个文件夹(可以以不同端口号命名) 3.复制redis解压包路径下的(上一篇博客解压包)redis.conf到6个文件夹中。 4.将如下文件分别复制到上面的六个文件夹 5.配置6个文件夹下的redis.conf,这里以6383为例,其余五个同理,(其中port属性值为各自端口号) 按a键后可以通过箭头上下查找,需要改的属性参数如下(除port值不同,其余一样) 保存命令为:先按ESC 然后 按”:!wq”(引号内部为命令内容,不包括引号) 6.启动6个redis服务,指向各自的conf 7.查看启动的redis服务进程状态: 二、创建集群 1.安装ruby环境 因为redis-trib.rb是有ruby语言编写的所以需要安装ruby环境。 2.创建集群 首先,进入redis的解压包路径,然后配置6个节点之间互相通信 出现提示后按yes继续 3.测试 向主节点set一个值,然后从从节点中获取 到此为止redis集群搭建结束,以上过程中有问题指出还请即使交流。
redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。 下面主要介绍Linux环境通过控制命令进行redis的安装 1.环境准备 首先需要安装Linux操作系统或者Windows系统性安装虚拟机(本文不对此做介绍)。 下面是在Linux准备redis安装环境的命令 yum -y install cpp binutils glibc glibc-kernheaders glibc-common glibc-devel gcc make gcc-c++ libstdc++-devel tcl 2.创建文件夹 mkdir -p /usr/local/src/redis(路径可以自己定义) 3.进入刚刚创建好的文件夹 cd /usr/local/src/redis 4.下载redis安装包到上面的路径 wget http://download.redis.io/releases/redis-3.0.7.tar.gz 5.解压安装包 tar -xvf redis-3.0.7.tar.gz 6.编译 进入安装包路径 cd redis3.0.7 make 7.安装 make PREFIX=/usr/local/redis install(安装路径可以自己定义) 8.检查是否安装成功 进入安装目录cd /usr/local/redis/bin 输入 ll 9.启动redis服务 ./redis-server 出现大方块就启动成功了,到目前为止安装任务全部完成,这时候我们退出时,redis服务会关闭,所以要修改配置文件中的如下配置 10.Ctrl+Z 退出 11.进入配置文件: vi /usr/local/bin/redis.conf 按a键后可以通过箭头上下查找,将daemonize改为yes并保存: daemonize yes 保存命令为:先按ESC 然后 按”:!wq”(引号内部为命令内容,不包括引号) 12.查看当前开启的服务进程: ps –ef|grep redis 以上内容就Linux环境下redis的安装配置过程,本人亲测有效,有不足之处请大家批评指正!
前面文章介绍过EJB的一些基本概念和三种bean,今天重点介绍一下会话bean中的有状态会话bean和无状态会话bean。 会话bean分为有状态的会话bean和无状态的会话bean: 有状态会话bean : 每个用户有自己特有的一个实例,在用户的生存期内,bean保持了用户的信息,即“有状态”;一旦用户灭亡(调用结束或实例结束),bean的生命期也告结束。即每个用户最初都会得到一个初始的bean。 无状态会话bean : bean一旦实例化就被加进会话池中,各个用户都可以共用。即使用户已经消亡,bean 的生命期也不一定结束,它可能依然存在于会话池中,供其他用户调用。由于没有特定的用户,那么也就不能保持某一用户的状态,所以叫无状态bean。但无状态会话bean 并非没有状态,如果它有自己的属性(变量),那么这些变量就会受到所有调用它的用户的影响,这是在实际应用中必须注意的。 实例 代码结构 配置文件 java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces java.naming.provider.url=localhost 有状态会话bean代码 服务端接口 package com.bjsxt.ejb; public interface StatefulEjb { public void compute(int i); public int getResult(); } 服务端实现 package com.bjsxt.ejb; import javax.ejb.Remote; import javax.ejb.Stateful; @Stateful @Remote public class StatefulEjbBean implements StatefulEjb { int state; public void compute(int i) { state=state+i; } public int getResult() { return state; } } 客户端 package com.bjsxt.ejb; import javax.naming.InitialContext; public class StatefulEjbClient { /** * @param args */ public static void main(String[] args) throws Exception { InitialContext context = new InitialContext(); StatefulEjb ejb1=(StatefulEjb)context.lookup("StatefulEjbBean/remote"); System.out.println(ejb1.getResult()); ejb1.compute(1); System.out.println(ejb1.getResult()); ejb1.compute(1); System.out.println(ejb1.getResult()); ejb1.compute(1); System.out.println(ejb1.getResult()); ejb1.compute(1); System.out.println(ejb1.getResult()); ejb1.compute(1); System.out.println(ejb1.getResult()); StatefulEjb ejb2=(StatefulEjb)context.lookup("StatefulEjbBean/remote"); System.out.println(ejb2.getResult()); ejb2.compute(1); System.out.println(ejb2.getResult()); ejb2.compute(1); System.out.println(ejb2.getResult()); ejb2.compute(1); System.out.println(ejb2.getResult()); ejb2.compute(1); System.out.println(ejb2.getResult()); ejb2.compute(1); System.out.println(ejb2.getResult()); } } 输出结果 无状态会话bean代码 服务端接口 package com.bjsxt.ejb; public interface StatelessEjb { public void compute(int i); public int getResult(); } 服务端实现 package com.bjsxt.ejb; import javax.ejb.Remote; import javax.ejb.Stateless; @Stateless @Remote public class StatelessEjbBean implements StatelessEjb { int state; public void compute(int i) { state=state+i; } public int getResult() { return state; } } 客户端 package com.bjsxt.ejb; import javax.naming.InitialContext; public class StatelessEjbClient { /** * @param args */ public static void main(String[] args) throws Exception { InitialContext context = new InitialContext(); StatelessEjb ejb1=(StatelessEjb)context.lookup("StatelessEjbBean/remote"); System.out.println(ejb1.getResult()); ejb1.compute(1); System.out.println(ejb1.getResult()); ejb1.compute(1); System.out.println(ejb1.getResult()); ejb1.compute(1); System.out.println(ejb1.getResult()); ejb1.compute(1); System.out.println(ejb1.getResult()); ejb1.compute(1); System.out.println(ejb1.getResult()); StatelessEjb ejb2=(StatelessEjb)context.lookup("StatelessEjbBean/remote"); System.out.println(ejb2.getResult()); ejb2.compute(1); System.out.println(ejb2.getResult()); ejb2.compute(1); System.out.println(ejb2.getResult()); ejb2.compute(1); System.out.println(ejb2.getResult()); ejb2.compute(1); System.out.println(ejb2.getResult()); ejb2.compute(1); System.out.println(ejb2.getResult()); } } 输出结果 从实例中可以看出,有状态的会话bean 每个用户有自己特有的一个实例,每次会话都从零开始。无状态的会话bean各个用户都可以共用bean,第二次会话在第一次的基础上累加。 如何选择 根据上面分析的有状态会话Bean和无状态会话Bean的优缺点。如果要频繁的访问,并且多次访问之间会共享一些信息,这时候应该使用有状态会话Bean。对于不经常使用的功能,可以使用无状态会话Bean。无状态会话Bean的使用要比有状态会话Bean的使用多。
EJB作为EJB是sun的JavaEE服务器端组件模型,设计目标与核心应用是部署分布式应用程序。简单来说就是把已经编写好的程序(即:类)打包放在服务器上执行。凭借java跨平台的优势,用EJB技术部署的分布式系统可以不限于特定的平台。EJB (Enterprise JavaBean)是J2EE(javaEE)的一部分,定义了一个用于开发基于组件的企业多重应用程序的标准。 目前主要是用的是EJB3,与EJB2相比EJB3是JavaEE平台遇到了Dot NET平台之后的反击,也可以认为是Java开源领域的一次胜利,EJB3吸收了spring、hibernate等开源框架的优点。 EJB的分类 在J2EE里,Enterprise Java Beans(EJB)称为Java 企业Bean,是Java的核心代码,分别是会话Bean(Session Bean),实体Bean(Entity Bean)和消息驱动Bean(MessageDriven Bean)。 session bean session bean作为代表一个可处理应用中业务逻辑的完整组件,主要包括了各种业务逻辑的实现代码。session bean部署到EJB容器之后,其他客户端就可以获取该EJB组件,并调用他的方法。从客户端获取session bean到客户端调用该方法结束,可称为客户端与session bean之间的一次会话,因此session bean也称为会话bean。会话bean又分为有状态会话bean和无状态会话bean(后面文件有介绍)。 message driven bean message driven bean简称MDB,也就是消息驱动bean,本质上是由无状态会话bean发展而来的,但它与session bean的最大不同在于,他不允许客户端调用,只是JMS消息的异步消费者。它基于JMS消息,只能接收客户端发送的JMS消息然后处理。MDB实际上是一个异步的无状态SessionBean,客户端调用MDB后无需等待,立刻返回,MDB将异步处理客户请求。这适合于需要异步处理请求的场合,比如订单处理,这样就能避免客户端长时间的等待一个方法调用直到返回结果。 entity bean entity bean(实体bean)是域模型[1] 对象,用于实现O/R映射,负责将数据库中的表记录映射为内存中的Entity对象,事实上,创建一个Entity Bean对象相当于新建一条记录,删除一个Entity Bean会同时从数据库中删除对应记录,修改一个Entity Bean时,容器会自动将Entity Bean的状态和数据库同步。 EJB的作用 EJB是JavaEE的核心规范,而且是RMI、JNDI、JMS等规范的具体运用。是分布式技术的一种实现,它提供了一种良好的组件封装,EJB容器负责处理如事务、访问控制等系统级别问题,而EJB开发者则集中精力去实现业务逻辑;对于页面开发者来说,无需关心EJB的存在和实现方式,他们只需要调用EJB的方法就可以了。EJB组件是一种可移植的、与前端技术无关的服务器组件。 对于规模小、伸缩性要求不大的企业级应用而言,使用spring+hibernate为核心的技术开发即可,但对于应用规模较大,增长速度快,伸缩性能要求高,而且可能需要使用jsp页面以外的其他客户端来说的企业级应用来说选择EJB为核心的技术更为合适。
云计算这三个字,近些年听得比较多了,到底什么是云计算呢?云计算又有什么作用呢?接下来和大家共同揭开云计算的面纱。 云的出现,彻底的改变了我们的生活。通俗的讲,家里需要排污水的话需要修一条管道直通污水处理厂。但是这条管道并不是全天都在占用,而且只有一家使用的话,维护成本较高,闲置的时候造成了资源的浪费。云就起到了将这条管道虚拟化的作用,当管道闲置或者不超过管道的处理能力的情况下,可以将资源供给其他用户使用。用户不用关心这条管道是怎样设计和维护的,就可以直接使用这条管道,整个资源的调度分配。管理维护都交给云端负责,这就是云给我带来的便捷。 概念定义 云计算是分布式计算、效用计算、虚拟化技术、web服务、网络计算等技术的统合和发展。 关于云计算的定义是美国国家标准技术研究院NIST提出的,包括以下4点: 1.云计算是一种利用互联网实现随时随地、按需、便捷地访问共享资源池(如计算设施、存储设备、应用程序等)的计算模式。 2.云计算模式具有5个基本特征:按需自助服务、广泛的网络访问、共享的资源池、快速弹性能力、可度量的服务。 3.云计算有3中服务模式:软件即服务(SaaS)、平台即服务(PaaS)、基础设施即服务(IaaS)。 4.云计算有4中部署方式:私有云、社区云、公有云、混合云, 云计算的分类 云计算按照提供的服务类型可以分为基础设施即服务(infrastructure as a service)、平台即服务(platform as a service)和软件即服务(software as a service) 1.IaaS IaaS是云计算的基础,为上层云计算服务提供必要的硬件资源,同时在虚拟化技术的支持下,IaaS层可以实现硬件资源的按需配置,创建虚拟的计算、存储中心,使其能够把计算单元、存储器、I/O设备、宽带计算机基础设施集中起来,成为一个虚拟的资源池来对外提供服务。可以满足小规模开发人员对集群系统的需求,减小维护的负担。 2.PaaS PaaS要为SaaS层提供可靠的分布式编程框架,又要为IaaS层提供资源调度、数据管理、屏蔽底层系统的复杂性等支持;同时PaaS又将自己的软件研发平台作为一种服务开发给用户。PaaS层需要具备存储与处理海量数据的能力,用于支撑SaaS层提供的各种应用。因此,PaaS的关键技术包括并行编程模型、海量数据库、资源调度与监控、超大型分布式文件系统等分布式并行计算平台技术。 3.SaaS SaaS层部署在PaaS和IaaS平台之上,同时用户可以在PaaS平台上开发并部署SaaS服务。SaaS面向云计算终端用户,提供基于互联网的软件应用服务。 我们平时是用的云平台软件都是在这三层服务的基础上,目前很多云平台产品做得已经相当成熟,将来或许有一天我们所用的产品都来自一个云平台,共享一套基础实施,只需要个浏览器,就能满足我们所有的需求,真正的步入云时代。
概述 Web Service的出现是为了解决平台的互操作性差、和异构性等问题严重影响了Web应用的发展。它是一种跨平台跨语言的远程调用技术,服务端和客户端可以采用不同的语言编写,也可以在不同的平台上运行。 特点 Web Service是一种部署在Web上的对象或应用组件,客户端可以方便的调用Internet上暴露出来的接口来获得服务。它具有一下特点: 1)良好的封装性 Web Service是一种部署在Web上的对象,因此具有对象的特点,及良好的封装性,因此客户端只需要知道接口的相关信息而不需要关系具体实现。 2)松耦合 只要Web Service的调用接口不变,其内部改变对于客户端来说是透明的。 3)使用标准协议规范 Web服务是基于XML的消息交换机制的,所有的公共契约都采用Internet开放标准协议。 4)高度可集成性 由于Web Service采用了标准协议作为组件描述,因此他们之间可以通过Web Service来实现互操作。 5)易于构建 开发人员可以使用任何常用的编程语言及其现有的应用程序组件来构建。 应用场景 1)跨防火墙通信 分布式应用程序的使用者可能遍布世界各地,客户端和服务端之间可能会有防火墙或者代理服务器,因此客户端和服务端通信问题尤为棘手,Web Service的出现就很好地解决了这一问题。 2)应用程序集成 对于企业级应用来说,把不同语言不同平台下的应用集成起来可谓是难上加难,Web Service跨平台跨语言的特性只需要各应用之间暴露出来对方需要的接口,而不用关心程序之间的异构性。 3)B2B集成 Web Service的开放性、跨平台和跨语言是的B2B应用集成更加便捷,缩短集成时间,降低开发成本。 4)软件和数据重用 组件提供商可以把组件变成Web Service,并把相应的接口提供给服务使用者。这样服务使用者就可以无需将服务组件下载到本地,而是直接在应用中调用服务。 技术架构 目前有三种主流的Web服务实现方案:REST、SOAP、XML-RPC。XML-RPC已经慢慢被SOAP取代了,现在很少采用。SOAP在成熟度、安全性方面都优于REST,,但是在效率和易使用上,REST更胜一筹,SOAP是Web Service的核心技术,现在很多Web Service是基于SOAP的。 工作原理 Web Service角色包括服务提供者、服务注册中心和服务使用者。服务提供者在服务注册中心注册和发布自己的服务,并对服务请求进行响应。服务注册中心担任中介的作用,一边接收服务提供者发来的服务,一边供服务使用者在其统一目录中查找合适的服务。服务使用者是根据具体的应用需求调用服务的。
泛型定义为一种模式例子或模型。今天和大家共同学习一下分布式计算泛型,分布式计算泛型总共可划分为五大类共九种常见泛型,接下来一一介绍。 一、消息相关 消息相关的泛型包括消息传递泛型和消息系统泛型。 1.消息传递泛型 消息传递是进程之间互相通信的基本途径。两个进程间传递消息,一个为发送者,一个为接收者。发送者发送一条请求消息,该消息被传送到接收者,由接收着处理后返回一条应答消息。 2.消息系统泛型 消息系统泛型或面向对象的中间件(MOM)是在基本的消息传递泛型的基础上扩展来的。该消息系统可以理解成独立与进程间的中介,这样两个互相通信的进程之间就没有了请耦合关系。由发送者发送一条消息,消息被存入消息系统,然后由消息系统转发的对应的接收者,发送者一旦将消息发送出去,就可以执行其他任务了,剩下的转发工作有消息系统完成。 消息系统泛型可以进一步划分为两种子类型:点对点消息泛型和发布/订阅消息泛型。 1)点对点消息泛型 这种泛型是发送者和接收者一一对应的泛型,由发送者发送一条消息到消息系统,消息系统再转发到接收者的消息队列中,消息系统可以提供暂存机制,将消息的发送和接收分离。接收者从自己的消息队列中提取消息,然后加以处理。 2)发布/订阅消息泛型 这种泛型是多对多的泛型,多个订阅者可以有多个订阅,由发送者发送一条消息到消息系统,消息系统根据订阅者的订阅类型和消息类型将该消息转发到每一个订阅该类型消息的订阅者。这种泛型可以提供一个进程向一组进程组播消息。 二、服务器相关 服务器相关的泛型包括客户/服务器泛型和P2P泛型。 3.客户/服务器泛型 客户/服务器泛型由客户端和服务器组成,将非对称角色分配各两个协作进程,客户进程向服务器发起请求,并等待服务器响应,服务器等待来自客户的请求,处理并给出回应。 4.P2P泛型 P2P泛型源于P2P网络(又称为对等计算机网络)。这是一种无中心服务器,依赖用户群交换的互联网体系,每个用户既是一个节点,又充当服务器职责。可以说是没有服务器,也可以说每个用户端都是一台服务器。 三、远程调用相关 远程调用相关的泛型包括远程过程调用泛型、分布式对象泛型和网络服务泛型。 5.远程过程调用泛型 提供了一种能使开发人员可以像编写在单处理器上运行的传统应用程序一样,编写分布式软件系统的泛型。可以采用与本地过程调用类似的思想与概念,以进行进程间通信。 6.分布式对象泛型 分布式对象泛型将面向对象应用到分布式系统中,是面向对象软件开发技术的自然扩展。可以使应用程序访问分布于网络上的各个对象,通过调用对象的方法,应用层序可以获取对服务的访问。 1)远程方法调用 远程方法调用(RMI)是面向对象版本的RPC。进程可以调用对象方法,该对象可以驻留于某远程主机中。 2)对象请求代理 对象请求代理泛型有对象请求者、对象提供者和对象请求代理组成。进程向对象请求代理发出请求,对象请求代理将请求转发到能提供预期服务的对象。 7.网络服务泛型 网络服务泛型有服务请求者、服务提供者(对象)和目录服务三者组成。首先服务提供者将自身注册到网络上的目录服务器上,当服务请求者需要访问服务时,直接与服务器目录联系,如果请求的服务可用,则由目录服务器返回一个该服务的引用或地址,进程利用该引用与所需的服务进行交互。 四、移动代理 8.移动代理泛型 移动代理泛型是一种可移动的程序或对象。一个代理从源主机出发,然后根据其自身携带的执行路线,自动地在网上主机间移动。在每一台主机上代理访问所需的资源或服务,并执行必要的任务来完成使命。 五、云服务 9.云服务泛型 美国国家标准与技术研究院定义了云计算的三种服务模型:IaaS、PaaS和SaaS。 1)基础实施即服务(IaaS) 以服务的形式提供虚拟硬件资源、用户无需购买服务器、网络设备、存储设备,只需通过互联网租赁即可搭建自己的应用系统。 2)平台即服务(PaaS) 提供应用服务引擎,如互联网应用编程接口、运行平台等。用户基于该应用服务引擎可以构建该类应用。 3)软件即服务(SaaS) 用户通过Internet来使用软件,用户不必购买软件,只需按需租赁软件。
分布式计算是当前计算机领域常见的名词,那么到底什么事分布式,什么又是分布式计算呢?今天和大家共同研究一下这个话题。 定义 一个分布式系统是由若干通过网络互联的计算机组成的硬件系统,而且者系计算机互相配合以完成共同的目标。分布式计算的一种简单定义是在分布式系统上执行的计算。更为正式的定义是,分布式计算研究如何把一个需要非常巨大的计算能力才能解决的问题分成许多小的部分,然后把这些部分分配给许多计算机处理,最后把各部分的计算结果结合起来得到最终的结果。归其本质其实体现了一种算法的精髓:分而治之。 这其实跟我们生活中是一样的,比如城市排污问题,在没有下水道的情况下,每家每天都会产生很大废水。假如说每家把废水都到处乱泼,显然是不合理的,但是如果让政府单独为每家都处理废水也是有点不太现实。因此这时候就需要各小区的物业了,每个小区物业给装好下水道,每家的下水道通过自己的管道集中到小区总的管道,各小区的管道又汇总到整个城市的管道,最后来统一处理。这样就是一个分而治之的思想,也是我们分布式计算的思想。 分布式计算的优缺点 一种新科技的兴起就会伴随着正负两方面影响,下面简单介绍一些分布式计算的优缺点。 优点 低廉的计算机价格和网络访问的可用性。现在计算机的价格普遍可以接受,网络相当普及,大量互联计算机为分布式计算穿件了一个理想的环境。 资源共享,采用分布式可以有效地汇集资源。 可伸缩性,对资源需求的增加可以通过提供额外资源来有效解决。 缺点 多故障点,分布式计算涉及多台计算机,而且都依赖于网络通信,因此一台或多台计算机以及一条或多条网络出现问题,都会影响分布式系统,而且一旦出现问题不易排除。 安全性低,分布式系统为非授权用户的攻击提供了更多机会。非集中式管理使安全策略的实现和增强变得更为困难。 分布式计算的相关计算形式 单机计算 单机计算是最简单的计算形式,就是利用单台计算机进行计算,不用连接任何网络,因此只能使用本计算机系统内可以被即时访问的资源。 并行计算 并行计算是相对于串行计算的概念,指在并行计算机上所做的计算,即采用多个处理器来执行命令。并行计算机可分为时间上并行和空间上并行。 网络计算 网络计算是一个比较广泛的概念, 利用互联网把地理上广泛分布的各种资源连成一个逻辑整体,就像一台超级计算机。其核心思想是把网络连接起来的各种自治资源和系统组合起来,以实现资源共享、协同工作和联合计算,为各种用户提供基于网络的各类综合性服务。 云计算 云计算包含两层含义,一是商业层面,以云的方式提供服务;二是技术层面,即各种客户端的计算都有网络负责完成。通常云计算是指由网络计算、分布式计算、并行计算、效用计算等传统计算机和网络技术融合而成的一种商业计算模型。当前云计算主要形式包括基础设施即服务(IaaS)、平台即服务(PaaS)、和软件即服务(SaaS)。云计算强调专有,即请求或获取的资源是专有的,并且由少数团体提供、使用者不需要贡献自己的资源。
Hibernate延迟加载是项目中非常常用的技术。通过使用Hibernate延迟加载机制可以在加载数据是不必加载全部数据,而是只加载我们需要的那部分,其余部分在需要使用时才从数据库装载,以此来减少数据量提高系统性能。 Hibernate延迟加载大致可以分为两类,一类是延迟属性加载,另一类是延迟关联实体加载。 属性加载 属性加载又可以分为两类,一类是集合属性,一类是非集合属性。 关联加载 关联加载也分两种情况,一种是多对一,另一种是一对一。 今天遇到的问题属于关联加载的,A、B部门属于关联部门,B属于A的子部门,当查询B部门的时候并没有用的A部门的相关属性,所以为了保证性能我们并没有将A部门的信息加载到页面。但是当我们在B部门页面点击”返回上一级“按钮是,需要传入一个A部门的ID,这时候B部门信息已经显示到页面,相关的session已经关闭了,系统会抛出异常,错误信息如下: 解决办法: (1)将延迟加载属性设置为false 这种方法最简单,但是这样我们就不能用hibernate的延迟加载机制了,也就是说每次加载都有可能加载很多用不到的数据,影响系统性能,这是不推荐的。 (2)在web.xml中配置openSessionInViewFilter这个过滤器,必须将openSessionInViewFilter过滤器配置中Struts2核心过滤器之前。 <!-- 配置Spring的用于解决懒加载问题的过滤器 --> <filter> <filter-name>OpenSessionInViewFilter</filter-name> <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class> </filter> <filter-mapping> <filter-name>OpenSessionInViewFilter</filter-name> <url-pattern>*.action</url-pattern> </filter-mapping> 过滤所有的*.action。这种方法,让session关闭延迟在界面层才关闭。这样在界面取数据的时候就不会报错啦。
我们平时在写程序中常常使用多线程来提高CPU以及其他资源的利用率,但是当CPU中的线程超过了CPU调度范围时,我们的程序就会变得缓慢甚至出现死锁导致程序卡死等现象。也有很多时候我们需要创建的线程量巨大,但是每个线程的执行时间却相对较小,这样在新启线程和关闭线程的时候消耗的系统资源要比花在处理实际的用户请求的时间和资源更多。除了创建和销毁线程的开销之外,活动的线程也消耗系统资源。在一个 JVM 里创建太多的线程可能会导致系统由于过度消耗内存而用完内存或“切换过度”。 线程池的出现能较好的解决以上问题,线程池为线程生命周期开销问题和资源不足问题提供了解决方案。通过对多个任务重用线程,线程创建的开销被分摊到了多个任务上。其好处是,因为在请求到达时线程已经存在,所以无意中也消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使应用程序响应更快。而且,通过适当地调整线程池中的线程数目,也就是当请求的数目超过某个阈值时,就强制其它任何新到的请求一直等待,直到获得一个线程来处理为止,从而可以防止资源不足。 在使用线程池之前,必须知道如何去创建一个线程池,在Java5中,需要了解的是java.util.concurrent.Executors类的API,这个类提供了四种比较常用线程池。 MyThread类代码 public class MyThread extends Thread { @Override public void run() { System.out.println(Thread.currentThread().getName() + "正在执行。。。"); } } 1.newCachedThreadPool newCachedThreadPool() 是根据需求创建新线程的,需求多时,创建的就多,需求少时,JVM自己会慢慢的释放掉多余的线程,它是一种可变尺寸的线程池。 import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class CachedThreadPool { public static void main(String[] args) { ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); Thread t1 = new MyThread(); Thread t2 = new MyThread(); Thread t3 = new MyThread(); Thread t4 = new MyThread(); Thread t5 = new MyThread(); cachedThreadPool.execute(t1); cachedThreadPool.execute(t2); cachedThreadPool.execute(t3); cachedThreadPool.execute(t4); cachedThreadPool.execute(t5); // 关闭线程池 cachedThreadPool.shutdown(); } } 运行效果: 2.newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。新的线程加入后,如果正在运行的线程达到了上限,则会阻塞,直到有了空闲的线程来运行。他是定长尺寸的线程池。 import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class FixedThreadPool { public static void main(String[] args) { ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3); Thread t1 = new MyThread(); Thread t2 = new MyThread(); Thread t3 = new MyThread(); Thread t4 = new MyThread(); Thread t5 = new MyThread(); fixedThreadPool.execute(t1); fixedThreadPool.execute(t2); fixedThreadPool.execute(t3); fixedThreadPool.execute(t4); fixedThreadPool.execute(t5); // 关闭线程池 fixedThreadPool.shutdown(); } } 此时线程池的最大线程为3,运行效果: 3.newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。延迟执行示例代码如下: import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class ScheduledThredPool { public static void main(String[] args) { ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(2); // 创建实现了Runnable接口对象,Thread对象当然也实现了Runnable接口 Thread t1 = new MyThread(); Thread t2 = new MyThread(); Thread t3 = new MyThread(); // 将线程放入池中进行执行 scheduledThreadPool.execute(t1); // 使用延迟执行风格的方法 scheduledThreadPool.schedule(t2, 1000, TimeUnit.MILLISECONDS); scheduledThreadPool.schedule(t3, 10, TimeUnit.MILLISECONDS); // 关闭线程池 scheduledThreadPool.shutdown(); } } 运行效果(延迟过程需要自己实践): 4.newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。示例代码如下: import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class SingleThreadExecutor { public static void main(String[] args) { ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor(); Thread t1 = new MyThread(); Thread t2 = new MyThread(); Thread t3 = new MyThread(); Thread t4 = new MyThread(); Thread t5 = new MyThread(); singleThreadExecutor.execute(t1); singleThreadExecutor.execute(t2); singleThreadExecutor.execute(t3); singleThreadExecutor.execute(t4); singleThreadExecutor.execute(t5); // 关闭线程池 singleThreadExecutor.shutdown(); } } 运行效果: 以上四中线程池分别运用于不同场景,可在项目中根据实际情况选择应用,线程池的作用不仅仅是可控制创建线程的数量,更多的是在多线程中省去了频繁的线程创建和销毁过程,降低了系统销毁。
日志管理往往在项目中起着非常重要的作用,日志信息便于我们分析程序的执行情况,比较常用的日志工具有logging和log4j,当然还有其他很多种。为了实现日志的统一管理和配置,继而出现了slf4j来统一logging和log4j的接口。slf4j并不是用来做日志工作的,他是在对外提供logging和log4j的一个统一接口,程序只需要调用slf4j的接口不需要关系是哪种日志工具在发挥作用。而slf4j则是根据程序引用的jar包来觉定具体使用哪种日志工具。今天我们主要讲解log4j的使用及配置。 简介 Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。 需要引入的jar包: 日志级别: 调试信息——>Debug 一般信息——>info 警告信息——>warn 错误信息——>error 严重错误——>fatal 实例 根据我们项目的实际情况显示需要的信息,需要修改log4j.properties文件,例如: log4j.rootLogger=debug, stdout 设置日志级别为debug,显示如下: 但是有时候我们需要有些类显示debug信息,有些类显示error信息,(除了cn.itcast.oa包下的类显示error信息,其他都显示debug信息)则需要将log4j.properties修改为: log4j.rootLogger=debug, stdout log4j.logger.cn.itcast.oa=error 显示效果:
简介 Servlet(Server Applet),全称Java Servlet,未有中文译文。是用Java编写的服务器端程序。其主要功能在于交互式地浏览和修改数据,生成动态Web内容。狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。 servlet通常被称为服务器端小程序,试运行在服务端的程序, 用于处理以及响应客户端的请求。 servlet的配置 编辑好的servlet源文件并不能响应用户请求,还必须将其编译成class文件,将编译好的class文件放到WEB-INF/classes路径下,如果servlet有包,则还需要将class文件放到包路径下。 servlet的生命周期 我们编写的JSP页面最终将由web容器编译成对应的servlet,当servlet在容器中运行时,其实例的创建及销毁等都不是有程序猿决定的,而是由web容器进行控制的。 访问servlet的配置参数 配置servlet是,还可以增加额外的配置参数,通过使用配置参数,可以实现提供更好的可移植性,避免将参数以编码方式写在程序代码中。 servlet配置参数有两种方式 通过@WebServlet的initParams属性来指定。 通过在web.xml文件的<servlet.../>元素中添加<init-param.../>子元素来指定。 前面文章介绍过,JSP本质上就是servlet,JSP通过编译生成对应的servlet。servlet的效率非常低,尤其是当使用servlet生成表象层页面的时候,页面中所有的HTML标签都需要通过servlet的输出流来输出,因此极其繁琐。而且servlet是标准的Java类,必须有程序员开发、修改,前端以及美工不易操作,这对于分工来说也是一大弊端。 servlet作为表现出有以下三大缺点 开发效率地 程序可移植性差 程序可维护性差 在标准的mvc模式中servlet仅作为控制器使用,JavaEE应用架构正是遵循mvc模式的,其中JSP仅作为表现层技术,其作用有两点: 负责收集用户请求参数 将应用的处理结果、状态、数据呈现给用户。
JSP(Java server page)是Java EE规范最基本成员,他是Java Web开发的重点知识,虽然我们一直在用,但其原理知之甚少。今天重点研究一些JSP核心内容以及其工作原理。 JSP和Servlet的本质是一样的,因为JSP最终需要编译成Servlet才能运行,换句话说JSP是生成Servler的草稿文件。 JSP比较简单,就是在HTML中嵌入Java代码,或者使用JSP标签,包括使用用户自定义标签,从而可以动态的提供内容。早起JSP应用比较广泛,一个web应用可以全部由JSP页面组成,只需要少量的JavaBean即可,但是这样导致了JSP职责过于复杂,这是Java EE标准的出现无疑是雪中送炭,因此JSP慢慢发展成单一的表现技术,不再承担业务逻辑组件以及持久层组件的责任。 JSP基本原理 JSP的本质是servlet,当用户指定servlet发送请求时,servlet利用输出流动态生成HTML页面。由于包含大量的HTML标签。静态文本等格式导致servlet的开发效率极低,所有的表现逻辑,包括布局、色彩及图像等,都必须耦合在Java代码中,起静态的部分无需Java程序控制,只有那些需要从数据库读取或者需要动态生成的页面内容才使用Java脚本控制。 因此JSP页面内容有以下两部分组成 静态部分:HTML标签 动态部分:Java脚本 JSP的4种基本语法 JSP注释 JSP注释用于标注程序开发过程中的开发提示,不会输出到客户端。<%-- 注释内容 --%> JSP声明 JSP声明用于声明变量和方法,在JSP中声明方法看起来很特别,似乎不需要定义类就可以直接定义方法,方法似乎可以脱离类独立存在。实际上JSP声明会转换成servlet的成员变量或成员方法,因此JSP声明仍然符合Java语法。<%! 声明部分 %> 输出JSP表达式 <%=表达式 %> JSP脚本 以前JSP应用非常广泛,通俗点讲任何可以执行的Java代码都可以通过JSP脚本嵌入到HTML页面中。<% Java脚本 %> JSP的3个预编译指令 page指令 该指令是针对当前页面的指令,通常位于JSP页面的顶端,一个JSP页面可以使用多条page指令。 include指令 用于指定包含另一个页面的指令,使用include指令可以将一个外部文件嵌入到当前JSP文件中,同时解析这个页面中的JSP。include即可以包含静态文本,又可以包含动态JSP页面。 taglib指令 用于定义和访问自定义标签。 JSP的7个动作指令 forword指令 用于将页面响应转发到另一个页面,既可以转发到静态的HTML,也可以转发到动态的JSP页面,或者转发到容器中的servlet。 param指令 用于执行参数,必须与其支持参数的标签一起使用。本身不能单独使用,因为单独的param指令没有实际意义。 include指令 用于动态引入一个JSP页面,它是一个动态include指令,也用于包含某个页面,不会导入被include页面的编译指令,仅仅将被导入页面的body内容插入本页面。 plugin指令 用于下载JavaBean或applet到客户端执行。 useBean指令 创建一个JavaBean的实例 setProperty指令 设置JavaBean实例的属性值 getProperty指令 输出JavaBean实例的属性值 JSP脚本中的9个内置对象 application对象 javax.servlet.ServletContext的实例,该实例代表JSP所属的web应用本身,可以用于JSP页面,或者servlet之间交换信息。 config对象 javax.servlet.ServletConfig的实例,该实例代表JSP的配置信息 exception对象 java.lang.Throwable的实例,该实例代表其他页面中的异常和错误。 out对象 javax.servlet.jsp.JspWriter的实例,该实例代表JSP的输出流,用于输出内容,形成HTML页面。 page对象 代表页面本身,通常没有太大用处。 pageContext对象 javax.servlet.jsp.PageContext的实例。该对象代表JSP页面的上下文,使用该对象可以访问页面中的共享数据。 request对象 javax.servlet.http.HttpServletRequest的实例,该对象封装了一次请求,客户端请求的参数都被封装在这个对象里。 response对象 javax.servlet.http.HttpServletResponse的实例,代表服务器对客户端的响应,通常很少使用该对象直接响应,而是使用out对象,除非需要生成非字符响应。 session对象 javax.servlet.http.HttpSession的实例,该对象代表一次会话,当客户端浏览器与站点连接时,会话开始,当客户端关闭浏览器是会话结束。
现在项目中有需求比较两个字符串数组,找出其中不同的部分,并保存到本地txt。实现方式每个人都有自己的思路,这里提供一种通过归并排序实现的方式供大家参考。 基本思路是数组A和数组B对比,使用数组a来保存数组A中比数组B中多的元素(即在A中存在,B中不存在的元素),b来保存数据B中比数组A中多的元素(即B中存在,A中不存在的元素)。开始需要分别调用Sort()函数对A、B数组进行排序,然后使用CompareTo从两个数组中第一个数组进行比较,当A.0(A中第一个元素)>B.0时A.CompareTo(B)==1,当A.0=B.0时A.CompareTo(B)==0,当A.0<B.0时A.CompareTo(B)==-1。通过判断A.CompareTo(B)的值来执行a.add和b.add操作,最终就能得到a、b数组,然后写入到txt就可以了。 核心代码如下: /// <summary> /// 归并排序: 查找两个集合中的不同数据 /// </summary> /// <param name="root">源数据集合</param> /// <param name="source">新数据集合</param> /// <param name="remove">需在源数据中移除的集合</param> /// <param name="add">需在源数据中添加的集合</param> public void FindDistinct(List<string> root, List<string> source, out List<string> remove, out List<string> add) { remove = new List<string>(); add = new List<string>(); root.Sort(); source.Sort(); //foreach (string str in root) Console.WriteLine(str); //Console.WriteLine("\r\n"); //foreach (string str in source) Console.WriteLine(str); //Console.WriteLine("\r\n"); int i = 0, j = 0; while (i < root.Count && j < source.Count) { switch (root[i].CompareTo(source[j])) { case -1: remove.Add(root[i]); i++; break; case 0: i++; j++; break; case 1: add.Add(source[j]); j++; break; } } if (i < root.Count) { for (int m = i; m < root.Count; m++) remove.Add(root[m]); } else if (j < source.Count) { for (int m = j; m < source.Count; m++) add.Add(source[m]); } //Console.WriteLine("\r\nroot中不同的数据:"); //foreach (string str in remove) Console.WriteLine(str); //Console.WriteLine("\r\nsource中不同的数据:"); //foreach (string str in add) Console.WriteLine(str); } 调用: List<string> remove; List<string> add; FindDistinct(rpqlist, pdflist, out remove, out add); //将strArray输出到文本文件 using (TextWriter tw = new StreamWriter(@"D:\RPQ.txt")) { int index = 0; foreach (string str in remove) { string s = string.Format("{0:d3}\t{1}", index, str); tw.WriteLine(s); index++; } } using (TextWriter tw = new StreamWriter(@"D:\PDF.txt")) { int index = 0; foreach (string str in add) { string s = string.Format("{0:d3}\t{1}", index, str); tw.WriteLine(s); index++; } } MessageBox.Show("数据对比完毕,文件已经保存到D盘!"); 这里是对归并排序算法的一个小应用,希望对大家有所帮助,不足之处请大家批评指正。
在多线程编程中,如果你从非创建这个控件的线程中访问这个控件或者操作这个控件的话就会抛出这个异常。这是微软为了保证线程安全以及提高代码的效率所做的改进,但是也 给大家带来很多不便。 今天我就遇到了一个类似的问题,对DataGridView指定 DataSource 来填充数据,更新数据的时候,会导致DataGridView出现假死,显示错误或者滚动条无法显示的问题,在保证了DataGridView的ScrollBars设置为了Both,数据量大于DataGridView显示的的范围,而且没有冻结列的情况下,解决方法有两种: 第一种是通过设置 System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false; 在你的程序初始化的时候设置了这个属性,而且在你的控件中使用的都是微软Framework类库中的控件的话,程序就不会再出现错误信息了,但是这种办法不能从根本是解决问题,而且本人亲测貌似只能解决了程序报错问题,在DataGridView数据能正常显示,但不能拖动滚动条,即假死现象没有得到根本解决(个人现象,有待深入考究)。 第二种就是用委托 在子线程中,把给控件赋值委托个主线程来实现。代码如下: 主线程定义委托 #region datagrideview数据绑定委托 private delegate void InvokeHandler(); #endregion 在子线程中调用 this.Invoke(new InvokeHandler(delegate() { this.dataGrid.DataSource = dt2; })); 效果图:
错误信息: Fatal error encountered during command execution.(命令执行过程中遇到的致命错误。) 出现这种问题有可能是两种情况: 1.查询结果包含海量数据。长时间检索无响应。这种情况,需要优化SQL,尽量减少数据输出量。 2.表结构损坏。可以通过 CHECK TABLE REPAIR TABLE 语句进行检测或修复。 初步判断是第一种情况,有两种解决方案:优化sql语句,但是如果数据量确实大,而且对程序性能要求不高的情况下,有一种简单的解决方法就是设置CommandTimeOut属性。 MySqlCommand optcom = new MySqlCommand(strOpt, mysqlcon, trans); optcom.CommandTimeout = 0; CommandTimeout 属性 指示执行命令期间在终止尝试和产生错误之前需等待的时间。 设置和返回值 设置或返回 Long 值,该值指示等待命令执行的秒数。默认值为 30。 说明 用 Connection 对象或 Command 对象的 CommandTimeout 属性来允许因网络拥挤或服务器负载过重产生的延迟而取消 Execute 方法调用。如果在 CommandTimeout 属性设置的时间间隔内未执行完命令,将产生错误,并且 ADO 取消该命令。如果将属性设置为零,ADO 将一直等待到命令执行完毕。请确保正在为其编写代码的提供者和数据源支持 CommandTimeout 功能。 Connection 对象的 ConnectionTimeout 设置对同一 Connection 中 Command 对象上的 CommandTimeout 设置没有影响,即 Command 对象的 CommandTimeout 属性不继承 Connection 对象的ConnectionTimeout 值。 在 Connection 对象上,打开 Connection 后,ConnectionTimeout 属性将保持为读/写。 ConnectionTimeout 属性 指示在终止尝试和产生错误前建立连接期间所等待的时间。 设置和返回值 设置或返回指示等待连接打开的时间的长整型值(单位为秒)。默认值为 15。 说明 如果由于网络拥塞或服务器负载过重导致的延迟使得必须放弃连接尝试时,请使用 Connection 对象的 ConnectionTimeout 属性。如果打开连接前所经过的时间超过 ConnectionTimeout 属性上设置的时间,将产生错误,并且 ADO 将取消该尝试。如果将该属性设置为零,ADO 将无限等待直到连接打开。请确认正在对其编写代码的提供者会支持 ConnectionTimeout 功能。 连接关闭时 ConnectionTimeout 属性为读/写,而打开时其属性为只读。
我们在使用中MySQL的时候难免会遇到大批量数据inset的情况,通常最简单的方法就是写一个insert,然后通过循环给变量赋值,批量插入数据库: //save rddform for (int i = 0; i < rddformlist.Count; i++) { string cmdText = "insert into rddform (ID,CreatedTime,ModifiedTime,CreatedBy,ModifiedBy,FormType) values ('" + rddformlist[i].RddFormGuid + "','" + rddformlist[i].CreateTime + "','" + rddformlist[i].ModifyTime + "','" + rddformlist[i].CreateBy + "','" + rddformlist[i].ModifyBy + "'," + rddformlist[i].FormType + ")"; MySqlCommand mysqlcom = new MySqlCommand(cmdText, mysqlcon); mysqlcom.Transaction = trans; //绑定事务 mysqlcom.ExecuteNonQuery(); } 经过方法经过亲测,性能不太好,本地大概是一秒insert20条数据,云上大概是一秒insert3条数据。两千条数据本地大概就要一分半,云上大概十几分,显然速度太慢了。 现在我们改为多条数据合并插入,就是用一个insert语句插入所有的数据: //save rddform for (int i = 0; i < rddformlist.Count; i++) { strRddForm.Append("('" + rddformlist[i].RddFormGuid + "','" + rddformlist[i].CreateTime + "','" + rddformlist[i].ModifyTime + "','" + rddformlist[i].CreateBy + "','" + rddformlist[i].ModifyBy + "','" + rddformlist[i].FormType + "')"); if (i<rddformlist.Count-1) { strRddForm.Append(","); } } MySqlCommand rddformcom = new MySqlCommand(strRddForm.ToString(), mysqlcon); rddformcom.Transaction = trans; //绑定事务 rddformcom.ExecuteNonQuery(); 亲测之后两千条本地真的是秒插,云上大概一秒多,这速度让人根本没有拒绝的理由,果断的把代码改了。当然速度可能根据个人机器以及网络环境因素有关,下面是网上牛人做的性能对比 注意事项: SQL语句是有长度限制,在进行数据合并在同一SQL中务必不能超过SQL长度限制,通过max_allowed_packet配置可以修改,默认是1M,测试时修改为8M。 事务需要控制大小,事务太大可能会影响执行的效率。MySQL有innodb_log_buffer_size配置项,超过这个值会把innodb的数据刷到磁盘中,这时,效率会有所下降。所以比 较好的做法是,在数据达到这个这个值前进行事务提交。
云计算引发了一系列XX即服务的新模式,从早期的软件即服务(Saas)到现在流行的网络即服务(Naas),各种“XX即服务”的术语也让很多IT工作者觉得一头雾水,本期的信息化内参,带你全面了解各种各样的XX即服务。分析即服务(Analytics-as-a-Service,AaaS ) 在云计算之前,上一个数据仓库和商业智能项目通常意味着花费数月获取硬件和软件,实现自定制设计的数据仓库,同时符合其他业务需求。转移到基于云的数据分析服务,允许你用更多的时间分析,用更少的时间管理硬件和软件。 使用分析即服务可以协助企业获取客户和运营的洞察力,同时减少管理支出,尽管不能消除。企业IT能够对数据管理负责,因此要看看服务是否有很好的元数据支持。业务分析元数据应该包括数据集加载、源系统命名、数据元素定义以及质量控制衡量的相关信息。后端即服务(Backend-as-a-Service,BaaS) BaaS也作为移动后端即服务(MBaaS)而出名,是连接移动应用到云服务的一种方式。作为移动中间件的一个备选方案,BaaS方法使用统一的应用编程接口(API)和软件开发工具包(SDK)来连接移动应用到后端云存储。 BaaS与移动中间件不同,并且,它和传统的移动应用流程相反,BaaS把移动应用的前端连接到后端的各种基于云的服务上。那么,BaaS的这种特征会怎样影响企业开发者?BaaS目前的使用者又是谁呢?什么时候可以部署BaaS?数据库即服务(DataBase-as-a-Service,DBaaS) 简而言之,DBaaS向客户提供了许多与其他云服务相类似的优势:一个灵活的、可扩展的、按需服务的平台,它以自助服务和便捷管理为导向,可以对环境中的资源进行调配。 从DBaaS的优势来看,DBaaS依然着力于如何降低企业的投入成本,这对很多中小企业吸引力很大。另一方面如果是大公司,DBaaS可以提供部门级解决方案,而无需IT部门和采购部门的介入,提供更快和更容易的方法来实现小型解决方案。DBaaS的不足之处也是目前云服务的局限所在,譬如当客户开始将数据放入云端时,他们会遭遇到无法控制的网络性能问题等。接下来,DBaaS的走向又会如何呢?数据即服务(Data-as-a-Service,DaaS) 资源利用率不足,数据蔓延到整个企业IT系统导致管理的复杂程度不断加深,这是每一位CIO都要面对的难题。现实中的困境也促进了技术发展,数据即服务(Data-as-a-Service,DaaS)通过资源的集中化管理,为提升IT效率以及系统性能指明了方向。 如何在正确的时间将正确的数据传输到正确的位置?如何将困在应用系统中的数据进行虚拟化?如何在数据复杂度以及数据量不断增长的情况下,依然能够支撑多变的业务需求?这些问题都在加速数据即服务的普及。 数据即服务的元素包括:数据采集、数据治理和标准化、数据聚合、数据服务等,其精髓在于使数据管理更为集中化,让更多的用户无需去注意底层数据的问题,而将注意力完全放在如何使用这些数据。桌面即服务(Desk-as-a-Service,DaaS) 桌面即服务(DaaS)是一个桌面交付模型,允许公司将虚拟桌面交由云服务提供商管理。由于不是在内部虚拟桌面基础设施中部署,所以IT从管理在线虚拟桌面的负担中解放出来。 但桌面即服务的许可方面很复杂,你可以部署在公有云中,也可以放置在私有云里,这其中涉及到大量许可,例如,微软要求访问虚拟桌面的所有设备都要有虚拟桌面访问(VDA)许可。对虚拟桌面来说,VDA许可扮演的角色是操作系统许可。例如,如果客户端要访问Windows 8虚拟桌面,则需要一系列VDA许可,但不需要Windows 8许可。网络即服务(Net-as-a-Service,NaaS) 网络即服务似乎被认为是多余的(如果不是服务,服务供应商在卖什么?),同时也是定义不明确的,但我们不能忽视它的存在,因为它代表了网络的未来。它源于概念源自云计算,以及对OpenStack中虚拟网络的特定支持,Quantum界面描述了如何创建网络连接服务来支持云计算和存储服务的连接。 网络即服务与物理网络之间是有区别的。 果说,网络即服务的根源在云计算,那么,第一个实用NaaS选择就是虚拟覆盖网络。网络软件供应商Nicira(2012年由VMWare收购)开发了完全基于软件的Nicira NVP虚拟网络架构,并可以通过云API来控制。虚拟覆盖网络是隧道(使用一个合适的隧道协议,包括GRE)和虚拟交换机的组合,它在物理网络设备(交换机和路由器)“之上”创建了一个网络。 摘自http://cio.zdnet.com.cn/cio/2013/0516/2159741.shtml
检测到 ContextSwitchDeadlock Message: CLR 无法从 COM 上下文 0x622b440 转换为 COM 上下文 0x622b5b0,这种状态已持续 60 秒。拥有目标上下文/单元的线程很有可能执行的是非泵式等待或者在不发送 Windows 消息的情况下处理一个运行时间非常长的操作。这种情况通常会影响到性能,甚至可能导致应用程序不响应或者使用的内存随时间不断累积。要避免此问题,所有单线程单元(STA)线程都应使用泵式等待基元(如 CoWaitForMultipleHandles),并在运行时间很长的操作过程中定期发送消息。 图片信息: 解决办法: 注意: 这种解决办法只能让程序不再抛出异常,要从根本上解决还需要追踪一下程序,找到错误根源。希望能够对您有所帮助,有不足之处请批评指正。
简介 Filter 技术是servlet 2.3 新增加的功能。servlet2.3是sun公司于2000年10月发布的,它的开发者包括许多个人和公司团体,充分体现了sun公司所倡导的代码开放性原则。在众多参与者的共同努力下,servlet2.3比以往功能都强大了许多,而且性能也有了大幅提高。——来自百度百科 功能 其中最重要的就是filter功能.它使用户可以改变一个request和修改一个response. Filter 不是一个servlet,它不能产生一个response,它能够在一个request到达servlet之前预处理request,也可以在response离开servlet时处理response.换种说法,filter其实是一个“servlet chaining“(servlet 链). 包括 1. 在servlet被调用之前截获; 2. 在servlet被调用之前检查servlet request; 3. 根据需要修改request头和request数据; 4. 根据需要修改response头和response数据; 5. 在servlet被调用之后截获. 实例 我们在项目中每次请求jsp的时候为了避免出现中文乱码,都要设置字符集,如下所示: <span style="font-family:KaiTi_GB2312;font-size:18px;">request.setCharacterEncoding("GB18030");</span> 如果一个项目只请求单个jsp那就好办了,但是往往我们在一个项目中会多次请求jsp页面,这样我们就需要在每次请求jsp之前设置一遍字符集,重复而可扩展性差。 这里我们就通使用filter同一给每个jsp设置字符集,而字符集信息保存着xml文件中,用的时候在读取,这样既实现代码复用,有提供了可扩展性。 核心代码: filter <span style="font-family:KaiTi_GB2312;font-size:18px;">package com.bjpowernode.drp.util.filter; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; /** * 采用filter统一处理字符集 * @author Hongxin * */ public class CharsetEncodingFilter implements Filter { //成员变量用来接收xml配置的字符集 private String encoding; @Override public void destroy() { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { //进入filter //System.out.println("CharsetEncodingFilter--->>>begin"); //设置字符集 request.setCharacterEncoding(encoding); //继续执行 chain.doFilter(request, response); //离开filter //System.out.println("CharsetEncodingFilter--->>>end"); } //初始化filter @Override public void init(FilterConfig filterConfig) throws ServletException { this.encoding = filterConfig.getInitParameter("encoding"); } } </span> xml <span style="font-family:KaiTi_GB2312;font-size:18px;"><?xml version="1.0" encoding="UTF-8"?> <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <filter> <filter-name>CharsetEncodingFilter</filter-name> <filter-class>com.bjpowernode.drp.util.filter.CharsetEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>GBK</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharsetEncodingFilter</filter-name> <url-pattern>*.jsp</url-pattern> </filter-mapping> </web-app> </span> 通过使用filter就不用再每次请求jsp的时候分别设置字符集了,filter的思想是实现切面编程(AOP),可以再任何一个需要的点切断程序执行,并完成相应的功能然后继续执行程序。对此也是初步使用,有不足之处请大家批评指正。
Ajax最为网页异步交互技术相信大家并不陌生,这里我也不做过多介绍,详情点击(百度百科)。很多框架也封装了Ajax技术,使用起来更加简单,比如jQuery等等,这里为了了解原理,我们使用原生的Ajax。 我们以注册用户时,判断用户ID是否存在为例,实现一个最简单的异步请求,当我们鼠标离开输入框时自动判该用户ID是否存在,同时我们可以继续填写其他注册信息。 界面如下: HTML代码: <span style="font-family:KaiTi_GB2312;font-size:18px;"><input name="userId" type="text" class="text1" id="userId" size="10" maxlength="10" onkeypress="userIdOnKeyPress()" value="<%=userId %>" onblur="validate(this)"> <span id="spanUserId"></span></span> 创建Ajax核心对象XMLHttpRequest: <span style="font-family:KaiTi_GB2312;font-size:18px;">var xmlHttp; function createXMLHttpRequest() { //表示当前浏览器不是ie,如ns,firefox if(window.XMLHttpRequest) { xmlHttp = new XMLHttpRequest(); } else if (window.ActiveXObject) { xmlHttp = new ActiveXObject("Microsoft.XMLHTTP"); } }</span> 光标离开响应事件 <span style="font-family:KaiTi_GB2312;font-size:18px;">function validate(field) { //alert(document.getElementById("userId").value); //alert(field.value); if (trim(field.value).length != 0) { //创建Ajax核心对象XMLHttpRequest createXMLHttpRequest(); var url = "user_validate.jsp?userId=" + trim(field.value) + "&time=" + new Date().getTime(); //设置请求方式为GET,设置请求的URL,设置为异步提交 xmlHttp.open("GET", url, true); //将方法地址复制给onreadystatechange属性 //类似于电话号码 xmlHttp.onreadystatechange=callback; //将设置信息发送到Ajax引擎 xmlHttp.send(null); } else { document.getElementById("spanUserId").innerHTML = ""; } }</span> Ajax引擎状态改变时回调方法 <span style="font-family:KaiTi_GB2312;font-size:18px;">function callback() { //alert(xmlHttp.readyState); //Ajax引擎状态为成功 if (xmlHttp.readyState == 4) { //HTTP协议状态为成功 if (xmlHttp.status == 200) { if (trim(xmlHttp.responseText) != "") { //alert(xmlHttp.responseText); document.getElementById("spanUserId").innerHTML = "<font color='red'>" + xmlHttp.responseText + "</font>" }else { document.getElementById("spanUserId").innerHTML = ""; } }else { alert("请求失败,错误码=" + xmlHttp.status); } }</span> 目标jsp(设置休眠时间5秒,如果异步请求设置为false的话,这5秒钟内用户不能进行任何操作) <span style="font-family:KaiTi_GB2312;font-size:18px;"><% Thread.currentThread().sleep(5000); String userId = request.getParameter("userId"); if (UserManager.getInstance().findUserById(userId) != null) { out.println("用户代码已经存在"); } %></span> 因为我们这里给大家演示异步请求,所以异步请求值设为了true,这段时间内用户是可以继续输入其他信息的,等到5秒时间到了,会自动弹出用户已经存在 效果图 Ajax 的核心是 JavaScript 对象 XmlHttpRequest,它是一种支持异步请求的技术。简而言之,XmlHttpRequest 使您可以使用 JavaScript 向服务器提出请求并处理响应,而不阻塞用户。以上内容为实践中总结的经验,有不足之处请大家批评指正。
设计模式可谓再熟悉不过了,工厂三姐妹是设计模式中最常见的,也是面试中最常问的,但是就在昨天联想面试的时候面试官问我工厂方法和抽象工厂的区别,我心里特别明白两者之间的关系,但是就是用语言组织不起来,说了半天面试官也只能听懂个大概。还是平时总结的少,今天我们重新回顾一下这三个设计模式。 工厂是什么意思呢?结合三者的特点,我认为可以这样理解:工厂可以看做一个特殊的类,在这个类中专门负责生产一系列产品(对象)的一个集合就可以成为工厂。 那么上述三种模式之间究竟是怎样的关系呢?各自又有什么优缺点呢? 一、简单工厂模式 VS 工厂方法模式 1、先来看一个简单工厂的一段代码: <span style="font-family:KaiTi_GB2312;font-size:18px;">public class OperationFactory { //声明一个方法用来实例化,可是为什么要用静态方法呢 public static Operation createOperate(string operate) { Operation oper = null; switch (operate ) { //通过分支语句对子类进行实例化 case "+": oper = new OperationAdd(); break; case "-": oper = new OperationSub(); break; case "*": oper=new OperationMul (); break; case "/": oper =new OperationDiv (); break; } return oper ; } </span> 通过代码我们可以看出,在简单工厂中,可以讲多种需要实例化的对象在一个分支结构中一次性的完成。具体的选择要留给客户端去做。 例如: <span style="font-family:KaiTi_GB2312;font-size:18px;">oper= OperationFactory.createOperate("+");</span> 那我们继续扩展,当我们想要在程序中增加一种算法符号,这时,我们必须对简单工厂中的分支语句进行修改,这样其实就违背了设计模式中的开放——封闭原则。为了避免对内修改,我们再来看一下工厂方法模式。 2、工厂方法模式 核心思想:创建一个接口,子类去实现这个接口,同时,根据子类来决定究竟要实例化哪个对象。 看一段代码:<span style="font-family:KaiTi_GB2312;font-size:18px;"> interface IFactory //定义一个接口 {//非静态 Operation CreateOperation(); } class AddFactory:IFactory { public Operation CreateOperation() { return new OperationAdd();//子类j决定具体对哪个子类进行实例化 } } class SubFactory:IFactory { public Operation CreateOperation() { return new OperationSub(); } }</span> 其余代码类似略。 通过工厂方法模式,我们可以发现,如果现在再增加一个运算符的话,我们只需要额外增加一个运算符子类和实现接口的子类就好了,而不用去对已经写好的类进行修改了。可见与简单工厂相比,工厂方法模式遵循了开放——封闭原则:即对外扩展,对内封闭。 还有一个小小的区别:在简单工厂中用static声明方法为静态方法,而工厂方法中却未用到静态方法,原因是:在简单工厂中由于不需要子类进行继承,所以使用静态方法可以避免实例化,可以用类来直接调用方法。而在工厂方法中由于存在继承关系,所以不能使用静态方法 二、 工厂方法 VS 抽象工厂 我认为其实这二者本属于一类。 最主要的区别是: 工厂方法:涉及到的是只需定义一个产品类和一个对象接口,可以派生出多个子类。 而抽象工厂:定义一系列即多个产品类和多个对象接口,分别派生出多个子类。 这里,不多做解释。 三、简单工厂 VS 抽象工厂 在抽象工厂中可以定义多个产品类和对象接口,可是,如果我们想要在此基础上多加一个产品,那么此时,需要增加的类就会很多,同时还需要去更改涉及到的各个工厂。这其实无形中就已经增加了代码的维护量。对于编程来说又是一个不好的兆头。那么有什么办法可以改进它呢?其实,简单工厂可以弥补这个缺陷。 举一个大话设计模式中的例子:创建数据库:SqlServer和Access两类,同时包含IUser和IDepartment两张表如果用抽象工厂方法来设计如图: 如图,如果此时,我们想要在增加一个项目表Project时,必须增加项目接口,SqlserverProject和AccessProject的类同时,还必须修改企业两个工厂,在两个工厂中增加实例化Project的项,造成代码维护量增加。 此时,如果我们把IFactory抽象工厂用一个DataAccess来代替,同时增加一个字符串变量,利用简单工厂来实例化字符串变量中所指定的数据库。 简单工厂方法的类图如下: 当然,我们可以看出来,利用简单工厂方法,虽然改动相对少了,但是仍然违背了开放——封闭原则,因为在增加其他表时,仍需要改动DataAccess中的分支语句,所以,对于简单工厂和抽象工厂方法二者应该酌情使用,二者都有自己的优点和缺点,我们应该根据具体情况具体分析。 当然为了进一步改进,大话设计模式中还提到了利用“反射”和配置文件的技术。总之,设计模式是一门艺术,只有更好,没有最好!
前面一篇博客已经介绍了SOA的基本概念,至此我们应该对SOA这种编程理念有了一个基本的认识,今天我们继续介绍SOA的一些基本特性来帮助大家更深刻的认识SOA。 对于每套成熟的架构体系都会有着他自己的特性以及思想,下面我们根本从五个方面来介绍SOA的特性。 服务自治 服务自治原则要求单个服务在底层逻辑控制方面要尽可能是独立和自包含的,服务不依赖于访问它的客户端或其他服务。服务可以独立的进行部署以及实施版本策略和安全策略。 依赖开放的标准 SOA的一个目标是让不同厂商开发的服务能够进行互相操作,这样就需要依赖于一个开放的被不同厂商普遍接受的标准。SOA采用基于消息的通讯方式,从消息交换的角度来想就是要求消息自身标准化,在此方法SOAP(简单对象访问协议)消息的采用对消息承载的内容提供了一致性的表示。另外SOA真正的被用于企业级应用时,还需要考虑一下额外因素,比如消息安全、可靠传输、事务的支持等。要实现真正意义上的跨平台操作,实现这些特性的互操作方式同样需要一种开放的标准定下来。在这方面一些主流的IT厂商比如:微软、IBM和BEA等联合一些国际组织如:W3C、OASIS、WS-1等,对标准和规范的指定做出了极大的贡献,这些标准和规范定义在Web Service规范中。 支持跨平台 能够让不同平台进行通讯是SOA产生的最主要动机。正因为SOA采用的开放的标准,才使跨平台得以实现。跨平台最大的好处就是促进了异质系统的集成,使Java应用能够调用.NET平台暴露出来的服务接口。此外使用标准的服务兑现有逻辑的封装,实现了对历史遗留应用的重用,也给企业提供了一种节约成本的捷径。 组合和复用 按照提供功能大小的差异,不同的服务具有不同的粒度。我们可以把提供具有最小粒度功能实现的服务成为原子服务,多个原子服务可以通过合理的组合,编排成一个新的聚合型服务。功能的复用是我软件设计思想不变的主题,SOA鼓励创建具有高复用的服务。服务的组合性另一方面也促进了服务的重用。为了提高服务的复用程度,SOA甚至强调了创建与场景无关的服务,这样同样的服务就在不同场景的解决方案中使用了。 松耦合 SOA通过契约实现客户端对服务的调用,双方只需要采用能够匹配的契约就能保证正常的交往。基于契约的服务交往,又进一步促进了服务的自治,只要契约不发生改变,服务本身的实现就可以自由的变化,因此这样的耦合度是极低的。 对于SOA的基本特性就介绍这么多,下面的博客中将会继续更新有关SOA的更深入的内容,请大家持续关注!
SOA是面向服务架构,面向服务(SO:Service Orientation)代表的是一种设计理念,和面向对象(OO:Object Orientation)、面向组件(CO:Component Orientation)一样,对关注点进行分解的思想,面向服务是和技术无关的。 面向服务的体系结构,是一个组件模型,它将应用程序的不同功能单元(称为服务)通过这些服务之间定义良好的接口和契约联系起来。接口是采用中立的方式进行定义的,它应该独立于实现服务的硬件平台、操作系统和编程语言。这使得构建在各种这样的系统中的服务可以以一种统一和通用的方式进行交互。 这种具有中立的接口定义(没有强制绑定到特定的实现上)的特征称为服务之间的松耦合。松耦合系统的好处有两点,一点是它的灵活性,另一点是,当组成整个应用程序的每个服务的内部结构和实现逐渐地发生改变时,它能够继续存在。而另一方面,紧耦合意味着应用程序的不同组件之间的接口与其功能和结构是紧密相连的,因而当需要对部分或整个应用程序进行某种形式的更改时,它们就显得非常脆弱。 对松耦合的系统的需要来源于业务,应用程序需要根据业务的需要变得更加灵活,以适应不断变化的环境,比如经常改变的政策、业务级别、业务重点、合作伙伴关系、行业地位以及其他与业务有关的因素,这些因素甚至会影响业务的性质。我们称能够灵活地适应环境变化的业务为按需(On demand)业务,在按需业务中,一旦需要,就可以对完成或执行任务的方式进行必要的更改。 面向服务架构,它可以根据需求通过网络对松散耦合的粗粒度应用组件进行分布式部署、组合和使用。服务层是SOA的基础,可以直接被应用调用,从而有效控制系统中与软件代理交互的人为依赖性。 SOA是一种粗粒度、松耦合服务架构,服务之间通过简单、精确定义接口进行通讯,不涉及底层编程接口和通讯模型。SOA可以看作是B/S模型、XML(标准通用标记语言的子集)/Web Service技术之后的自然延伸。 SOA将能够帮助软件工程师们站在一个新的高度理解企业级架构中的各种组件的开发、部署形式,它将帮助企业系统架构者以更迅速、更可靠、更具重用性架构整个业务系统。较之以往,以SOA架构的系统能够更加从容地面对业务的急剧变化。 目前对于SOA其实没有一个统一的定义,不同人站在不同角度对SOA认识可能有所不同。但是,不管对SOA有着怎样的分歧,SOA的设计思想还是被大家普遍接受的,下面博客中我会继续介绍SOA的一些基本特性,希望大家持续关注!
被人揭下面具是一种失败,自己揭下面具是一种胜利。 ——雨果 四天前我们做了一件非常失败的事情,那就是今目标不合格。四天的时间里我们又做了一件非常成功的事情,从今目标的事情中反思自己,深刻的剖析自己,升华自己的思想。塞翁失马焉知非福,年轻就是用来犯错的,但是我们要在错误中成才,不断提升自己。 君子博学而日三省乎已,圣人亦云:“吾日三省吾身,为人谋而不忠乎?与朋友交而不信乎?传而不习乎?”。反思是成功认识的毕竟之路,但是我们这次是被老师告知今目标不合格采才去反思。只能说我们还很年轻,如果想要成熟应该是自己主动反思而不是被动反思,被动反思只能是我们成功之路上那些贵人的扶持。 今目标是一个非常好的管理软件,从思想的层次上帮助我们管理自身和团队,但是如此之好我们为什么还不按规定做呢?说到头就是人性的懒惰,项目开始建立到结束,大部分同志就是建项目,建协商,完成项目,几乎不按时更新进度和描述。这是毫无意义的,完全与我们使用今目标的初衷相违背,没能给我们带来帮助,恰恰相反是对我们的一种制约。那怎样才能充分发挥今目标的作用呢?首先我们先按照规范去做,三天一更新,项目按时建立与结束,通过今目标的帮助养成我们良好的习惯,然后把这些习惯融入我们的企业文化,这样的企业就会蒸蒸日上。 其实今目标问题只是只是表象,其实暴露出来的还是我们思想上的问题。管理我们做的严重不够,对自己要求不严格,进度更新不及时,对徒弟不负责,对自己不负责……这些问题如果带到一个企业里面,身为老板,对自己要求不严,对下属要求不严;身为员工不听上司安排,不按规定办事,这些无疑是这个企业的灾难。 归根到底就是我们不会经常反思自己,剖析自己。加入我们真的向古人那样吾日三省吾身的话,这些问题我们自己就能发现,自己发现自己的问题就是一种胜利。 每一个成功认识的路上都犯过很多错,自己发现错误并改正了,在别人眼里你就没有错误,你就非常的完美。被别人发现错误,即使你改正了,你都会给人家留下不好的印象。 小人无错,君子常过!今后的生活中,不管自己有没有犯错,都有经常自我反思,有则改之无则加勉。有问题及时暴露,不怕自己犯错,越早的发现问题我们成长的越快。
我们在访问一些大型购物网站的时候,都有添加到购物车这一项,而购物车里面的东西都是临时的,商品买完之后购物车里面的东西可能就没有价值了。如果把这些临时的东西都保存到服务器的话,无疑是一种资源浪费。因此今天我们就引入一个新名词Cookie,说是新名词其实一点也不陌生,我们电脑或者浏览器清理垃圾的时候都有轻触Cookie这一项。其实这就是我们访问一些网站的时候,开发者为了避免自己服务器压力,或者减少服务器的存储浪费,而把一些临时的与用户相关的文件或者信息保存到用户电脑上。 这样的技术是怎样实现的呢,这里我做了一个简单的实例,供大家参考。核心代码如下: <span style="font-family:KaiTi_GB2312;font-size:18px;color:#333333;background-color: rgb(255, 255, 255);">import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class SetCookies extends HttpServlet{ public void doGet(HttpServletRequest request,HttpServletResponse response)throws ServletException,IOException{ Cookie c1=new Cookie("password","123"); response.addCookie(c1); response.getWriter().println("add cookie success!"); } }</span> <span style="font-family:KaiTi_GB2312;font-size:18px;color:#333333;background-color: rgb(255, 255, 255);">import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class ShowCookies extends HttpServlet{ public void doGet(HttpServletRequest request,HttpServletResponse response)throws ServletException,IOException{ Cookie[] Cookies=request.getCookies(); for(int i=0;i<Cookies.length;i++){ Cookie c=Cookies[i]; response.getWriter().println(c.getName()+","+c.getValue()); } } }</span> 配置文件: <span style="font-family:KaiTi_GB2312;font-size:18px;color:#333333;background-color: rgb(255, 255, 255);"> <servlet> <servlet-name>SetCookies</servlet-name> <servlet-class>SetCookies</servlet-class> </servlet> <servlet-mapping> <servlet-name>SetCookies</servlet-name> <url-pattern>/SetCookies</url-pattern> </servlet-mapping> <servlet> <servlet-name>ShowCookies</servlet-name> <servlet-class>ShowCookies</servlet-class> </servlet> <servlet-mapping> <servlet-name>ShowCookies</servlet-name> <url-pattern>/ShowCookies</url-pattern> </servlet-mapping></span> 运行效果: 原理很简单,其实到具体应用用到cookie的地方特别多,因此我们还需要多多实践,多多应用,多读交流,有不足之处请大家批评指正。
我们经常遇到在网站或者软件注册新用户时需要向我们的注册邮箱发送一封激活邮件,然后我们去邮箱点击激活连接后我们的用户名才能登陆,其过程是当我们注册成功后数据库已经存入该用户的相关信息,但是用户状态为不可用,所以这时候该用户名是不能正常使用的。因此系统需要向我们的注册邮箱发一封激活邮件,我们点击激活连接后系统会将数据库中用户状态字段更改为可用状态,至此用户激活成功,该用户可以正常使用。下面是实现过程: 为了方便起见我们还是编写一个发送邮箱工具类。 <span style="font-family:KaiTi_GB2312;font-size:18px;color:#333333;">package cn.itcast.shop.utils; import java.util.Properties; import javax.mail.Authenticator; import javax.mail.Message; import javax.mail.Message.RecipientType; import javax.mail.MessagingException; import javax.mail.PasswordAuthentication; import javax.mail.Session; import javax.mail.Transport; import javax.mail.internet.AddressException; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; /** * 邮件发送工具类 * @author shx * */ public class MailUitls { /** * 发送邮件的方法 * @param to :收件人 * @param code :激活码 */ public static void sendMail(String to,String code){ /** * 1.获得一个Session对象. * 2.创建一个代表邮件的对象Message. * 3.发送邮件Transport */ // 1.获得连接对象 Properties props = new Properties(); props.setProperty("mail.host", "localhost"); Session session = Session.getInstance(props, new Authenticator() { @Override protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication("service@shop.com", "111"); } }); // 2.创建邮件对象: Message message = new MimeMessage(session); // 设置发件人: try { message.setFrom(new InternetAddress("service@shop.com")); // 设置收件人: message.addRecipient(RecipientType.TO, new InternetAddress(to)); // 抄送 CC 密送BCC // 设置标题 message.setSubject("来自官方激活邮件"); // 设置邮件正文: message.setContent("<h1>官方激活邮件!点下面链接完成激活操作!</h1><h3><a href='http://192.168.24.162:8080/shop/user_active.action?code="+code+"'>http://192.168.24.162:8080/shop/user_active.action?code="+code+"</a></h3>", "text/html;charset=UTF-8"); // 3.发送邮件: Transport.send(message); } catch (AddressException e) { e.printStackTrace(); } catch (MessagingException e) { e.printStackTrace(); } } public static void main(String[] args) { sendMail("aaa@shop.com","11111111111111"); } } </span> Action: <span style="font-family:KaiTi_GB2312;font-size:18px;color:#333333;"> /** * * 用户注册 */ public String regist(){ userService.save(user); this.addActionMessage("注册成功,情趣邮箱激活!"); return "msg"; } /** * 用户激活的方法 */ public String active() { // 根据激活码查询用户: User existUser = userService.findByCode(user.getCode()); // 判断 if (existUser == null) { // 激活码错误的 this.addActionMessage("激活失败:激活码错误!"); } else { // 激活成功 // 修改用户的状态 existUser.setState(1); existUser.setCode(null); userService.update(existUser); this.addActionMessage("激活成功:请去登录!"); } return "msg"; }</span> Service: <span style="font-family:KaiTi_GB2312;font-size:18px;color:#333333;"> // 业务层完成用户注册代码: public void save(User user) { // 将数据存入到数据库 user.setState(0); // 0:代表用户未激活. 1:代表用户已经激活. String code = UUIDUtils.getUUID()+UUIDUtils.getUUID();//调用随机ID生成工具 user.setCode(code); userDao.save(user); // 发送激活邮件; MailUitls.sendMail(user.getEmail(), code); } // 业务层根据激活码查询用户 public User findByCode(String code) { return userDao.findByCode(code); } // 修改用户的状态的方法 public void update(User existUser) { userDao.update(existUser); }</span> Dao: <span style="font-family:KaiTi_GB2312;font-size:18px;color:#333333;"> public void save(User user) { // TODO Auto-generated method stub this.getHibernateTemplate().save(user); } // 根据激活码查询用户 public User findByCode(String code) { String hql = "from User where code = ?"; List<User> list = this.getHibernateTemplate().find(hql,code); if(list != null && list.size() > 0){ return list.get(0); } return null; } // 修改用户状态的方法 public void update(User existUser) { this.getHibernateTemplate().update(existUser); }</span> 注册成功后激活之前数据库截图 激活之后数据库截图 因为我们只希望用户激活一次,所以激活成功后将数据的code字段清空,state改为1。目前系统没有上线,为了方便测试,我用的本地邮箱测试,仅支持局域网,需要外网的请参见博客http://blog.csdn.net/huyuyang6688/article/details/48031347,以上内容有不足之处请大家批评指正,谢谢!
概念 Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选。对于一个初学者来说,可以这样认为,当在一台机器上配置好Apache 服务器,可利用它响应HTML(标准通用标记语言下的一个应用)页面的访问请求。实际上Tomcat 部分是Apache 服务器的扩展,但它是独立运行的,所以当你运行tomcat 时,它实际上作为一个与Apache 独立的进程单独运行的。 Servlet 是在服务器上运行的小程序。服务器上需要一些程序,常常是根据用户输入访问数据库的程序。这些通常是使用公共网关接口(Common Gateway Interface,CGI)应用程序完成的。然而,在服务器上运行 Java,这种程序可使用 Java 编程语言实现。在通信量大的服务器上,JavaServlet 的优点在于它们的执行速度更快于 CGI 程序。各个用户请求被激活成单个程序中的一个线程,而无需创建单独的进程,这意味着服务器端处理请求的系统开销将明显降低。 技术前提 首先要下载好Tomcat和jdk,并配置好相应的系统变量,详情查看http://blog.csdn.net/wangyy130/article/details/47166695,然后在这个小例子中,还需要将Tomcat 中的servlet包的安装路径添加到用户变量中,添加方法跟添加系统变量类似,在用户变量中添加一个classpath,变量值就是你安装的Tomcat中servlet包的路径。然后要在Tomcat 中搭好一个小的系统框架,由于Tomcat版本可能不同,相关技术百度就可以。 实例 Servlet实现顺序图 代码 <span style="font-family:KaiTi_GB2312;font-size:18px;color:#666666;"><strong><html> <head> <title>登录</title> </head> <body> <form action="loginServlet"> 用户:<input type="text" name="username"><br> 密码:<input type="password" name="password"><br> <input type="submit" value="登录"> </form> </body> </html></strong></span> <span style="font-family:KaiTi_GB2312;font-size:18px;color:#666666;"><strong>import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class LoginServlet extends HttpServlet{ protected void doGet(HttpServletRequest request,HttpServletResponse response)throws ServletException,IOException{ String username=request.getParameter("username"); String password=request.getParameter("password"); System.out.println("username=" + username); System.out.println("password=" + password); response.setContentType("text/html"); response.getWriter().println("Login Sucess!!!"); } }</strong></span> <span style="font-family:KaiTi_GB2312;font-size:18px;color:#666666;"><strong><?xml version="1.0" encoding="ISO-8859-1"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0" metadata-complete="true"> <servlet> <servlet-name>MyServlet</servlet-name> <servlet-class>LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>MyServlet</servlet-name> <url-pattern>/loginServlet</url-pattern> </servlet-mapping> </web-app></strong></span> 界面 运行结果 例子很简单,有配置变量问题或者Tomcat搭建问题的可以私下联系我,左上角联系方式。有不足之处请大家批评指正!
云计算多租户几乎用于所有软件即服务 (Software as a Service, SaaS) 应用程序,因为计算资源是可伸缩的,而且这些资源的分配由实际使用决定。话虽如此,用户可以通过 Internet 访问多种类型的 SaaS 应用程序,从小的基于 Internet 的小部件到大型企业软件应用程序。根据存储在企业网络之外的软件供应商的基础架构上的数据不同,安全需求也在不断增长。应用程序需要多租户是有许多原因的,其中最明显的原因就是成本:在大多数情况下,为每个客户增加几个服务器和一个数据库是远远不够的,尽管在安全要求很高的情况下这么做有点用处。 多租户就是说多个租户共用一个实例,租户的数据既有隔离又有共享,说到底就是如何解决数据存储的问题。 现在SaaS Multi-Tenant在数据存储上存在两大类共三种主要的方案,分别是:独立数据库和共享数据库,其中共享数据库又可分为共享数据库,隔离数据架构和共享数据库,共享数据架构。下面就分别对这三种方案进行介绍: 独立数据库: 这是第一种方案,即一个Tenant一个Database,这种方案的用户数据隔离级别最高,安全性最好,但成本也高。 优点: 为不同的租户提供独立的数据库,有助于简化数据模型的扩展设计,满足不同租户的独特需求;如果出现故障,恢复数据比较简单。 缺点: 增大了数据库的安装数量,随之带来维护成本和购置成本的增加。 这种方案与传统的一个客户、一套数据、一套部署类似,差别只在于软件统一部署在运营商那里。如果面对的是银行、医院等需要非常高数据隔离级别的租户,可以选择这种模式,提高租用的定价。如果定价较低,产品走低价路线,这种方案一般对运营商来说是无法承受的。 共享数据库,隔离数据架构 这是第二种方案,即多个或所有租户共享Database,但一个Tenant一个Schema 优点: 为安全性要求较高的租户提供了一定程度的逻辑数据隔离,并不是完全隔离;每个数据库可以支持更多的租户数量。 缺点: 如果出现故障,数据恢复比较困难,因为恢复数据库将牵扯到其他租户的数据;如果需要跨租户统计数据,存在一定困难。 共享数据库,共享数据架构 这是第三种方案,即租户共享同一个Database、同一个Schema,但在表中通过TenantID区分租户的数据。这是共享程度最高、隔离级别最低的模式。 优点: 三种方案比较,第三种方案的维护和购置成本最低,允许每个数据库支持的租户数量最多。 缺点: 隔离级别最低,安全性最低,需要在设计开发时加大对安全的开发量;数据备份和恢复最困难,需要逐表逐条备份和还原。 如果希望以最少的服务器为最多的租户提供服务,并且租户接受以牺牲隔离级别换取降低成本,这种方案最适合。 总结 三种方案好比学生分宿舍,隔离数据库就好比10名学生一人一个宿舍,每个人拿着自己宿舍的钥匙,一般贵族学校才有的待遇,学生都得是土豪;共享数据库,隔离数据架构就好比10名学生一个宿舍,没人一把宿舍钥匙,一般家庭的学生都能住;共享数据库,共享数据架构就是家里条件比较差的学生,10个人一个宿舍,连宿舍钥匙都配不起,大家只有一把钥匙。 简单的举个例子,这三种方案各有优缺点,根据实际情况选择合适的方案。以上内容是本人通过网上大牛的分享以及和小伙伴的交流加上自己的理解,有不足之处,请大家批评指正。
反射 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的以及动态调用对象的方法的功能称为反射机制。反射机制动态获取方法并使用方法和自己直接创建一个类的对象去直接调用时完全不一样的。比如一个类里面有一个属性为private的属性或者方法,我们是不能直接去调用的,但是可以使用反射机制去动态调用。 IOC IOC最大的好处是把对象生成放在了XML里定义,所以当我们需要换一个实现子类将会变成很简单(一般这样的对象都是实现于某种接口的),只要修改XML就可以了,这样我们甚至可以实现对象的热插拨(有点象USB接口和SCSI硬盘了)。在不适用IOC之前一个对象如果依赖于另一个对象(后面我们简称依赖对象和被依赖对象),我们要在依赖对象中实例化一个被依赖对象,这样才能调用被依赖对象中的方法。显然这样耦合度比较高,不符合我们编程的原则。因此这时候我们就会引入一个第三方对象,它负责给依赖对象直接输送一个被依赖对象,降低二者之间的耦合性。下图是加入IOC容器前后,系统中对象耦合度的对比 软件系统在没有引入IOC容器之前,如图1所示,对象A依赖于对象B,那么对象A在初始化或者运行到某一点的时候,自己必须主动去创建对象B或者使用已经创建的对象B。无论是创建还是使用对象B,控制权都在自己手上。 软件系统在引入IOC容器之后,这种情形就完全改变了,如图2所示,由于IOC容器的加入,对象A与对象B之间失去了直接联系,所以,当对象A运行到需要对象B的时候,IOC容器会主动创建一个对象B注入到对象A需要的地方。 通过前后的对比,我们不难看出来:对象A获得依赖对象B的过程,由主动行为变为了被动行为,控制权颠倒过来了,这就是“控制反转”这个名称的由来。 实例 反射实例代码 <span style="font-family:KaiTi_GB2312;font-size:18px;color:#666666;"><strong>using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace StudentDAL { public class Student { //属性 public string Name{get;set;} public int Age { get; set; } //无参数构造函数 public Student() { this.Name = "无参数"; this.Age = 0; } //有参数构造函数 public Student(string name, int age) { this.Name = "name"; this.Age = age; } //public带参带返回值函数 public string PublishMethodReturn() { return string.Format("我叫"+Name+"年龄" +Age); } } }</strong></span> <span style="font-family:KaiTi_GB2312;font-size:18px;color:#666666;"><strong>using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Reflection; namespace ITOO_Reflection { class Program { static void Main(string[] args) { //使用 Assembly 定义和加载程序集, //加载在程序集清单中列出的模块, //以及从此程序集中查找类型并创建该类型的实例. //获取程序集 Assembly assembly = Assembly.Load("StudentDAL"); //从程序及获取指定对象类型 Type type = assembly.GetType("StudentDAL.Student"); var instance = assembly.CreateInstance("StudentDAL.Student"); //为学生类的属性赋值 type.GetProperty("Name").SetValue(instance, "shx", null); type.GetProperty("Age").SetValue(instance, 18, null); //获取Student类的方法 var method = type.GetMethod("PublishMethodReturn"); //调用Student类的成员方法PublishMethodReturn var S= method.Invoke(instance, null); Console.WriteLine(S); Console.Read(); } } }</strong></span> 运行结果 IOC实例代码 <span style="font-family:KaiTi_GB2312;font-size:18px;color:#666666;"><strong>using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ITOO.IOC.IDAL { public interface IUserDal { void HelloWord(); } } </strong></span> <span style="font-family:KaiTi_GB2312;font-size:18px;color:#666666;"><strong>using System; using System.Collections.Generic; using System.Linq; using System.Text; using ITOO.IOC.IDAL; namespace ITOO.IOC.DAL { public class User:IUserDal { public void HelloWord() { Console.WriteLine("helloword"); } } } </strong></span> <span style="font-family:KaiTi_GB2312;font-size:18px;color:#666666;"><strong>using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ITOO.IOC.IBLL { public interface IUserBll { void HelloWord(); } } </strong></span> <span style="font-family:KaiTi_GB2312;font-size:18px;color:#666666;"><strong>using System; using System.Collections.Generic; using System.Linq; using System.Text; using ITOO.IOC.IBLL; using ITOO.IOC.IDAL; using ITOO.Library.Core.AOP; namespace ITOO.IOC.BLL { public class UserBll:IUserBll { public void HelloWord() { //使用底层封装的SpringHelper从IOC容器中拿到D层的类的对象实例 IUserDal iuser = SpringHelper.GetObject<IUserDal>("User"); iuser.HelloWord(); } } } </strong></span> <span style="font-family:KaiTi_GB2312;font-size:18px;color:#666666;"><strong>using System; using System.Collections.Generic; using System.Linq; using System.Text; using ITOO.IOC.IBLL; using ITOO.Library.Core.AOP; namespace ITOO.IOC.Client { class Program { static void Main(string[] args) { //客户端通过底层封装的SpringHelper从IOC容器中根据B层类的对象的id拿到UserBll类的实例 IUserBll iuserbll = SpringHelper.GetObject<IUserBll>("UserBll"); //调用UserBll类的方法 iuserbll.HelloWord(); Console.Read(); } } } </strong></span> 运行结果 以上内容是本人根据网上大牛的分享以及其他资料的一些整理和理解,有不足之处请大家批评指正。
今天回顾线程方面的知识,发现一个很有意思的小程序,是用来说明多线程的下面贴出来分享下,对初学者理解线程有很大的帮助 爸爸和儿子的故事 <span style="font-family:KaiTi_GB2312;font-size:18px;">public class FatherThread extends Thread{ @Override public void run() { System.out.println("爸爸想抽烟,发现烟抽完了"); System.out.println("爸爸让儿子去买包红塔山"); Thread son = new SonThread(); son.start(); System.out.println("爸爸等儿子买烟回来"); try { //join含义:等待son线程执行完毕,father线程才继续执行 son.join(); } catch (InterruptedException e) { System.out.println("爸爸出门去找儿子跑哪去了"); System.exit(1); } System.out.println("爸爸高兴的接过烟开始抽,并把零钱给了儿子"); } } </span> <span style="font-family:KaiTi_GB2312;font-size:18px;">public class SonThread extends Thread{ @Override public void run() { String tags ="\t\t\t\t\t"; System.out.println(tags+"儿子去买烟了"); System.out.println(tags+"儿子去买烟要10分钟"); try { for(int i =0; i<10;){ Thread.sleep(1000); System.out.println(tags+"儿子出去第"+ ++i +"分钟"); } } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(tags+"儿子去买烟回来了"); } } </span> <span style="font-family:KaiTi_GB2312;font-size:18px;">public class Main { public static void main(String[] args){ System.out.println("爸爸和儿子的故事"); Thread faThread =new FatherThread(); faThread.start(); try { Thread.sleep(5000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } </span> 运行结果爸爸和儿子的故事 爸爸想抽烟,发现烟抽完了 爸爸让儿子去买包红塔山 爸爸等儿子买烟回来儿子去买烟了儿子去买烟要10分钟儿子出去第1分钟儿子出去第2分钟儿子出去第3分钟儿子出去第4分钟儿子出去第5分钟儿子出去第6分钟儿子出去第7分钟儿子出去第8分钟儿子出去第9分钟儿子出去第10分钟儿子去买烟回来了 爸爸高兴的接过烟开始抽,并把零钱给了儿子 程序进入主函数,首先father线程开始执行。爸爸让儿子去买烟,然后等待儿子买烟回来,这时son线程启动10分钟后儿子回来fathrer线程继续执行。
ASPX ASPX文件是微软的在服务器端运行的动态网页文件,通过IIS解析执行后可以得到动态页面,是微软推出的一种新的网络编程方法,而不是ASP的简单升级,因为它的编程方法和ASP有很大的不同,他是在服务器端靠服务器编译执行的程序代码,ASP 使用脚本语言,每次请求的时候,服务器调用脚本解析引擎来解析执行其中的程序代码,而ASP.NET则可以使用多种语言编写,而且是全编译执行的,比ASP 快,而且,不仅仅是快的问题,有很多优点。.asp是asp的文件后缀名,.aspx是asp.net的文件后缀名 Razor Razor是一种简单的编程语法,用于在网页中嵌入服务器端代码。Razor 语法基于 ASP.NET 框架,该框架是微软的 .NET 框架特别为 web 应用程序开发而设计的组成部分。Razor 语法赋予您所有 ASP.NET 的能力,但是使用了简化过的语法,如果您是初学者,则更容易学习,如果您是专家,则更有利于提高生产力。Razor 网页可被描述为带有两种内容的 HTML 页面:HTML 内容和 Razor 代码。当服务器读取这种页面后,在将 HTML 页面发送到浏览器之前,会首先运行 Razor 代码。这些在服务器上执行的代码能够完成浏览器中无法完成的任务,比如访问服务器数据库。服务器代码能够在页面被发送到浏览器之前创建动态的 HTML 内容。从浏览器来看的话,由服务器代码生成的 HTML 与静态 HTML 内容没有区别。使用 Razor 语法的 ASP.NET 网页拥有特殊的文件扩展名 cshtml(使用 C# 的 Razor 语法)或者 vbhtml(使用 VB 的 Razor)。 语法区别 总结 这里只是简单地比较,发现Razor比ASPX简单一些,Razor是作为一种后起的视图模板被ASP.NET MVC3使用。在最近的项目中也是再用Razor,个人用的比较多的就是在HTML页面中嵌入C#代码,感觉特别方便,以上总结只是本人对于网上及其他资料的一些整理和自己的认识,有不足之处,请大家批评指正。
前一篇博客写到项目中的广告位管理,当时没有写到今天的问题,这个问题当时也是困扰我好久。经过自己的努力和同志们的帮助,最后终于解决。 实现要求把所有的广告位后面的单选按钮设成一组,目的是一个广告位只能显示一张图片。只是简单的在特定列添加单选按钮其实并没有太大的难度,后期还要通过选中单选按钮把选中内容传回到Controller(这里用的是MVC),从网上找了不少资料并且通过Leader Wang的帮助,可以说实现方式有些曲折,但最终效果还是令人满意。 核心代码如下: <table id="tt"></table> $(function () { $('#tt').datagrid({ url: '/AdvertisementManage/QueryAdvertisement', title: '广告位管理(类型一)', width: 700, height: 'auto', fitColumns: true, pagination: true,//分页显示 rownumbers: true, onClickCell: onClickCell,//点击单元格触发事件 columns: [[ { field: 'AdvertisementID', title: '序号', width: 90, align: 'center' }, { field: 'AdvertisementName', title: '赞助商', width: 111, align: 'center' }, { field: 'AdvertisementUrl', title: '广告位链接', width: 160, align: 'center' }, { field: 'TimeStamp', title: '添加时间', width: 80, align: 'center' }, { field: 'UserID', title: '操作员', width: 80, align: 'center' }, { field: 'AdvertisementLocation', title: '广告位置', width: 80, align: 'center' }, { field: 'IsEnable', title: '是否显示', width: 60, align: 'center', //调用formater函数对列进行格式化,使其显示单选按钮(所有单选按钮name属性设为统一,这样就只能有一个处于选中状态) formatter: function (value, row, index) { if (row.IsEnable == 1) { //如果属性值等于1,则处于选中状态(默认表格中所有单选按钮最多只能有一个值等于1) var s = '<input name="isShow" type="radio" checked="checked" onclick="clk()"/> '; } else { var s = '<input name="isShow" type="radio" onclick="clk()"/> '; } return s; } } ]], onHeaderContextMenu: function (e, field) { e.preventDefault(); if (!$('#tmenu').length) { createColumnMenu(); } $('#tmenu').menu('show', { left: e.pageX, top: e.pageY }); } }); }); var id = undefined;//公共变量 //触发单元格事件 function onClickCell(rowIndex, field, value) { var row = $("#tt").datagrid('selectRow', rowIndex);//返回触发单元格的行标 var r1 = $("#tt").datagrid('getSelected');//返回被选中的行 id = r1.AdvertisementID;//返回该行的id } 当选中了单选按钮,触发单元格事件就被执行,获得该单元格的所在的行对象,然后就可以得到该行任意属性了。
广告位是网站中必不可少的内容之一,也是能直接给我们网站带来经济收益的内容之一。好的广告位不仅不会强宾压主,而会为我们的网站锦上添花,起到画龙点睛的作用,因此设计好广告位也是开发过程中一大重要环节。 最近在做一个珠宝检测站的项目,其中管理页面就涉及到了广告位管理,之前项目中也有不少有广告位的,但都不是自己负责。其大致思路就是要从管理页面上传相应的图片,并且要保存到数据库,然后从所有上传过的图片中选择一张显示到网站首页。原理并不太难,但是鉴于性能问题,我们只需要数据库保存图片的路径即可。 重点是在MVC+WCF这样的框架之下,可能稍微有点难度,经过网上博友的分享以及团队内交流,实现了最基本的上传和保存数据库功能。网站首页有五个广告位,由于广告位的位置以及尺寸不同,当然收费标准也是不一样的,哈哈~~我们初步设计每一个广告位有单独的管理单元,我的具体实现是广告位的管理页面有五个Datagrad,每个表格用来显示数据库中保存的本类广告位的图片路径以及相应信息。 如下图: 表格中显示数据库所有数据,通过操作是否显示列的单选按钮来确定首先显示哪一张广告图片。(需要把单选按钮的name属性统一命名,整个表格的单选按钮设为一组) 这里我们重点说的就是文件上传,为什么只在数据库保存路径而不直接保存图片呢?首先这是大多数开发人员的做法,其次从一定程度上保存路径要比图片性能高一些。我实在MVC中实现的,话不多说,直接上代码。 View <h2>上传文件</h2> @using(Html.BeginForm("FileUpLoad","AdvertisementManage",FormMethod.Post,new{enctype="multipart/form-data"})) { <br /> @*输入赞助商名称*@<input type="text" name="AdvertisementName" /> @*上传文件*@<input type="file" name="file" /> @*提交*@<input type="submit" name="UploadFile" /> } Controller public ActionResult FileUpLoad() { HttpPostedFileBase file = Request.Files["file"];//获得上传文件 //判断文件内容是否为空 if (file != null) { string filePath = Path.Combine(HttpContext.Server.MapPath("../images"), Path.GetFileName(file.FileName)); //设置文件保存路径 file.SaveAs(filePath);//将文件保存到filePath路径下 Guid g = new Guid("6dc3f7db-f038-4c48-9564-0ac52e0e29c1"); //实例化viewmodel,给属性赋值 AdvertisementManageViewModel advertisement=new AdvertisementManageViewModel(); advertisement .AdvertisementID=System.Guid.NewGuid(); advertisement.AdvertisementName = Request.Form["AdvertisementName"]; advertisement.AdvertisementUrl = filePath; advertisement.TimeStamp = DateTime.Now; advertisement.UserID = g; advertisement.IsEnable = 0; var service = ServiceFactory.GetService();//声明WCF服务 var s = service.AddAdvertisement(advertisement);//调用服务端的添加方法,将广告内容保存到数据库 //如果保存成功,返回FileUpLoad视图 if (true) { return RedirectToAction("FileUpLoad", "AdvertisementManage"); } } else { //return Content("<script>alert('上传失败!')</script>"); return View(); } } 效果图 以上这些都是目前实现的功能,样式上和逻辑上都需要优化,简单的原理分享给大家,有不足之处请批评指正。
简介 EJB是sun的JavaEE服务器端组件模型,设计目标与核心应用是部署分布式应用程序。凭借java跨平台的优势,用EJB技术部署的分布式系统可以不限于特定的平台。EJB (Enterprise JavaBean)是J2EE(javaEE)的一部分,定义了一个用于开发基于组件的企业多重应用程序的标准。其特点包括网络服务支持和核心开发工具(SDK)。 在J2EE里,Enterprise Java Beans(EJB)称为Java 企业Bean,是Java的核心代码,分别是会话Bean(Session Bean),实体Bean(Entity Bean)和消息驱动Bean(MessageDriven Bean)。 会话Bean SessionBean用于实现业务逻辑,它可以是有状态的,也可以是无状态的。每当客户端请求时,容器就会选择一个SessionBean来为客户端服务。Session Bean可以直接访问数据库,但更多时候,它会通过Entity Bean实现数据访问。 实体Bean Entity Bean是域模型对象,用于实现O/R映射,负责将数据库中的表记录映射为内存中的Entity对象,事实上,创建一个Entity Bean对象相当于新建一条记录,删除一个Entity Bean会同时从数据库中删除对应记录,修改一个Entity Bean时,容器会自动将Entity Bean的状态和数据库同步。 消息驱动Bean 是EJB2.0中引入的新的企业Bean,它基于JMS消息,只能接收客户端发送的JMS消息然后处理。MDB实际上是一个异步的无状态SessionBean,客户端调用MDB后无需等待,立刻返回,MDB将异步处理客户请求。这适合于需要异步处理请求的场合,比如订单处理,这样就能避免客户端长时间的等待一个方法调用直到返回结果。 EJB实际上是SUN的J2EE中的一套规范,并且规定了一系列的API用来实现把EJB概念转换成EJB产品。EJB是BEANS,BEANS是什么概念?那就是得有一个容纳她,让她可劲造腾的地方,就是得有容器。EJB必须生存在EJB容器中,这个容器可是功能强大之极!她首先要包装你BEAN,。EJB的客户程序实际上从来就不和你编写的EJB直接打交道,他们之间是通过HOME/REMOTE接口来发生关系的。它负责你的BEAN的所有的吃喝拉撒睡,比如BEAN的持续化,安全性,事务管理。 个人对于EJB也不是很理解,只是做一些知识点的梳理,后面加深理解后再跟大家分享经验……
XML是一种可扩展标记语言,之前学习HTML的时候就已经学习过了,为什么今天要在这里重新提起呢?因为XML是J2EE的十三个规范之一。 重新回顾 可扩展标记语言,标准通用标记语言的子集,是一种用于标记电子文件使其具有结构性的标记语言。 在电子计算机中,标记指计算机所能理解的信息符号,通过此种标记,计算机之间可以处理包含各种的信息比如文章等。它可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。 它非常适合万维网传输,提供统一的方法来描述和交换独立于应用程序或供应商的结构化数据。是Internet环境中跨平台的、依赖于内容的技术,也是当今处理分布式结构信息的有效工具。早在1998年,W3C就发布了XML1.0规范,使用它来简化Internet的文档信息传输。 加深理解 一、什么是可扩展标记语言? 可扩展标记语言是一种很像超文本标记语言的标记语言。 它的设计宗旨是传输数据,而不是显示数据。 它的标签没有被预定义。您需要自行定义标签。 它被设计为具有自我描述性。 它是W3C的推荐标准。 二、可扩展标记语言和超文本标记语言之间的差异 它不是超文本标记语言的替代。 它是对超文本标记语言的补充。 它和超文本标记语言为不同的目的而设计: 它被设计用来传输和存储数据,其焦点是数据的内容。 超文本标记语言被设计用来显示数据,其焦点是数据的外观。 超文本标记语言旨在显示信息,而它旨在传输信息。 对它最好的描述是: 它是独立于软件和硬件的信息传输工具。 两种解析方式 XML基本的解析方式有两种,一种叫SAX,另一种叫DOM。SAX是基于事件流的解析,DOM是基于XML文档树结构的解析.假设我们XML的内容和结构如下: <?xml version="1.0" encoding="UTF-8"?> <employees> <employee> <name>ddviplinux</name> <sex>m</sex> <age>30</age> </employee> </employees> 1.DOM生成和解析XML文档 为 XML 文档的已解析版本定义了一组接口。解析器读入整个文档,然后构建一个驻留内存的树结构,然后代码就可以使用 DOM 接口来操作这个树结构。优点:整个文档树在内存中,便于操作;支持删除、修改、重新排列等多种功能;缺点:将整个文档调入内存(包括无用的节点),浪费时间和空间;使用场合:一旦解析了文档还需多次访问这些数据;硬件资源充足(内存、CPU)。 public void parserXml(String fileName) { try { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); Document document = db.parse(fileName); NodeList employees = document.getChildNodes(); for (int i = 0; i < employees.getLength(); i++) { Node employee = employees.item(i); NodeList employeeInfo = employee.getChildNodes(); for (int j = 0; j < employeeInfo.getLength(); j++) { Node node = employeeInfo.item(j); NodeList employeeMeta = node.getChildNodes(); for (int k = 0; k < employeeMeta.getLength(); k++) { System.out.println(employeeMeta.item(k).getNodeName() + ":" + employeeMeta.item(k).getTextContent()); } } } System.out.println("解析完毕"); } catch (Exception e) { System.out.println(e.getMessage()); } } 2.SAX生成和解析XML文档 为解决DOM的问题,出现了SAX。SAX ,事件驱动。当解析器发现元素开始、元素结束、文本、文档的开始或结束等时,发送事件,程序员编写响应这些事件的代码,保存数据。优点:不用事先调入整个文档,占用资源少;SAX解析器代码比DOM解析器代码小,适于Applet,下载。缺点:不是持久的;事件过后,若没保存数据,那么数据就丢了;无状态性;从事件中只能得到文本,但不知该文本属于哪个元素;使用场合:Applet;只需XML文档的少量内容,很少回头访问;机器内存少; public void parserXml(String fileName) { SAXParserFactory saxfac = SAXParserFactory.newInstance(); try { SAXParser saxparser = saxfac.newSAXParser(); InputStream is = new FileInputStream(fileName); saxparser.parse(is, new MySAXHandler()); } catch (Exception e) { e.printStackTrace(); } }
简介 JNDI(Java Naming and Directory Interface,Java命名和目录接口)是SUN公司提供的一种标准的Java命名系统接口,JNDI提供统一的客户端API,通过不同的访问提供者接口JNDI服务供应接口(SPI)的实现,由管理者将JNDI API映射为特定的命名服务和目录系统,使得Java应用程序可以和这些命名服务和目录服务之间进行交互。目录服务是命名服务的一种自然扩展。两者之间的关键差别是目录服务中对象不但可以有名称还可以有属性(例如,用户有email地址),而命名服务中对象没有属性。 命名服务 命名服务是一种服务,它提供了为给定的数据集创建一个标准名字的能力。它允许把名称同Java对象或资源关联起来,而不必指出对象或资源的物理ID。这类似于字典结构(或者是Java的map结构),该结构中键映射到值。例如在Internet上的域名服务(domain naming service,DNS)就是提供将域名映射到IP地址的命名服务,在打开网站时一般都是在浏览器中输入名字,通过DNS找到相应的IP地址,然后打开。 所有的因特网通信都使用TCP、UDP或IP协议。IP地址由4个字节32位二进制数字组成,数字和名字相比,对于人来说名字比数字要容易记忆,但对于计算机来讲,它更善于处理数字。 其实所有的命名服务都提供DNS这种基本功能,即一个系统向命名服务注册,命名服务提供一个值到另一个值的映射。然后,另外一个系统访问命名服务就可以取得映射信息。这种交互关系对分布式企业级应用来讲显得非常重要,在Java中,基本的名字操作包含在Context接口中。 目录服务 目录服务是一种特殊类型的数据库,与SQL Server、Access、Oracle等关系数据库管理系统相反,构造目录服务的目的是为了处理基于行为的事务,并且使用一种关系信息模型。目录服务将命名服务的概念进一步引申为提供具有层次结构的信息库,这一信息库除了包含一对一的关系外,还有信息的层次结构。对目录服务而言,这种层次结构通常用于优化搜索操作,并且也可以按实际情况进行分布或者跨网络复制。 一个目录服务通常拥有一个名字服务(但是一个名字服务不必具有一个目录服务)。如电话簿就是一个典型的目录服务,一般先在电话簿里找到相关的人名,再找到这个人的电话号码。 每一种目录服务都可以存储有关用户名、用户密码、用户组(如有关访问控制的 信息)、以太网地址、IP地址等信息。它所支持的信息和操作会因为所使用的目录服务的不同而不同。遗憾的是,访问不同目录服务的协议也会不同,所以读者需要了解多 种API。 这就是JNDI的起源,就像JDBC一样,JNDI充当不同名称和目录服务的通用API或者说是前端,然后使用不同的后端适配器来连接实际服务。 JNDI是J2EE技术中的一个完整的组件。它支持通过一个单一的方法访问不同的、新的和已经存在的服务的方法。这种支持允许任何服务提供商执行通过标准服务提供商接口(SPI)协定插入JNDI框架。 作用 JNDI的功能简单说就是可以简单的方式去查找某种资源。 JNDI是一个应用程序设计的API,为开发人员提供了查找和访问各种命名和目录服务的通用、统一的接口,类似JDBC都是构建在抽象层。比如在Tomcat中配置了一个JNDI数据源,那么在程序中之需要用Java标准的API就可以查找到这个数据源,以后数据源配置发生变化了,等等,程序都不需要改动,之需要改改JNDI的配置就行。增加了程序的灵活性,也给系统解耦了。 总结 J2EE 规范要求所有 J2EE 容器都要提供 JNDI 规范的实现。JNDI 在 J2EE 中的角色就是“交换机” —— J2EE 组件在运行时间接地查找其他组件、资源或服务的通用机制。在多数情况下,提供 JNDI 供应者的容器可以充当有限的数据存储,这样管理员就可以设置应用程序的执行属性,并让其他应用程序引用这些属性(Java 管理扩展(Java Management Extensions,JMX)也可以用作这个目的)。JNDI 在 J2EE 应用程序中的主要角色就是提供间接层,这样组件就可以发现所需要的资源,而不用了解这些间接性。 在 J2EE 中,JNDI 是把 J2EE 应用程序合在一起的粘合剂,JNDI 提供的间接寻址允许跨企业交付可伸缩的、功能强大且很灵活的应用程序。这是 J2EE 的承诺,而且经过一些计划和预先考虑,这个承诺是完全可以实现的。