
暂无个人介绍
前言 对于一个应用程序而言,数据持久化是必不可少的,Android程序也不例外。这篇博客将介绍Android中关于SQLite的使用,SQLite是一种嵌入式的数据库引擎,专门适用于资源有限的设备上进行适量的数据存储,而Android就全面支持标准的SQLite数据库。在本片博客中,将说明SQLite数据库的创建以及维护,还有使用SQLite执行CRUD的两种方式,以及SQLite中事务的使用,最后都会使用示例讲解博客中所提到的概念性的内容。 SQLite Android对SQLite数据库,提供了完全的支持,而所有创建的SQLite数据库,仅限于当前应用访问,如果其他应用需要访问,则必须提供的Content Provider的支持,并且SQLite数据库会随着Android应用的卸载而被删除。SQLite是一个嵌入式的数据库引擎,最后是以文件的形式保存数据的。从本质上来看,SQLite的操作方式只是一种更为便捷的文件操作,当应用程序创建或打开一个SQLite数据库时,其实只是打开一个文件准备读写。因为SQLite仅适用于资源有限的小型设备,所以本身就不应该把大量数据存储在设备的SQLite数据库里,SQLite只适合存储一些小型的数据。 为了使SQLite和其他数据库间的兼容性最大化,SQLite支持对列上类型进行“类型近似”,列的类型近似指的是存储在列上的数据进行推荐类型存储。所以虽然SQLite内部只支持NULL、INTEGER、REAL(浮点书)、TEXT(文本)和BLOB(大二进制对象)这五种数据类型,但实际上SQLite完全可以接受varchar(n)、char(n)、decimal(p,s)、date等类型数据,只不过SQLite会在运算或保存时将它们转换为上面五种数据类型中相应的类型。大多数数据库的引擎都是使用静态的、强类型的数据类型,数据的类型是由它的容器决定的,这个容器是指被存放的特定列。而SQLite使用的是动态类型,在SQLite中,值的数据类型跟值本身相关,而不是与它的容器相关,所以SQLite允许把各种类型的数据保存到任何类型字段中,开发者可以不用关心声明该字段说使用的数据类型。但是有一种情况例外,定义为INTEGER PRIMARY KEY的字段只能存储64位整数,当向这种字段保存除整数意外的其他类型的数据时,SQLite会产生错误。 SQLite数据库创建与维护 从官方文档上了解到,在Android项目中,创建SQLite数据库推荐继承SQLiteOpenHelper类,然后重写其中的onCreate()方法,在onCreate()方法中,对执行数据库创建的SQL语句。而SQLiteOpenHelper不仅仅用于SQLite数据的创建,还可以对其进行维护,以及获得SQLiteDatabase这个数据库操作对象。 SQLiteOpenHelper提供了两个构造器,用于传递当前上下文对象以及SQLite数据库版本信息,在SQLiteOpenHelper的继承类的构造函数中,会调用它,构造器的签名如下: SQLiteOpenHelper(Context context,String name,SQLiteDatabase.CursorFactory factory,int version). SQLiteOpenHelper(Context context,String name,SQLiteDatabase.CursorFactroy factory,int version,DatabaseErrorHandler errorHandler). 上面的构造函数中,都是用于创建一个SQLite数据库,context为一个当前应用的上下文对象;name是数据库名称;factory是一个允许子类在查询时使用的游标,一般不用传Null;version是数据库版本号;errorHandler是一个接口,传递当数据库错误的时候,执行的补救方法。 在SQLiteOpenHelper中,可以进行SQLite数据库的创建、维护、日志以及获取可读写的数据库对象,通过下面几个常用方法得到支持: String getDatabaseName():获取数据库名。 SQLiteDatabase getReadableDatabase():创建或者打开一个可读的数据库对象。 SQLiteDatabase getWritableDatabase():创建或者打开一个可读/写的数据库对象。 abstract void onCreate(SQLiteDatabase db):当第一次调用SQLiteOpenHelper的时候执行,之后再次调用将不再执行,一般用于完成数据库初始化的工作。 void onUpgrade(SQLiteDatabase db,int oldVersion,int newVersion):当数据库版本号发生向上更新时,被执行。 void onDowngrade(SQLiteDatabase db,int oldVersion,int newVersion):当数据库版本号发生向下更新时,被执行。 下面提供一个简单的SQLiteOpenHelper的继承类代码,用于创建数据库以及表结构: 1 package com.example.sqlitedbdemo.db; 2 3 import android.content.Context; 4 import android.database.sqlite.SQLiteDatabase; 5 import android.database.sqlite.SQLiteOpenHelper; 6 7 public class DbOpenHelper extends SQLiteOpenHelper { 8 private static String name = "mydb.db"; 9 private static int version = 1; 10 11 public DbOpenHelper(Context context) { 12 super(context, name, null, version); 13 } 14 15 @Override 16 public void onCreate(SQLiteDatabase db) { 17 // 只能支持基本数据类型 18 String sql = "create table person(id integer primary key autoincrement,name varchar(64),address varchar(64))"; 19 db.execSQL(sql); 20 } 21 @Override 22 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 23 // TODO Auto-generated method stub 24 String sql="alter table person add sex varchar(8)"; 25 db.execSQL(sql); 26 } 27 } Tips:当创建好SQLite数据库的之后,可以在/data/data/<package name>/databases目录下找到SQLite数据库文件。 执行CRUD操作 当使用SQLiteOpenHelper的getReadableDatabase()或者getWritableDatabase()方法获取到SQLiteDatabase对象,就可以对这个数据库进行操作了。 对于熟悉SQL语句的开发者而言,其实只需要使用两个方法,即可执行所有CRUD操作,以下方法提供多个重载方法: void execSQL():通过SQL语句执行一条非查询语句。 Cursor rawQuery():通过SQL语句执行一条查询语句。 下面以一个示例讲解一下单纯使用SQL语句实现CRUD操作: 接口代码: 1 package com.examle.sqlitedbdemo.service; 2 3 import java.util.List; 4 import java.util.Map; 5 6 public interface PersonService { 7 8 public boolean addPerson(Object[] params); 9 public boolean deletePerson(Object[] params); 10 public boolean updatePerson(Object[] params); 11 public Map<String, String> viewPerson(String[] selectionArgs); 12 public List<Map<String, String>> listPersonMaps(String[] selectionArgs); 13 } 接口的实现代码: 1 package com.examle.sqlitedbdemo.dao; 2 3 import java.util.ArrayList; 4 import java.util.HashMap; 5 import java.util.List; 6 import java.util.Map; 7 8 import android.content.Context; 9 import android.database.Cursor; 10 import android.database.sqlite.SQLiteDatabase; 11 12 import com.examle.sqlitedbdemo.service.PersonService; 13 import com.example.sqlitedbdemo.db.DbOpenHelper; 14 15 public class PersonDao implements PersonService { 16 private DbOpenHelper helper = null; 17 18 public PersonDao(Context context) { 19 helper = new DbOpenHelper(context); 20 } 21 22 @Override 23 public boolean addPerson(Object[] params) { 24 boolean flag = false; 25 SQLiteDatabase database = null; 26 try { 27 // insert一条数据 28 String sql = "insert into person(name,address,sex) values(?,?,?)"; 29 database = helper.getWritableDatabase(); 30 // 执行SQL 31 database.execSQL(sql, params); 32 flag = true; 33 } catch (Exception e) { 34 e.printStackTrace(); 35 } finally { 36 if (database != null) { 37 // finally中关闭数据库 38 database.close(); 39 } 40 } 41 return flag; 42 } 43 44 @Override 45 public boolean deletePerson(Object[] params) { 46 boolean flag = false; 47 SQLiteDatabase database = null; 48 try { 49 // 删除一条数据 50 String sql = "delete from person where id=?"; 51 database = helper.getWritableDatabase(); 52 database.execSQL(sql, params); 53 flag = true; 54 } catch (Exception e) { 55 e.printStackTrace(); 56 } finally { 57 if (database != null) { 58 database.close(); 59 } 60 } 61 return flag; 62 } 63 64 @Override 65 public boolean updatePerson(Object[] params) { 66 boolean flag = false; 67 SQLiteDatabase database = null; 68 try { 69 // 更新一条数据 70 String sql = "update person set name=?,address=?,sex=? where id=?"; 71 database = helper.getWritableDatabase(); 72 // 执行SQL 73 database.execSQL(sql, params); 74 flag = true; 75 } catch (Exception e) { 76 e.printStackTrace(); 77 } finally { 78 if (database != null) { 79 database.close(); 80 } 81 } 82 return flag; 83 } 84 85 @Override 86 public Map<String, String> viewPerson(String[] selectionArgs) { 87 Map<String, String> map = new HashMap<String, String>(); 88 SQLiteDatabase database = null; 89 try { 90 // 查询单条记录 91 String sql = "select * from person where id=?"; 92 // 以只读的形式打开数据库 93 database = helper.getReadableDatabase(); 94 // 执行SQL语句,返回一个游标 95 Cursor cursor = database.rawQuery(sql, selectionArgs); 96 97 int colums = cursor.getColumnCount(); 98 while (cursor.moveToNext()) { 99 for (int i = 0; i < colums; i++) { 100 String cols_name = cursor.getColumnName(i); 101 String cols_value = cursor.getString(cursor 102 .getColumnIndex(cols_name)); 103 if (cols_value == null) { 104 cols_value = ""; 105 } 106 map.put(cols_name, cols_value); 107 } 108 } 109 } catch (Exception e) { 110 e.printStackTrace(); 111 } finally { 112 if (database != null) { 113 database.close(); 114 } 115 } 116 return map; 117 } 118 119 @Override 120 public List<Map<String, String>> listPersonMaps(String[] selectionArgs) { 121 List<Map<String, String>> list = new ArrayList<Map<String, String>>(); 122 String sql = "select * from person"; 123 SQLiteDatabase database = null; 124 try { 125 database = helper.getReadableDatabase(); 126 Cursor cursor = database.rawQuery(sql, selectionArgs); 127 int colums = cursor.getColumnCount(); 128 while (cursor.moveToNext()) { 129 Map<String, String> map = new HashMap<String, String>(); 130 for (int i = 0; i < colums; i++) { 131 String cols_name = cursor.getColumnName(i); 132 String cols_value = cursor.getString(cursor 133 .getColumnIndex(cols_name)); 134 if (cols_value == null) { 135 cols_value = ""; 136 } 137 map.put(cols_name, cols_value); 138 } 139 list.add(map); 140 } 141 } catch (Exception e) { 142 e.printStackTrace(); 143 } finally { 144 if (database != null) { 145 database.close(); 146 } 147 } 148 return list; 149 } 150 } 再写一个测试类测试这个数据操作类是否有效,Android下JUnit的配置参见另外一篇博客:Android--JUnit单元测试: 1 package com.example.sqlitedbdemo.db; 2 3 import java.util.List; 4 import java.util.Map; 5 6 import com.examle.sqlitedbdemo.dao.PersonDao; 7 import com.examle.sqlitedbdemo.service.PersonService; 8 9 import android.test.AndroidTestCase; 10 import android.util.Log; 11 12 public class TestDb extends AndroidTestCase { 13 private final String TAG = "main"; 14 15 public TestDb() { 16 // TODO Auto-generated constructor stub 17 } 18 19 public void createDB() { 20 DbOpenHelper helper = new DbOpenHelper(getContext()); 21 helper.getWritableDatabase(); 22 } 23 24 public void insertDb() { 25 PersonService service = new PersonDao(getContext()); 26 Object[] params1 = { "张龙", "beijing", "male" }; 27 boolean flag = service.addPerson(params1); 28 Object[] params2 = { "赵虎", "shanghai", "male" }; 29 flag = flag&&service.addPerson(params2); 30 Object[] params3 = { "王朝", "HK", "male" }; 31 flag = flag&&service.addPerson(params3); 32 Object[] params4 = { "马汉", "beijing", "female" }; 33 flag = flag&&service.addPerson(params4); 34 Log.i(TAG, "-----插入数据----->>" + flag); 35 } 36 37 public void deleteDb() { 38 PersonService service = new PersonDao(getContext()); 39 Object[] params = { 1 }; 40 boolean flag = service.deletePerson(params); 41 Log.i(TAG, "-----删除数据----->>" + flag); 42 } 43 44 public void updateDb() { 45 PersonService service=new PersonDao(getContext()); 46 Object[] params = { "张三", "上海", "男","2" }; 47 boolean flag=service.updatePerson(params); 48 Log.i(TAG, "---------->>" + flag); 49 } 50 51 public void getDb(){ 52 PersonService service=new PersonDao(getContext()); 53 Map<String, String> map = service.viewPerson(new String[]{"2"}); 54 Log.i(TAG, "---------->>" + map.toString()); 55 } 56 57 public void listDb() { 58 PersonService service = new PersonDao(getContext()); 59 List<Map<String, String>> list = service.listPersonMaps(null); 60 Log.i(TAG, "---------->>" + list.toString()); 61 } 62 } insertDB()后,如果是在模拟器上调试,可以使用FIle Explorer工具导出mydb.db文件,使用SQLite Expert Professional(这是一个SQLite的管理软件,博客最后提供下载地址),打开数据库: 执行deleteDb()删除第一条数据: 执行updateDb()更新第二条数据: 执行getDb(),查询第二条数据,执行listDb(),查询全部数据,查看日志输出: 而如果是从事Android开发,还有必要了解另外一种操作SQLite的方式,使用SQLiteDatabase所提供的方法实现CRUD操作。主要有以下几个方法: long insert(String table ,String nullColumnHack,ContentValues values):插入一条数据。 int delete(String table ,String whereCaluse,String[] whereArgs):根据条件,删除数据。 int updata(String table,ContentValues values,String whereCaluse,String[] whereArgs):根据条件,更新数据 Cursor query(...):根据条件,查询数据。提供多种重载方法,主要查询不同的条件。 下面以一个示例程序讲解一下使用SQLiteDatabase所提供的方法实现CRUD操作: 接口代码: 1 package com.examle.sqlitedbdemo.service; 2 3 import java.util.List; 4 import java.util.Map; 5 6 import android.content.ContentValues; 7 8 public interface PersonService2 { 9 10 public boolean addPerson(ContentValues values); 11 12 public boolean deletePerson(String whereClause, String[] whereArgs); 13 14 public boolean updatePerson(ContentValues values, String whereClause, 15 String[] whereArgs); 16 17 public Map<String, String> viewPerson(String selection, 18 String[] selectionArgs); 19 20 public List<Map<String, String>> listPersonMaps(String selection, 21 String[] selectionArgs); 22 } 实现代码: 1 package com.examle.sqlitedbdemo.dao; 2 3 import java.util.ArrayList; 4 import java.util.HashMap; 5 import java.util.List; 6 import java.util.Map; 7 8 import android.content.ContentValues; 9 import android.content.Context; 10 import android.database.Cursor; 11 import android.database.sqlite.SQLiteDatabase; 12 13 import com.examle.sqlitedbdemo.service.PersonService2; 14 import com.example.sqlitedbdemo.db.DbOpenHelper; 15 16 public class PersonDao2 implements PersonService2 { 17 private DbOpenHelper helper = null; 18 19 public PersonDao2(Context context) { 20 helper = new DbOpenHelper(context); 21 } 22 23 @Override 24 public boolean addPerson(ContentValues values) { 25 boolean flag = false; 26 SQLiteDatabase database = null; 27 long id = -1; 28 try { 29 database = helper.getWritableDatabase(); 30 // 执行insert,返回当前行ID 31 id = database.insert("person", null, values); 32 flag = (id != -1 ? true : false); 33 } catch (Exception e) { 34 e.printStackTrace(); 35 } finally { 36 if (database != null) { 37 database.close(); 38 } 39 } 40 return flag; 41 } 42 43 @Override 44 public boolean deletePerson(String whereClause, String[] whereArgs) { 45 boolean flag = false; 46 SQLiteDatabase database = null; 47 int count = 0; 48 try { 49 database = helper.getWritableDatabase(); 50 // 执行删除操作,返回影响行数 51 count = database.delete("person", whereClause, whereArgs); 52 flag = (count > 0 ? true : false); 53 } catch (Exception e) { 54 e.printStackTrace(); 55 } finally { 56 if (database != null) { 57 database.close(); 58 } 59 } 60 return flag; 61 } 62 63 @Override 64 public boolean updatePerson(ContentValues values, String whereClause, 65 String[] whereArgs) { 66 boolean flag = false; 67 SQLiteDatabase database = null; 68 int count = 0; 69 try { 70 database = helper.getWritableDatabase(); 71 // 执行更新操作,返回影响行数 72 count = database.update("person", values, whereClause, whereArgs); 73 flag = (count > 0 ? true : false); 74 } catch (Exception e) { 75 e.printStackTrace(); 76 } finally { 77 if (database != null) { 78 database.close(); 79 } 80 } 81 return flag; 82 } 83 84 @Override 85 public Map<String, String> viewPerson(String selection, 86 String[] selectionArgs) { 87 SQLiteDatabase database = null; 88 Cursor cursor = null; 89 Map<String, String> map = new HashMap<String, String>(); 90 try { 91 database = helper.getReadableDatabase(); 92 // 设置查询条件 93 cursor = database.query(true, "person", null, selection, 94 selectionArgs, null, null, null, null); 95 int cols_len = cursor.getColumnCount(); 96 while (cursor.moveToNext()) { 97 for (int i = 0; i < cols_len; i++) { 98 String cols_key = cursor.getColumnName(i); 99 String cols_value = cursor.getString(cursor 100 .getColumnIndex(cols_key)); 101 if (cols_value == null) { 102 cols_value = ""; 103 } 104 map.put(cols_key, cols_value); 105 } 106 } 107 } catch (Exception e) { 108 e.printStackTrace(); 109 } 110 return map; 111 } 112 113 @Override 114 public List<Map<String, String>> listPersonMaps(String selection, 115 String[] selectionArgs) { 116 List<Map<String, String>> list = new ArrayList<Map<String, String>>(); 117 SQLiteDatabase database = null; 118 Cursor cursor = null; 119 try { 120 database = helper.getReadableDatabase(); 121 cursor = database.query(false, "person", null, selection, 122 selectionArgs, null, null, null, null); 123 int cols_len = cursor.getColumnCount(); 124 while (cursor.moveToNext()) { 125 Map<String, String> map = new HashMap<String, String>(); 126 for (int i = 0; i < cols_len; i++) { 127 String cols_key = cursor.getColumnName(i); 128 String cols_value = cursor.getString(cursor 129 .getColumnIndex(cols_key)); 130 if (cols_value == null) { 131 cols_value = ""; 132 } 133 map.put(cols_key, cols_value); 134 } 135 list.add(map); 136 } 137 } catch (Exception e) { 138 e.printStackTrace(); 139 } 140 return list; 141 } 142 143 } 最后和上面一下,创建一个测试类来测试这个数据库操作: 1 package com.example.sqlitedbdemo.db; 2 3 import java.util.List; 4 import java.util.Map; 5 6 import com.examle.sqlitedbdemo.dao.PersonDao2; 7 import com.examle.sqlitedbdemo.service.PersonService2; 8 9 import android.content.ContentValues; 10 import android.test.AndroidTestCase; 11 import android.util.Log; 12 13 public class TestDb2 extends AndroidTestCase { 14 private final String TAG = "main"; 15 16 public TestDb2() { 17 // TODO Auto-generated constructor stub 18 } 19 20 public void addPerson() { 21 PersonService2 service2 = new PersonDao2(getContext()); 22 ContentValues values1 = new ContentValues(); 23 values1.put("name", "张龙"); 24 values1.put("address", "beijing"); 25 values1.put("sex", "male"); 26 boolean flag = service2.addPerson(values1); 27 ContentValues values2 = new ContentValues(); 28 values2.put("name", "赵虎"); 29 values2.put("address", "shanghai"); 30 values2.put("sex", "male"); 31 flag = flag&&service2.addPerson(values2); 32 ContentValues values3 = new ContentValues(); 33 values3.put("name", "王朝"); 34 values3.put("address", "HK"); 35 values3.put("sex", "male"); 36 flag = flag&&service2.addPerson(values3); 37 ContentValues values4 = new ContentValues(); 38 values4.put("name", "王朝"); 39 values4.put("address", "HK"); 40 values4.put("sex", "male"); 41 flag = flag&&service2.addPerson(values4); 42 Log.i(TAG, "----------->>" + flag); 43 } 44 45 public void deletePerson() { 46 PersonService2 service2 = new PersonDao2(getContext()); 47 boolean flag = service2.deletePerson(" id =?", new String[]{"1"}); 48 Log.i(TAG, "----------->>" + flag); 49 } 50 51 public void updatePerson(){ 52 PersonService2 service2 = new PersonDao2(getContext()); 53 ContentValues values = new ContentValues(); 54 values.put("name", "张三"); 55 values.put("address", "上海"); 56 values.put("sex", "男"); 57 boolean flag=service2.updatePerson(values, " id=? ", new String[]{"2"}); 58 Log.i(TAG, "----------->>" + flag); 59 } 60 61 public void viewPerson(){ 62 PersonService2 service2 = new PersonDao2(getContext()); 63 Map<String, String> map=service2.viewPerson(" id=? ", new String[]{"2"}); 64 Log.i(TAG, "----------->>" + map.toString()); 65 } 66 public void listPerson(){ 67 PersonService2 service2 = new PersonDao2(getContext()); 68 List<Map<String, String>> list=service2.listPersonMaps(null,null); 69 Log.i(TAG, "----------->>" + list.toString()); 70 } 71 } 实现的功能和上面一样,这里就不展示效果图了,但是因为是上面两种操作数据库的方式是在一个应用中完成的,并且数据一样,执行第二个测试类的时候,需要把之前创建的数据库删除,详情参见源码。 SQLite事务 SQLite的事务通过SQLiteDatabase中包含的两个方法对其进行控制:beginTransaction(),开始事务;endTransaction(),结束事务。除此之外,SQLiteDatabase还提供了一个inTransaction()方法用来判断当前上下文是否处于事务环境中。当程序执行endTransaction()方法时将会结束事务,到底是回滚事务还是提交事务取决于SQLiteDatabase是否调用了setTransactionSuccessful()方法来设置事务标志,如果程序事务执行中调用该方法设置了事务成功则提交事务,否则程序将回滚事务。 示例源码下载 总结 上面就基本讲解了SQLite在Android中的时候,虽然有两种操作方式,并且直接使用SQL语句操作数据库对于熟悉SQL语句的开发者开说,是非常贴心的,但是在Android中,还是有必要了解一下使用SQLiteDatabase提供的方法操作数据库的方式,因为Android有一个内容提供者(Content Provider),可以使用外部应用访问内部应用的数据,它传递数据的形式,大部分是与SQLiteDatabase内置方法的参数一致的,所以如果同一使用SQLiteDatabase提供的方法操作数据库,是很方便的,无需额外转换SQL语句。 本文转自承香墨影博客园博客,原文链接:http://www.cnblogs.com/plokmju/p/android_SQLite.html,如需转载请自行联系原作者
前言 Android提供了对设备传感器的支持,只要Android设备的硬件提供了这些传感器,Android应用可以通过传感器来获取设备的外界条件,包括手机的运行状态、当前摆放的方向等。Android系统还提供了驱动程序去管理这些传感器硬件,可以通过监听器的方式监听传感器硬件感知到的外部环境的变化。本篇博客主要讲解如何简单的使用Android下的传感器,最后将以一个简单的Demo演示。 开发传感器应用步骤 开发一个对于传感器支持的应用十分简单,开发人员只要在传感器管理器SensorManager中为所要监听的传感器指定一个监听器即可,当外部环境发生变化的时候,Android系统会通过传感器获取外部环境的数据,然后将数据传递给监听器的监听回调方法。具体步骤如下: 获取传感器服务。 从传感器服务中获取到指定类型的传感器。 使用传感器服务添加传感器的监听器。 在使用完之后,注销传感器的监听器。 获取传感器服务 Android中内置了很多系统级的服务,用于给开发人员使用,而传感器也是通过传感器服务,SensorManager来管理的。而在Android组件中获取系统服务,使用方法Context.getSystemService(String)即可,它的参数均以static final的方式定义在Context中,而获取SensorManager需要传入Context.SENSOR_SERVICE。 1 manager=(SensorManager) getSystemService(SENSOR_SERVICE); 从传感器服务中获取到指定类型的传感器 传感器服务管理设备上所有的传感器,所以需要指定待监听的传感器。获取待监听的传感器,需要使用SensorManager.getDefaultSensor()方法,它的完整签名如下: Sensor getDefaultSensor(int type) Android中的传感器需要Sensor支持,getDefaultSensor()方法通过指定的type参数获取到相对应的传感器。type参数被以static final的方式定义在Sensor内部,方便开发人员可以直接使用。下面介绍几个常用传感器的type: Sensor.TYPE_ORIENTATION:方向传感器。 Sensor.TYPE_ACCELEROMETER:重力传感器。 Sensor.TYPE_LIGHT:光线传感器。 Sensor.TYPE_MAGNETIC_FIELD:磁场传感器。 使用传感器服务添加传感器的监听器 获得SensorManager和Sensor对象之后,就可以为其Sensor注册监听器了。为传感器注册监听器,使用SensorManager.registerListener()方法即可,它存在多个重载方法,但是有些方法已经过时了,下面提供一个常用的方法的签名: boolean registerListener(SensorEventListener listener,Sensor sensor,int rateUs) 上面方法参数的意义:listener:传感器的监听器、sensor:待监听的传感器、rateUs:传感器的采样率。 从registerListener()方法可以看出,它需要传递一个SensorEventListener对象,它就是传感器的监听器,其中包含两个方法,需要开发人员去实现它: void onAccuracyChanged(Sensor sensor,int accuracy):当传感器精度发生变化时回调。 void onSensorChanged(SensorEvent event):当传感器感应的值发生变化时回调。 对于上面两个方法,传感器的精度一般是不会发生改变的,所以我们一般主要的代码量在onSensorChanged()中。 registerListener()方法还有一个rateUs的参数,它表示监听传感器改变的采样率,就是从传感器获取值的频率。它被定义以static final的形式定义在SensorManager中,方便我们直接使用,它定义了如下几个选项: SensorManager.SENSOR_DELAY_FASTEST:最快,延迟最小。 SensorManager.SENSOR_DELAY_GAME:适合游戏的频率。 SensorManager.SENSOR_DELAY_NORMAL:正常频率。 SensorManager.SENSOR_DELAY_UI:适合普通用户界面UI变化的频率。 Android为我们提供了这几个采样率的参数,方便我们使用。但对于选择那种采样率而言,并不是越快越好,要参照实际开发的应用的情况来说,采样率越大,将越耗费资源,包括电量、CPU等,所以要根据实际情况选择,毕竟再强大的应用,如果造成设备续航能力的降低,也是会被用户所不喜的。 在使用完之后,注销传感器的监听器 当使用完传感器之后,需要为其注销监听器,因为传感器的监听器并不会因为应用的结束而自行释放资源,需要开发人员在适当的时候主动注销。注销传感器监听器使用SensorManager.unregisterListener()方法即可,和监听器的注册方法一样,它也具有多个重载的方法,但是有一些已经被弃用了,下面介绍一个常用的完整签名: void unregisterListener(SensorEventListener listener) 指南针Demo 上面已经讲解了在应用中使用传感器的步骤以及具体内容,下面通过一个简单的Demo来演示一下如何使用传感器。在Demo中监听方向传感器,使其角度的变化改变来操作方向,模拟一个指南针的效果。 重写监听器的onSensorChanged()方法,其中event获取当当前监听事件的参数,可以使用values[0]获取到当前的方向传感器感应到的角度。参照官方文档,可以看出,它代表一个360°的角度,规则是: 0=North, 90=East, 180=South, 270=West。 下面直接贴代码了,注释比较全,就不再赘述了,会用到补间动画的内容,对补间动画不了解的朋友,可以看看另外一篇博客:Android--Tween补间动画。还有一点需要注意的是就是传感器的Demo需要在真机上测试,因为模拟器上不存在传感器硬件。 1 package com.example.sensormanagerdemo; 2 3 import android.hardware.Sensor; 4 import android.hardware.SensorEvent; 5 import android.hardware.SensorEventListener; 6 import android.hardware.SensorManager; 7 import android.os.Bundle; 8 import android.view.animation.Animation; 9 import android.view.animation.RotateAnimation; 10 import android.widget.ImageView; 11 import android.app.Activity; 12 13 public class MainActivity extends Activity { 14 private ImageView iv_compass; 15 private SensorManager manager; 16 private float startDegree = 0f; 17 18 @Override 19 protected void onCreate(Bundle savedInstanceState) { 20 super.onCreate(savedInstanceState); 21 setContentView(R.layout.activity_main); 22 23 iv_compass = (ImageView) findViewById(R.id.iv_compass); 24 // 获得传感器管理器 25 manager = (SensorManager) getSystemService(SENSOR_SERVICE); 26 27 } 28 29 @Override 30 protected void onResume() { 31 super.onResume(); 32 // 为方向传感器注册监听器 33 manager.registerListener(listener, 34 manager.getDefaultSensor(Sensor.TYPE_ORIENTATION), 35 SensorManager.SENSOR_DELAY_UI); 36 37 } 38 39 private SensorEventListener listener = new SensorEventListener() { 40 41 @Override 42 public void onSensorChanged(SensorEvent event) { 43 if (event.sensor.getType() == Sensor.TYPE_ORIENTATION) { 44 // 获取当前传感器获取到的角度 45 float degree = -event.values[0]; 46 // 通过补间动画旋转角度 从上次的角度旋转 47 RotateAnimation ra = new RotateAnimation(startDegree, degree, 48 Animation.RELATIVE_TO_SELF, 0.5f, 49 Animation.RELATIVE_TO_SELF, 0.5f); 50 ra.setDuration(200); 51 iv_compass.startAnimation(ra); 52 // 记录当前旋转后的角度 53 startDegree = degree; 54 } 55 } 56 57 @Override 58 public void onAccuracyChanged(Sensor sensor, int accuracy) { 59 60 } 61 }; 62 63 @Override 64 protected void onStop() { 65 // 为传感器注销监听器 66 manager.unregisterListener(listener); 67 super.onStop(); 68 } 69 } 效果展示: 源码下载 总结 本篇博客讲解了Android下如何使用传感器,只是简单的介绍了一下,对于传感器获取到的数值,都在event.values数组中,但是不同传感器获取到的值不同,具体情况具体分析,这里就不再深入讨论了。 本文转自承香墨影博客园博客,原文链接:http://www.cnblogs.com/plokmju/p/android_SensorManager.html,如需转载请自行联系原作者
相信所有做前端开发的同学都会经常使用 setTimeout(0) 来做很多事情,这个一度成为解决了很多前端疑难杂症的法宝。而且大家也知道 setTImeout(0) 的极限在16ms左右。也许很多人没想到这个是一个很严重的问题,但是在w3c性能小组的专家眼里这是一个非常纠结的设置。 那setTimeout和16ms会带来什么问题呢? 1. 我们都知道,我们在做页面动画的时候大多数都是在用setTImeout来控制每一帧的动画的, 而多个setTimeout的存在会导致很多次CPU中断调度的开销,为了减少这些开销,我们希望同一个统一的CPU中断调度管理调度单元来管理所有动画, 于是就出现了 requestAnimationFrame。 requestAnimationFrame 的出现不仅仅可以解决中断调度的问题,还可以更加优化得统一管理动画单元里dom元素的repaint方式。 2. 杯具的16ms,在早期。js的callback执行,是依赖CPU的中断来进行控制的,如果两个中断之间时间太短会导致,CPU性能消耗很高,同时影响能耗,于是微软和英特公司为了解决这个问题,就约定每个中断之间的间隔是15.6ms(64 fps)所以就是我们常见的约等于16ms的间隔。不过随着web的要求不断增加,大家对这个要求希望是放宽的态度,于是在高端浏览器,这个性能被提升了4倍左右,所以在chrome,ie10等浏览器,setTimeout的间隔缩短到了 4ms (250 fps)。 但是问题来了,这么高的消耗以传统的方式,并不能从根本上解决CPU的调度问题,而且能耗也会提升40% 于是W3C,提出需要取代 setTimeout(0) 的代替品 --- setImmediate 不过到底底层如何解决了这个CPU调度,还能保证脚本能够很快执行,这块舜子还在研究,了解的朋友也欢迎一起交流哈。 这是两个非常好的方法用来取代setTimeout的api,舜子也做了一些小小的实验来验证 requestAnimationFrame,setImmediate 和 setTimeout 之间的执行效率区别,在IE10下可以看到,setImmediate 接口的callback次数可以达到每秒 6000 次的的执行。而requestAnimationFrame主要目的是为了保证动画的圆滑播放,所以基本上是控制在60 fps的范围,而且根据文档介绍,只是一个时钟控制器在进行调度,而且会更加需要来进行按需渲染。 http://www.pjhome.net/web/html5/timing_test.htm 本文转自艾伦 Aaron博客园博客,原文链接:http://www.cnblogs.com/aaronjs/archive/2012/07/05/2577177.html,如需转载请自行联系原作者
Linux进程间通信机制: 1.同一主机进程间通信机制: Unix方式:有名管道FIFO、无名管道PIPE、信号Signal SystemV方式:信号量、消息队列、共享内存 2.网络通信:RPC(Remote Procedure Call)、Socket 管道 管道是进程间通信中最古老的方式,它包括无名管道和有名管道两种,前者可用于具有亲缘关系进程间的通信,即可用于父进程和子进程间的通信,后者额克服了管道没有名字的限制,因此,除具有前者所具有的功能外,它还允许无亲缘关系进程间的通信,即可用于运行于同一台机器上的任意两个进程间的通信。 无名管道由pipe()函数创建: #i nclude <unistd.h> int pipe(int filedis[2]); 参数filedis返回两个文件描述符:filedes[0]为读而打开,filedes[1]为写而打开。filedes[1]的输出是filedes[0]的输入。 无名管道占用两个文件描述符, 不能被非血缘关系的进程所共享, 一般应用在父子进程中. 特点: 半双工单向、父子进程间、一端写一段读、没有名字、缓冲区有限、数据无格式 无名管道常用于父子进程中, 可简单分为单向管道流模型和双向管道流模型. 其中, 单向管道流根据流向分为从父进程流向子进程的管道和从子进程流向父进程的管道. 下面设计一个实例, 数据从父进程流向子进程:父进程向管道写入一行字符, 子进程读取数据并打印到 屏幕上. [bill@billstone Unix_study]$ cat pipe1.c #include <unistd.h> #include <stdio.h> #include <sys/types.h> #include <assert.h> int main() { int fildes[2]; pid_t pid; int i,j; char buf[256]; assert(pipe(fildes) == 0); // 创建管道 assert((pid = fork()) >= 0); // 创建子进程 if(pid == 0){ // 子进程 close(fildes[1]); // 子进程关闭管道输出 memset(buf, 0, sizeof(buf)); j = read(fildes[0], buf, sizeof(buf)); fprintf(stderr, "[child] buf=[%s] len[%d]\n", buf, j); return; } close(fildes[0]); // 父进程关闭管道输入 write(fildes[1], "hello!", strlen("hello!")); write(fildes[1], "world!", strlen("world!")); return 0; } [bill@billstone Unix_study]$ make pipe1 cc pipe1.c -o pipe1 [bill@billstone Unix_study]$ ./pipe1 [child] buf=[hello!world!] len[12] // 子进程一次就可以读出两次父进程写入的数据 [bill@billstone Unix_study]$ 从上面可以看出, 在父进程中关闭 fildes[0], 向 fildes[1]写入数据; 在子进程中关闭 filedes[1], 从 fildes[0]中读取数据可实现从父进程流向子进程的管道. 在进程的通信中, 我们无法判断每次通信中报文的字节数, 即无法对数据流进行 自行拆分, 侧耳发生了上例中子进程一次性读取父进程两次通信的报文情况. 管道是进程之间的一种单向交流方法, 要实现进程间的双向交流, 就必须通过两个管道来完成. 双向管道流的创立过程如下: (1) 创建管道, 返回两个无名管道文件描述符 fildes1 和 fildes2: (2) 创建子进程, 子进程中继承管道 fildes1 和 fildes2. (3) 父进程关闭只读文件描述符 fildes1[0], 只写描述符 fildes2[1] (4) 子进程关闭只写文件描述符 fildes1[1], 只读描述符 fildes2[0] 创建的结果如下: 父进程 --写--> fildes1[1] --管道--> fildes1[0] --读--> 子进程 父进程 <--读-- fildes2[0] <--管道-- fildes2[1] <--写-- 子进程 这里实现一个父子进程间双向通信的实例: 父进程先向子进程发送两次数据, 再接收子进程传送刚来 的两次数据. 为了正确拆分时间留从父进程流向子进程的管道采用'固定长度'方法传送数据; 从子进程流向 父进程的管道采用'显式长度'方法传回数据. (1) 固定长度方式 char bufG[255]; void WriteG(int fd, char *str, int len){ memset(bufG, 0, sizeof(bufG)); sprintf(bufG, "%s", str); write(fd, bufG, len); } char *ReadG(int fd, int len){ memset(bufG, 0, sizeof(bufG)); read(fd, bufG, len); return(bufG); } 在此设计中, 父子程序需要约定好每次发送数据的长度; 且长度不能超过 255 个字符. (2) 显式长度方式 char bufC[255]; void WriteC(int fd, char str[]){ sprintf(bufC, "%04d%s", strlen(str), str); write(fd, bufC, strlen(bufC)); } char *ReadC(int fd){ int i, j; memset(bufC, 0, sizeof(bufC)); j = read(fd, bufC, 4); i = atoi(bufC); j = read(fd, bufC, i); return(bufC); } 父子进程约定在发送消息前先指明消息的长度. (3) 主程序 #include <unistd.h> #include <stdio.h> #include <assert.h> #include <sys/types.h> int main() { int fildes1[2], fildes2[2]; pid_t pid; char buf[255]; assert(pipe(fildes1) == 0); assert(pipe(fildes2) == 0); assert((pid = fork()) >= 0); if(pid == 0){ close(fildes1[1]); close(fildes2[0]); strcpy(buf, ReadG(fildes1[0], 10)); fprintf(stderr, "[child] buf = [%s]\n", buf); WriteC(fildes2[1], buf); strcpy(buf, ReadG(fildes1[0], 10)); fprintf(stderr, "[child] buf = [%s]\n", buf); WriteC(fildes2[1], buf); return(0); } close(fildes1[0]); close(fildes2[1]); WriteG(fildes1[1], "hello!", 10); WriteG(fildes1[1], "world!", 10); fprintf(stderr, "[father] buf = [%s] \n", ReadC(fildes2[0])); fprintf(stderr, "[father] buf = [%s] \n", ReadC(fildes2[0])); return 0; } 执行结果如下: [bill@billstone Unix_study]$ make pipe2 cc pipe2.c -o pipe2 [bill@billstone Unix_study]$ ./pipe2 [child] buf = [hello!] [child] buf = [world!] [father] buf = [hello!] [father] buf = [world!] [bill@billstone Unix_study]$ dup dup2复制文件描述符 在Linux系统下,有名管道可由两种方式创建:命令行方式mknod系统调用和函数mkfifo。 下面的两种途径都在当前目录下生成了一个名为myfifo的有名管道: 方式一:mkfifo("myfifo","rw"); 方式二:mknod myfifo p 生成了有名管道后,就可以使用一般的文件I/O函数如open、close、read、write等来对它进行操作。 管道是 UNIX 中最古老的进程间通信工具, 它提供了进程之间的一种单向通信的方法. popen 模型 从前面的程序可以看出, 创建连接标准 I/O 的管道需要多个步骤, 这需要使用大量的代码, UNIX 为了 简化这个操作, 它提供了一组函数实现之. 原型如下: #include <stdio.h> FILE *popen(const char *command, char *type); int pclose(FILE *stream); 函数 popen 调用成功时返回一个标准的 I/O 的 FILE 文件流, 其读写属性由参数 type 决定. 这里看一个模拟 shell 命令'ps -ef | grep init'的实例. [bill@billstone Unix_study]$ cat pipe3.c #include <stdio.h> #include <assert.h> int main() { FILE *out, *in; char buf[255]; assert((out = popen("grep init", "w")) != NULL); // 创建写管道流 assert((in = popen("ps -ef", "r")) != NULL); // 创建读管道流 while(fgets(buf, sizeof(buf), in)) // 读取 ps -ef 的结果 fputs(buf, out); // 转发到 grep init pclose(out); pclose(in); return 0; } [bill@billstone Unix_study]$ make pipe3 cc pipe3.c -o pipe3 [bill@billstone Unix_study]$ ./pipe3 root 1 0 0 Apr15 ? 00:00:04 init bill 1392 1353 0 Apr15 ? 00:00:00 /usr/bin/ssh-agent /etc/X11/xinit/Xclients bill 14204 14203 0 21:33 pts/0 00:00:00 grep init [bill@billstone Unix_study]$ ps -ef | grep init root 1 0 0 Apr15 ? 00:00:04 init bill 1392 1353 0 Apr15 ? 00:00:00 /usr/bin/ssh-agent /etc/X11/xinit/Xclients bill 14207 1441 0 21:35 pts/0 00:00:00 grep init [bill@billstone Unix_study]$ 读者可以从上面自行比较同 Shell 命令'ps -ef | grep init'的执行结果. 有名管道 FIFO FIFO 可以在整个系统中使用. 在 Shell 中可以使用 mknod 或者 mkfifo 命令创建管道; 而在 C 程序中, 可以使用 mkfifo 函数创建有名管道. 要使用有名管道, 需要下面几个步骤: (1) 创建管道文件 (2) 在某个进程中以只写方式打开管道文件, 并写管道 (3) 在某个进程中以只读方式打开管道文件, 并读管道 (4) 关闭管道文件. 低级文件编程库和标准文件编程库都可以操作管道. 管道在执行读写操作之前, 两端必须同时打开, 否 则执行打开管道某端操作的进程将一直阻塞到某个进程以相反方向打开管道为止. C代码 /*fifoserver.c:向FIFO中写入信息*/ #include <sys/types.h> #include <sys/stat.h> #include <errno.h> #include <fcntl.h> #define FIFO_SERVER "FIFO4" main(int argc,char** argv) { int fd=0; char w_buf[4096]; int real_wnum; memset(w_buf,0,4096); if((mkfifo(FIFO_SERVER,O_CREAT|O_EXCL|0666)<0)&&(errno!=EEXIST)) printf("cannot create fifoserver\n"); /*此处存在着打开依赖,即若没有读端打开FIFO的话,写端就阻塞在写端*/ fd=open(FIFO_SERVER,O_WRONLY); if(fd==-1) printf("open error; no reading process\n"); printf("%d\n",fd); real_wnum=write(fd,w_buf,2048); if(real_wnum==-1) printf("write to fifo error; try later\n"); else printf("real write num is %d\n",real_wnum); /*往FIFO写入的数据都是原子的,如果没有足够的空间,则会等待,而不是一点一点的写入。*/ real_wnum=write(fd,w_buf,4096); if(real_wnum==-1) printf("write to fifo error; try later\n"); else printf("real write num is %d\n",real_wnum); } C代码 /*fifoclient.c:从FIFO中读出数据*/ #include <sys/types.h> #include <sys/stat.h> #include <errno.h> #include <fcntl.h> #define FIFO_SERVER "FIFO4" main(int argc,char** argv) { char r_buf[4096]; int fd; int r_size; int ret_size; r_size=atoi(argv[1]); memset(r_buf,0,sizeof(r_buf)); fd=open(FIFO_SERVER,O_RDONLY); if(fd==-1) { printf("open %s for read error\n"); exit(1); } printf("%d\n",fd); while(1) { ret_size=read(fd,r_buf,r_size); if(ret_size==-1) printf("no data avlaible\n"); else printf("real read bytes %d\n",ret_size); sleep(1); } unlink(FIFO_SERVER); } 下面是一个简单的实例. 首先是写进程: 创建 FIFO 文件, 再打开写端口, 然后读取标准输入并将输入信息发送到管道中, 当键 盘输入'exit'或'quit'时程序退出. [bill@billstone Unix_study]$ cat fifo1.c #include <stdio.h> #include <assert.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/errno.h> extern int errno; int main() { FILE *fp; char buf[255]; assert((mkfifo("myfifo", S_IFIFO|0666) > 0) || (errno == EEXIST)); while(1){ assert((fp = fopen("myfifo", "w")) != NULL); printf("please input: "); fgets(buf, sizeof(buf), stdin); fputs(buf, fp); fclose(fp); if(strncmp(buf, "quit", 4) == 0 || strncmp(buf, "exit", 4) == 0) break; } return 0; } [bill@billstone Unix_study]$ make fifo1 cc fifo1.c -o fifo1 [bill@billstone Unix_study]$ 然后是读进程: 打开管道的读端口, 从管道中读取信息(以行为单位), 并将此信息打印到屏幕上. 当读 取到'exit'或者'quit'时程序退出. [bill@billstone Unix_study]$ cat fifo2.c #include <stdio.h> #include <assert.h> #include <sys/types.h> #include <sys/stat.h> int main() { FILE *fp; char buf[255]; while(1){ assert((fp = fopen("myfifo", "r")) != NULL); fgets(buf, strlen(buf), fp); printf("gets: [%s]", buf); fclose(fp); if(strncmp(buf, "quit", 4) == 0 || strncmp(buf, "exit", 4) == 0) break; } return 0; } [bill@billstone Unix_study]$ make fifo2 cc fifo2.c -o fifo2 [bill@billstone Unix_study]$ 在一个终端上执行 fifo1, 而在另一个终端上执行 fifo2. 我们先输入'hello', 'world', 然后再输入'exit'退出: [bill@billstone Unix_study]$ ./fifo1 please input: hello please input: world please input: exit [bill@billstone Unix_study]$ 我们可以看到读出结果如下: [bill@billstone Unix_study]$ ./fifo2 gets: [hello ]gets: [world]gets: [exit][bill@billstone Unix_study]$ 看到上面的输出结果, 您可能认为是我写错了. 其实不是的, 读出结果正是如此, 其实按照我们的本意, 正确的输出结果应该是这样的: [bill@billstone Unix_study]$ ./fifo2 gets: [hello ]gets: [world ]gets: [exit 本文转自feisky博客园博客,原文链接:http://www.cnblogs.com/feisky/archive/2010/03/24/1693484.html,如需转载请自行联系原作者
在前面我们讲到了DNN,以及DNN的特例CNN的模型和前向反向传播算法,这些算法都是前向反馈的,模型的输出和模型本身没有关联关系。今天我们就讨论另一类输出和模型间有反馈的神经网络:循环神经网络(Recurrent Neural Networks ,以下简称RNN),它广泛的用于自然语言处理中的语音识别,手写书别以及机器翻译等领域。 1. RNN概述 在前面讲到的DNN和CNN中,训练样本的输入和输出是比较的确定的。但是有一类问题DNN和CNN不好解决,就是训练样本输入是连续的序列,且序列的长短不一,比如基于时间的序列:一段段连续的语音,一段段连续的手写文字。这些序列比较长,且长度不一,比较难直接的拆分成一个个独立的样本来通过DNN/CNN进行训练。 而对于这类问题,RNN则比较的擅长。那么RNN是怎么做到的呢?RNN假设我们的样本是基于序列的。比如是从序列索引1到序列索引ττ的。对于这其中的任意序列索引号tt,它对应的输入是对应的样本序列中的x(t)x(t)。而模型在序列索引号tt位置的隐藏状态h(t)h(t),则由x(t)x(t)和在t−1t−1位置的隐藏状态h(t−1)h(t−1)共同决定。在任意序列索引号tt,我们也有对应的模型预测输出o(t)o(t)。通过预测输出o(t)o(t)和训练序列真实输出y(t)y(t),以及损失函数L(t)L(t),我们就可以用DNN类似的方法来训练模型,接着用来预测测试序列中的一些位置的输出。 下面我们来看看RNN的模型。 2. RNN模型 RNN模型有比较多的变种,这里介绍最主流的RNN模型结构如下: 上图中左边是RNN模型没有按时间展开的图,如果按时间序列展开,则是上图中的右边部分。我们重点观察右边部分的图。 这幅图描述了在序列索引号tt附近RNN的模型。其中: 1)x(t)x(t)代表在序列索引号tt时训练样本的输入。同样的,x(t−1)x(t−1)和x(t+1)x(t+1)代表在序列索引号t−1t−1和t+1t+1时训练样本的输入。 2)h(t)h(t)代表在序列索引号tt时模型的隐藏状态。h(t)h(t)由x(t)x(t)和h(t−1)h(t−1)共同决定。 3)o(t)o(t)代表在序列索引号tt时模型的输出。o(t)o(t)只由模型当前的隐藏状态h(t)h(t)决定。 4)L(t)L(t)代表在序列索引号tt时模型的损失函数。 5)y(t)y(t)代表在序列索引号tt时训练样本序列的真实输出。 6)U,W,VU,W,V这三个矩阵是我们的模型的线性关系参数,它在整个RNN网络中是共享的,这点和DNN很不相同。 也正因为是共享了,它体现了RNN的模型的“循环反馈”的思想。 3. RNN前向传播算法 有了上面的模型,RNN的前向传播算法就很容易得到了。 对于任意一个序列索引号tt,我们隐藏状态h(t)h(t)由x(t)x(t)和h(t−1)h(t−1)得到: h(t)=σ(z(t))=σ(Ux(t)+Wh(t−1)+b)h(t)=σ(z(t))=σ(Ux(t)+Wh(t−1)+b) 其中σσ为RNN的激活函数,一般为tanhtanh, bb为线性关系的偏倚。 序列索引号tt时模型的输出o(t)o(t)的表达式比较简单: o(t)=Vh(t)+co(t)=Vh(t)+c 在最终在序列索引号tt时我们的预测输出为: y^(t)=σ(o(t))y^(t)=σ(o(t)) 通常由于RNN是识别类的分类模型,所以上面这个激活函数一般是softmax。 通过损失函数L(t)L(t),比如对数似然损失函数,我们可以量化模型在当前位置的损失,即y^(t)y^(t)和y(t)y(t)的差距。 4. RNN反向传播算法推导 有了RNN前向传播算法的基础,就容易推导出RNN反向传播算法的流程了。RNN反向传播算法的思路和DNN是一样的,即通过梯度下降法一轮轮的迭代,得到合适的RNN模型参数U,W,V,b,cU,W,V,b,c。由于我们是基于时间反向传播,所以RNN的反向传播有时也叫做BPTT(back-propagation through time)。当然这里的BPTT和DNN也有很大的不同点,即这里所有的U,W,V,b,cU,W,V,b,c在序列的各个位置是共享的,反向传播时我们更新的是相同的参数。 为了简化描述,这里的损失函数我们为对数损失函数,输出的激活函数为softmax函数,隐藏层的激活函数为tanh函数。 对于RNN,由于我们在序列的每个位置都有损失函数,因此最终的损失LL为: L=∑t=1τL(t)L=∑t=1τL(t) 其中V,c,V,c,的梯度计算是比较简单的: ∂L∂c=∑t=1τ∂L(t)∂c=∑t=1τ∂L(t)∂o(t)∂o(t)∂c=∑t=1τy^(t)−y(t)∂L∂c=∑t=1τ∂L(t)∂c=∑t=1τ∂L(t)∂o(t)∂o(t)∂c=∑t=1τy^(t)−y(t) ∂L∂V=∑t=1τ∂L(t)∂V=∑t=1τ∂L(t)∂o(t)∂o(t)∂V=∑t=1τ(y^(t)−y(t))(h(t))T∂L∂V=∑t=1τ∂L(t)∂V=∑t=1τ∂L(t)∂o(t)∂o(t)∂V=∑t=1τ(y^(t)−y(t))(h(t))T 但是W,U,bW,U,b的梯度计算就比较的复杂了。从RNN的模型可以看出,在反向传播时,在在某一序列位置t的梯度损失由当前位置的输出对应的梯度损失和序列索引位置t+1t+1时的梯度损失两部分共同决定。对于WW在某一序列位置t的梯度损失需要反向传播一步步的计算。我们定义序列索引tt位置的隐藏状态的梯度为: δ(t)=∂L∂h(t)δ(t)=∂L∂h(t) 这样我们可以像DNN一样从δ(t+1)δ(t+1)递推δ(t)δ(t) 。 δ(t)=∂L∂o(t)∂o(t)∂h(t)+∂L∂h(t+1)∂h(t+1)∂h(t)=VT(y^(t)−y(t))+WTδ(t+1)diag(1−(h(t+1))2)δ(t)=∂L∂o(t)∂o(t)∂h(t)+∂L∂h(t+1)∂h(t+1)∂h(t)=VT(y^(t)−y(t))+WTδ(t+1)diag(1−(h(t+1))2) 对于δ(τ)δ(τ),由于它的后面没有其他的序列索引了,因此有: δ(τ)=∂L∂o(τ)∂o(τ)∂h(τ)=VT(y^(τ)−y(τ))δ(τ)=∂L∂o(τ)∂o(τ)∂h(τ)=VT(y^(τ)−y(τ)) 有了δ(t)δ(t),计算W,U,bW,U,b就容易了,这里给出W,U,bW,U,b的梯度计算表达式: ∂L∂W=∑t=1τ∂L∂h(t)∂h(t)∂W=∑t=1τdiag(1−(h(t))2)δ(t)(h(t−1))T∂L∂W=∑t=1τ∂L∂h(t)∂h(t)∂W=∑t=1τdiag(1−(h(t))2)δ(t)(h(t−1))T ∂L∂b=∑t=1τ∂L∂h(t)∂h(t)∂b=∑t=1τdiag(1−(h(t))2)δ(t)∂L∂b=∑t=1τ∂L∂h(t)∂h(t)∂b=∑t=1τdiag(1−(h(t))2)δ(t) ∂L∂U=∑t=1τ∂L∂h(t)∂h(t)∂U=∑t=1τdiag(1−(h(t))2)δ(t)(x(t))T∂L∂U=∑t=1τ∂L∂h(t)∂h(t)∂U=∑t=1τdiag(1−(h(t))2)δ(t)(x(t))T 除了梯度表达式不同,RNN的反向传播算法和DNN区别不大,因此这里就不再重复总结了。 5. RNN小结 上面总结了通用的RNN模型和前向反向传播算法。当然,有些RNN模型会有些不同,自然前向反向传播的公式会有些不一样,但是原理基本类似。 RNN虽然理论上可以很漂亮的解决序列数据的训练,但是它也像DNN一样有梯度消失时的问题,当序列很长的时候问题尤其严重。因此,上面的RNN模型一般不能直接用于应用领域。在语音识别,手写书别以及机器翻译等NLP领域实际应用比较广泛的是基于RNN模型的一个特例LSTM,下一篇我们就来讨论LSTM模型。 本文转自刘建平Pinard博客园博客,原文链接:http://www.cnblogs.com/pinard/p/6509630.html,如需转载请自行联系原作者
因为浏览器兼容性的原因,表单元素往往是整个网页设计过程中最难以美化的,尤其是单选按钮、下拉选择框和复选框。不过我们可以通过jquery来美化它样。下面为大家推荐11个简单易用的jQurey插件,其中既有干净小巧的按钮,也有独特的iphone风格的棒形按钮切换,相信它们能帮助你打造自己的表单版式。 Fancy checkboxes and radio buttons with CSS 带圆角矩形的Checkbox较传统风格,更显得柔和;同时在鼠标选择处增加了阴影和高亮,增强了控件的质感与美观。 jqTransform 清爽、简约的外观与系统默认的控件外观非常类似,但是带有一定灰度的checked状态配合圆角与阴影,让你的排版更自由、更统一。 FancyForm Radio Button以醒目的绿色小圆点标识checked状态,而Checkbox则以绿色和红色来标识checked状态,非常特别且美观。 jQuery Checkbox 灵感或许来源自家用电器的开关,仿佛鼠标可以推动控制块一样。非常独特的设计,相信会让你的网页与众不同。 jNice 清新简单的外观,细微处结合了时下流行的一些元素:圆角、醒目的checked状态标识。 NiceForms 颜色清爽,样式简单,非常容易与设计好的网页融为一体。 iPhone-style Checkboxes iPhone特有的棒形控件风格,清晰的标识出控件的checked状态。或许你也能让自己的设计很“苹果”。 ezMark jQuery Checkbox & Radio Button plugin 这款控件的特点在于Checkbox被选中的状态表示,不闭合的矩形框以及整体红黑的渐变色打造出让人耳目一新的风格。 jQuery Fancy Custom Radio-button and Checkbox 这款控件拥有时下流行的玻璃质感,即美观又低调,试试看,或许你的网页马上令人眼前一亮。 iPhone Style Radio and Checkbox 同样是iPhone风格的控件,不同之处在于色块的切换来标识checked状态。 Create iPhone-style buttons with the iButton jQuery Plug-in 依然是很红的iPhone风格的按钮,渐变的背景色营造出立体的效果。 本文转自寒意博客园博客,原文链接:http://www.cnblogs.com/hnyei/archive/2011/09/26/2191209.html,如需转载请自行联系原作者
上次随便画了一篇关于GIX4项目目前的类的结构图(见:GIX4 目前大致的类结构),目的是为了更好的认识系统,并对其进行改进。发现AutoUI部分的耦合性比较大。所以提取了一个IUIFactory的接口,并用同事推荐的EA重新画了一下目前系统的图,如下: 图1 框架包图 图2 实体元数据 结构图 图3 逻辑视图(ObjectViews) 结构图 图4 命令结构图 图5 列表编辑器(ListEditors) 图6 属性编辑器(PropertyEditors) 图7 列适配器(Columns) 图8.1 视图控制器(1) 图8.2 视图控制器(2) 图8.3 视图(窗体) 图9 AutoUI结构图 目前就画到这里,已经可以从图中比较清晰地看出目前框架的主要结构和功能。 系统中有部分耦合是需要解除的,修正后补上。 本文转自BloodyAngel博客园博客,原文链接:http://www.cnblogs.com/zgynhqf/archive/2010/03/22/1691500.html,如需转载请自行联系原作者
在当今网页设计/开发实践中,使用CSS来为语义化的(X)HTML标记添加样式风格是重要的关键。在设计师们的梦想中都存在着这样的一个完美世界:所有的浏览器都能够理解和适用多有CSS规则,并且呈现相同的视觉效果(没有兼容性问题)。但是,我们并没有生活在这个完美的世界,现实中发生的失窃却总是恰恰相反,很多CSS样式在不同的浏览器中有着不同的解释和呈现。 当今流行的浏览器(如:Firefox、Opera、Internet Explorer、Chrome、Safari等等)中,有一些都是以自己的方式去理解CSS规范,这就会导致有的浏览器对CSS的解释与设计师的CSS定义初衷相冲突,使得网页的样子在某些浏览器下能正确按照设计师的想法显示,但有些浏览器却并没有按照设计师想要的样子显示出来,这就导致浏览器的兼容性问题。更糟的是,有的浏览器完全无视CSS的一些声明和属性。 正因为上述冲突和问题依然存在于这个”不完美的世界”,所以一些设计师想到了一种避免浏览器兼容性问题的方法,那就是CSS Reset,什么是CSS Reset?我们可以把它叫做CSS重设,也有人叫做CSS复位、默认CSS、CSS重置等。CSS重设就是由于各种浏览器解释CSS样式的初始值有所不同,导致设计师在没有定义某个CSS属性时,不同的浏览器会按照自己的默认值来为没有定义的样式赋值,所以我们要先定义好一些CSS样式,来让所有浏览器都按照同样的规则解释CSS,这样就能避免发生这种问题。 一.最简化的CSS Reset(重设) : * { padding: 0; margin: 0; } 这是最普遍最简单的CSS重设,将所有元素的padding和margin值都设为0,可以避免一些浏览器在理解这两个属性默认值上的”分歧”。 * { padding: 0; margin: 0; border: 0; } 这是在上一个重设的基础上添加了对border属性的重设,初始值为0的确能避免一些问题。 * { outline: 0; padding: 0; margin: 0; border: 0; } 在前两个的基础上添加了outline属性的重设,防止一些冲突。 二.浓缩实用型CSS Reset(重设): * { vertical-align: baseline; font-weight: inherit; font-family: inherit; font-style: inherit; font-size: 100%; outline: 0; padding: 0; margin: 0; border: 0; } 该CSS重设方法出自Perishable Press,这是他常用的方法。 三.Poor Man 的CSS Reset: html, body { padding: 0; margin: 0; } html { font-size:1em; } body { font-size:100%; } a img, :link img, :visited img { border:0px; } 这个重设方法将html和body下元素的padding和margin都设为0,并分别为html标签和body标签下的所有元素设置了初始的字体大小,最重要的是把有链接的图片的默认边框去掉了。 四.Siolon’s Global Reset * { vertical-align: baseline; font-family: inherit; fo nt-style: inherit; font-size: 100%; border: none; padding: 0; margin: 0; } body { padding: 5px; } h1, h2, h3, h4, h5, h6, p, pre, blockquote, form, ul, ol, dl { margin: 20px 0; } li, dd, blockquote { margin-left: 40px; } table { border-collapse: collapse; border-spacing: 0; } 五.Shaun Inman’s Global Reset body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, form, fieldset, input, p, blockquote, table, th, td, embed, object { padding: 0; margin: 0; } table { border-collapse: collapse; border-spacing: 0; } fieldset, img, abbr { border: 0; } address, caption, cite, code, dfn, em, h1, h2, h3, h4, h5, h6, strong, th, var { font-weight: normal; font-style: normal; } ul { list-style: none; } caption, th { text-align: left; } h1, h2, h3, h4, h5, h6 { font-size: 1.0em; } q:before, q:after { content: ”; } a, ins { text-decoration: none; } 六.Yahoo(YUI) CSS Reset: body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre, form,fieldset,input,textarea,p,blockquote,th,td { padding: 0; margin: 0; } table { border-collapse: collapse; border-spacing: 0; } fieldset,img { border: 0; } address,caption,cite,code,dfn,em,strong,th,var { font-weight: normal; font-style: normal; } ol,ul { list-style: none; } caption,th { text-align: left; } h1,h2,h3,h4,h5,h6 { font-weight: normal; font-size: 100%; } q:before,q:after { content:”; } abbr,acronym { border: 0; } 七.Eric Meyer’s CSS Reset html, body, div, span, applet, object, iframe, table, caption, tbody, tfoot, thead, tr, th, td, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, dl, dt, dd, ol, ul, li, fieldset, form, label, legend { vertical-align: baseline; font-family: inherit; font-weight: inherit; font-style: inherit; font-size: 100%; outline: 0; padding: 0; margin: 0; border: 0; } :focus { outline: 0; } body { background: white; line-height: 1; color: black; } ol, ul { list-style: none; } table { border-collapse: separate; border-spacing: 0; } caption, th, td { font-weight: normal; text-align: left; } blockquote:before, blockquote:after, q:before, q:after { content: “”; } blockquote, q { quotes: “” “”; } 八.Condensed Meyer Reset: body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, form, fieldset, input, textarea, p, blockquote, th, td { padding: 0; margin: 0; } fieldset, img { border: 0; } table { border-collapse: collapse; border-spacing: 0; } ol, ul { list-style: none; } address, caption, cite, code, dfn, em, strong, th, var { font-weight: normal; font-style: normal; } caption, th { text-align: left; } h1, h2, h3, h4, h5, h6 { font-weight: normal; font-size: 100%; } q:before, q:after { content: ”; } abbr, acronym { border: 0; } 九.Ateneu Popular CSS Reset html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td { margin: 0; padding: 0; border: 0; outline: 0; font-weight: inherit; font-style: inherit; font-size: 100%; font-family: inherit; vertical-align: baseline; } :focus { outline: 0; } a, a:link, a:visited, a:hover, a:active{ text-decoration:none } table { border-collapse: separate; border-spacing: 0; } th, td { text-align: left; font-weight: normal; } img, iframe { border: none; text-decoration:none; } ol, ul { list-style: none; } input, textarea, select, button { font-size: 100%; font-family: inherit; } select { margin: inherit; } hr { margin: 0; padding: 0; border: 0; color: #000; background-color: #000; height: 1px } 十.Chris Poteet’s Reset CSS * { vertical-align: baseline; font-family: inherit; font-style: inherit; font-size: 100%; border: none; padding: 0; margin: 0; } body { padding: 5px; } h1, h2, h3, h4, h5, h6, p, pre, blockquote, form, ul, ol, dl { margin: 20px 0; } li, dd, blockquote { margin-left: 40px; } table { border-collapse: collapse; border-spacing: 0; } 十一.Tantek Celik Reset CSS :link,:visited { text-decoration:none } ul,ol { list-style:none } h1,h2,h3,h4,h5,h6,pre,code { font-size:1em; } ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,body,html,p,blockquote,fieldset,input { margin:0; padding:0 } a img,:link img,:visited img { border:none } address { font-style:normal } 十二.Christian Montoya Reset CSS html, body, form, fieldset { margin: 0; padding: 0; font: 100%/120% Verdana, Arial, Helvetica, sans-serif; } h1, h2, h3, h4, h5, h6, p, pre, blockquote, ul, ol, dl, address { margin: 1em 0; padding: 0; } li, dd, blockquote { margin-left: 1em; } form label { cursor: pointer; } fieldset { border: none; } input, select, textarea { font-size: 100%; font-family: inherit; } 十三.Rudeworks Reset CSS * { margin: 0; padding: 0; border: none; } html { font: 62.5% “Lucida Grande”, Lucida, Verdana, sans-serif; text-shadow: #000 0px 0px 0px; } ul { list-style: none; list-style-type: none; } h1, h2, h3, h4, h5, h6, p, pre, blockquote, ul, ol, dl, address { font-weight: normal; margin: 0 0 1em 0; } cite, em, dfn { font-style: italic; } sup { position: relative; bottom: 0.3em; vertical-align: baseline; } sub { position: relative; bottom: -0.2em; vertical-align: baseline; } li, dd, blockquote { margin-left: 1em; } code, kbd, samp, pre, tt, var, input[type='text'], textarea { font-size: 100%; font-family: monaco, “Lucida Console”, courier, mono-space; } del { text-decoration: line-through; } ins, dfn { border-bottom: 1px solid #ccc; } small, sup, sub { font-size: 85%; } abbr, acronym { text-transform: uppercase; font-size: 85%; letter-spacing: .1em; border-bottom-style: dotted; border-bottom-width: 1px; } a abbr, a acronym { border: none; } sup { vertical-align: super; } sub { vertical-align: sub; } h1 { font-size: 2em; } h2 { font-size: 1.8em; } h3 { font-size: 1.6em; } h4 { font-size: 1.4em; } h5 { font-size: 1.2em; } h6 { font-size: 1em; } a, a:link, a:visited, a:hover, a:active { outline: 0; text-decoration: none; } a img { border: none; text-decoration: none; } img { border: none; text-decoration: none; } label, button { cursor: pointer; } input:focus, select:focus, textarea:focus { background-color: #FFF; } fieldset { border: none; } .clear { clear: both; } .float-left { float: left; } .float-right { float: right; } body { text-align: center; } #wrapper { margin: 0 auto; text-align: left; } 十四. Anieto2K Reset CSS html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, center, u, b, i { margin: 0; padding: 0; border: 0; outline: 0; font-weight: normal; font-style: normal; font-size: 100%; font-family: inherit; vertical-align: baseline } body { line-height: 1 } :focus { outline: 0 } ol, ul { list-style: none } table { border-collapse: collapse; border-spacing: 0 } blockquote:before, blockquote:after, q:before, q:after { content: “” } blockquote, q { quotes: “” “” } input, textarea { margin: 0; padding: 0 } hr { margin: 0; padding: 0; border: 0; color: #000; background-color: #000; height: 1px } 十五.CSSLab CSS Reset html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td { margin: 0; padding: 0; border: 0; outline: 0; font-weight: inherit; font-style: inherit; font-size: 100%; font-family: inherit; vertical-align: baseline; } :focus { outline: 0; } table { border-collapse: separate; border-spacing: 0; } caption, th, td { text-align: left; font-weight: normal; } a img, iframe { border: none; } ol, ul { list-style: none; } input, textarea, select, button { font-size: 100%; font-family: inherit; } select { margin: inherit; } /* Fixes incorrect placement of numbers in ol’s in IE6/7 */ ol { margin-left:2em; } /* == clearfix == */ .clearfix:after { content: “.”; display: block; height: 0; clear: both; visibility: hidden; } .clearfix {display: inline-block;} * html .clearfix {height: 1%;} .clearfix {display: block;} 好了,CSS重设目前先总结到这里,这15套重设方法其实都是有共同点的,也许有的实现方法不同,但大部分都是同一个目的,就是为了让更多的浏览器能显示同样的效果。有了这些CSS重设作为资料和参考,也许会对你的工作有所帮助甚至提高效率,但是,毕竟这些重设都是别人写的,你完全也可以为自己量身定制一套CSS重设。 本文转自寒意博客园博客,原文链接:http://www.cnblogs.com/hnyei/archive/2011/10/04/2198779.html,如需转载请自行联系原作者
为 OEA 框架 做 WPF 树型表格控件时,浪费了许多时间在一些知识点上,所以写了这篇博客总结一下,和各位博友分享。 ICollectionView 接口的简易使用方法 它的 SortDescriptions、GroupDescription 等属性会影响视图的结构,一般情况下会自动刷新。 也可以直接使用 Refresh 方法来命令视图的刷新(内部实现一般为重建内部视图模型集合)。 如果是批量操作,也可以使用 RefreshDefer 方法来抑制多次刷新,待多个更新操作后再统一刷新。 CollectionView 相关知识点 实现它的类是 CollectionView,而这个类的最主要的两个子类是 ListCollectionView、BindingListCollectionView。这两个类的主要区别如下: ListCollectionView 一般用于数据列表是 IList 而不是 IBindingList 的集合的情况。由于视图集合无法监听底层数据列表的变更,所以数据列表变更后,需要手动进行刷新操作。也正是由于它与底层数据列表的关系不大,使得它的 Sort 操作是直接实现在此类中,可以简单地直接使用。 BindingListCollectionView 用于 IBindingList 作为底层数据列表的情况。它可以监听数据列表的变更,自动进行刷新。但是它的 Sort、过滤 操作与底层数据相关。(见 MSDN:This is because BindingListCollectionView objects use the underlying IBindingList for sorting and filtering functionalities. 来自:BindingListCollectionView 类) CollectionView 的创建一般使用 CollectionViewSource.GetDefaultView 方法,这一点是 WPF 中的默认机制。但是我们也可以直接使用构造函数来进行创建,例如,我们要为 IBindingList 列表构建 ListCollectionView 时,我们可以直接使用 new ListCollectionView(IList) 构造函数。当在 XAML 中进行类似的特殊构造时,我们则可以使用 CollectionViewSource 标签,并设置它的 CollectionViewType 属性。 ItemsControl 相关知识点 ItemsControl 是 WPF 中最重要的集合控件基类,目前我见到的集合控件都是从这个类继承下来的。当然,这也是 WPF 控件设计规约之一。 它的使用方法简单地说有两种:一种是使用 ItemsSource + ItemTemplate 来进行数据绑定;一种是直接使用 Items 属性来添加或者删除元素,这种使用方法和在 WinForm 下的使用方案比较类似,所以比较简单。 ItemsControl.Items 属性 ItemsControl 中的属性 Items 是 ItemCollection 类型,而它是继承自 CollectionView!!!也就是说,Items 其实是 ItemsSource 属性的视图集合类,我们可以通过这个属性来设置 ItemsControl 中集合的显示方案(Filter、Sorting、Grouping、Current)。 ItemsCollection 对接口的所有实现基本上都是基于内部的 CollectionView 来实现的,它存储在 private ICollectionView _collectionView 这个字段中。当 ItemsSource 存在时,_collectionView 字段是 CollectionViewSource.GetDefaultView 来为 ItemsSource 找到它所对应的 CollectionView 对象。而当我们没有设置 ItemsSource 属性而是直接使用 Items 属性集合时,ItemsCollection 则会生成一个 InnerItemCollectionView 类的对象。 目前就总结这些吧,以后有了再添加上来。 本文转自BloodyAngel博客园博客,原文链接:http://www.cnblogs.com/zgynhqf/archive/2011/11/19/2255450.html,如需转载请自行联系原作者
我们在做网页的时候,当背景是一张完整的图片,不动让其跟随滚动条滚动,怎么办?下面详细讲解一下。 CSS代码示例-背景颜色属性(background-color): <html> <head> <title>背景颜色 background-color</title> <style type="text/css"> body {background-color:#99FF00;} </style> </head> <body> <p>这个HTML使用了CSS的background-color属性,将HTML的背景颜色变成翠绿色。<p> </body> </html> 演示结果: 这个HTML使用了CSS的background-color属性,将HTML的背景颜色变成翠绿色。 CSS代码示例-背景图片属性(background-image): <html><head> <title>背景图片background-image</title></head> <body style="background-image:url(../images/css_tutorials/background.jpg)"> <p>这个HTML使用了CSS的background-image属性,设置了背景图片。<p> </body></html> 演示结果: 这个HTML使用了CSS的background-image属性,设置了背景图片。 CSS代码示例- 背景重复属性(background-repeat) <html><head><title>背景重复 background-repeat</title> <style type="text/css"> body {background-image:url(../images/css_tutorials/background.jpg); background-repeat:repeat-y} </style> </head> <body> <p>这个HTML使用了CSS的background-repeat属性,使背景图片竖向重复。<p><p>常用的background-repeat的属性值有: repeat-x(横向重复),repeat-x(横向重复), no-repeat(不重复)。</p><p>background-repeat属性要和background-image一起用。</p> </body> </html> 演示结果: 这个HTML使用了CSS的background-repeat属性,使背景图片竖向重复。 常用的background-repeat的属性值有: repeat-x(横向重复),repeat-x(横向重复), no-repeat(不重复)。 background-repeat属性要和background-image一起用。 CSS代码示例-背景附着属性(background-attachment)-[背景图固定不动,不跟随滚动条滚动]: <html><head> <title>背景附着属性 background-attachment</title> <style type="text/css"> body {background-image:url(../images/css_tutorials/background.jpg); background-repeat:no-repeat; background-attachment:fixed} </style> </head> <body> <p>这个HTML使用了CSS的background-attachment属性,将背景图片固定,不随内容滚动而滚动。<p> <p>背景附着(background-attachment)属性有两个值。一个是scroll,表示随内容滚动而动;一个是fixed,表示固定不动,不受内容滚动影响。缺省值是scroll。</p><p>background-attachment要和background-image一起用。</p> </body></html> 演示结果: 这个HTML使用了CSS的 background-attachment 属性,将背景图片固定,不随内容滚动而滚动。 背景附着(background-attachment)属性有两个值。一个是scroll,表示随内容滚动而动;一个是fixed,表示固定不动,不受内容滚动影响。缺省值是scroll。 background-attachment要和background-image一起用。 这个HTML使用了CSS的background-attachment属性,将背景图片固定,不随内容滚动而滚动。 背景附着(background-attachment)属性有两个值。一个是scroll,表示随内容滚动而动;一个是fixed,表示固定不动,不受内容滚动影响。缺省值是scroll。 CSS代码示例-背景位置属性(background-position): <html><head> <title>背景位置属性 background-position</title> <style type="text/css">body {background-image:url(../images/css_tutorials/background.jpg);background-repeat:no-repeat;background-position:20px 60px}</style></head><body><p>这个HTML使用了CSS的background-position属性。这个属性和background-image属性连在一起使用,决定了背景图片的最初位置。</p><p>上面的代码表示背景图片的初始位置距离网页最左面20px,距离网页最上面60px。</p></body></html> 演示结果: 这个HTML使用了CSS的background-position属性。这个属性和background-image属性连在一起使用,决定了背景图片的最初位置。 上面的代码表示背景图片的初始位置距离网页最左面20px,距离网页最上面60px。 CSS代码示例-背景属性(background): <html><head> <title>背景属性 background</title><style type="text/css">body{background:#99FF00 url(../images/css_tutorials/background.jpg) no-repeat fixed 40px 100px} </style></head> <body><p>这个属性是设置背景相关属性的一种快捷的综合写法,包括background-color, background-image, background-repeat, backgroundattachment, background-position。</p><p>这个HTML所用的背景属性表示,网页的背景颜色是翠绿色,背景是background.jpg图片,图片不重复显示,背景图片不随内容滚动而动,背景图片距离网页最左面40px,距离网页最上面100px。</p> </body></html> 演示结果: 这个属性是设置背景相关属性的一种快捷的综合写法, 包括background-color, background-image, background-repeat, backgroundattachment, background-position。 这个HTML所用的背景属性表示,网页的背景颜色是翠绿色,背景图片是background.jpg图片,背景图片不重复显示,背景图片不随内容滚动而动,背景图片距离网页最左面40px,距离网页最上面100px。 这个属性是设置背景相关属性的一种快捷的综合写法, 包括background-color, background-image, background-repeat, backgroundattachment, background-position。 本文转自寒意博客园博客,原文链接:http://www.cnblogs.com/hnyei/archive/2012/03/12/2392017.html,如需转载请自行联系原作者
1 初学iphone开发时,想做一个点击表视图上的单元格,然后从底部弹出一个时间选择器的功能。查阅了手边的几本iphone资料书都没有找到,在网上搜了半天也是一无所获。现这个问题我已经成功解决了,特将代码贴出来供初学者参考 3 UIActionSheet与UIDatepicker巧妙结合使用。 4 5 6 首先,将时间选择器嵌入到操作表中,让它以操作表的方式从底部弹出来 7 8 -(void)StartdateSheet 9 10 {11 12 NSString *title = UIDeviceOrientationIsLandscape([UIDevice currentDevice].orientation) ? @"\n\n\n\n\n\n\n\n\n" : @"\n\n\n\n\n\n\n\n\n\n\n\n" ;13 14 startsheet = [[UIActionSheet alloc] initWithTitle:title 15 16 delegate:self 17 18 cancelButtonTitle:nil 19 20 destructiveButtonTitle:nil 21 22 otherButtonTitles:@"Set", 23 24 nil];25 26 startsheet.actionSheetStyle = self.navigationController.navigationBar.barStyle;27 28 [startsheet showInView:self.view];29 30 UIDatePicker *datePicker = [[[UIDatePicker alloc] init] autorelease];31 32 datePicker.tag = 101;33 34 [startsheet addSubview:datePicker];35 36 37 38 }39 40 41 然后,在set方法里获取时间选择器的时间,并刷新表视图。42 43 -(void)set{44 45 46 47 UIDatePicker *datePicker = (UIDatePicker *)[actionSheet viewWithTag:101];48 49 NSDateFormatter *formatter = [[[NSDateFormatter alloc] init] autorelease];50 51 formatter.dateFormat = @"YY/MM/dd h:mm a";52 53 startDate=datePicker.date;54 55 NSString *timestamp = [formatter stringFromDate:datePicker.date];56 57 //显示时间的变量 58 59 [Datevalues replaceObjectAtIndex:0 withObject:timestamp];60 61 [tableViewList reloadData]; //刷新表视图62 63 64 65 } http://blog.sina.com.cn/s/blog_73615d370100oq3t.html图片请看原帖 本文转自编程小翁博客园博客,原文链接:http://www.cnblogs.com/wengzilin/archive/2012/03/12/2392322.html,如需转载请自行联系原作者
Notificationcenter的用法: 【原】NSNotificationCenter未必一定要建在消息接收者的类中。可以放在别的类中,先实例化一下,然后把observer赋值为刚对象。 这里的observer相当于接受者(receiver),object相当于发送者(poster)。理解了这点就可以较灵活地使用通知了。 iPhone软件开发的时候会遇到这种情况:打开APP后会在后台运行某个方法,例如下载文件,下载完成后可能需要调用某个方法来刷新界面,这时候可能没法在下载的函数中回调。NSNotificationCenter(通知)是一个很好的选择。 通知使用起来灰常的简单: 1、定义通知: [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(callBack) name: @"back" object: nil]; 2、定义通知中使用的方法: - (void)callBack{ NSLog(@"i am back.");} 3、调用通知: - (void)getIT{ NSLog(@"get it."); //发出通知 [[NSNotificationCenter defaultCenter] postNotificationName:@"back" object:self];} notificationcenter的讲解 【转自】http://www.devdiv.com/home.php?mod=space&uid=82357&do=blog&id=3835 Notifications Notification 包装了事件的信息, 比如窗口正在获取焦点或者网络连接正在断开. 需要订阅事件(例如, 一个文件想要知道正在编辑它的窗口将要被关闭)的object需要在notification center注册, 之后当事件发生的时候就会得到通知. 当事件发生的时候, 一个notification会被发送到notification center, 而后notification center马上就会把这个notification转发给所有订阅过这个事件的object. 当需要的时候, notification会被缓存在notification queue中…. Notification的原理 在 两个object之间传递信息最标准的方法是传递消息 – 一个object调用另外一个object的方法. 但是, 传递的消息这种方法要求发送消息的object知道消息的接收者和它能接收的消息类型. 这将会把两个object紧密的绑定起来 – 最值得注意的是这会让两个本来独立的子系统耦合在一起. 为了解决这些问题, 广播模型被提了出来. Object只是负责发送notification, 而NSNotificationCenter将负责把这个notification转发给所有相关的object. 一个 NSNotification(在这片文章里面简称notification)包含一个name, 一个object和一个可选的dictionary. Name是notification的标识. Object包含notification发送者想要发送的任意类型的object(一般来说就是发送这个notification的object本 身). Dictionary用来保存其他相关的东西(如果有的话). 任意object都可以发送notification. 任意object都可以在notification center注册以便在某个事件发生的时候能够得到通知. Notification center负责把接受的notification发送给所有注册过的消息接收者. 发送notification的object, notification里面包含的object和接收这个notification的object可以是同一个object, 也可以是三个不同的object. 发送notification的object不需要知道关于接受者的任何信息. 但是, 接受者至少需要知道notification的name和其所包含dictionary的key(如果有的话). Notification和Delegation 就使用上看, notification系统和delegate很像, 但是他们有以下不同: *Notification的接受者可以是多个object. 但是delegate object只能有一个. 这就阻止了返回值. *一个object可以从notification center接受它想要的任意数量个notification, 而delegate只能接受预先定义好的delegate方法. *发送notification的object完全不知到接受者是否存在. Notification Centers Notification center负责接收和发送notification. 当它接受到notification的时候会通知所有符合特定条件的接受者. Notification信息被包装在NSNotification里. Notification接收者在notification center里面注册以获得其他object发出的notification. 当事件发生的时候, 一个object发送相关的notification到notification center. Notification center将消息分发给每一个注册过的接受者. 发送notification的object和接受者可能是同一个. Cocoa包含两种notification center: *NSNotificationCenter类管理单个进程内部的notification. *NSDistributedNotificationCenter管理一台机器上跨进程的notification. NSNotificationCenter 每 一个进程都有一个默认的notification center, 你可以通过访问 NSNotificationCenter 的 +defaultCenter方法来得到它. 这种类型的notification center负责管理单个进程内部的notification. 如果需要管理同一台机器上不同进程之间的notification则需要用到NSDistributedNotificationCenter. Notification center发送notification给接收者的方法是同步的. 也就是说, 当发送一个notification的时候, 除非所有的接收者都接到和处理了这个notification, 否则不会返回. 想要发送异步notification的话就需要用到notification queue了. 在一个多线程应用程序里, notification总是和发送者处于同一个线程里, 但是接受者可以在其他线程里. NSDistributedNotificationCenter 每 一个进程都有一个默认的distributed notification center, 你可以通过访问 NSDistributedNotificationCenter 的 +defaultCenter方法来得到它. 这种类型的notification center负责管理一台机器上多个进程之间的notification. 如果需要在多台机器间通讯的话, 使用distributed objects. 发送一个distributed notification是非常昂贵的. Notification首先会被发送到一个系统级别的服务器上, 然后在分别分发到每一个注册过的进程里. 从发从消息到消息被接受到之间的延迟理论上来说是无限的. 事实上, 如果太多的notification被发送到服务器上, 那么服务器上的notification队列可能会被撑满, 这就有可能会造成notification的丢失. Distributed notification会在一个进程的主循环里被发送出去. 一个进程必须保证有一个主循环在其内部运行, 例如 NSDefaultRunLoopMode, 然后才能接受到distributed notification. 如果接收进程是多线程的, 那么notification并不一定会被主线程接受到. 一般来说notification会被分发到主线程的主循环, 但是其他线程一样可以接收到. 一 般类型的notification center可以注册所有object的notification, 但是 distributed notification center只能注册字符串类型的notification. 因为发送者和接受者可能在不同进程里, notification里面包含的object不能保证指向同一个object. 所以, distributed notification center只能接受包含字符串类型的notification. Notification会基于字符串来匹配. NotificationCenter的使用 1. 定义一个方法 update 2.订阅通知 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(update) name:@"update" object:nil] 3. 在要发出通知消息的地方 [[NSNotificationCenter defaultCenter] postNotificationName:@"update" object:nil]; ---------------------------- 虚拟键盘显示和消失的通知 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWasShown:) name:UIKeyboardDidShowNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWasHidden:) name:UIKeyboardDidHideNotification object:nil]; ------ - (void)keyboardWasShown:(NSNotification *) aNotification{ if(keyboardShown) return; NSDictionary *info = [aNotification userInfo];//获取通知信息 //get the size of the keyboard. NSValue *aValue = [info objectForKey:UIKeyboardFrameBeginUserInfoKey]; CGSize keyboardSize = [aValue CGRectValue].size; //Resize the scroll view CGRect viewFrame = [scrollView frame]; viewFrame.size.height -= keyboardSize.height; //Scroll the active text field into view CGRect textFieldRect = [activeField frame]; [scrollView scrollRectToVisible:textFieldRect animated:YES]; keyboardShown = YES; } //Called when the UIKeyboardDidHideNotification is sent - (void)keyboardWasHidden:(NSNotification *) aNotification{ NSDictionary *info = [aNotification userInfo]; //Get the size of the keyboard. NSValue *aValue = [info objectForKey:UIKeyboardFrameEndUserInfoKey]; CGSize keyboardSize = [aValue CGRectValue].size; //Reset the height of the scroll view to its original value CGRect viewFrame = [scrollView Frame]; viewFrame.size.height += keyboardSize.height; scrollView.frame = viewFrame; keyboardShown = NO; } 官方文档说明如下: postNotificationName:object:userInfo: Creates a notification with a given name, sender, and information and posts it to the receiver. - (void)postNotificationName:(NSString *)notificationName object:(id)notificationSender userInfo:(NSDictionary *)userInfo Parameters notificationName The name of the notification. notificationSender The object posting the notification. userInfo Information about the the notification. May be nil. 从以上可以看出,object表示发送者!!而不是要发送的参数。要发送的参数可以放到information里面,是NSDictionary类型的 本文转自编程小翁博客园博客,原文链接:http://www.cnblogs.com/wengzilin/archive/2012/03/17/2403939.html,如需转载请自行联系原作者
导航是网站最重要的组成部分之一,设计优秀的导航能够引导用户浏览网站中的更多内容。为了能让导航和网页内容完美的融合在一起,设计前需要设仔细分析网页结构。这篇文章收集了30个优秀的网站导航设计案例,希望这些实例能给你带来灵感。 Fully Illustrated The Idea Lists Collision Tobias Ahlin Carbonmade dConstruct 2010 Jeroen Homan Brizk Design Dribbble Buffalo TedxToronto David A. Molanphy Live Books Rich Brown I Love Dust Blue Sky Resumes Soh Tanaka Kyle Myer Yaron Schoen Unconventional Guides HAUS Christopher Meeks Dan Wiersema Williamson County Casa Marie Catrib’s The Design Cubicle Exclusive Reels AFD Folio Veerle’s Blog Drexler 本文转自寒意博客园博客,原文链接:http://www.cnblogs.com/hnyei/archive/2012/03/16/2399807.html,如需转载请自行联系原作者
Java的一个最显著的优势是内存管理。你只需要简单的创建对象而不需要负责释放空间,因为Java的垃圾回收器会负责内存的回收。然而,情况并不是这样简单,内存泄露还是经常会在Java应用程序中出现。 本篇文章将介绍什么是内存泄露,为什么会发生,以及怎样阻止内存泄露。 什么是内存泄露 内存泄露的定义:对于应用程序来说,当对象已经不再被使用,但是Java的垃圾回收器不能回收它们的时候,就产生了内存泄露。 要理解这个定义,我们需要理解对象在内存中的状态。如下图所示,展示了哪些对象是无用对象,哪些是未被引用的对象; 上图中包含了未引用对象和引用对象。未引用对象将会被垃圾回收器回收,而引用对象却不会。未引用对象很显然是无用的对象。然而,无用的对象并不都是未引用对象,有一些无用对象也有可能是引用对象,这部分对象正是内存泄露的来源。 为什么内存泄露会发生 让我们用下面的例子来看看为什么会发生内存泄露。如下图所示,对象A引用对象B,A的生命周期(t1-t4)比B的生命周期(t2-t3)要长,当B在程序中不再被使用的时候,A仍然引用着B。在这种情况下,垃圾回收器是不会回收B对象的,这就可能造成了内存不足问题,因为A可能不止引用着B对象,还可能引用其它生命周期比A短的对象,这就造成了大量无用对象不能被回收,且占据了昂贵的内存资源。 同样的,B对象也可能引用着一大堆对象,这些被B对象引用着的对象也不能被垃圾回收器回收,所有的这些无用对象消耗了大量内存资源。 怎样阻止内存泄露 以下是一些阻止内存泄露的经验提示: 多关注集合类,比如HashMap,ArrayList等,这些对象经常会发生内存泄露。比如当它们被声明为静态对象时,它们的生命周期会跟应用程序的生命周期一样长,很容易造成内存不足。 多关注事件监听(listeners)和回调(callbacks),比如注册了一个listener,当它不再被使用的时候,忘了注销该listener,可能就会产生内存泄露。 “如果一个类自己管理自己的内存空间,开发人员需要特别警惕内存泄露”,引用其它对象的成员变量记得要置为null?不太理解(原文:Often times member variables of an object that point to other objects need to be null out); 本文转自风一样的码农博客园博客,原文链接:http://www.cnblogs.com/chenpi/p/5490114.html,如需转载请自行联系原作者
前言 项目的模块化有利于任务分工,后期维护,易扩展,模块还可以独立成服务单独部署等; 创建packaging类型为POM的父项目 我用的maven插件是m2e,相信大部分人在eclipse装的也是m2e插件;废话不说,直接开始: 菜单选择新建maven project,注意选择创建一个简单工程,如下图红圈所示,因为我们要创建的是packaging类型为pom的maven项目,自带的archetype里貌似没有对应的类型,反正我是没找到。 接着点击next,在packaging选项里选择pom,然后输入必要的信息即可,如下图: 这时候我们的pom.xml文件是这样的: <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>cn.edu.hdu.mm</groupId> <artifactId>multi-module</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>pom</packaging> <name>multi-module</name> <description>multi module projects.</description> </project> 创建各个子模块 这里打算简单的创建一个web项目作为示例,按层划分;其实复杂点的项目是可以划分成多个子系统模块、多个服务模块,模块之间的通信可以通过jms、webservice等方式。 那么,我们就开始简单的创建common,dao,service,web几个模块吧, 这里,除了web模块的packaging类型是war,其它的都是jar,也就是说,在创建这些模块的时候,web模块选择的archetype为webapp,其它的选quickstart即可; 鼠标右键父工程,选择创建maven module; 创建完各个模块之后,父模块与子模块的POM配置文件如下,很好理解: 父模块POM配置: <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>cn.edu.hdu.mm</groupId> <artifactId>multi-module</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>pom</packaging> <name>multi-module</name> <description>multi module projects.</description> <modules> <module>multi-module-common</module> <module>multi-module-dao</module> <module>multi-module-service</module> <module>multi-module-web</module> </modules> </project> 其中的一个子模块POM配置: <?xml version="1.0"?> <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>cn.edu.hdu.mm</groupId> <artifactId>multi-module</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <artifactId>multi-module-common</artifactId> <name>multi-module-common</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> </dependencies> </project> 配置各个模块之间的依赖关系 在这个demo中,模块之间的依赖关系如下: web依赖service,service依赖dao,dao依赖common模块; 配置依赖关系很简单,比如我们需要在dao模块里配置其依赖common模块,只需在pom添加如下代码片段,跟平时的添加依赖一样: <dependency> <groupId>${project.groupId}</groupId> <artifactId>multi-module-common</artifactId> <version>${project.version}</version> </dependency> 注意这里的${project.groupId}和${project.version}指的是本模块的groupId和version,因为该模块和multi-module-common模块的共同父模块为multi-module,其groupId和version都继承自multi-module,所以我们这里直接写本模块的groupId和version即可。 开始开发程序 完成以上配置后,就可以开始开发我们的程序了,往common模块写一些公共的东西,dao模块访问数据源,service模块业务需求,web可以写一些jsp,action、controller等; 这里,我简单的写了个spring + spring mvc + mybatis的demo,添加了一个用户增删改查模块,前端页面就没写了~代码见最后的链接。 工程目录大致如下: 运行war包,调试等 代码写完之后,添加数据库(脚本在工程里),然后可以在父工程的pom.xml文件里添加tomcat7-maven-plugin插件配置,右键执行tomcat:run命令,运行war包,也可以右键调试工程,debug As... 执行命令,可看到如下日志,通过日志可以看到具体的执行过程: [INFO] Scanning for projects... [INFO] ------------------------------------------------------------------------ [INFO] Reactor Build Order: [INFO] [INFO] multi-module [INFO] multi-module-common [INFO] multi-module-dao [INFO] multi-module-service [INFO] multi-module-web Maven Webapp [INFO] [INFO] Using the builder org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder with a thread count of 1 [INFO] [INFO] ------------------------------------------------------------------------ [INFO] Building multi-module 0.0.1-SNAPSHOT [INFO] ------------------------------------------------------------------------ [INFO] [INFO] >>> tomcat-maven-plugin:1.1:run (default-cli) @ multi-module >>> [INFO] [INFO] <<< tomcat-maven-plugin:1.1:run (default-cli) @ multi-module <<< [INFO] [INFO] --- tomcat-maven-plugin:1.1:run (default-cli) @ multi-module --- [INFO] Skipping non-war project [INFO] [INFO] ------------------------------------------------------------------------ [INFO] Building multi-module-common 0.0.1-SNAPSHOT .......................................... 执行完命令后,浏览器地址输入http://localhost:8080/multi-module-web/user/getUserList.do?pageNo=0&pageSize=5,访问controller,访问成功: 本文转自风一样的码农博客园博客,原文链接:http://www.cnblogs.com/chenpi/p/5987332.html,如需转载请自行联系原作者
/* This is an example of the SetConsoleCtrlHandler function that is used to install a control handler. When a CTRL+C signal is received, the control handler returns TRUE, indicating that it has handled the signal. Doing this prevents other control handlers from being called. When a CTRL_CLOSE_EVENT signal is received, the control handler returns TRUE, causing the system to display a dialog box that gives the user the choice of terminating the process and closing the console or allowing the process to continue execution. If the user chooses not to terminate the process, the system closes the console when the process finally terminates. When a CTRL+BREAK, CTRL_LOGOFF_EVENT, or CTRL_SHUTDOWN_EVENT signal is received, the control handler returns FALSE. Doing this causes the signal to be passed to the next control handler function. If no other control handlers have been registered or none of the registered handlers returns TRUE, the default handler will be used, resulting in the process being terminated. Note that MyErrorExit is a placeholder for an application-defined function to display and handle error conditions.*/ BOOL CtrlHandler(DWORD fdwCtrlType) { switch (fdwCtrlType) { // Handle the CTRL+C signal. case CTRL_C_EVENT: Beep(1000, 1000); return TRUE; // CTRL+CLOSE: confirm that the user wants to exit. case CTRL_CLOSE_EVENT: return TRUE; // Pass other signals to the next handler. case CTRL_BREAK_EVENT: case CTRL_LOGOFF_EVENT: case CTRL_SHUTDOWN_EVENT: default: return FALSE; } } void main(void) { BOOL fSuccess; fSuccess = SetConsoleCtrlHandler( (PHANDLER_ROUTINE) CtrlHandler, // handler function TRUE); // add to list if (! fSuccess) MyErrorExit("Could not set control handler"); } 本文转自cococo点点博客园博客,原文链接:http://www.cnblogs.com/coder2012/p/3177802.html,如需转载请自行联系原作者
相信有很多的程序员,不愿意进行用户管理这块代码实现。 原因之一,不同的JavaEE 系统,用户管理都会有个性化的实现,逻辑很繁琐。 而且是系统门面,以后背锅的几率非常大,可谓是低收益高风险。 最近在系统中集成了 Shiro,感觉这个小家伙还是相当灵活的。 完善的用户认证和授权,干净的API,让人如沐春分。 Apache Shiro 作为一个强大而灵活的开源安全框架,它干净利落地处理身份认证,授权,企业会话管理和加密。 安全有时候是很复杂的,甚至是痛苦的,但它没有必要这样。框架应该尽可能掩盖复杂的地方,露出一个干净而直观的 API。 Apache Shiro 的首要目标是易于使用和理解。 以下是你可以用 Apache Shiro 所做的事情: a.验证用户来核实他们的身份 b.对用户执行访问控制,如: c.判断用户是否被分配了一个确定的安全角色。 d.判断用户是否被允许做某事。 e.在任何环境下使用 Session API,即使没有 Web 或 EJB 容器。 f.在身份验证,访问控制期间或在会话的生命周期,对事件作出反应。 g.聚集一个或多个用户安全数据的数据源,并作为一个单一的复合用户“视图”。 h.启用单点登录(SSO)功能。 i.并发登录管理(一个账号多人登录作踢人操作)。 j.为没有关联到登录的用户启用"Remember Me"服务。 … 以及更多——全部集成到紧密结合的易于使用的 API 中。 目前主流安全框架有 SpringSecurity 和 Shiro,相比于 SpringSecurity,Shiro 轻量化,简单容易上手。 SpringSecurity 太笨重了,难以上手,且只能在 Spring 里用,所以极力推荐Shiro。 本文重点描述集成过程,能让你迅速的将 Shiro 集成到 JavaEE 项目中,毕竟项目都挺紧张的。 回到顶部 1.前戏 Shiro 核心jar: <!--权限控制 shiro--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>${shiro.version}</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-ehcache</artifactId> <version>${shiro.version}</version> </dependency> JavaEE 应用开始的地方,web.xml 配置: <filter> <filter-name>shiroFilter</filter-name> <filter-class> org.springframework.web.filter.DelegatingFilterProxy </filter-class> </filter> <filter-mapping> <filter-name>shiroFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> Spring-Shiro-context 配置应用启动的使用,会帮助你做很多事情: <!--Shiro 关键过滤器配置--> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager"/> <property name="loginUrl" value="/sys/login"/> <!--请求 Url 为 get方式--> <property name="successUrl" value="/sys/index"/> <property name="filters"> <map> <entry key="authc" value-ref="formAuthenticationFilter"/> </map> </property> <property name="filterChainDefinitions" ref="shiroFilterChainDefinitions"/> </bean> <!-- Shiro 安全管理器 --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="systemAuthorizingRealm"/> <property name="cacheManager" ref="shiroCacheManager"/> </bean> <!--自定义系统认证域--> <bean id="systemAuthorizingRealm" class="com.rambo.spm.core.shiro.SysAuthorizingRealm"/> <!--shiro ehcache缓存--> <bean id="shiroCacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"> <property name="cacheManager" ref="cacheManagerFactory"/> </bean> <!--扩展表单认证过滤器--> <bean id="formAuthenticationFilter" class="com.rambo.spm.core.shiro.FormAuthenticationFilter"/> <!--权限过滤链定义 --> <bean name="shiroFilterChainDefinitions" class="java.lang.String"> <constructor-arg> <value> /static/** = anon /captcha-image = anon /sys/login = authc /sys/logout = logout /** =user </value> </constructor-arg> </bean> <!--借助 SpringAOP 扫描那些使用 Shiro 注解的类--> <aop:config proxy-target-class="true"/> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> <property name="securityManager" ref="securityManager"/> </bean> <!--用于在实现了Initializable/Destroyable接口的 Shiro bean 初始化时回调--> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/> 回到顶部 2.个性化 请求发起的地方一般是前端,不管你是 .jsp/.php/.net.......方式都是类似 <html> <body> <h1>login page</h1> <form id="" action="service/dologin" method="post"> <label>账号:</label><input name="userName" maxLength="40"/> <input title="是否是管理员" type="checkbox" name="isAdmin"><label>是否为管理员</label><br> <label>密码:</label><input title="密码" type="password" name="password" /><br> <input type="submit" value="登录"/> </form> <%--用于输入后台返回的验证错误信息 --%> <P><c:out value="${message }"/></P> </body> </html> 自定义项目验证域(验证域可以有多个,已可以有多种方式)。 public class SysAuthorizingRealm extends AuthorizingRealm { private Log log = LogFactory.get(); @Autowired private SysUserService sysUserService; @Autowired private SysRoleService sysRoleService; @Autowired private SysMenuService sysMenuService; /** * 获取当前用户的认证信息 * * @param authcToken 携带用户认证所需的信息 * @return 认证结果信息 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException { CaptchaUsernamePasswordToken spmToken = (CaptchaUsernamePasswordToken) authcToken; log.info("token:{}", ReflectionToStringBuilder.toString(spmToken)); SysUser sysUser = sysUserService.getSysUserByLoginName(spmToken.getUsername()); if (sysUser == null) { return null; } byte[] salt = Hex.decode(sysUser.getPasswd().substring(0, 16)); return new SimpleAuthenticationInfo(new SpmPrincipal(sysUser), sysUser.getPasswd().substring(16), ByteSource.Util.bytes(salt), getName()); } /** * 获取当前用户授权信息 * * @param principals 该用户身份集合 * @return 当前用户授权信息 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { SpmPrincipal spmPrincipal = (SpmPrincipal) super.getAvailablePrincipal(principals); log.info("授权当前:{}", ReflectionToStringBuilder.toString(spmPrincipal)); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); List<SysRole> sysRoleList = sysUserService.listSysRoleByUserId(spmPrincipal.getId()); for (SysRole sysRole : sysRoleList) { info.addRole(sysRole.getRoleType()); List<SysMenu> sysMenuList = sysRoleService.listSysMenuByRoleId(sysRole.getUuid()); for (SysMenu sysMenu : sysMenuList) { info.addStringPermission(sysMenu.getPermisson()); } } info.addStringPermission("user"); return info; } /** * 设定密码校验的Hash算法与迭代次数 */ @PostConstruct public void initCredentialsMatcher() { HashedCredentialsMatcher matcher = new HashedCredentialsMatcher("SHA-1"); matcher.setHashIterations(1024); setCredentialsMatcher(matcher); } } 请求的后台服务,在这里你可以在进行一点业务逻辑 /** * shiro 登录请求控制,真正的请求由 shiroFilter --> formAuthenticationFilter 进行处理 * 登录成功: 跳转配置的 succssUrl,不触发该方法; * 登录失败: 触发该方法,可以从扩展的 formAuthenticationFilter 中获取具体的错误信息; */ @PostMapping("/sys/login") public Object postSysLogin(HttpServletRequest httpRequest, ModelAndView modelAndView) { String exceptionName = (String) httpRequest.getAttribute(FormAuthenticationFilter.DEFAULT_ERROR_KEY_ATTRIBUTE_NAME); String errorMsg = null; if (IncorrectCaptchaException.class.getName().equals(exceptionName)) { errorMsg = "验证码错误!"; } else if (UnknownAccountException.class.getName().equals(exceptionName)) { errorMsg = "用户不存在!"; } else if (IncorrectCredentialsException.class.getName().equals(exceptionName)) { errorMsg = "用户或密码错误!"; } else if (exceptionName != null && StrUtil.startWith(exceptionName, "msg:")) { errorMsg = StrUtil.removeAll(exceptionName, "msg:"); } return setModelAndView(modelAndView, "sys/sysLogin", errorMsg); } 整个集成工作就结束了,是不是简单的不要不要的。 等下次项目经理指派你做用户管理的时候,只需要花半天的时间做设计。 借助Shiro 半天时间实现代码。 然后将剩下的时间,做做自己喜欢的其他研究工作。 本文转自Orson博客园博客,原文链接:http://www.cnblogs.com/java-class/p/5475373.html,如需转载请自行联系原作者
又要发这类环境配置随笔了,也不是俺对这些配置很感兴趣,只不过是在有当下部署问题和需求的时候,现解决一下,实属无奈。这也就是业余和专业的区别吧。看来俺又专业些了。之前写过《最新版 apache_2.2.11 与 apache-tomcat-6.0.18 集成方案》这个方案是以tomcat为主,把apache集成进来,但是效果并不是俺想要的,在正式需要使用的时候才发现这种方案达不到要求。很早前还有一些多端口和多虚拟主机的随笔Tomcat6 配置多虚拟主机,多域名绑定一IP ,Tomcat6.0 配置多端口访问 。这些都是学校资源紧张,需要一台服务器部署多个项目才搞的。 为了提高网站的访问性能,我们往往会在服务器端选择合适的部署环境。最简单的就是一个tomcat了,但是在高并发的情况下,非常容易当机。因为tomcat配置的不优化。我最早也没有注意这些细节,因为我知道一个项目的性能不是通过搭建环境体现的,而是算法的高效,代码实现的合理性,以及数据库的合理使用,优化技术的使用等等。不过通过优化部署环境应该说是最直接的一种方案了。目前我知道的一种tomcat优化方案是使用tomcat线程池。只需要在server.xml中改改配置就可以了。核心配置代码: <Executor name="tomcatThreadPool" namePrefix="catalina-exec-" maxThreads="1000" minSpareThreads="50" maxIdleTime="600000"/> <!-- A "Connector" represents an endpoint by which requests are received and responses are returned. Documentation at : Java HTTP Connector: /docs/config/http.html (blocking & non-blocking) Java AJP Connector: /docs/config/ajp.html APR (HTTP/AJP) Connector: /docs/apr.html Define a non-SSL HTTP/1.1 Connector on port 8080 --> <!-- <Connector port="8086" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" URIEncoding="UTF-8"/> --> <!-- A "Connector" using the shared thread pool--> <Connector executor="tomcatThreadPool" port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" /> 这样的性能响应能够高于默认配置不少。别的方案我还没有研究过,希望有这方面经验的朋友可以给出点建议。谢谢。然后就是apache+tomcat的集成,我最早写的那个是围绕tomcat配置的。这次补充一个更方便也是更科学的实现,围绕apache做集成。需要下载的工具还是那三个,实现的第一步与我的上一篇是一样的。1.将mod_jk-1.2.28-httpd-2.2.3.so复制到C:\Apache2.2\modules(我的apache安装目录),我把他改了下名,方便后面的操作 改名为mod_jk.so。 2.在tomcat的config目录下新建workers.properties 文件,内容如下: workers.tomcat_home=C:\Tomcat workers.java_home=C:\Program Files\Java\jre1.5.0_05 worker.list=ajp13 worker.ajp13.port=8009 worker.ajp13.host=localhost worker.ajp13.type=ajp13 worker.ajp13.lbfactor=1 3.在apache的httpd.conf的内容底部添加配置: LoadModule jk_module modules/mod_jk.so JkWorkersFile "C:/Tomcat/conf/workers.properties" JkLogFile "C:/Tomcat/logs/mod_jk2.log" JkLogLevel info<VirtualHost localhost> ServerAdmin @hbu.cn DocumentRoot D:/ ServerName localhost DirectoryIndex index.html index.htm index.jsp ErrorLog logs/shsc-error_log.txt CustomLog logs/shsc-access_log.txt common JkMount /servlet/* ajp13 JkMount /*.jsp ajp13 JkMount /*.do ajp13 </VirtualHost> 配置完毕,经测试apache将jsp,servlet,.do请求都交由tomcat处理。其他文件由apache处理,因为apache的稳定性和 安全性,以及相对于tomcat而言快速的静态页处理速度,也可以提高网站的负载能力,前提是网站的静态页比较多,例如做了静态化。 在上面最后的请求派发给tomcat是可以不断添加的。例如我做的站有FCKeditor,DWR,我就需要添加这两者的过滤。 格式保持一致,例如: JKMount /dwr/* ajp13。 这样由于apache和tomcat集成造成的功能出错就可以一一解决了。这就是一种较完美的集成方案。 一些静态请求由apache处理,动态请求由tomcat处理。 园子首页有一篇网站的发展过程,就是说数据库为什么要和网站分别部署在两台服务器上。做了一些考证,我觉得写的很不错,考虑了 读写和传输数据的速度,虽然数据基本是理论层次的。 俺最早也看过这方面的网站架构演变史。俺不太喜欢磨蹭,于是直接用最简练的话概述了。下面列举数字,由早到晚说说网站的发展过程。 1.一台服务器,数据库和网站都在这上面架着。我觉得在添加一台服务器之前应该先做以下尝试,毕竟比较费钱。 首先就是优化代码,重构和提炼算法。然后优化sql查询等等关于数据库的操作,合理的使用事务。建立数据库索引, 优化服务环境,关掉不必要的服务,优化搭载环境。其次静态化,缓存(页面片段缓存,数据库缓存,对象级缓存), 数据持久化。 2.网站与数据库分离,需要两台服务器。 3.搭载环境的负载均衡。分散请求,提高容错能力。 4.数据库集群 5.网站分布式,采取读写分离,网站很大比重的请求都是在查数据。 6.提高服务器的硬件配置。 7.还是不够的话,再增加服务器......... 本文转自施杨博客园博客,原文链接:http://www.cnblogs.com/shiyangxt/archive/2009/08/03/1537842.html,如需转载请自行联系原作者
Dwr 是一个 Java 开源库,帮助你实现Ajax网站。 它可以让你在浏览器中的Javascript代码调用Web服务器上的Java,就像在Java代码就在浏览器中一样。 Dwr 主要包括两部分: 在服务器上运行的 Servlet 来处理请求并把结果返回浏览器。 运行在浏览器上的 Javascript,可以发送请求,并动态改变页面。 Dwr 会根据你的 Java 类动态的生成Javascript代码。 这些代码的魔力是让你感觉整个Ajax调用都是在浏览器上发生的,但事实上是服务器执行了这些代码,DWR负责数据的传递和转换。 从DWR官网下载最新版本的jar包,地址:http://directwebremoting.org/dwr/downloads/index.html 将jar包放入WEB-INF的lib文件夹下。同时,dwr依赖于commons-logging.jar这个包 配置 web.xml 文件如下 <servlet> <servlet-name>dwr-invoker</servlet-name> <servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class> <init-param> <param-name>debug</param-name> <param-value>true</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>dwr-invoker</servlet-name> <url-pattern>/dwr/*</url-pattern> </servlet-mapping> 配置 dwr 如下 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 2.0//EN" "http://www.getahead.ltd.uk/dwr/dwr20.dtd"> <!-- 关键配置 --> <dwr> <allow> <create creator="new" javascript="Chat"> <param name="class" value="com.epri.xts.wxqyh.app.chatroom.module.Chat"/> </create> <convert converter="bean" match="com.epri.xts.wxqyh.app.chatroom.bean.Message"/> </allow> </dwr> <!-- <!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 1.0//EN" "http://www.getahead.ltd.uk/dwr/dwr10.dtd"> <!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 2.0//EN" "http://www.getahead.ltd.uk/dwr/dwr20.dtd"> --> dwr.xml 是 dwr 的核心配置文件,主要的标签有:<converter>、<convert>、<create>这三个标签。 <create> 标签是 dwr 中重要的标签,用来描述 java(服务器端) 与 javascript (客户端)的交互方式。其基本格式如下: <allow> <create creator="..." javascript="..." scope="..."> <param name="..." value="..."/> <auth method="..." role="..."/> <exclude method="..."/> <include method="..."/> </create> ... </allow> 其中,creator 和 javascript 是必须属性,其他可以忽略。creator 包含有以下几个值: new:Java用“new”关键字创造对象 none:它不创建对象 (v1.1+) scripted:通过BSF使用脚本语言创建对象,例如BeanShell或Groovy spring:通过Spring框架访问Bean struts:使用Struts的FormBean (v1.1+) jsf:使用JSF的Bean (v1.1+) pageflow:访问Weblogic或Beehive的PageFlow (v1.1+) ejb3:使用EJB3 session bean (v2.0+) 这里初学,实用java new创建对象。 4、页面配置 页面需要引入3个JS <script src="<%=ctxPath%>/dwr/interface/Chat.js" type="text/javascript"></script> <script src="<%=ctxPath%>/dwr/engine.js" type="text/javascript"></script> <script src="<%=ctxPath%>/dwr/util.js" type="text/javascript"></script> 其中 engine.js 必须要,如果需要用到dwr提供的一些方便的工具要引用util.js ,然后是dwr自动生成的js文件。 名字和 dwr.xml 中 create 标签的 javascript 属性值一样,且必须是 dwr/interface 开头的目录 5、javascript代码: // 发送消息 function sendMessage() { var text = DWRUtil.getValue("message"); if("" != text){ DWRUtil.setValue("message", ""); Chat.addMessage(text, name, taskId, gotMessages); } } function gotMessages(messages) { var chatlog = ""; for ( var data in messages) { chatlog = "<div> <li class='even'>" + messages[data].text + "</li></div } DWRUtil.setValue("chatlog", chatlog); setTimeout("queryMessage()", 2000); } function queryMessage() { Chat.getMessages(taskId, gotMessages); } html代码 <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <% String ctxPath = request.getContextPath(); %> <html> <head> <title>聊天组</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="../css/jquery.mobile-1.4.5.min.css"> <link rel="stylesheet" type="text/css" href="../css/bootstrap.min.css"> <link rel="stylesheet" href="../css/add.css"> <script src="../js/jquery-1.8.3.min.js"></script> <script src="../js/jquery.mobile-1.4.5.min.js"></script> </head> <body class="bgc" onload="queryMessage()"> <div data-role="page" id="pageone"> <!-- 聊天内容显示区域 --> <div data-role="content" class="container" role="main"> <ul class="content-reply-box mg10" id="chatlog"> </ul> </div> <!-- 下方输入框 --> <div data-position="fixed" data-role="footer"> <ul class="footer"> <li class="col-xs-3"> <span class="b_pic"> <form action="<%=request.getContextPath()%>/chat" method="post" enctype="multipart/form-data" data-ajax="false" name="form" id="form" target="relnews"> <input type="hidden" name="name" id="name" value="" /> <input type="hidden" name="taskId" id="taskId" value="" /> <input type="file" name="image" id="image" class="photo" onchange="sendImage(this)" /> </form> </span> </li> <li class="col-xs-6"><input type="text" name="fname" id="message"></li> <li class="col-xs-4"><a href="" data-role="button" class="b_submit" id="submitInfo" onclick='sendMessage();'>发送</a></li> </ul> </div> </div> <!-- 隐藏域作为表单提交后页面不跳转使用 --> <iframe align="center" frameborder="0" marginheight="0" marginwidth="0" name="relnews" id="hiddenIframe" scrolling="no" style="width: 100%; height: 100%; display: none"> </iframe> </body> <script src="<%=ctxPath%>/dwr/interface/Chat.js" type="text/javascript"></script> <script src="<%=ctxPath%>/dwr/engine.js" type="text/javascript"></script> <script src="<%=ctxPath%>/dwr/util.js" type="text/javascript"></script> <script type="text/javascript" src="../js/chat.js"></script> </html> 6、其他 dwr可以设置是否采用异步方式访问java代码: dwr.engine.setAsync(false); //false为同步,true(默认)为异步 以及dwr对于异常的处理,这里标注一个简单的全局异常,细分的后续学习补充: dwr.engine.setErrorHandler(errh); function errh(errorString, exception) { errorFlag = true; alert("操作失败!"); } 本文转自Orson博客园博客,原文链接:http://www.cnblogs.com/java-class/p/6653739.html,如需转载请自行联系原作者
weblogic 做为商用中间件在(EJB、jndi 数据源、日志管理、内存管理、资源配置管理...) 是一些开源免费小型容器无法望其项背的。 weblogic 最早由 weblogic Inc. 开发,后并入 BEA 公司,最终 BEA 公司又并入 Oracle 公司。 weblogic 是用于开发、集成、部署和管理大型分布式 Web 应用、网络应用和数据库应用的 Java 应用服务器。 因此 weblogic 一般做为项目上线发布和部署的服务器,而本地研发时大多会选用 Tomcat/Jetty.... 小型容器。 这样难免会出现问题,本地运行良好的 web 程序,部署在 weblogic 时会出现这样或那样的问题。 绝大一部分问题都是因为 weblogic 依赖的 jar 与项目中的 jar 冲突(相同 jar 不同版本)或 weblogic 不支持项目 jar 所导致的。 当部署 weblogic 出现问题时(自己总结三板斧): 1. 确认公司使用的 weblogic 版本并翻阅官网对应 weblogic 版本所支持的标准; 2. 对照 weblogic 出错日志确定冲突 jar 或确定该 webloigc 不支持的 jar; 3. 对于出现冲突的 jar 在项目 WEB-INFO 目录下添加 weblogic.xml 配置文件,其中申明要预先加载的项目 jar; 4. 对于出现该 webloigc 版本不支持的 jar,升级 weblogic 版本,或降低项目使用的 jar 版本。 回到顶部 1. 确定 weblogic 版本并查看相应支持的标准 知已知彼才能百战不殆,因 weblogic 小版本较多且版本之间差异较大。 查看 weblogic 版本有很多方法,最直接方式是询问安装 weblogic 的同事,或者登录到 linux 像下面这样。 获悉部署的版本后,去瞅瞅 oracle weblogic 官网,那里的文档资料是最全面也是最权威的。 http://www.oracle.com/technetwork/middleware/weblogic/documentation/index.html 找到对应版本点 view Library 跳转进去,里面是整个版本的概览,咱们的关注点应该是该 weblogic 版本支持的标准。 里面有详细的该 weblogic 支持的 java、web Service、一些杂七杂八的标准,比如 JDK、EL、Servlet、JAX-RS、JAX-WS.... 回到顶部 2. 定位问题 有了上述具体的支持标准,配合部署报错的具体类和方法,就可以对症下药。 下面列举部署 weblogic 时常见的几个报错异常(还有很多遇到在补充): 01) java.lang.UnsupportedClassVersionError: Unsupported major.minor version 51.0 weblogic Jdk 版本与项目 jdk 版本不一致 02) request.getRealPath() 为 null servlet-api.jar 本地和 weblogic 有差异,使用类加载 Thread.currentThread().getContextClassLoader() 获取相对路径; (classpath 下找不到 .doc 和 .exe ?可以读取配置文件?)将资源放置到 WEB-INF 下; 03) ClassNotFoundException:org.hibernate.hql.ast.HqlToken weblogic 已依赖较早版本的 antlr.jar 04) 统一配置 tomcat 和 weblogic 上的数据源 <jee:jndi-lookup id="dataSource" jndi-name="jdbc/user" /> 05) java.lang.ClassCastException: weblogic.xml.jaxp.RegistryDocumentBuilderFactory cannot be cast to javax.xml.parsers.DocumentBuilderFactory 移除项目 xml-apis.jar 或 jaxp-api.jar 06)weblogic NoClassDefFoundError: Could not initialize class sun.awt.X11Graphi 修改 startWebLogic.sh export JAVA_OPTIONS=-Djava.awt.headless=true 07)weblogic.jdbc.wrapper.Clob_oracle_sql_CLOB cannot be cast to oracle.sql.CLOB 使用 weblogic JNDI 数据源读取 clob 字段时出现的强转错误:http://blog.csdn.net/arvinrong/article/details/22732219 08) java.lang.NoSuchMethodError: com.google.common.collect.Lists.reverse(Ljava/util/List;)Ljava/util/List weblogic 依赖的 guava 版本较低(包含14.0.1 及以下) 09)WebLogic下使用 URL.openConnection 获取连接返回 SOAPHttpsURLConnection 而不是 HttpsURLConnection 在实例化 URL 对象是指定JDK Handler new URL(null, urlStr, new sun.net.www.protocol.http.Handler.Handler()) 10)java.lang.LinkageError: loader constraint violation: when resolving method'XXXXX(XXXX)V' weblogic 类加载器和 JVM 类加载器加载了不同 Jar 的相同类导致冲突(删除XXX所在jar):http://www.lxway.com/981865964.htm 11)java.lang.NoSuchMethodError: javax.validation.spi.ConfigurationState.getParameterNameProvider()Ljavax/validation/ParameterNameProvider weblogic 依赖的 hibernate-validator 版本较低(包含 4.3.2.Final 及以下) 12)java.lang.AbstractMethodError: org.hibernate.validator.internal.engine.ConfigurationImpl.getDefaultParameterNameProvider()Ljavax/validation/ParameterNameProvider; 升级 hibernate-validator 版本 13)java.lang.RuntimeException: Invalid schema document passed to AbstractDataBinding.addSchemaDocument, not in W3C schema namespace: schema com.sun.xml.bind.jaxb-impl 与 weblogic JDK jar 冲突,排除该 jar 回到顶部 3. 解决方法 解决此类问题的方式大体有三种,遇到部署问题时也可以自己都实践一下。 a. 删除 weblogic 下冲突 jar 文件夹: (1)删除 /bea/weblogic12.1.3/server/lib/weblogic.jar中的冲突的文件夹; (2)copy 冲突的 XXX.ja r到 /bea/weblogic12.1.3/server/lib, 重启weblogic。 b. 通过修改启动脚本来优先加载某些 jar (1)将冲突的 jar 包拷贝到 ${DOMAIN_HOME}/lib 目录下。 (2)${DOMAIN_HOME}/bin 下找到 startWeblogic.sh 文件用文本编辑器将其打开,并在里面加入如下内容: export first_classpath=${DOMAIN_HOME}/lib/xxxx.jar SAVE_CLASSPATH="${first_classpath}:${CLASSPATH}" c. 在应用中通过 weblogic .xml 的配置,优先加载应用的 jar 因 weblogic 版本不用,weblogic 的配置项会有差异,不要盲目从网上 copy。 官网版本描述(其他版本将1213换一下即可):http://docs.oracle.com/middleware/1213/wls/WBAPP/weblogic_xml.htm#WBAPP571 关注其中的 prefer-application-resources/prefer-application-packages(预先加载应用对应 jar ) 节点的配置: <?xml version="1.0" encoding="UTF-8"?> <weblogic-web-app xmlns="http://xmlns.oracle.com/weblogic/weblogic-web-app"> <container-descriptor> <prefer-web-inf-classes>false</prefer-web-inf-classes> <prefer-application-packages> <package-name>预先加载项目 lib 中的包名,支持通配符</package-name> <package-name>com.sun.faces.*</package-name> <package-name>com.bea.faces.*</package-name> </prefer-application-packages> </container-descriptor> </weblogic-web-app> 解决方案 a/b/c 中, c 方案无异是最优解,做为生产环境的 weblogic 可能会部署多个应用,不太可能让你随便重启。 方案 a,b 会破坏 weblogic 的整体性,可能导致其他部署在 weblogic 上的应用出现问题。 本文转自Orson博客园博客,原文链接:http://www.cnblogs.com/java-class/p/7110266.html,如需转载请自行联系原作者
关于.Net垃圾收集器(Garbage Collection),Aicken已经在“.Net Discovery 系列”文章中有2篇的涉及,这一篇文章是对上2篇文章的补充,关于“.Net Discovery 系列”文章索引请见本文结尾。 第一节.垃圾回收算法与完整收集(Full GC) 垃圾收集器就是跟踪所有被引用到的对象,整理对象不再被引用的对象,回收相应的内存,它使用“标记与清除”算法,分两步回收对象: Step 1.Mark-Sweep :从应用程序的root出发,利用相互引用关系,遍历其在Heap上动态分配的所有对象,指明需要回收的对象,标记出那些存活的对象,予以标记。 Step 2.Compact: 对内存中存活的对象进行移动,修改它们的指针,使之在内存中连续,这样空闲的内存也就连续了,即完成了内存释放工作,也解决了内存碎片问题,这个过程也可以成为指针的压缩。 垃圾收集器一般将托管堆中的对象分为3代,这可以通过调用GC.MaxGeneration得知,对象按照存在时间长短进行分代,最短的分在第0代,最长的分在第2代,第2代中的对象往往是比较大的,第二代空间被称作Large Object Heap,对于2代对象的回收,与第0、1代回收方式相比最大的不同在于,没有了指针移动的压缩过程。 图1 对象的回收 如上图所示,左边的区域为第一次GC时的结构,需要注意的是GC标记的是那些存活的对象,而不是需要回收的,所以第一次回收,对象B、D没有被标记,所以被回收了,之后GC移动了对象内存指针,使空间连续。 接下来看中间的部分,第二次GC开始了,C对象没有被标记,所以被回收了,接下来A、D、F三个对象被压缩,形成连续的内存空间,并且形成了第1、2、3代区域。 接下来看最右边的部分,D对象没有被标记,由于D对象处于第2代中,所以回收D对象后,GC没有启动压缩步骤,因为对于大对象的指针移动,资源耗费成本很高。 对于第2代的GC称为Full GC,新分配的对象在第0代(0代空间最大长度通常为256K),按地址顺序分配,它们通常是一些局部变量;第1代(1代空间最大长度通常为2 MB)是经过0代垃圾收集后仍然驻留在内存中的对象,它们通常是一些如表单,按钮等对象;第2代是经历过几次垃圾收集后仍然驻留在内存中的对象,它们通常是一些应用程序对象。 可见一次Full GC需要的资源是最多的,可能是几秒或十几秒。 托管堆的内存分配以段(Segment)为单位,CLR启动时通常为GC Heap创建2个段,分别用来存储第0、1代对象和第2代对象,以下是通过Windbg工具查看到的GC Heap情况: 图2 WinDbg 查看GC Heap情况 可以看出,GC堆被分成了两个段,三代,每代起始地址十进制差值为12。 在理解方面需要注意的是,GC回收的是程序中的引用类型,值类型是保存在堆栈之中,当值类型对象出了作用域后会自动释放内存----即弹栈,不需要垃圾收集器管理。 第二节.GC的工作模式 GC的工作模式分3种,Workstation GC with Concurrent GC off、 Workstation GC with Concurrent GC on、Server GC ,在.Net 2.0以上版本可以通过修改Config文件来改变GC工作模式,例如启用Server GC: <configuration> <runtime> <gcServer enabled="true" /> </runtime> </configuration> 或者通过.Net配置工具,查看“我的电脑”节点属性可以方便的改变GC工作模式,如下图: 图3 GC工作模式 Workstation GC without Concurrent: 用于单CPU的服务器,策略引擎会调节GC工作频率,使用挂起->查找与标记->压缩->恢复的流程进行GC工作。 Workstation GC with Concurrent: Concurrent GC与Non Concurrent GC模式相比,有着更敏捷的反应速度,Winform应用程序和Windows services 服务程序默认采用这种模式,单CPU机器上只能使用workstation GC方式,默认为 Workstation GC with Concurrent。 在这种模式下,第0、1代的收集仍然是要暂时挂起应用程序的,只有在收集第2代时,才会并行处理,这种并行收集是利用多CPU 对Full GC进行并行处理,具体原理是将Full GC过程切分成多个短暂子过程对线程进行冻结,在线程冻结时间之外,应用程序仍然可 以正常运行。这主要通过将0代空间设置的很大,使Full GC时,CLR仍然能够在0代中进行内存分配,如果Full GC时0代内存也已用尽,那么应用程序将被挂起,等待Full GC的完成。 Server GC: 用于多CPU的服务器,这种GC模式有着很高的性能和效率。这种模式下,CLR为每个CPU创建一个专用的GC线程,每个CPU可以独立的为相应的heap执行GC操作,这些GC线程是以非并发的形式工作的,收集工作与线程正常工作不能同时进行,这就是说第0、1、2代的收集都会挂起应用线程。 在.Net 4.0中,有一种新的垃圾收集机制,叫做后台收集。这种机制以concurrent GC为基础的,如上文所讲,Workstation GC with Concurrent模式中,在Full GC过程时,CLR仍然能够在0代中进行内存分配,如果Full GC时0代内存也已用尽,那么应用程序将被挂起,等待Full GC的完成。 这个过程在后台收集机制中是这样工作的,在进行Full GC时可以同时进行第0、1代收集,并且后台收集是一个独立线程完成的,这个进程任务优先级低于第0、1代收集,如果在后台收集中需要对第0、1代收集,后台收集将会等待第0、1代收集完成后再进行工 作,当然第0、1代收集是需要短暂挂起应用的。 后台收集还会根据策略引擎的指示,动态调节第0、1代的容量,减少前台收集(第0、1代收集)次数。 第三节 .Net 4.0中的垃圾收集器 在.Net 3.5 SP1中,FrameWork中新增了如下方法,并且在4.0中进行了优化,GC.RegisterForFullGCNotification 、GC.WaitForFullGCApproach 、GC.WaitForFullGCComplete 、GC.CancelFullGCNotification,这几个方法都是针对Full GC(完整收集)的。 1.GC.RegisterForFullGCNotification:这个方法将返回一个将要Full GC的信号通知,该方法有2个参数: int maxGenerationThresholdint largeObjectHeapThreshold 这两个参数的含义是指的是第2代中存活的对象个数和大对象堆中对象个数,满足这两个参数后,便会引发通知,由此看来LOH也许并不是第2代,.Net GC也许也并不只是3代, 这一点在.Net Discovery 系列之三--深入理解.Net垃圾收集机制(上)中已有描述。 2.GC.CancelFullGCNotification:取消已经注册的垃圾收集通知 这两个方法调用示例: 代码 // Variable for continual checking in the // While loop in the WaitForFullGCProc method.static bool checkForNotify = false;// Variable for suspending work // (such servicing allocated server requests)// after a notification is received and then // resuming allocation after inducing a garbage collection.static bool bAllocate = false;// Variable for ending the example.static bool finalExit = false;// Collection for objects that // simulate the server request workload.static List<byte[]> load = new List<byte[]>();public static void Main(string[] args) {try {// Register for a notification. GC.RegisterForFullGCNotification(10, 10); Console.WriteLine("Registered for GC notification."); checkForNotify = true; bAllocate = true;// Start a thread using WaitForFullGCProc. Thread thWaitForFullGC = new Thread(new ThreadStart(WaitForFullGCProc)); thWaitForFullGC.Start();// While the thread is checking for notifications in// WaitForFullGCProc, create objects to simulate a server workload. try {int lastCollCount = 0;int newCollCount = 0;while (true) {if (bAllocate) { load.Add(new byte[1000]); newCollCount = GC.CollectionCount(2);if (newCollCount != lastCollCount) {// Show collection count when it increases: Console.WriteLine("Gen 2 collection count: {0}", GC.CollectionCount(2).ToString()); lastCollCount = newCollCount; }// For ending the example (arbitrary). if (newCollCount == 500) { finalExit = true; checkForNotify = false;break; } } } }catch (OutOfMemoryException) { Console.WriteLine("Out of memory."); } finalExit = true; checkForNotify = false; GC.CancelFullGCNotification(); }catch (InvalidOperationException invalidOp) { Console.WriteLine("GC Notifications are not supported while concurrent GC is enabled.\n"+ invalidOp.Message); } } 3.GC.WaitForFullGCApproach:用来获得垃圾收集器是否将要启动完整垃圾收集的工作,该方法返回GCNotificationStatus枚举值,当枚举为Succeeded时,你应当做一些工作,例如阻止手动调用GC.Collect()方法,以免浪费资源。 该方法应与GC.WaitForFullGCComplete()同时使用,以确定CLR执行了完整垃圾收集。 代码 // 查看是否将启动完整收集GCNotificationStatus s = GC.WaitForFullGCApproach();if (s == GCNotificationStatus.Succeeded) {//do not GC.Collect()}else if (s == GCNotificationStatus.Canceled) {// GC.Collect()} 4.GC.WaitForFullGCComplete: 用来获得垃圾收集器是否已经完成完整垃圾收集: 代码 本文转自Aicken(李鸣)博客园博客,原文链接:http://www.cnblogs.com/isline/archive/2010/01/01/1637241.html,如需转载请自行联系原作者
转眼间《.Net Discovery》系列文章已经推出1年了,本文为该系列的第10-13篇文章,在本文中将对以前所讲的.Net平台知识做一个小小的总结与机制分析,引出并重点介绍这些机制对程序性能的影响与改进建议。 本文将分为四部分,分别讲述了:垃圾回收机制、即时编译机制、异常处理机制、字符串驻驻留机制的原理与性能改进建议。《.Net Discovery》系列的每篇文章撰写耗时都在2天以上,转载时麻烦著名作者Aicken(李鸣),并且未经作者同意,禁止一切商业用途! 一.关于垃圾回收机制 ● 机制分析 垃圾收集器是.Net平台的一个特性,它自动回收托管堆上不再使用的对象,及时清理内存,这一切都是对开发人员透明的,当然你也可以手动把它召唤出来,它的本质就是跟踪所有被引用到的对象,整理对象不再被引用的对象,回收相应的内存。垃圾收集机制采用“标记与清除(Mark Sweep)”算法来完成上述任务,整个过程分为两步: Step 1.Mark-Sweep :从应用程序的root出发,利用相互引用关系,遍历其在Heap上动态分配的所有对象,指明需要回收的对象,标记出那些存活的对象,予以标记。 Step 2.Compact: 对内存中存活的对象进行移动,修改它们的指针,使之在内存中连续,这样空闲的内存也就连续了,即完成了内存释放工作,也解决了内存碎片问题,这个过程也可以成为指针的压缩。 垃圾收集器一般将托管堆中的对象分为3代,这可以通过调用GC.MaxGeneration得知,对象按照存在时间长短进行分代,最短的分在第0代,最长的分在第2代,第2代中的对象往往是比较大的,第二代空间被称作Large Object Heap,对于2代对象的回收,与第0、1代回收方式相比最大的不同在于,没有了指针移动的压缩过程。 如下图,第一次GC时,左边第一列A-F表示内存中的对象,位于浅蓝色 区域,经过Mark后,ACDF标记为可用,Sweep过程清除了BE,Compact过程移动了ACDF,使之位于连续存储区域中;第二次使用绿色做标记;第三次GC使用蓝色表示标记;可以看出第三次GC过程没有了指针移动的压缩过程。 图1 对象的回收 ●性能影响分析 这个过程看起来有点复杂,的确垃圾收集器的启动是会占用一些CPU时间,从而影响系统的性能,但这种影响很有限,并且这些损失是有所值的。 1.垃圾收集器并不是没有规律的启动,而是当代龄达到一定触发条件时启动,而且垃圾收集器只是移动代龄较低的1、2代的资源,并不会移动LOH中的对象。这就在一定程度上避免了GC长时间锁定线程导致的性能损失。 2.GC有三种不同的工作模式,适用于不同环境的情况,并不是所有环境都是“使用挂起->查找与标记->压缩->恢复” 的流程。“Workstation GC with Concurrent”模式可以第0、1代的收集仍然是要暂时挂起应用程序,在收集第2代时,会并行处理,具体原理是将Full GC过程切分成多个短暂子过程对线程进行冻结,在线程冻结时间之外,应用程序仍然可以正常运行。这主要通过将0代空间设置的很大,使Full GC时,CLR仍然能够在0代中进行内存分配,如果Full GC时0代内存也已用尽,那么应用程序将被挂起,等待Full GC的完成。 在多CPU的情况下,可以使用和“Server GC”模式。这种GC模式有着很高的性能和效率。这种模式下,CLR为每个CPU创建一个专用的GC线程,每个CPU可以独立的为相应的heap执行GC操作,这些GC线程是以非并发的形式工作的,收集工作与线程正常工作不能同时进行,这就是说第0、1、2代的收集都会挂起应用线程。 在.Net 4.0中,有一种新的垃圾收集机制,叫做后台收集。这种机制以concurrent GC为基础的,如上文所讲,Workstation GC with Concurrent模式中,在Full GC过程时,CLR仍然能够在0代中进行内存分配,如果Full GC时0代内存也已用尽,那么应用程序将被挂起,等待Full GC的完成。 3.垃圾收集器是配合策略引擎工作的。策略引擎可以唤醒GC,它会根据GC启动的次数、频率、代龄情况等自发的启动GC,使GC工作。特别要注意的是,由程序人员手动的调用GC收集的代码,同样会影响策略引擎的工作,这样会给策略引擎错误的信号,从而导致GC的错误启动,所以在没有必要的情况下,一般不建议使用GC.Collect();手动回收。 ● 综述 比起垃圾收集器带来的微乎的性能损失,我们应该把精力放在程序的优化上,非托管资源的及时释放、字符串拼接、循环内的业务代码都是需要注意的地方。垃圾收集机制不是.Net也不是Java的专利,它已经有一段进化的历史,越来越多的案例也证明垃圾收集机制的优点,Exchange 2010的大部分模块就是基于托管环境的。 二.关于实时编译机制 JIT(Just In Time简称JIT)是.Net边运行边编译的一种机制,这种机制的命名来源于丰田汽车在20世纪60年代实行的一种生产方式,中文译为“准时制”。 .Net 的JIT编译器在设计初衷和运行方式来上讲,都与丰田汽车的这种“准时生产”思想体系有着很大的相似之处,所以让我们先来透过“准时生产”方式来理解.Net的JIT机制吧。 “准时生产”的基本思想可概括为“在需要的时候,按需要的量生产所需的产品”,这正是.Net JIT编译器的设计初衷,即在需要的时候编译需要的代码。 本文转自Aicken(李鸣)博客园博客,原文链接:http://www.cnblogs.com/isline/archive/2010/04/06/1705131.html,如需转载请自行联系原作者
redis与memcached redis支持更多的数据结构 redis支持数据持久化 redis支持两种存储方式:snapshot(快照)和aof(append only mode),快照是定时将内存快照持久化到硬盘(crash会丢失数据),aof是在写入数据的同时将操作命令保存到日志(不会丢失数据,但操作日志管理维护成本高) redis单线程,memcached是多线程的 redis修改libevent实现小巧的epoll,memcached完全依赖libevent(性能影响) memcached使用CAS避免资源竞争修改,redis提供了事务功能 CAS是通过为每一个cacke key设置一个隐藏的cas token作为版本号,每次set操作都会检查并更新token 数据类型 string string是key/value的存储结构(同memcached一样),支持的命令: get/set/del incr/incrby decr/descby append/strlen getrange/setrange 问题:计数 一般情况都需要额外使用锁来避免并发写的问题。 解决方法: 使用incr命令来实现原子递增,使用get/set来重置计数状态。 与memcached相同,可作为key/value数据库,例如session共享。 hash hash的value实际上是一个hashmap,可以直接操作value的各个feild的值,支持的命令: hset/hget/hgetall hmset/hmget hlen/hexists hkeys/hvals hash两种实现方式, 当数据较少时,为了节省内存采用线性存储来节省空间 当数据较多时,才会使用hashmap来降低时间复杂度 问题:商品维度计数 商品有各种计数(喜欢、评论、鉴定、浏览等) 解决方法: 使用hash的field字段来存储喜欢、评论等计数值。 同理,用户维度计数(动态、关注、粉丝、喜欢、发帖等)。 该存储结构适用于常用的商品、热门新闻动态等经常被大量访问的数据对象。 list list通过双向链表实现,支持反向查找和遍历,支持的命令: lpush/rpush lpop/rpop lrange/lrem 利用push和pop操作可以实现消息队列,也可以实现关注列表、粉丝/在线好友列表等功能。 问题:显示最新的项目列表 SELECT * FROM foo WHERE ... ORDER BY TIME DESC LIMIT 10; 数据库上的查询语句如上,随着数据的增多,排序会越来越慢。 解决方法: 为每一条数据选择一个唯一的ID(可选自增ID) 缓存最新的N个数据,利用redis做数据缓存(list结构不断LPUSH最新的数据) 当查询内容超过redis缓存内容后,才会穿透缓存访问数据库 消息队列的实现: 用list数据结构作为channel 生产者lpush消息 消费者rpop消息 设定超时时间 上述消息队列与PUB/SUB相比,不会丢失数据,但也没有失败重传的机制(也就是没有消息状态) set set是一个不允许重复的数据组合,支持的命令: sadd/spop sismembers sinter/sunion/sdiff smembers可以用来实现好友列表中判断是否为已存在好友或关注好友,也可以通过集合操作来实现共同好友、共同兴趣、共同关注列表等。 问题:判断微博的共同好友、共同兴趣、是否为关注好友 解决方法: 利用set集合存储用户的好友ID 通过sismembers判断用户是否存在好友集合中 通过sinter来判断两个好友集合的共同好友 通过sunion来获取两个集合的所有好友 通过sdiff来获取两个集合的非共同好友,用于好友推荐 sorted set sorted set是有序的set,通过提供一个优先级score来实现自动排序,支持的命令: zadd/zrem zrange/zcard score可以用来实现权重队列,也可以实现按时间、评分的自动排序列表。 问题:在线游戏实时排行榜 在线游戏的排行榜都需要实时更新操作,不可能去频繁地更改关系型数据库, 解决方法: 为用户设置唯一的ID,采用sorted set来存储用户的得分情况 利用有序集合来对用户得分进行排名,这里不可能存储全部的用户,可能只关心前100个用户,那么就需要在redis之外做限制 问题:新闻排序 新闻排序是按照新闻的关注度(点击率)和时间做排序, score = points / (time^alpha) 上面的公式可以说明,点击率越高,越可能获得更多的评分;时间越久远,也会降低新闻的评分。 因此,需要有一个专门用于计算新闻评分的进程,实时地处理最新的N条数据 解决方法: 拉取最新的N条数据 计算各个数据的评分 添加到有序集合中 Pub/Sub 实现消息队列、实时消息系统。 Transactions redis提供事务的支持, 事务可以一次执行多个命令,多个命令按照顺序执行,不会被其他客户单的命令所打断 事务是原子操作,要么全部执行,要么不执行 使用方法: > MULTI > ... > EXEC 错误处理: 在EXEC之前产生的错误,redis会自动放弃这个事务 在EXEC之后产生的错误,并没有特殊处理 其他应用 实时统计,而全量操作数据记录到log日志,利用Hadoop进行更全面的离线分析 web缓存,减轻数据库压力 本文转自cococo点点博客园博客,原文链接:http://www.cnblogs.com/coder2012/p/5365846.html,如需转载请自行联系原作者
OC是运行时语言,只有在程序运行时,才会去确定对象的类型,并调用类与对象相应的方法。利用runtime机制让我们可以在程序运行时动态修改类、对象中的所有属性、方法,就算是私有方法以及私有属性都是可以动态修改的。本文旨在对runtime的部分特性小试牛刀,更多更全的方法可以参考系统API文件<objc/runtime.h>,demo例子可以参见CSDN的runtime高级编程系列文章。 我们出发吧! 先看一个非常平常的Father类: #import <Foundation/Foundation.h> @interface Father : NSObject @property (nonatomic, assign) int age; @end #import "Father.h" @interface Father () { NSString *_name; } - (void)sayHello; @end @implementation Father - (id)init { if (self = [super init]) { _name = @"wengzilin"; [_name copy]; self.age = 27; } return self; } - (void)dealloc { [_name release]; _name = nil; [super dealloc]; } - (NSString *)description { return [NSString stringWithFormat:@"name:%@, age:%d", _name, self.age]; } - (void)sayHello { NSLog(@"%@ says hello to you!", _name); } - (void)sayGoodbay { NSLog(@"%@ says goodbya to you!", _name); } 如果你没接触过runtime,那当我问你:“Father之外的类能控制的属性有哪些?能控制的方法有哪些?”时,你估计会回答:“我们可以访问age属性,不能访问_name变量;可以访问age的setter/getter方法,其他方法都不行”。这种回答是OK的,因为教科书上以及面向对象的思想告诉我们,事实如此。但是,我会说,有一种方法是APPLE允许的而且可以不受这些规则限制的途径可以做到想访问什么就访问什么、想修改什么就修改什么,那就是本文的主题:RUNTIME! 现在我们简单地将本文的主题分为两部分:(1)控制私有变量 (2)控制私有函数,因为二者所用的runtime差异较大,函数部分会复杂一些 (1)控制变量 想要控制一个类的私有变量,那第一步就要知道这个类到底有哪些隐藏的变量,以及这些隐藏的变量类型是什么。或许你会说:“这不是很显然吗?.h文件都写着呢!”。如果你真这么想就特错特错了,很多正规的写法都是尽量避免在.h文件中出现私有变量,绝大部分都会选择方法.m文件的extension中,extension就是匿名的category。我猜测这也是一种防止hack的措施吧。不管这些变量放在何处,runtime都可以让他们无所遁形!先看代码,看不懂不要紧,后面会有解释: - (void)tryMember { Father *father = [[Father alloc] init]; NSLog(@"before runtime:%@", [father description]); unsigned int count = 0; Ivar *members = class_copyIvarList([Father class], &count); for (int i = 0 ; i < count; i++) { Ivar var = members[i]; const char *memberName = ivar_getName(var); const char *memberType = ivar_getTypeEncoding(var); NSLog(@"%s----%s", memberName, memberType); } } 显示如下: 2015-03-17 16:10:28.003 WZLCodeLibrary[38574:3149577] before runtime:name:wengzilin, age:27 2015-03-17 16:10:28.003 WZLCodeLibrary[38574:3149577] _name----@"NSString" 2015-03-17 16:10:28.003 WZLCodeLibrary[38574:3149577] _age----i 从log中我们知道了,Father类有两个变量,一个公开的包装成属性的age, 类型是int,一个花括号{}内的私有变量_name,类型是NSString。代码中标红色的部分就是runtime.h的api, class_copyIvarList:获取类的所有属性变量,count记录变量的数量IVar是runtime声明的一个宏,是实例变量的意思,instance variable,在runtime中定义为 typedef struct objc_ivar *Ivari var_getName:将IVar变量转化为字符串 ivar_getTypeEncoding:获取IVar的类型 如果我们现在想对_name动手,不经过Father同意偷偷修改它呢?我们继续往下做:(接着上面的代码) Ivar m_name = members[0]; object_setIvar(father, m_name, @"zhanfen"); NSLog(@"after runtime:%@", [father description]); 显示如下: 2015-03-17 16:10:28.004 WZLCodeLibrary[38574:3149577] after runtime:name:zhanfen, age:27 我们发现,_name属性被强制改过来了,有wengzilin改为现在zhanfen。 (2)控制私有函数 对于私有变量,我们能做的顶多修改变量的值,但对于私有函数,我们可以玩非常多的花样,比如:在运行时动态添加新的函数、修改私有函数、交换其中两个私有函数的实现、替换私有函数... 同样地,控制的第一步是获得Father类的所有私有方法,我们可以得到.m文件中所有有显式实现的方法以及属性变量的setter+getter方法都会被找到: - (void)tryMemberFunc { unsigned int count = 0; Method *memberFuncs = class_copyMethodList([Father class], &count);//所有在.m文件显式实现的方法都会被找到 for (int i = 0; i < count; i++) { SEL name = method_getName(memberFuncs[i]); NSString *methodName = [NSString stringWithCString:sel_getName(name) encoding:NSUTF8StringEncoding]; NSLog(@"member method:%@", methodName); } } 显示如下: 2015-03-17 17:02:33.343 WZLCodeLibrary[38748:3170794] member method:setAge: 2015-03-17 17:02:33.343 WZLCodeLibrary[38748:3170794] member method:age 2015-03-17 17:02:33.344 WZLCodeLibrary[38748:3170794] member method:sayHello 2015-03-17 17:02:33.344 WZLCodeLibrary[38748:3170794] member method:sayGoodbay 2015-03-17 17:02:33.344 WZLCodeLibrary[38748:3170794] member method:description 2015-03-17 17:02:33.344 WZLCodeLibrary[38748:3170794] member method:dealloc 2015-03-17 17:02:33.344 WZLCodeLibrary[38748:3170794] member method:init Method:runtime声明的一个宏,表示一个方法,typedef struct objc_method *Method; class_copyMethodList:获取所有方法 method_getName:读取一个Method类型的变量,输出我们在上层中很熟悉的SEL ========= 接下来我们试着添加新的方法试试(这种方法等价于对Father类添加Category对方法进行扩展): - (void)tryAddingFunction { class_addMethod([Father class], @selector(method::), (IMP)myAddingFunction, "i@:i@"); } //具体的实现,即IMP所指向的方法 int myAddingFunction(id self, SEL _cmd, int var1, NSString *str) { NSLog(@"I am added funciton"); return 10; } - (void)tryMemberFunc { //动态添加方法 [self tryAddingFunction]; count = 0; memberFuncs = class_copyMethodList([Father class], &count);//所有在.m文件显式实现的方法都会被找到 for (int i = 0; i < count; i++) { SEL name = method_getName(memberFuncs[i]); NSString *methodName = [NSString stringWithCString:sel_getName(name) encoding:NSUTF8StringEncoding]; NSLog(@"member method:%@", methodName); } //尝试调用新增的方法 Father *father = [[Father alloc] init]; [father method:10 :@"111"];//当你敲入father实例后,是无法获得method的提示的,只能靠手敲。而且编译器会给出"-method" not found的警告,可以忽略 [father release]; } 输出结果: 2015-03-17 17:02:33.345 WZLCodeLibrary[38748:3170794] member method:method:: 2015-03-17 17:02:33.345 WZLCodeLibrary[38748:3170794] member method:setAge: 2015-03-17 17:02:33.345 WZLCodeLibrary[38748:3170794] member method:age 2015-03-17 17:02:33.345 WZLCodeLibrary[38748:3170794] member method:sayHello 2015-03-17 17:02:33.345 WZLCodeLibrary[38748:3170794] member method:sayGoodbay 2015-03-17 17:02:33.345 WZLCodeLibrary[38748:3170794] member method:description 2015-03-17 17:02:33.346 WZLCodeLibrary[38748:3170794] member method:dealloc 2015-03-17 17:02:33.346 WZLCodeLibrary[38748:3170794] member method:init 我们可以看到,method::方法的确被添加进类中了。有童鞋会问,如果在其他类文件中实例化Father类,还能调用到-method方法吗?答案是可以的,我试验过,在MRC下尽管无法获得代码提示,但请坚定不移地敲入[father method:xx :xx]方法!(在ARC下会报no visible @interface 错误) 接下来,我们拿系统函数玩玩,目标是让NSString函数的大小写转换功能对调,让APPLE乱套: - (void)tryMethodExchange { Method method1 = class_getInstanceMethod([NSString class], @selector(lowercaseString)); Method method2 = class_getInstanceMethod([NSString class], @selector(uppercaseString)); method_exchangeImplementations(method1, method2); NSLog(@"lowcase of WENG zilin:%@", [@"WENG zilin" lowercaseString]); NSLog(@"uppercase of WENG zilin:%@", [@"WENG zilin" uppercaseString]); } 输出结果: 2015-03-17 17:20:16.073 WZLCodeLibrary[38861:3180978] lowcase of WENG zilin:WENG ZILIN 2015-03-17 17:20:16.290 WZLCodeLibrary[38861:3180978] uppercase of WENG zilin:weng zilin 本文转自编程小翁博客园博客,原文链接:http://www.cnblogs.com/wengzilin/p/4344952.html,如需转载请自行联系原作者
首先,我想告诉大家,在Unix/Linux下,最有效率技巧的不是操作图形界面,而是命令行操作,因为命令行意味着自动化。如果你看过《你可能不知道的Shell》以及《28个Unix/Linux的命令行神器》你就会知道Linux有多强大,这个强大完全来自于命令行,于是,就算你不知道怎么去做一个环保主义的程序员,至少他们可以让你少熬点夜,从而有利于你的身体健康和性生活。下面是一个有点长的列表,正如作者所说,你并不需要知道所有的这些东西,但是如果你还在很沉重地在使用Linux的话,这些东西都值得你看一看。 (注:如果你想知道下面涉及到的命令的更多的用法,你一定要man一点。对于一些命令,你可能需要先yum或apt-get来安装一下,如果有什么问题,别忘了Google。如果你要Baidu的话,我仅代表这个地球上所有的生物包括微生物甚至细菌病毒和小强BS你到宇宙毁灭) 基础 学习 Bash 。你可以man bash来看看bash的东西,并不复杂也并不长。你用别的shell也行,但是bash是很强大的并且也是系统默认的。(学习zsh或tsch只会让你在很多情况下受到限制) 学习 vim 。在Linux下,基本没有什么可与之竞争的编译辑器(就算你是一个Emacs或Eclipse的重度用户)。你可以看看《简明vim攻略》和 《Vim的冒险游戏》以及《给程序员的Vim速查卡》还有《把Vim变成一个编程的IDE》等等。 了解 ssh。明白不需要口令的用户认证(通过ssh-agent, ssh-add),学会用sshFQ,用scp而不是ftp传文件,等等。你知道吗?scp 远端的时候,你可以按tab键来查看远端的目录和文件(当然,需要无口令的用户认证),这都是bash的功劳。 熟悉bash的作业管理,如: &, Ctrl-Z, Ctrl-C, jobs, fg, bg, kill, 等等。当然,你也要知道Ctrl+\(SIGQUIT)和Ctrl+C (SIGINT)的区别。 简单的文件管理 : ls 和 ls -l (你最好知道 “ls -l” 的每一列的意思), less, head, tail 和 tail -f, ln 和 ln -s (你知道明白hard link和soft link的不同和优缺点), chown, chmod, du (如果你想看看磁盘的大小 du -sk *), df, mount。当然,原作者忘了find命令。 基础的网络管理: ip 或 ifconfig, dig。当然,原作者还忘了如netstat, ping, traceroute, 等 理解正则表达式,还有grep/egrep的各种选项。比如: -o, -A, 和 -B 这些选项是很值得了解的。 学习使用 apt-get 和 yum 来查找和安装软件(前者的经典分发包是Ubuntu,后者的经典分发包是Redhat),我还建议你试着从源码编译安装软件。 日常 在 bash 里,使用 Ctrl-R 而不是上下光标键来查找历史命令。 在 bash里,使用 Ctrl-W 来删除最后一个单词,使用 Ctrl-U 来删除一行。请man bash后查找Readline Key Bindings一节来看看bash的默认热键,比如:Alt-. 把上一次命令的最后一个参数打出来,而Alt-* 则列出你可以输入的命令。 回到上一次的工作目录: cd – (回到home是 cd ~) 使用 xargs。这是一个很强大的命令。你可以使用-L来限定有多少个命令,也可以用-P来指定并行的进程数。如果你不知道你的命令会变成什么样,你可以使用xargs echo来看看会是什么样。当然, -I{} 也很好用。示例: 1 2 3 find . -name \*.py | xargs grep some_function cat hosts | xargs -I{} ssh root@{} hostname pstree -p 可以帮你显示进程树。(读过我的那篇《一个fork的面试题》的人应该都不陌生) 使用 pgrep 和 pkill 来找到或是kill 某个名字的进程。 (-f 选项很有用). 了解可以发给进程的信号。例如:要挂起一个进程,使用 kill -STOP [pid]. 使用 man 7 signal 来查看各种信号,使用kill -l 来查看数字和信号的对应表 使用 nohup 或 disown 如果你要让某个进程运行在后台。 使用netstat -lntp来看看有侦听在网络某端口的进程。当然,也可以使用 lsof。 在bash的脚本中,你可以使用 set -x 来debug输出。使用 set -e 来当有错误发生的时候abort执行。考虑使用 set -o pipefail 来限制错误。还可以使用trap来截获信号(如截获ctrl+c)。 在bash 脚本中,subshells (写在圆括号里的) 是一个很方便的方式来组合一些命令。一个常用的例子是临时地到另一个目录中,例如: 1 2 3 # do something in current dir (cd /some/other/dir; other-command) # continue in original dir 在 bash 中,注意那里有很多的变量展开。如:检查一个变量是否存在: ${name:?error message}。如果一个bash的脚本需要一个参数,也许就是这样一个表达式 input_file=${1:?usage: $0 input_file}。一个计算表达式: i=$(( (i + 1) % 5 ))。一个序列: {1..10}。 截断一个字符串: ${var%suffix} 和 ${var#prefix}。 示例: if var=foo.pdf, then echo ${var%.pdf}.txt prints “foo.txt”. 通过 <(some command) 可以把某命令当成一个文件。示例:比较一个本地文件和远程文件 /etc/hosts: diff /etc/hosts <(ssh somehost cat /etc/hosts) 了解什么叫 “here documents” ,就是诸如 cat <<EOF 这样的东西。 在 bash中,使用重定向到标准输出和标准错误。如: some-command >logfile 2>&1。另外,要确认某命令没有把某个打开了的文件句柄重定向给标准输入,最佳实践是加上 “</dev/null”,把/dev/null重定向到标准输入。 使用 man ascii 来查看 ASCII 表。 在远端的 ssh 会话里,使用 screen 或 dtach 来保存你的会话。(参看《28个Unix/Linux的命令行神器》) 要来debug Web,试试curl 和 curl -I 或是 wget 。我觉得debug Web的利器是firebug,curl和wget是用来抓网页的,呵呵。 把 HTML 转成文本: lynx -dump -stdin 如果你要处理XML,使用 xmlstarlet 对于 Amazon S3, s3cmd 是一个很方便的命令(还有点不成熟) 在 ssh中,知道怎么来使用ssh隧道。通过 -L or -D (还有-R) ,FQ神器。 你还可以对你的ssh 做点优化。比如,.ssh/config 包含着一些配置:避免链接被丢弃,链接新的host时不需要确认,转发认证,以前使用压缩(如果你要使用scp转文件): 1 2 3 4 5 6 TCPKeepAlive=yes ServerAliveInterval=15 ServerAliveCountMax=6 StrictHostKeyChecking=no Compression=yes ForwardAgent=yes 如果你有输了个命令行,但是你改变注意了,但你又不想删除它,因为你要在历史命令中找到它,但你也不想执行它。那么,你可以按下 Alt-# ,于是这个命令关就被加了一个#字符,于是就被注释掉了。 数据处理 了解 sort 和 uniq 命令 (包括 uniq 的 -u 和 -d 选项). 了解用 cut, paste, 和 join 命令来操作文本文件。很多人忘了在cut前使用join。 如果你知道怎么用sort/uniq来做集合交集、并集、差集能很大地促进你的工作效率。假设有两个文本文件a和b已解被 uniq了,那么,用sort/uniq会是最快的方式,无论这两个文件有多大(sort不会被内存所限,你甚至可以使用-T选项,如果你的/tmp目录很小) 1 2 3 4 5 cat a b | sort | uniq > c # c is a union b 并集 cat a b | sort | uniq -d > c # c is a intersect b 交集 cat a b b | sort | uniq -u > c # c is set difference a - b 差集 了解和字符集相关的命令行工具,包括排序和性能。很多的Linux安装程序都会设置LANG 或是其它和字符集相关的环境变量。这些东西可能会让一些命令(如:sort)的执行性能慢N多倍(注:就算是你用UTF-8编码文本文件,你也可以很安全地使用ASCII来对其排序)。如果你想Disable那个i18n 并使用传统的基于byte的排序方法,那就设置export LC_ALL=C (实际上,你可以把其放在 .bashrc)。如果这设置这个变量,你的sort命令很有可能会是错的。 了解 awk 和 sed,并用他们来做一些简单的数据修改操作。例如:求第三列的数字之和: awk ‘{ x += $3 } END { print x }’。这可能会比Python快3倍,并比Python的代码少三倍。 使用 shuf 来打乱一个文件中的行或是选择文件中一个随机的行。 了解sort命令的选项。了解key是什么(-t和-k)。具体说来,你可以使用-k1,1来对第一列排序,-k1来对全行排序。 Stable sort (sort -s) 会很有用。例如:如果你要想对两例排序,先是以第二列,然后再以第一列,那么你可以这样: sort -k1,1 | sort -s -k2,2 我们知道,在bash命令行下,Tab键是用来做目录文件自动完成的事的。但是如果你想输入一个Tab字符(比如:你想在sort -t选项后输入<tab>字符),你可以先按Ctrl-V,然后再按Tab键,就可以输入<tab>字符了。当然,你也可以使用$’\t’。 如果你想查看二进制文件,你可以使用hd命令(在CentOS下是hexdump命令),如果你想编译二进制文件,你可以使用bvi命令(http://bvi.sourceforge.net/ 墙) 另外,对于二进制文件,你可以使用strings(配合grep等)来查看二进制中的文本。 对于文本文件转码,你可以试一下 iconv。或是试试更强的 uconv 命令(这个命令支持更高级的Unicode编码) 如果你要分隔一个大文件,你可以使用split命令(split by size)和csplit命令(split by a pattern)。 系统调试 如果你想知道磁盘、CPU、或网络状态,你可以使用 iostat, netstat, top (或更好的 htop), 还有 dstat 命令。你可以很快地知道你的系统发生了什么事。关于这方面的命令,还有iftop, iotop等(参看《28个Unix/Linux的命令行神器》) 要了解内存的状态,你可以使用free和vmstat命令。具体来说,你需要注意 “cached” 的值,这个值是Linux内核占用的内存。还有free的值。 Java 系统监控有一个小的技巧是,你可以使用kill -3 <pid> 发一个SIGQUIT的信号给JVM,可以把堆栈信息(包括垃圾回收的信息)dump到stderr/logs。 使用 mtr 会比使用 traceroute 要更容易定位一个网络问题。 如果你要找到哪个socket或进程在使用网络带宽,你可以使用 iftop 或 nethogs。 Apache的一个叫 ab 的工具是一个很有用的,用quick-and-dirty的方式来测试网站服务器的性能负载的工作。如果你需要更为复杂的测试,你可以试试 siege。 如果你要抓网络包的话,试试 wireshark 或 tshark。 了解 strace 和 ltrace。这两个命令可以让你查看进程的系统调用,这有助于你分析进程的hang在哪了,怎么crash和failed的。你还可以用其来做性能profile,使用 -c 选项,你可以使用-p选项来attach上任意一个进程。 了解用ldd命令来检查相关的动态链接库。注意:ldd的安全问题 使用gdb来调试一个正在运行的进程或分析core dump文件。参看我写的《GDB中应该知道的几个调试方法》 学会到 /proc 目录中查看信息。这是一个Linux内核运行时记录的整个操作系统的运行统计和信息,比如: /proc/cpuinfo, /proc/xxx/cwd, /proc/xxx/exe, /proc/xxx/fd/, /proc/xxx/smaps. 如果你调试某个东西为什么出错时,sar命令会有用。它可以让你看看 CPU, 内存, 网络, 等的统计信息。 使用 dmesg 来查看一些硬件或驱动程序的信息或问题。 本文转自feisky博客园博客,原文链接:http://www.cnblogs.com/feisky/archive/2013/01/09/2852663.html,如需转载请自行联系原作者
1. 回顾感知机模型 在感知机原理小结中,我们讲到了感知机的分类原理,感知机的模型就是尝试找到一条直线,能够把二元数据隔离开。放到三维空间或者更高维的空间,感知机的模型就是尝试找到一个超平面,能够把所有的二元类别隔离开。对于这个分离的超平面,我们定义为wTx+b=0wTx+b=0,如下图。在超平面wTx+b=0wTx+b=0上方的我们定义为y=1y=1,在超平面wTx+b=0wTx+b=0下方的我们定义为y=−1y=−1。可以看出满足这个条件的超平面并不止一个。那么我们可能会尝试思考,这么多的可以分类的超平面,哪个是最好的呢?或者说哪个是泛化能力最强的呢? 接着我们看感知机模型的损失函数优化,它的思想是让所有误分类的点(定义为M)到超平面的距离和最小,即最小化下式: ∑xi∈M−y(i)(wTx(i)+b)/||w||2∑xi∈M−y(i)(wTx(i)+b)/||w||2 当w和bw和b成比例的增加,比如,当分子的w和bw和b扩大N倍时,分母的L2范数也会扩大N倍。也就是说,分子和分母有固定的倍数关系。那么我们可以固定分子或者分母为1,然后求另一个即分子自己或者分母的倒数的最小化作为损失函数,这样可以简化我们的损失函数。在感知机模型中,我们采用的是保留分子,固定分母||w||2=1||w||2=1,即最终感知机模型的损失函数为: ∑xi∈M−y(i)(wTx(i)+b)∑xi∈M−y(i)(wTx(i)+b) 如果我们不是固定分母,改为固定分子,作为分类模型有没有改进呢? 这些问题在我们引入SVM后会详细解释。 2. 函数间隔与几何间隔 在正式介绍SVM的模型和损失函数之前,我们还需要先了解下函数间隔和几何间隔的知识。 在分离超平面固定为wTx+b=0wTx+b=0的时候,|wTx+b||wTx+b|表示点x到超平面的距离。通过观察wTx+bwTx+b和y是否同号,我们判断分类是否正确,这些知识我们在感知机模型里都有讲到。这里我们引入函数间隔的概念,定义函数间隔γ′γ′为: γ′=y(wTx+b)γ′=y(wTx+b) 可以看到,它就是感知机模型里面的误分类点到超平面距离的分子。对于训练集中m个样本点对应的m个函数间隔的最小值,就是整个训练集的函数间隔。 函数间隔并不能正常反应点到超平面的距离,在感知机模型里我们也提到,当分子成比例的增长时,分母也是成倍增长。为了统一度量,我们需要对法向量ww加上约束条件,这样我们就得到了几何间隔γγ,定义为: γ=y(wTx+b)||w||2=γ′||w||2γ=y(wTx+b)||w||2=γ′||w||2 几何间隔才是点到超平面的真正距离,感知机模型里用到的距离就是几何距离。 3. 支持向量 在感知机模型中,我们可以找到多个可以分类的超平面将数据分开,并且优化时希望所有的点都离超平面远。但是实际上离超平面很远的点已经被正确分类,我们让它离超平面更远并没有意义。反而我们最关心是那些离超平面很近的点,这些点很容易被误分类。如果我们可以让离超平面比较近的点尽可能的远离超平面,那么我们的分类效果会好有一些。SVM的思想起源正起于此。 如下图所示,分离超平面为wTx+b=0wTx+b=0,如果所有的样本不光可以被超平面分开,还和超平面保持一定的函数距离(下图函数距离为1),那么这样的分类超平面是比感知机的分类超平面优的。可以证明,这样的超平面只有一个。和超平面平行的保持一定的函数距离的这两个超平面对应的向量,我们定义为支持向量,如下图虚线所示。 支持向量到超平面的距离为1/||w||21/||w||2,两个支持向量之间的距离为2/||w||22/||w||2。 4. SVM模型目标函数与优化 SVM的模型是让所有点到超平面的距离大于一定的距离,也就是所有的分类点要在各自类别的支持向量两边。用数学式子表示为: maxγ=y(wTx+b)||w||2s.tyi(wTxi+b)=γ′(i)≥γ′(i=1,2,...m)maxγ=y(wTx+b)||w||2s.tyi(wTxi+b)=γ′(i)≥γ′(i=1,2,...m) 一般我们都取函数间隔γ′γ′为1,这样我们的优化函数定义为: max1||w||2s.tyi(wTxi+b)≥1(i=1,2,...m)max1||w||2s.tyi(wTxi+b)≥1(i=1,2,...m) 也就是说,我们要在约束条件yi(wTxi+b)≥1(i=1,2,...m)yi(wTxi+b)≥1(i=1,2,...m)下,最大化1)||w||21)||w||2。可以看出,这个感知机的优化方式不同,感知机是固定分母优化分子,而SVM是固定分子优化分母,同时加上了支持向量的限制。 由于1||w||21||w||2的最大化等同于12||w||2212||w||22的最小化。这样SVM的优化函数等价于: min12||w||22s.tyi(wTxi+b)≥1(i=1,2,...m)min12||w||22s.tyi(wTxi+b)≥1(i=1,2,...m) 由于目标函数12||w||2212||w||22是凸函数,同时约束条件不等式是仿射的,根据凸优化理论,我们可以通过拉格朗日函数将我们的优化目标转化为无约束的优化函数,这和最大熵模型原理小结中讲到了目标函数的优化方法一样。具体的,优化函数转化为: L(w,b,α)=12||w||22−∑i=1mαi[yi(wTxi+b)−1]满足αi≥0L(w,b,α)=12||w||22−∑i=1mαi[yi(wTxi+b)−1]满足αi≥0 由于引入了朗格朗日乘子,我们的优化目标变成: minw,bmaxαi≥0L(w,b,α)min⏟w,bmax⏟αi≥0L(w,b,α) 和最大熵模型一样的,我们的这个优化函数满足KKT条件,也就是说,我们可以通过拉格朗日对偶将我们的优化问题转化为等价的对偶问题来求解。如果对凸优化和拉格朗日对偶不熟悉,建议阅读鲍德的《凸优化》。 也就是说,现在我们要求的是: maxαi≥0minw,bL(w,b,α)max⏟αi≥0min⏟w,bL(w,b,α) 从上式中,我们可以先求优化函数对于w和bw和b的极小值。接着再求拉格朗日乘子αα的极大值。 首先我们来求w和bw和b的极小值,即minw,bL(w,b,α)min⏟w,bL(w,b,α)。这个极值我们可以通过对w和bw和b分别求偏导数得到: ∂L∂w=0⇒w=∑i=1mαiyixi∂L∂w=0⇒w=∑i=1mαiyixi ∂L∂b=0⇒∑i=1mαiyi=0∂L∂b=0⇒∑i=1mαiyi=0 从上两式子可以看出,我们已经求得了w和αw和α的关系,只要我们后面接着能够求出优化函数极大化对应的αα,就可以求出我们的ww了,至于b,由于上两式已经没有b,所以最后的b可以有多个。 好了,既然我们已经求出w和αw和α的关系,就可以带入优化函数L(w,b,α)L(w,b,α)消去ww了。我们定义: ψ(α)=minw,bL(w,b,α)ψ(α)=min⏟w,bL(w,b,α) 现在我们来看将ww替换为αα的表达式以后的优化函数ψ(α)ψ(α)的表达式: ψ(α)=12||w||22−∑i=1mαi[yi(wTxi+b)−1]=12wTw−∑i=1mαiyiwTxi−∑i=1mαiyib+∑i=1mαi=12wT∑i=1mαiyixi−∑i=1mαiyiwTxi−∑i=1mαiyib+∑i=1mαi=12wT∑i=1mαiyixi−wT∑i=1mαiyixi−∑i=1mαiyib+∑i=1mαi=−12wT∑i=1mαiyixi−∑i=1mαiyib+∑i=1mαi=−12wT∑i=1mαiyixi−b∑i=1mαiyi+∑i=1mαi=−12(∑i=1mαiyixi)T(∑i=1mαiyixi)−b∑i=1mαiyi+∑i=1mαi=−12∑i=1mαiyixTi∑i=1mαiyixi−b∑i=1mαiyi+∑i=1mαi=−12∑i=1mαiyixTi∑i=1mαiyixi+∑i=1mαi=−12∑i=1,j=1mαiyixTiαjyjxj+∑i=1mαi=∑i=1mαi−12∑i=1,j=1mαiαjyiyjxTixj(1)(2)(3)(4)(5)(6)(7)(8)(9)(10)(11)(1)ψ(α)=12||w||22−∑i=1mαi[yi(wTxi+b)−1](2)=12wTw−∑i=1mαiyiwTxi−∑i=1mαiyib+∑i=1mαi(3)=12wT∑i=1mαiyixi−∑i=1mαiyiwTxi−∑i=1mαiyib+∑i=1mαi(4)=12wT∑i=1mαiyixi−wT∑i=1mαiyixi−∑i=1mαiyib+∑i=1mαi(5)=−12wT∑i=1mαiyixi−∑i=1mαiyib+∑i=1mαi(6)=−12wT∑i=1mαiyixi−b∑i=1mαiyi+∑i=1mαi(7)=−12(∑i=1mαiyixi)T(∑i=1mαiyixi)−b∑i=1mαiyi+∑i=1mαi(8)=−12∑i=1mαiyixiT∑i=1mαiyixi−b∑i=1mαiyi+∑i=1mαi(9)=−12∑i=1mαiyixiT∑i=1mαiyixi+∑i=1mαi(10)=−12∑i=1,j=1mαiyixiTαjyjxj+∑i=1mαi(11)=∑i=1mαi−12∑i=1,j=1mαiαjyiyjxiTxj 其中,(1)式到(2)式用到了范数的定义||w||22=wTw||w||22=wTw, (2)式到(3)式用到了上面的w=∑i=1mαiyixiw=∑i=1mαiyixi, (3)式到(4)式把和样本无关的wTwT提前,(4)式到(5)式合并了同类项,(5)式到(6)式把和样本无关的bb提前,(6)式到(7)式继续用到w=∑i=1mαiyixiw=∑i=1mαiyixi,(7)式到(8)式用到了向量的转置。由于常量的转置是其本身,所有只有向量xixi被转置,(8)式到(9)式用到了上面的∑i=1mαiyi=0∑i=1mαiyi=0,(9)式到(10)式使用了(a+b+c+…)(a+b+c+…)=aa+ab+ac+ba+bb+bc+…(a+b+c+…)(a+b+c+…)=aa+ab+ac+ba+bb+bc+…的乘法运算法则,(10)式到(11)式仅仅是位置的调整。 从上面可以看出,通过对w,bw,b极小化以后,我们的优化函数ψ(α)ψ(α)仅仅只有αα向量做参数。只要我们能够极大化ψ(α)ψ(α),就可以求出此时对应的αα,进而求出w,bw,b. 对ψ(α)ψ(α)求极大化的数学表达式如下: maxα−12∑i=1m∑j=1mαiαjyiyj(xi∙xj)+∑i=1mαimax⏟α−12∑i=1m∑j=1mαiαjyiyj(xi∙xj)+∑i=1mαi s.t.∑i=1mαiyi=0s.t.∑i=1mαiyi=0 αi≥0i=1,2,...mαi≥0i=1,2,...m 可以去掉负号,即为等价的极小化问题如下: minα12∑i=1m∑j=1mαiαjyiyj(xi∙xj)−∑i=1mαimin⏟α12∑i=1m∑j=1mαiαjyiyj(xi∙xj)−∑i=1mαi s.t.∑i=1mαiyi=0s.t.∑i=1mαiyi=0 αi≥0i=1,2,...mαi≥0i=1,2,...m 只要我们可以求出上式极小化时对应的αα向量就可以求出w和bw和b了。具体怎么极小化上式得到对应的αα,一般需要用到SMO算法,这个算法比较复杂,我们后面会专门来讲。在这里,我们假设通过SMO算法,我们得到了对应的αα的值α∗α∗。 那么我们根据w=∑i=1mαiyixiw=∑i=1mαiyixi,可以求出对应的ww的值 w∗=∑i=1mα∗iyixiw∗=∑i=1mαi∗yixi 求b则稍微麻烦一点。注意到,对于任意支持向量(xx,ys)(xx,ys),都有 ys(wTxs+b)=ys(∑i=1SαiyixTixs+b)=1ys(wTxs+b)=ys(∑i=1SαiyixiTxs+b)=1 假设我们有S个支持向量,则对应我们求出S个b∗b∗,理论上这些b∗b∗都可以作为最终的结果, 但是我们一般采用一种更健壮的办法,即求出所有支持向量所对应的b∗sbs∗,然后将其平均值作为最后的结果。 怎么得到支持向量呢?根据KKT条件中的对偶互补条件α∗i(yi(wTxi+b)−1)=0αi∗(yi(wTxi+b)−1)=0,如果αi>0αi>0则有yi(wTxi+b)=1yi(wTxi+b)=1 即点在支持向量上,否则如果αi=0αi=0则有yi(wTxi+b)≥1yi(wTxi+b)≥1,即样本在支持向量上或者已经被正确分类。 5. 线性可分SVM的算法过程 这里我们对线性可分SVM的算法过程做一个总结。 输入是线性可分的m个样本(x1,y1),(x2,y2),...,(xm,ym),(x1,y1),(x2,y2),...,(xm,ym),,其中x为n维特征向量。y为二元输出,值为1,或者-1. 输出是分离超平面的参数w∗和b∗w∗和b∗和分类决策函数。 算法过程如下: 1)构造约束优化问题 minα12∑i=1m∑j=1mαiαjyiyj(xi∙xj)−∑i=1mαimin⏟α12∑i=1m∑j=1mαiαjyiyj(xi∙xj)−∑i=1mαi s.t.∑i=1mαiyi=0s.t.∑i=1mαiyi=0 αi≥0i=1,2,...mαi≥0i=1,2,...m 2)用SMO算法求出上式最小时对应的αα向量的值α∗α∗向量. 3) 计算w∗=∑i=1mα∗iyixiw∗=∑i=1mαi∗yixi 4) 找出所有的S个支持向量,即满足αs>0对应的样本(xs,ys)αs>0对应的样本(xs,ys),通过 ys(∑i=1SαiyixTixs+b)=1ys(∑i=1SαiyixiTxs+b)=1,计算出每个支持向量(xx,ys)(xx,ys)对应的b∗sbs∗,计算出这些b∗s=ys−∑i=1SαiyixTixsbs∗=ys−∑i=1SαiyixiTxs. 所有的b∗sbs∗对应的平均值即为最终的b∗=1S∑i=1Sb∗sb∗=1S∑i=1Sbs∗ 这样最终的分类超平面为:w∗∙x+b∗=0w∗∙x+b∗=0,最终的分类决策函数为:f(x)=sign(w∗∙x+b∗)f(x)=sign(w∗∙x+b∗) 线性可分SVM的学习方法对于非线性的数据集是没有办法使用的, 有时候不能线性可分的原因是线性数据集里面多了少量的异常点,由于这些异常点导致了数据集不能线性可分, 那么怎么可以处理这些异常点使数据集依然可以用线性可分的思想呢? 我们在下一节的线性SVM的软间隔最大化里继续讲。 本文转自刘建平Pinard博客园博客,原文链接:http://www.cnblogs.com/pinard/p/6097604.html,如需转载请自行联系原作者
neutron-openvswitch-agent代码分析 neutron.plugins.openvswitch.agent.ovs_neutron_agent:main # init ovs first by agent_config: # setup plugin_rpc, state_rpc, msgq consumer, periodically state report # setup br-int, br-tun, bridge_mapping # start sg_agent agent = OVSNeutronAgent(**agent_config) # start to process rpc events # process port up/down and related flow update/local vlan bounding # process security group updates agent.daemon_loop() OVSNeutronAgent的初始化 class OVSNeutronAgent(n_rpc.RpcCallback, sg_rpc.SecurityGroupAgentRpcCallbackMixin, l2population_rpc.L2populationRpcCallBackMixin): '''Implements OVS-based tunneling, VLANs and flat networks. Two local bridges are created: an integration bridge (defaults to 'br-int') and a tunneling bridge (defaults to 'br-tun'). An additional bridge is created for each physical network interface used for VLANs and/or flat networks. All VM VIFs are plugged into the integration bridge. VM VIFs on a given virtual network share a common "local" VLAN (i.e. not propagated externally). The VLAN id of this local VLAN is mapped to the physical networking details realizing that virtual network. For virtual networks realized as GRE tunnels, a Logical Switch (LS) identifier is used to differentiate tenant traffic on inter-HV tunnels. A mesh of tunnels is created to other Hypervisors in the cloud. These tunnels originate and terminate on the tunneling bridge of each hypervisor. Port patching is done to connect local VLANs on the integration bridge to inter-hypervisor tunnels on the tunnel bridge. For each virtual network realized as a VLAN or flat network, a veth or a pair of patch ports is used to connect the local VLAN on the integration bridge with the physical network bridge, with flow rules adding, modifying, or stripping VLAN tags as necessary. ''' # history # 1.0 Initial version # 1.1 Support Security Group RPC RPC_API_VERSION = '1.1' def __init__(self, integ_br, tun_br, local_ip, bridge_mappings, root_helper, polling_interval, tunnel_types=None, veth_mtu=None, l2_population=False, minimize_polling=False, ovsdb_monitor_respawn_interval=( constants.DEFAULT_OVSDBMON_RESPAWN), arp_responder=False, use_veth_interconnection=False): '''Constructor. :param integ_br: name of the integration bridge. :param tun_br: name of the tunnel bridge. :param local_ip: local IP address of this hypervisor. :param bridge_mappings: mappings from physical network name to bridge. :param root_helper: utility to use when running shell cmds. :param polling_interval: interval (secs) to poll DB. :param tunnel_types: A list of tunnel types to enable support for in the agent. If set, will automatically set enable_tunneling to True. :param veth_mtu: MTU size for veth interfaces. :param l2_population: Optional, whether L2 population is turned on :param minimize_polling: Optional, whether to minimize polling by monitoring ovsdb for interface changes. :param ovsdb_monitor_respawn_interval: Optional, when using polling minimization, the number of seconds to wait before respawning the ovsdb monitor. :param arp_responder: Optional, enable local ARP responder if it is supported. :param use_veth_interconnection: use veths instead of patch ports to interconnect the integration bridge to physical bridges. ''' super(OVSNeutronAgent, self).__init__() self.use_veth_interconnection = use_veth_interconnection self.veth_mtu = veth_mtu self.root_helper = root_helper self.available_local_vlans = set(moves.xrange(q_const.MIN_VLAN_TAG, # 1-1094 q_const.MAX_VLAN_TAG)) self.tunnel_types = tunnel_types or [] self.l2_pop = l2_population # TODO(ethuleau): Initially, local ARP responder is be dependent to the # ML2 l2 population mechanism driver. self.arp_responder_enabled = (arp_responder and self._check_arp_responder_support() and self.l2_pop) self.agent_state = { 'binary': 'neutron-openvswitch-agent', 'host': cfg.CONF.host, 'topic': q_const.L2_AGENT_TOPIC, 'configurations': {'bridge_mappings': bridge_mappings, 'tunnel_types': self.tunnel_types, 'tunneling_ip': local_ip, 'l2_population': self.l2_pop, 'arp_responder_enabled': self.arp_responder_enabled}, 'agent_type': q_const.AGENT_TYPE_OVS, 'start_flag': True} # Keep track of int_br's device count for use by _report_state() self.int_br_device_count = 0 self.int_br = ovs_lib.OVSBridge(integ_br, self.root_helper) # Create integration bridge and patch ports, # remove all existing flows, and then witch all traffic using L2 learning # add a canary flow to int_br to track OVS restarts self.setup_integration_br() # Stores port update notifications for processing in main rpc loop self.updated_ports = set() # state_rpc: periodically report state by topic q-plugin # plugin_rpc:communicate with plugin by topic q-agent-notifier # consumer topic id includes: # q-agent-notifier-port_update # q-agent-notifier-network_delete # q-agent-notifier-tunnel_update # q-agent-notifier-security_group_update # q-agent-notifier-l2population_update (需要启动l2populcation) self.setup_rpc() # bridge_mappings = default:br-eth1 self.bridge_mappings = bridge_mappings # Creates physical network bridges and links them to integration bridge using veths. self.setup_physical_bridges(self.bridge_mappings) self.local_vlan_map = {} self.tun_br_ofports = {p_const.TYPE_GRE: {}, p_const.TYPE_VXLAN: {}} self.polling_interval = polling_interval self.minimize_polling = minimize_polling self.ovsdb_monitor_respawn_interval = ovsdb_monitor_respawn_interval if tunnel_types: self.enable_tunneling = True else: self.enable_tunneling = False self.local_ip = local_ip self.tunnel_count = 0 self.vxlan_udp_port = cfg.CONF.AGENT.vxlan_udp_port self.dont_fragment = cfg.CONF.AGENT.dont_fragment self.tun_br = None # Creates tunnel bridge, and links it to the integration bridge # using a patch port. # create default flow tables if self.enable_tunneling: self.setup_tunnel_br(tun_br) # Collect additional bridges to monitor # Setup ancillary bridges - for example br-ex self.ancillary_brs = self.setup_ancillary_bridges(integ_br, tun_br) # Security group agent support # firewall_driver = neutron.agent.linux.iptables_firewall.OVSHybridIptablesFirewallDriver self.sg_agent = OVSSecurityGroupAgent(self.context, self.plugin_rpc, root_helper) # Initialize iteration counter self.iter_num = 0 self.run_daemon_loop = True OVSNeutronAgent初始化完成后启动agent.daemon_loop() def daemon_loop(self): with polling.get_polling_manager( self.minimize_polling, self.root_helper, self.ovsdb_monitor_respawn_interval) as pm: self.rpc_loop(polling_manager=pm) rpc_loop def rpc_loop(self, polling_manager=None): # sync tunnel and check ovs restart if not polling_manager: polling_manager = polling.AlwaysPoll() sync = True ports = set() updated_ports_copy = set() ancillary_ports = set() tunnel_sync = True ovs_restarted = False while self.run_daemon_loop: start = time.time() port_stats = {'regular': {'added': 0, 'updated': 0, 'removed': 0}, 'ancillary': {'added': 0, 'removed': 0}} LOG.debug(_("Agent rpc_loop - iteration:%d started"), self.iter_num) if sync: LOG.info(_("Agent out of sync with plugin!")) ports.clear() ancillary_ports.clear() sync = False polling_manager.force_polling() # check ovs restart by checking exist for the canary flow ovs_restarted = self.check_ovs_restart() if ovs_restarted: # reset br-int, br-tun, and other physical bridges self.setup_integration_br() self.setup_physical_bridges(self.bridge_mappings) if self.enable_tunneling: self.setup_tunnel_br() tunnel_sync = True # Notify the plugin of tunnel IP if self.enable_tunneling and tunnel_sync: LOG.info(_("Agent tunnel out of sync with plugin!")) try: tunnel_sync = self.tunnel_sync() except Exception: LOG.exception(_("Error while synchronizing tunnels")) tunnel_sync = True # check if ports/security_groups updates and perform update if self._agent_has_updates(polling_manager) or ovs_restarted: try: LOG.debug(_("Agent rpc_loop - iteration:%(iter_num)d - " "starting polling. Elapsed:%(elapsed).3f"), {'iter_num': self.iter_num, 'elapsed': time.time() - start}) # Save updated ports dict to perform rollback in # case resync would be needed, and then clear # self.updated_ports. As the greenthread should not yield # between these two statements, this will be thread-safe updated_ports_copy = self.updated_ports self.updated_ports = set() reg_ports = (set() if ovs_restarted else ports) # { # 'current': ['ovs-vsctl', --format=json', '--', '--columns=name,external_ids,ofport','list', 'Interface'] and then filted in get_vif_port_set() # 'updated': check_changed_vlans() returns a set of port ids of the ports concerned by a vlan tag loss, and then updated_ports &= cur_ports # 'added' : cur_ports - registered_ports # 'removed': registered_ports - cur_ports # } port_info = self.scan_ports(reg_ports, updated_ports_copy) LOG.debug(_("Agent rpc_loop - iteration:%(iter_num)d - " "port information retrieved. " "Elapsed:%(elapsed).3f"), {'iter_num': self.iter_num, 'elapsed': time.time() - start}) # Secure and wire/unwire VIFs and update their status # on Neutron server if (self._port_info_has_changes(port_info) or self.sg_agent.firewall_refresh_needed() or ovs_restarted): LOG.debug(_("Starting to process devices in:%s"), port_info) # If treat devices fails - must resync with plugin sync = self.process_network_ports(port_info, ovs_restarted) LOG.debug(_("Agent rpc_loop - iteration:%(iter_num)d -" "ports processed. Elapsed:%(elapsed).3f"), {'iter_num': self.iter_num, 'elapsed': time.time() - start}) port_stats['regular']['added'] = ( len(port_info.get('added', []))) port_stats['regular']['updated'] = ( len(port_info.get('updated', []))) port_stats['regular']['removed'] = ( len(port_info.get('removed', []))) ports = port_info['current'] # Treat ancillary devices if they exist if self.ancillary_brs: port_info = self.update_ancillary_ports( ancillary_ports) LOG.debug(_("Agent rpc_loop - iteration:%(iter_num)d -" "ancillary port info retrieved. " "Elapsed:%(elapsed).3f"), {'iter_num': self.iter_num, 'elapsed': time.time() - start}) if port_info: rc = self.process_ancillary_network_ports( port_info) LOG.debug(_("Agent rpc_loop - iteration:" "%(iter_num)d - ancillary ports " "processed. Elapsed:%(elapsed).3f"), {'iter_num': self.iter_num, 'elapsed': time.time() - start}) ancillary_ports = port_info['current'] port_stats['ancillary']['added'] = ( len(port_info.get('added', []))) port_stats['ancillary']['removed'] = ( len(port_info.get('removed', []))) sync = sync | rc polling_manager.polling_completed() except Exception: LOG.exception(_("Error while processing VIF ports")) # Put the ports back in self.updated_port self.updated_ports |= updated_ports_copy sync = True # sleep till end of polling interval elapsed = (time.time() - start) LOG.debug(_("Agent rpc_loop - iteration:%(iter_num)d " "completed. Processed ports statistics: " "%(port_stats)s. Elapsed:%(elapsed).3f"), {'iter_num': self.iter_num, 'port_stats': port_stats, 'elapsed': elapsed}) if (elapsed < self.polling_interval): time.sleep(self.polling_interval - elapsed) else: LOG.debug(_("Loop iteration exceeded interval " "(%(polling_interval)s vs. %(elapsed)s)!"), {'polling_interval': self.polling_interval, 'elapsed': elapsed}) self.iter_num = self.iter_num + 1 rpc_loop()中最重要的两个函数为tunnel_sync(查询并建立隧道)和process_network_ports(处理port和安全组变更) tunnel_sync 查询并建立隧道 def tunnel_sync(self): resync = False try: for tunnel_type in self.tunnel_types: # tunnel_types = vxlan, gre # query tunnel details from plugin_rpc by local_ip and tunnel_type details = self.plugin_rpc.tunnel_sync(self.context, self.local_ip, tunnel_type) # establish tunnel with all other tunnel_ip if l2_pop disabled # if l2_pop enabled, tunnel sync is processed by agent in q-agent-notifier-l2population_update consumer id if not self.l2_pop: tunnels = details['tunnels'] for tunnel in tunnels: if self.local_ip != tunnel['ip_address']: tunnel_id = tunnel.get('id') # Unlike the OVS plugin, ML2 doesn't return an id # key. So use ip_address to form port name instead. # Port name must be <=15 chars, so use shorter hex. remote_ip = tunnel['ip_address'] remote_ip_hex = self.get_ip_in_hex(remote_ip) if not tunnel_id and not remote_ip_hex: continue tun_name = '%s-%s' % (tunnel_type, tunnel_id or remote_ip_hex) # setup tunnel_port and related flows self.setup_tunnel_port(tun_name, tunnel['ip_address'], tunnel_type) except Exception as e: LOG.debug(_("Unable to sync tunnel IP %(local_ip)s: %(e)s"), {'local_ip': self.local_ip, 'e': e}) resync = True return resync process_network_ports 处理port和安全组变更 def process_network_ports(self, port_info, ovs_restarted): resync_a = False resync_b = False # TODO(salv-orlando): consider a solution for ensuring notifications # are processed exactly in the same order in which they were # received. This is tricky because there are two notification # sources: the neutron server, and the ovs db monitor process # If there is an exception while processing security groups ports # will not be wired anyway, and a resync will be triggered # TODO(salv-orlando): Optimize avoiding applying filters unnecessarily # (eg: when there are no IP address changes) # 通过plugin_rpc到plugin查询安全组,然后通过sg_agent应用安全组 self.sg_agent.setup_port_filters(port_info.get('added', set()), port_info.get('updated', set())) # VIF wiring needs to be performed always for 'new' devices. # For updated ports, re-wiring is not needed in most cases, but needs # to be performed anyway when the admin state of a device is changed. # A device might be both in the 'added' and 'updated' # list at the same time; avoid processing it twice. devices_added_updated = (port_info.get('added', set()) | port_info.get('updated', set())) if devices_added_updated: start = time.time() try: # 添加或更新的ports: 查询port详细信息后通知plugin port up/down,接着再port_bound/port_dead skipped_devices = self.treat_devices_added_or_updated( devices_added_updated, ovs_restarted) LOG.debug(_("process_network_ports - iteration:%(iter_num)d -" "treat_devices_added_or_updated completed. " "Skipped %(num_skipped)d devices of " "%(num_current)d devices currently available. " "Time elapsed: %(elapsed).3f"), {'iter_num': self.iter_num, 'num_skipped': len(skipped_devices), 'num_current': len(port_info['current']), 'elapsed': time.time() - start}) # Update the list of current ports storing only those which # have been actually processed. port_info['current'] = (port_info['current'] - set(skipped_devices)) except DeviceListRetrievalError: # Need to resync as there was an error with server # communication. LOG.exception(_("process_network_ports - iteration:%d - " "failure while retrieving port details " "from server"), self.iter_num) resync_a = True if 'removed' in port_info: start = time.time() # 删除的port:删除安全组,通知plugin port down, 并unbound local_vlan, resync_b = self.treat_devices_removed(port_info['removed']) LOG.debug(_("process_network_ports - iteration:%(iter_num)d -" "treat_devices_removed completed in %(elapsed).3f"), {'iter_num': self.iter_num, 'elapsed': time.time() - start}) # If one of the above operations fails => resync with plugin return (resync_a | resync_b) neutron-ovs-cleanup This service starts on boot and ensures that Networking has full control over the creation and management of tap devices. def main(): """Main method for cleaning up OVS bridges. The utility cleans up the integration bridges used by Neutron. """ conf = setup_conf() conf() config.setup_logging(conf) configuration_bridges = set([conf.ovs_integration_bridge, conf.external_network_bridge]) ovs_bridges = set(ovs_lib.get_bridges(conf.AGENT.root_helper)) available_configuration_bridges = configuration_bridges & ovs_bridges if conf.ovs_all_ports: bridges = ovs_bridges else: bridges = available_configuration_bridges # Collect existing ports created by Neutron on configuration bridges. # After deleting ports from OVS bridges, we cannot determine which # ports were created by Neutron, so port information is collected now. ports = collect_neutron_ports(available_configuration_bridges, conf.AGENT.root_helper) for bridge in bridges: LOG.info(_("Cleaning %s"), bridge) ovs = ovs_lib.OVSBridge(bridge, conf.AGENT.root_helper) ovs.delete_ports(all_ports=conf.ovs_all_ports) # Remove remaining ports created by Neutron (usually veth pair) delete_neutron_ports(ports, conf.AGENT.root_helper) LOG.info(_("OVS cleanup completed successfully")) 最后看下nova与neutron-openvswitch-agent的交互,这张图片来源于GongYongSheng在香港峰会的PPT: 首先boot虚机时,nova-compute发消息给neutron-server请求创建port。之后,在driver里面在br-int上建立 port后,neutron-openvswitch-port循环检测br-int会发现新增端口,对其设定合适的openflow规则以及 localvlan,最后将port状态设置为ACTIVE。 本文转自feisky博客园博客,原文链接:http://www.cnblogs.com/feisky/p/3851479.html,如需转载请自行联系原作者
在实现验证或其他状态保存中,我不喜欢使用Session,因为在我认为经常会丢失数据,而且SessionID也是基于Cookie来保存的。最近几天我做了个多语言的小软件,使用Cookie来保存当前用户选择的Cookie,使用jQuery.Cookie插件来设置,对于这个Cookie得path搞得一塌糊涂。在这里记录一下我对此的理解,问题也能够得到相应的解决。 1.Path 在.NET中Cookie的的保存获取就不需要多说了,百度一下基本很多,而且都可以使用,就是这个Path以前一直没有注意的问题。这个Path描述为虚拟目录,Cookie允许不同目录下的值各自独立。如果没有指定这个Path,默认就是当前虚拟路径,这样就会造成了,设置语言的时候,不同路径下的语言没能够同步解决。其实解决的问题很简单,将Path设置为根目录即可,就是“/”。当然如果在当前目录下设置同样的键的Cookie,将覆盖根目录的Cookie,因为当前目录下的Cookie比较优先。 2.Domain 如果想解决子域名同样适用这个Cookie,那就可以将Domain设置为域名地址,那样相同域名下的二级,三级等域名将共享这个Cookie,当然上面提到的Path也需要设置相同的目录,否则是得不到的。在验证用户保存状态中一般设置为个目录即“/”,这个目录是整站公用的目录,所以在相同站点下的所有目录的这个Cookie值都是可以得到的。 3.Expires 过期时间,这个主要设置过期时间的长短,在jQuery中可以有两种设置方法:如果是数字则表示天数,从建立Cookie算起;如果是时间则表示特定的时间。在.NET中就是时间实例对象,如果需要移除这个Cookie可以将这个时间设置为过去的某一个时间,那样Cookie就会消失。 4.Secure 是否使用安全SSL进行访问,即HTTPS。 以下是jQuery.Cookie的使用文档,基本很简单顺便贴上: 1 /** 2 * Create a cookie with the given name and value and other optional parameters. 3 * 4 * @example $.cookie('the_cookie', 'the_value'); 5 * @desc Set the value of a cookie. 6 * @example $.cookie('the_cookie', 'the_value', { expires: 7, path: '/', domain: 'jquery.com', secure: true }); 7 * @desc Create a cookie with all available options. 8 * @example $.cookie('the_cookie', 'the_value'); 9 * @desc Create a session cookie. 10 * @example $.cookie('the_cookie', null); 11 * @desc Delete a cookie by passing null as value. Keep in mind that you have to use the same path and domain 12 * used when the cookie was set. 13 * 14 * @param String name The name of the cookie. 15 * @param String value The value of the cookie. 16 * @param Object options An object literal containing key/value pairs to provide optional cookie attributes. 17 * @option Number|Date expires Either an integer specifying the expiration date from now on in days or a Date object. 18 * If a negative value is specified (e.g. a date in the past), the cookie will be deleted. 19 * If set to null or omitted, the cookie will be a session cookie and will not be retained 20 * when the the browser exits. 21 * @option String path The value of the path atribute of the cookie (default: path of page that created the cookie). 22 * @option String domain The value of the domain attribute of the cookie (default: domain of page that created the cookie). 23 * @option Boolean secure If true, the secure attribute of the cookie will be set and the cookie transmission will 24 * require a secure protocol (like HTTPS). 25 * @type undefined 26 * 27 * @name $.cookie 28 * @cat Plugins/Cookie 29 * @author Klaus Hartl/klaus.hartl@stilbuero.de 30 */ 31 32 /** 33 * Get the value of a cookie with the given name. 34 * 35 * @example $.cookie('the_cookie'); 36 * @desc Get the value of a cookie. 37 * 38 * @param String name The name of the cookie. 39 * @return The value of the cookie. 40 * @type String 41 * 42 * @name $.cookie 43 * @cat Plugins/Cookie 44 * @author Klaus Hartl/klaus.hartl@stilbuero.de 45 */ 本文转自网魂小兵博客园博客,原文链接:http://www.cnblogs.com/xdotnet/archive/2012/09/26/cookie.html,如需转载请自行联系原作者
java package com.test.arithmetic; import java.util.Arrays; /** * Two point go together, small is left and big is right. * Thus the first meet will separate the array to half small and half big. * Created by Ryan on 2017/3/25/025. */ public class QuickSort { public int count = 0; public int[] sort(int[] source, int low, int high) { if (low >= high) { return source; } int first = low; int last = high; int key = source[first]; while (first < last) { //find the first smaller from the end while (last > first && source[last] >= key) { last--; } if (first < last) { source[first] = source[last]; first++; //The first one has already been sorted, move to the next one. }//else first = last, that is the same one, do not need to swap them //find the first bigger from the start while (first < last && source[first] <= key) { first++; } if (first < last) { source[last] = source[first]; last--; //The last one has already been sorted as a bigger, move to the previous one. } } source[first] = key; //put the key System.out.println("The " + ++count + " sort:"); Arrays.stream(source).forEach(item -> System.out.print(item + ", ")); System.out.println(); sort(source, low, first - 1); sort(source, first + 1, high); return source; } public void quickSort1(int arr[], int low, int high) { int l = low; int h = high; int key = arr[low]; while (l < h) { while (l < h && arr[h] >= key) { h--; } if (l < h) { int temp = arr[h]; arr[h] = arr[l]; arr[l] = temp; l++; } while (l < h && arr[l] <= key) { l++; } if (l < h) { int temp = arr[h]; arr[h] = arr[l]; arr[l] = temp; h--; } } System.out.print("l=" + (l + 1) + "h=" + (h + 1) + "key=" + key + "\n"); if (l > low) quickSort1(arr, low, l - 1); if (h < high) quickSort1(arr, l + 1, high); } public <T> void swap(T[] source, int i, int j) { T temp = source[i]; source[i] = source[j]; source[j] = temp; } /** * 方式2 更高效点的代码: */ public <T extends Comparable<? super T>> T[] quickSort2(T[] targetArr, int start, int end) { int i = start + 1, j = end; T key = targetArr[start]; if (start >= end) return (targetArr); /** * 从i++和j--两个方向搜索不满足条件的值并交换 *条件为:i++方向小于key,j--方向大于key */ while (true) { while (targetArr[j].compareTo(key) > 0) j--; while (targetArr[i].compareTo(key) < 0 && i < j) i++; if (i >= j) break; this.swap(targetArr, i, j); if (targetArr[i] == key) { j--; } else { i++; } } /** * 关键数据放到‘中间’* */ this.swap(targetArr, start, j); if (start < i - 1) { this.quickSort2(targetArr, start, i - 1); } if (j + 1 < end) { this.quickSort2(targetArr, j + 1, end); } return targetArr; } /** * 方式3:减少交换次数,提高效率 */ public <T extends Comparable<? super T>> void quickSort3(T[] targetArr, int start, int end) { int i = start, j = end; T key = targetArr[start]; while (i < j) { /*按j--方向遍历目标数组,直到比key小的值为止*/ while (j > i && targetArr[j].compareTo(key) >= 0) { j--; } if (i < j) { /*targetArr[i]已经保存在key中,可将后面的数填入*/ targetArr[i] = targetArr[j]; i++; } /*按i++方向遍历目标数组,直到比key大的值为止*/ /*此处一定要小于等于零,假设数组之内有一亿个1,0交替出现的话,而key的值又恰巧是1的话,那么这个小于等于的作用就会使下面的if语句少执行一亿次。*/ while (i < j && targetArr[i].compareTo(key) <= 0) { i++; } if (i < j) { /*targetArr[j]已保存在targetArr[i]中,可将前面的值填入*/ targetArr[j] = targetArr[i]; j--; } /*此时i==j*/ targetArr[i] = key; /*递归调用,把key前面的完成排序*/ this.quickSort3(targetArr, start, i - 1); /*递归调用,把key后面的完成排序*/ this.quickSort3(targetArr, j + 1, end); } } } Test: package com.test.arithmetic; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import java.util.Arrays; import static org.junit.Assert.*; /** * Created by Ryan on 2017/3/25/025. */ public class QuickSortTest { private int[] source; private Integer[] source2; private Integer[] source3; private QuickSort quick; private int[] expect; private Integer[] expect2; private Integer[] expect3; @Before public void setUp(){ source = new int[]{13, 16, 1, 1, 5, 15, 13, 15, 13, 8}; source2 = new Integer[]{13, 16, 1, 1, 5, 15, 13, 15, 13, 8}; source3 = new Integer[]{13, 16, 1, 1, 5, 15, 13, 15, 13, 8}; expect = new int[]{1, 1, 5, 8, 13, 13, 13, 15, 15, 16}; expect2 = new Integer[]{1, 1, 5, 8, 13, 13, 13, 15, 15, 16}; expect3 = new Integer[]{1, 1, 5, 8, 13, 13, 13, 15, 15, 16}; quick = new QuickSort(); } @Test public void testSort() throws Exception { int[] source = new int[]{13,16,1,1,5,15,13,15,13,8 }; int[] sort = quick.sort(source, 0, source.length - 1); Assert.assertArrayEquals(expect, sort); } @Test public void testSort1(){ QuickSort quick = new QuickSort(); quick.quickSort1(source, 0, source.length - 1); Assert.assertArrayEquals(expect, source); } @Test public void testSort2(){ QuickSort quick = new QuickSort(); quick.quickSort2(source2, 0, source2.length - 1); Assert.assertArrayEquals(expect2, source2); } @Test public void testSort3(){ QuickSort quick = new QuickSort(); quick.quickSort3(source3, 0, source.length - 1); Assert.assertArrayEquals(expect3, source3); } } Result: l=5h=5key=13 l=4h=4key=8 l=1h=1key=1 l=3h=3key=5 l=2h=2key=1 l=9h=9key=15 l=6h=6key=13 l=7h=7key=13 l=8h=8key=15 l=10h=10key=16 The 1 sort: 8, 5, 1, 1, 13, 15, 13, 15, 13, 16, The 2 sort: 1, 5, 1, 8, 13, 15, 13, 15, 13, 16, The 3 sort: 1, 5, 1, 8, 13, 15, 13, 15, 13, 16, The 4 sort: 1, 1, 5, 8, 13, 15, 13, 15, 13, 16, The 5 sort: 1, 1, 5, 8, 13, 13, 13, 15, 15, 16, The 6 sort: 1, 1, 5, 8, 13, 13, 13, 15, 15, 16, The 7 sort: 1, 1, 5, 8, 13, 13, 13, 15, 15, 16, 本文转自Ryan.Miao博客园博客,原文链接:http://www.cnblogs.com/woshimrf/p/6614144.html,如需转载请自行联系原作者
1.开始->Microsoft SQL Server 2008->导入和导出数据(32 位) 2.选择数据源,数据源里选择Microsoft Access,浏览选mdb文件,下一步。 3.选择目标,数据库点新建,名称自定,下一步。 4.复制一个或多个表或视图的数据,下一步。 5.选择源表和源视图,全勾选,选第一个表,点编辑映射; 出现列映射框,点击编辑SQL, 弹出SQL语句编辑框 在[ID] int NOT NULL,中间加入IDENTITY(1,1),后显示为:[ID] int IDENTITY(1,1) NOT NULL, 确定,确定,回到选择源表和源视图框,继续修改其它表,完成后,下一步。 6.立即运行,下一步。 7.完成。 最后用SQL Server Management Studio连接数据库查看刚转换的数据中的表,ID列。 标识为True,标识种子为1,标识增量为1, 以后添加数据就跟Access里的自动编号效果一样了。 补充:关于主键设置,可以第5步:[ID] int IDENTITY(1,1) NOT NULL, 里插入:Primary key,语句为: [ID] int Primary key IDENTITY(1,1) NOT NULL, 最后,如果要复制来的id数据不重置(转换后id重新从1来编号),可以勾选第5步列表映射框里:启用标识插入。 ====ACCESS转SQLSERVER后代码需要修改的语句===== 1,对于日期字段字段 access表示为:#1981-28-12# SQLSERVER2000表示为:‘‘1981-02-12‘‘ 2,SQL语句区别,select ,update 在对单表操作时都差不多, 但多表操作时update语句的区别ACCESS与SQLSERVER中的Update语句对比: SQLSERVER中更新多表的Update语句: Update Tab1 SET a.Name = b.Name FROM Tab1 a,Tab2 b Where a.ID = b.ID; 同样功能的SQL语句在ACCESS中应该是 Update Tab1 a,Tab2 b SET a.Name = b.Name Where a.ID = b.ID; 即:ACCESS中的Update语句没有FROM子句,所有引用的表都列在Update关键字后. 更新单表时:都为: Update table1 set ab=‘12‘,cd=444 where .... 3,delete语句 access中删除时用:delete * from table1 where a>2 即只要把select 语句里的select 换成delete就可以了。 sqlserve 中则为: delete from table1 where a>2 即没有*号 4,as 后面的计算字段区别 access中可以这样:select a,sum(num) as kc_num,kc_num*num as all_kc_num 即可以把AS后的字段当作一个数据库字段参与计算。 sqlserver 中则为:select a,sum(num) as kc_num,sum(num)*num as all_kc_num 即不可以把AS后的字段当作一个数据库字段参与计算。 5,[.]与[!]的区别 access中多表联合查询时:select tab1!a as tab1a,tab2!b tab2b from tab1,tab2 ,中间的AS可以不要。 sqlserve 中则:select tab1.a as tab1a,tab2.b tab2b from tab1,tab2 ,中间的AS可以不要。 6,联合查询时, access中多表联合查询:‘select a,b from( select a,b from tab1 where a>3 union select c,d from tab2 ) group by a,b sqlserve 中则‘select a,b from( select a,b from tab1 where a>3 union select c,d from tab2 ) tmptable group by a,b即要加一个虚的表tmptable,表名任意。--- 7,access升级到sqlserver时, 可以用sqlserver的数据导入工具导入数据,但要做必要的处理。 access中的自动编号,不会自动转换SQL中的自动编号,只能转换为int型,要把它手工改成标识字段,种子为1,把所有导入被sqlserver转化成的以n开头的字段类型的n去掉,如nvarchar->varchar.把需要有秒类型的日期字段改成datatime类型(SQL会把所有的日期开转化成smalldatetime型) 8,true与1=1 access用where true表示条件为真, sqlserver用where 1=1表示条件为真 9,判断字段值为空的区别 普通空: Access和sql server一样 where code is null 或 where code is nol null 条件空: Access:iif([num] is null,0,[num]) 或 iif([num] is null,[num1],[num]) SQLServer: isnull([num],0) 或 isnull([num],[num1]) 10,SQL语句取子串的区别 access:MID(字段,n1,[n2]),LEFT(字段,n),RIGHT(字段,n) 如:select left(cs1,4)+‘-‘+cs2 as cs3 SQLServer: SUBSTRING(expression, start, length) 如:select substring(cs1, 1, 2) + substring(cs1, 4, 2) + ‘-‘ + cs2 as cs3 补充: ACCESS与SQL2000的SQL语句有区别的 比如now()在SQL2000中必须改为getdate() 还有关键词必须加[] ,像ACCESS中字段名用name SQL20000必须加[name] 否则出错 数据库连接字重新配置 1. access 转sql 数据库后需要建立各表关键字以及递增量设置部分数据类型需要重新定义 2. now() 函数是可接受的,但在日期比较过程中需要用 getdate() 3. 保留字需要加 [] 4. 单双引号需要转变 5. 遵循标准sql定义(最关键的一条) 看看MSSQLServer联机丛书。 1.ACCESS的数据库中的自动编号类型在转化时,sql server并没有将它设为自动编号型,我们需在SQL创建语句中加上identity,表示自动编号! 2.转化时,跟日期有关的字段,SQL SERVER默认为smalldatetime型,我们最好将它变为datetime型,因为datetime型的范围比smalldatetime型大。我遇见这种情况,用smalldatetime型时,转化失败,而用datetime型时,转化成功。 3.对此两种数据库进行操作的sql语句不全相同,例如:在对ACCESS数据库进行删除纪录时用:"delete * from user where id=10",而对SQL SERVER数据库进行删除是用:"delete user where id=10". 4.日期函数不相同,在对ACCESS数据库处理中,可用date()、time()等函数,但对 SQL SERVER数据库处理中,只能用datediff,dateadd等函数,而不能用date()、time()等函数。 5.在对ACCESS数据库处理中,sql语句中直接可以用一些VB的函数,像cstr()函数,而对SQL SERVER数据库处理中,却不能用。 本文转自寒意博客园博客,原文链接:http://www.cnblogs.com/hnyei/archive/2012/05/29/2523728.html,如需转载请自行联系原作者
现在很多网站不仅是介绍,更多的是有一些功能,怎么样让客户快速的知道网站有哪些功能呢?这里pagewalkthrough.js插件能帮我们实现,它是一个轻量级的jQuery插件,它可以帮助我们创建一个遮罩引导层,实现页面功能引导功能,引导完成显示页面内容。 下载源码 html代码: <div id="walkthrough-content"> <div id="walkthrough-1"> <h3>欢迎来到网页引导示例DEMO演示页</h3> <p>页面功能介绍引导页的效果是通过一款叫做<a href="https://github.com/jwarby/jquery-pagewalkthrough" target="_blank">pagewalkthrough.js</a>的jQuery插件实现的。</p> <p>点击下一步了解更多...</p> </div> <div id="walkthrough-2"> 这里是网站LOGO,点击这里可以直通网站首页。 </div> <div id="walkthrough-3"> 点击这里可以直接看插件的使用教程。 </div> <div id="walkthrough-4"> 点击这里去下载源码,免费的哦。。 </div> <div id="walkthrough-5"> 这是页脚和版权信息。 </div> </div> js文件和代码: <script type="text/javascript" src="jquery.min.js"></script> <script type="text/javascript" src="jquery.pagewalkthrough.min.js"></script> <script> $(function() { // Set up tour $('body').pagewalkthrough({ name: 'introduction', steps: [ { popup: {content: '#walkthrough-1',type: 'modal' } }, {wrapper: '#logo',popup: {content: '#walkthrough-2',type: 'tooltip',position: 'bottom'} }, {wrapper: 'h2.top_title a',popup: {content: '#walkthrough-3',type: 'tooltip',position: 'bottom'} }, {wrapper: 'a[href="http://files.cnblogs.com/files/hnyei/jq_zhezhaoyd.rar"]', popup: {content: '#walkthrough-4',type: 'tooltip',position: 'right'} }, {wrapper: '#footer p',popup: {content: '#walkthrough-5',type: 'tooltip',position: 'top'} }] }); // Show the tour $('body').pagewalkthrough('show'); }); </script> css文件: <link rel="stylesheet" href="css/jquery.pagewalkthrough.css"> <!--[if lt IE 9]> <script src="html5.js"></script> <![endif]--> <style type="text/css"> .demo{text-align:center} .button {display: inline-block;outline: none;cursor: pointer;text-align: center;text-decoration: none;font: 22px/100% 'Microsoft yahei',Arial, Helvetica, sans-serif;padding: .5em 2em .55em;text-shadow: 0 1px 1px rgba(0,0,0,.3);-webkit-border-radius: .5em; -moz-border-radius: .5em;border-radius: .5em;-webkit-box-shadow: 0 1px 2px rgba(0,0,0,.2);-moz-box-shadow: 0 1px 2px rgba(0,0,0,.2);box-shadow: 0 1px 2px rgba(0,0,0,.2); } .button:hover {text-decoration: none;} .button:active {position: relative;top: 1px;} /* green */ .green {color: #e8f0de;border: solid 1px #538312;background: #64991e;background: -webkit-gradient(linear, left top, left bottom, from(#7db72f), to(#4e7d0e));background: -moz-linear-gradient(top, #7db72f, #4e7d0e);filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#7db72f', endColorstr='#4e7d0e');} .green:hover {background: #538018;background: -webkit-gradient(linear, left top, left bottom, from(#6b9d28), to(#436b0c));background: -moz-linear-gradient(top, #6b9d28, #436b0c);filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#6b9d28', endColorstr='#436b0c');color:#fff} .green:active {color: #a9c08c;background: -webkit-gradient(linear, left top, left bottom, from(#4e7d0e), to(#7db72f));background: -moz-linear-gradient(top, #4e7d0e, #7db72f);filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#4e7d0e', endColorstr='#7db72f');} #walkthrough-content{display:none} #walkthrough-content h3{height:30px; line-height:30px} #walkthrough-content p{line-height:28px} </style> 本文转自寒意博客园博客,原文链接:http://www.cnblogs.com/hnyei/p/4606887.html,如需转载请自行联系原作者
每一个前端开发人员很清楚的重要性和功能的JavaScript库提供。它提供了一个简单的接口,用于构建快速动态的接口,而无需大量的代码。 谢谢你的超级从事jQuery开发者社区,人始终是创造新的和令人惊叹的东西,那里是吨伟大的jQuery插件和JavaScript库在那里,插件和图书馆,涵盖了各种各样的功能。 在这篇文章中我们已经编译列表的JavaScript库为2015年7月,在这篇综述中,我们已经覆盖特征丰富和互动的JavaScript库,为您提供多种功能,能为你建立有效和有用的Web应用提供多种用途。请随时与我们分享您的意见,通过张贴评论如下。享受!! 1. WebCola WebCola 是一个开源的JavaScript库,安排你的HTML5文档和图表使用基于约束的优化技术。它与图书馆一样,d3.js和svg.js。 2. Exportify Exportify 是一个JavaScript库导出你的Spotify播放列表使用Web API。这个应用程序提供一个简单的界面,使用Spotify的Web API做的。 3. ExpandJS ExpandJS 是一个80 + 350 +自定义元素和实用功能来启动你的Web应用程序模块库。元素是建立在聚合物和跟随谷歌的材料设计指南,让美丽的响应接口。本图书馆是lodash扩展,添加更多的功能,并引入意想不到的参数检查。它是在bsd-3-clause许可证发布。 4. xType.js xtype.js 是一个优雅的,JavaScript的高效数据验证。它提供了简洁,performant,可读性强,数据类型验证JavaScript,使用近40高效,数据验证伪类型和统一的最基本但常见数据类型验证JavaScript应用程序,为单,简洁,高度优化操作。 5. anyToJson anyToJSON 是一个JavaScript库,将数据从任何地方并将其转换为JSON。 6. Paulzi Form Paulzi Form 是一个JavaScript表单辅助库,它与AJAX等形式发送,特征数的形式提醒,改变状态的提交按钮,HTML5表单属性polyfill和变化状况提交按钮。 7. Intence Intence 是一个新的用户体验的概念和JavaScript库反映滚动状态的一个简单而直观的方式。它集锦的滚动区根据滚动的量,从而有效地解释给用户究竟可以滚动,有很多滚动。强烈建议作为一个滚动区域统称滚动条替换。 8. Dyanmic.js Dynamics.js 是一个JavaScript库来创建物理为基础的动画。只是有dynamics.js进你的页面,然后您可以将任何DOM元素的CSS属性。你也可以将SVG特性。dynamics.js自有setTimeout。原因是,requestanimationframe和setTimeout有不同的行为。和任何JavaScript对象。它兼容Safari 7 + 35 +测试,火狐,Chrome 34 +,IE10 +。 9. Selectivity.js Selectivity.js 是一个模块化的、轻量级的jQuery和zepto.js选择图书馆。selectivity.js小于可比选择图书馆,即使你';重新使用全建立的所有功能包装。selectivity.js可以用于与jQuery和zepto.js和兼容jQuery构建不合适。选择性的模块化方法的目的是使它更容易创建自定义构建的功能,你只关心和容易地交换模块定制的。 10. Countdown.js Countdown.js 是一个简单的JavaScript API用于产生一个准确的日期,两个实例之间的时间跨度直观的描述。 11. Animate Plus Animate Plus 是一种高性能的JavaScript库,可以帮助你的CSS和SVG动画属性属性。动画加上非常适合于快速的用户界面的相互作用,以及更长的动画序列在桌面和移动。 12. X-editables X-editable 是一个库,允许你在你的网页上创建可编辑元素。它可以用来与引导,jQuery UI,jQuery只。它既包括弹出式和内嵌模式。 13. ToProgress ToProgress 是一个轻量级的、无依赖性的顶级进度条库。 14. CamanJS CamanJS 是(CA)评分(men)操控JavaScript。这是一个简单的使用界面,先进的和有效的图像/画布编辑技术的组合。camanjs是新的过滤器和插件扩展非常方便,它配备了一个广泛的图像编辑功能,并继续增长。这完全是图书馆独立作品无论是在Nodejs和浏览器。两camanjs和这个网站是完全开源的,接受的请求。 15. Trine Trine 是现代JavaScript可以让你挖掘到函数式编程的全部潜力的JS工具库。它的设计是模块化的从地面上。 16. KinWin.js KinWin.js 是一个简约的JavaScript DOM操作库。它的简单和易于使用。 本文转自寒意博客园博客,原文链接:http://www.cnblogs.com/hnyei/p/4634797.html,如需转载请自行联系原作者
如今,无数的框架出现在定期而少数人喜欢自助,Foundation和angular.js主宰了整个世界的发展。 CSS代表用于描述HTML(或XML)文档表示的样式表语言。一个框架被定义为一个包,它由一组结构化的文件和标准化代码(HTML、CSS、js文档等)组成,用于支持网站的开发,以此作为开始创建或建立站点的基础。 在本文中,我们将展示2017的一些最好的CSS框架,这些框架在我们的关注中是最好的,并且被几个开发人员使用,并且有很多功能可以帮助开发相关的任务。 让我们知道哪一个是你最喜欢的,为什么张贴评论如下。 1. Spectre spectre.css是一个轻量级的响应和现代更快和可扩展的开发CSS框架。幽灵提供的印刷和元素的基本风格,flexbox基于响应式布局系统,CSS组件和最佳实践编码一致的设计语言工具。在您的项目中,有4种方法可以开始使用CSS CSS框架。你可以手动安装或使用NPM,Yarn和Bower。 2. Mini.css mini.css旨在提供尽可能多的功能尽可能超过7KB gzip压缩。这个非常小的脚印意味着你的网站和Web应用程序将加载得更快,同时利用它提供的现代组件仍然很好。mini.css为你提供了一个可定制的风味体系,让你成为一个设计师的所有努力。这样你就可以创建你自己的定制设计和个性化你的网站任何你想要的方式! 3. Griddy CSS网格是下一件大事,它将改变我们如何安排布局和使用网格框架来构建网站。然而,考虑到复杂性,使用CSS网格对某些用户来说可能是压倒性的。在这种情况下,这个方便的Web应用,Griddy,可以学习CSS网格是非常有帮助的。 4. Waffle Grid 它是CSS网格框架的建立与flexbox。这里的CSS网格系统是超轻,重量在只有7KB缩小使用默认设置。使用中小型设备的类,Waffle Grid可以使您的布局完全响应。Waffle Grid系统完全是在萨斯写的,所以你可以很容易地改变或自定义网格以适合你的需要。 5. Luxbar luxbar是CSS框架,可以很容易地创建一个响应式导航;只需添加样式和添加HTML正确。你可以通过npm,凉亭安装这个库,或链接的样式表直接从CDN。 6. Bulma 布玛是一个基于flexbox现代CSS框架。它允许您使用框架中所有最新的速度,同时仍然使用CSS中最新的布局工具。响应,模块化,免费的,它的功能强大,重量轻。 7. CuteStrap 时髦的,自用的CSS框架。一个小小的替代引导。所有元素和组件都与基线网格完全对齐。轻松定制默认变量,只包含您需要的文件。文件是生成使用KSS,制作一个简单的cutestrap发射点你的风格。 8. Wing Wing 是一个最小的CSS框架,Wing是为那些较小的侧面项目,因为它没有充分的功能,如引导,基础等机翼样式基本元素,有一个网格,和一些褪色的动画。Wing使它变得非常简单,使网站响应和移动友好。网格,容器,排版,按钮……一切!它都是移动友好和易于使用。只是~ 4kb缩小。使超快速加载和使用。你不需要编译任何文件,没有什么复杂的。你所做的只是链接它,然后你就完成了。因为Wing的轻量级文件大小。 9. Miligram Milligram为快速清洁的起点提供了最少的样式设置。只是它!只有2KB gzip压缩!这不是一个UI框架。它是专为更好的性能和更高的生产力而设计的更少的属性重置,从而产生更干净的代码。希望你喜欢! 10. BlazeCSS blazecss是一个模块化的CSS框架建筑结构良好的网站迅速。这是在整体框架的引导和microframeworks像Milligram。你可以使用火焰的任何部分隔离,使你的有效载荷小而集中。火焰是一个移动的第一方法建立和它是完全响应,它可能9kb但其完整的和易于使用的。 11. Pavilion Pavilion 是一个简单的,unopinionated CSS框架的建立为前端Web开发者倾向于不使用预数以百计的UI组件框架和一些船。为前端开发人员建立的,他们倾向于创造性,探索和创建UI组件本身,而依赖于坚实的基础和未来的验证能力 12. RichCSS RichCSS是一个面向对象的CSS框架提供默认结构的可重用的CSS元素。它基于一种架构风格,它以一种允许您的CSS的可重用性、版本控制、CSS插件的使用和使用的方便性的方式组织代码。RichCSS把会议的组织,任何人都可以理解和使用CSS。 13. Vital Vital是现代Web应用程序的一种微创CSS框架。这只是6kb的压缩包,没有JavaScript和庞大的设备支持。Vital是建立在少即是多的原则之上的。用更少的代码工作意味着压缩的bug更少,阅读的文档更少。减少编码时间意味着有更多的时间专注于下一个杀手级功能。. 14. Scooter 组合框架的构建提供基础的样式,CSS组件,和Dropbox快速静态原型。 15. Kube Kube是一个Web框架的专业开发者和设计者一样。Kube是建造和设计提供最灵活而强大的CSS框架的社区。与许多其他Web框架,Kube是伟大的超快速启动和严重的前沿,面向未来网络发展。 Kube是一个线框,为你的项目框架,在某种程度上。它不强加设计风格或代码决策;它需要流畅性和熟练使用最新的HTML和CSS技术。 本文转自寒意博客园博客,原文链接:http://www.cnblogs.com/hnyei/p/7246527.html,如需转载请自行联系原作者
对上次的三个问题的个人理解: 1) 程序首先是从main函数开始执行的,假设main 函数不是 static ,就要先实例化这个类,然后调用 main 方法,这似乎是不现实的. 其次 用 static 修饰的 main 方法是存储在静态的存贮区当中的,也就是说在创建一个类之后,main 函数就已经存在了,去掉 static 修饰之后,编译可以通过,但是不能执行。 2)查 API之后才发现BufferedRead 对象的 readLine()方读到的数据是,读到有换行的地方为一行,直接用 readLine 判断的时候已经读入一行了,在输出数据时就会隔行输出。 FileReader file=new FileReader("C:\\123.txt"); BufferedReader br1=new BufferedReader(file); //判断的时候已经读入一行 while((br1.readLine())!=null) { //输出的是第二行的内容 System.out.println(br1.readLine()); } 所以用一个临时的字符串变量来存储读到的数据,程序改改这样就可以了: FileReader file=new FileReader("C:\\123.txt"); BufferedReader br1=new BufferedReader(file); String cd; while((cd=br1.readLine())!=null) { System.out.println(cd); } 3)如果将客户端、输入流、输出流的初始化全部放进 Send 按钮的事件当中时,程序会达到我想要的效果,点击连接之后就会有客户端连接上去,但总觉得这样会有其他的安全隐患,总有一天它会暴漏的。 今天要记录在这里的是老师随堂布置的一个小程序,实现一个计算器的雏形,里面只有加减运算,对其中的按钮注册有了一点新的认识,还是将代码贴出来先。 import javax.swing.*; import java.awt.*; import java.awt.event.*; public class ComboBoxTest extends JFrame{ private JButton done =new JButton(" Done "); private JButton clear=new JButton(" Clear "); private JLabel label = new JLabel("Please choose serverID:0(+)and1(-)"); public ComboBoxTest(){ //添加一个组合框并设置两个选项 final JComboBox c = new JComboBox(); int [] array = {0,1}; c.addItem(array[0]); c.addItem(array[1]); final JTextField operand1=new JTextField(10); //添加第一个操作数为输入文本框,占8列 final JLabel t=new JLabel("+"); //初始化中间的操作符为“+”号 final JTextField operand2=new JTextField(10); //第二个操作符 final JTextField result=new JTextField(4); //结果的文本域 ,初始化占4列 //给组合框c注册一个事件,当组合框选项发生变化的时候,触发的相应事件 c.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if(c.getSelectedIndex()==0) //选项为“0”的时候 令中间的操作符显示“+”号 t.setText(" + "); else t.setText(" - "); } }); //给按钮Done注册一个事件,当中间的操作符不同时进行不同的操作 done.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e) { if(c.getSelectedIndex()==0) { //当中间的操作符为“+”号时,进行两个操作数的加法 ,文本域的get()方法返回的是字符串,进行强制转换 int a=Integer.parseInt(operand1.getText())+Integer.parseInt(operand2.getText()); result.setText("="+" "+a+" "); //设置结果显示相应的结果 } else { //当中间的操作符为“-”号的时候,进行两个操作数的减法 int a=Integer.parseInt(operand1.getText())-Integer.parseInt(operand2.getText()); result.setText("="+" "+a+" "); } } }); // 给按钮clear注册一个事件,清空两个操作数和结果的内容 clear.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e) { operand1.setText(""); //清空操作数1 operand2.setText(""); //清空操作数2 result.setText(""); //清空结果框 } }); setLayout(new FlowLayout()); add(label); add(c); add(operand1); add(t); add(operand2); add(result); add(done); add(clear); setSize(350,140); setVisible(true); } public static void main(String[] args) { new ComboBoxTest(); } } 上面的代码中给选项框、“done”、"clear"按钮注册事件的时候所用的都是匿名类,这个类的创建就是为了给相应的组件添加事件,还可以这样写,用里面的“clear”这个按钮来做个例子。 实现 ActionListener 抽象类当中的唯一的一个接口函数,为此定义一个 ButtonListener 监听器的对象 class ButtonListener implements ActionListener{ public void actionPerformed(ActionEvent e){ operand1.setText(""); //清空操作数1 operand2.setText(""); //清空操作数2 result.setText(""); //清空结果框 } } 类属性当中需要定义一个 ButtonListener 的对象属性: private ButtonListener clearaction = new ButtonListener(); 最后一个步骤就是将这个按钮监听器的事件对象注册给按钮: clear.addActionListener(clearaction); 个人总结: 这一种注册事件的方式大致过程是这样的 ButtonListener =》 ActionListener => 注册给按钮,和匿名类相比,缺点是代码量有点多,但假设你有N个打算具备这种 功能的按钮并且事件实现的方法比较复杂时,就可以实现一个 ActionListener 的对象,同时定义N个 ButtonListener 监听器对象,将相同的事件实现注册给按钮就可以了,相比之下匿名类在这种情形下面会有很大的工作量,代码量会激增。 还可以通过事件 e.getSource()方法将所有的事件处理放进一个函数当中,这样似乎维护起来要更方便一点,在类的声明当中要强调实现接口中的抽象函数。 public class ComboBoxTest extends JFrame implements ActionListener 具体的实现过程如下: public void actionPerformed(ActionEvent e){ if(e.getSource()==c){ if(c.getSelectedIndex()==0) //选项为“0”的时候 令中间的操作符显示“+”号 t.setText(" + "); else t.setText(" - "); } if(e.getSource()==done){ if(c.getSelectedIndex()==0) { //当中间的操作符为“+”号时,进行两个操作数的加法 ,文本域的get()方法返回的是字符串,进行强制转换 int a=Integer.parseInt(operand1.getText())+Integer.parseInt(operand2.getText()); result.setText("="+" "+a+" "); //设置结果显示相应的结果 } else { //当中间的操作符为“-”号的时候,进行两个操作数的减法 int a=Integer.parseInt(operand1.getText())-Integer.parseInt(operand2.getText()); result.setText("="+" "+a+" "); } } if(e.getSource()==clear){ operand1.setText(""); //清空操作数1 operand2.setText(""); //清空操作数2 result.setText(""); //清空结果框 } 本文转自Orson博客园博客,原文链接:http://www.cnblogs.com/java-class/archive/2012/12/11/2812996.html,如需转载请自行联系原作者
JavaScript语言有一个机制:在解析时,能够在一句话后面自动插入一个分号,用来修改语句末尾遗漏的分号分隔符。 然而,由于这个自动插入的分号与JavaScript语言的另一个机制发生了冲突,即所有空格符都被忽略,因此程序可以利用空格格式化代码。 这两种机制的冲突,很容易掩盖更为严重的解析错误。有时会不合时宜地插入分号。 例如,在return语句中自动插入分号将会导致这样的后果: 如果return语句要返回一个值,这个值的表达式的开始部分必须和return在同一行上,例如: var f = function(){ return { status: true }; } 看起来这里要返回一个包含status成员元素的对象。 不幸的是,JavaScript自动插入分号让它返回了undefined,从而导致下面真正要返回的对象被忽略。 当自动插入分号导致程序被误解时,并不会有任何警告提醒。 如果把{放在上一行的尾部而不是下一行的头部,就可以避免该问题,例如: var f = function(){ return { status: true }; } 为了避免省略分号引起的错误,建议养成好的习惯,不管一行内语句是否完整,只要是完整的语句都必须增加分号以表示句子结束。 为了方便阅读,当长句子需要分行显示时,在分行时应确保一行内不能形成完整的逻辑语义。 例如,下面代码是一条连续赋值的语句,通过分行显示可以更清楚地查看它们的关系。 这种分行显示,由于一行内不能形成独立的逻辑语义,因此JavaScript不会把每一行视为独立的句子,从而不会产生歧义。 var a = b = c = 4; 以上语句在一行内显示如下: var a = b = c = 4; 对于下面这条语句,如果不能正确分行显示,就很容易产生歧义。 该句子的含义:定义一个变量i,然后为其赋值,如果变量a为true,则赋值为1,否则就判断变量b,如果b为true,则赋值为2,否则就判断变量c,如果c为true,则赋值为3,否则赋值为4. var i = a ? 1 : b ? 2 : c ? 3 : 4; 下面的分行显示就是错误的,因为表达式a ? 1: b能够形成独立的逻辑语义,所以JavaScript会自动在其后添加分号来表示一个独立的句子。 var i = a ? 1: b ? 2 : c ? 3 : 4; 安全的方法应该采用如下的分行显示,这样每一行都不能形成独立的语义。 var i = a ? 1 : b ? 2 : c ? 3 : 4; 总之,在编写代码时,应养成使用分号结束句子的良好习惯,凡是完整的句子就应该使用分号进行分隔。 分行显示的句子应该确保单行不容易形成独立的合法的逻辑语义。 本文转自Orson博客园博客,原文链接:http://www.cnblogs.com/java-class/p/3140675.html,如需转载请自行联系原作者
Array转ArrayList 当需要把Array转成ArrayList的时候,开发人员经常这样做: List<String> list = Arrays.asList(arr); Arrays.asList()会返回一个ArrayList,但是要特别注意,这个ArrayList是Arrays类的静态内部类,并不是java.util.ArrayList类。java.util.Arrays.ArrayList类实现了set(), get(),contains()方法,但是并没有实现增加元素的方法(事实上是可以调用add方法,但是没有具体实现,仅仅抛出UnsupportedOperationException异常),因此它的大小也是固定不变的。为了创建一个真正的java.util.ArrayList,你应该这样做: ArrayList<String> arrayList = new ArrayList<String>(Arrays.asList(arr)); ArrayList的构造方法可以接收一个Collection类型,而java.util.Arrays.ArrayList已经实现了该接口。 判断一个数组是否包含某个值 开发人员经常这样做: Set<String> set = new HashSet<String>(Arrays.asList(arr)); return set.contains(targetValue); 以上代码可以正常工作,但是没有必要将其转换成set集合,将一个List转成Set需要额外的时间,其实我们可以简单的使用如下方法即可: Arrays.asList(arr).contains(targetValue); 或者 for(String s: arr){ if(s.equals(targetValue)) return true; } return false; 第一种方法可读性更强。 在循环内部删除List中的一个元素 考虑如下代码,在迭代期间删除元素: ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c","d")); for (int i = 0; i < list.size(); i++) { list.remove(i); } System.out.println(list); 结果打印: [b, d] 在上面这个方法中有一系列的问题,当一个元素被删除的时候,list大小减小,然后原先索引指向了其它元素。所以如果你想在循环里通过索引来删除多个元素,将不会正确工作。 你也许知道使用迭代器是在循环里删除元素的正确方式,或许你也知道foreach循环跟迭代器很类似,但事实情况却不是这样,如下代码: ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c","d")); for (String s : list) { if (s.equals("a")) list.remove(s); } 将抛出ConcurrentModificationException异常。 然而接下来的代码却是OK的: ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c","d")); Iterator<String> iter = list.iterator(); while (iter.hasNext()) { String s = iter.next(); if (s.equals("a")) { iter.remove(); } } next()方法需要在remove()方法之前被调用,在foreach循环里,编译器会在删除元素操作化调用next方法,这导致了ConcurrentModificationException异常。更多详细信息,可以查看ArrayList.iterator()的源码。 HashTable与HashMap 从算法的角度来讲,HashTable是一种数据结构名称。但是在Java中,这种数据结构叫做HashMap。HashTable与HashMap的一个主要的区别是HashTable是同步的,所以,通常来说,你会使用HashMap,而不是Hashtable。 更多信息:HashMap vs. TreeMap vs. Hashtable vs. LinkedHashMap Top 10 questions about Map 使用集合原始类型(raw type) 在Java中,原始类型(raw type)和无界通配符类型很容易让人混淆。举个Set的例子,Set是原始类型,而Set<?>是无界通配符类型。 请看如下代码,add方法使用了一个原始类型的List作为入参: public static void add(List list, Object o){ list.add(o); } public static void main(String[] args){ List<String> list = new ArrayList<String>(); add(list, 10); String s = list.get(0); } 运行以上代码将会抛出异常: Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String at ... 使用原始类型集合非常危险,因为它跳过了泛型类型检查,是不安全的。另外,Set, Set<?>, 和Set<Object>这三个有很大的不同,具体请看:类型擦除和Raw type vs. Unbounded wildcard。 访问级别 开发人员经常使用public修饰类字段,虽然这很容易让别人直接通过引用获取该字段的值,但这是一个不好的设计。根据经验,应该尽可能的降低成员属性的访问级别。 相关阅读:public, default, protected, and private ArrayList和LinkedList 为什么开发人员经常使用ArrayList和LinkedList,却不知道他们之间的区别,因为它们看起来很像。然而它们之间有着巨大的性能差异。简单的说,如果有大量的增加删除操作并且没有很多的随机访问元素的操作,应该首选LinkedList。 相关阅读:ArrayList vs. LinkedList 可变与不可变 不可变对象有很多优点,如简单、安全等。但是对于每个不同的值都需要一个单独的对象,太多的对象会引起大量垃圾回收,因此在选择可变与不可变的时候,需要有一个平衡。 通常,可变对象用于避免产生大量的中间对象,一个经典的例子是大量字符串的拼接。如果你使用一个不可变对象,将会马上产生大量符合垃圾回收标准的对象,这浪费了CPU大量的时间和精力。使用可变对象是正确的解决方案(StringBuilder); String result=""; for(String s: arr){ result = result + s; } 另外,在有些其它情况下也是需要使用可变对象。例如往一个方法传入一个可变对象,然后收集多种结果,而不需要写太多的语法。另一个例子是排序和过滤:当然,你可以写一个方法来接收原始的集合,并且返回一个排好序的集合,但是那样对于大的集合就太浪费了。 更多阅读:Why String is Immutable?? Why we need mutable classes? 父类和子类的构造方法 之所以出现这个编译错误,是因为父类的默认构造方法未定义。在Java中,如果一个类没有定义构造方法,编译器会默认插入一个无参数的构造方法;但是如果一个构造方法在父类中已定义,在这种情况,编译器是不会自动插入一个默认的无参构造方法,这正是以上demo的情况; 对于子类来说,不管是无参构造方法还是有参构造方法,都会默认调用父类的无参构造方法;当编译器尝试在子类中往这两个构造方法插入super()方法时,因为父类没有一个默认的无参构造方法,所以编译器报错; 要修复这个错误,很简单: 1、在父类手动定义一个无参构造方法: public Super(){ System.out.println("Super"); } 2、移除父类中自定义的构造方法 3、在子类中自己写上父类构造方法的调用;如super(value); “”还是构造方法 有两种创建字符串的方式: //1. use double quotes String x = "abc"; //2. use constructor String y = new String("abc"); 它们之间有什么区别呢? 以下代码提供了一个快速回答: String a = "abcd"; String b = "abcd"; System.out.println(a == b); // True System.out.println(a.equals(b)); // True String c = new String("abcd"); String d = new String("abcd"); System.out.println(c == d); // False System.out.println(c.equals(d)); // True 本文转自风一样的码农博客园博客,原文链接:http://www.cnblogs.com/chenpi/p/5508949.html,如需转载请自行联系原作者
什么是内部接口 内部接口也称为嵌套接口,即在一个接口内部定义另一个接口。举个例子,Entry接口定义在Map接口里面,如下代码: public interface Map { interface Entry{ int getKey(); } void clear(); } 为什么要使用内部接口 如下是一些强有力的理由: 一种对那些在同一个地方使用的接口进行逻辑上分组; 封装思想的体现; 嵌套接口可以增强代码的易读性和可维护性; 在Java标准库中使用内部接口的一个例子是java.util.Map和Java.util.Map.Entry。这里java.util.Map同样被当成命名空间使用。Entry并不属于全局作用域范围. 内部类如何工作 为了弄清楚内部接口是如何工作的,我们可以拿它与内部类作比较。内部类可以被认为是一个外部类内部定义的一个常规方法。因为一个方法可以被声明为静态和非静态,类似的内部类也可以被声明为静态和非静态。静态类类似于静态方法,它只能访问外部类的静态成员属性。非静态方法可以访问外部类的所有成员属性。 因为接口是不能实例化的,内部接口只有当它是静态的才有意义。因此,默认情况下,内部接口是静态的,不能你是否手动加了static关键字。 内部接口例子 Map.java public interface Map { interface Entry{ int getKey(); } void clear(); } MapImpl.java public class MapImpl implements Map { class ImplEntry implements Map.Entry{ public int getKey() { return 0; } } @Override public void clear() { //clear } } 本文转自风一样的码农博客园博客,原文链接:http://www.cnblogs.com/chenpi/p/5518400.html,如需转载请自行联系原作者
前言 基于上篇文章"基于Tomcat + JNDI + ActiveMQ实现JMS的点对点消息传送"很容易就可以编写一个发布/订阅消息传送例子,相关环境准备与该篇文章基本类似,主要的区别如下。 在Tomcat中配置JNDI 配置连接工厂和话题 <Resource name="topic/connectionFactory" auth="Container" type="org.apache.activemq.ActiveMQConnectionFactory" description="JMS Connection Factory" factory="org.apache.activemq.jndi.JNDIReferenceFactory" brokerURL="failover:(tcp://localhost:61616)?initialReconnectDelay=100&amp;maxReconnectAttempts=5" brokerName="LocalActiveMQBroker" useEmbeddedBroker="false" /> <Resource name="topic/topic0" auth="Container" type="org.apache.activemq.command.ActiveMQTopic" description="My Topic" factory="org.apache.activemq.jndi.JNDIReferenceFactory" physicalName="TestTopic" /> 在Web工厂中编写代码 新建一个发布者Servlet package pubSub; import java.io.IOException; import java.io.PrintWriter; import javax.naming.InitialContext; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.jms.Topic; import javax.jms.Session; import javax.jms.TextMessage; import javax.jms.TopicPublisher; import javax.jms.DeliveryMode; import javax.jms.TopicSession; import javax.jms.TopicConnection; import javax.jms.TopicConnectionFactory; /** * Servlet implementation class JMSTest */ @WebServlet("/Publish") public class Publisher extends HttpServlet { private static final long serialVersionUID = 1L; /** * @see HttpServlet#HttpServlet() */ public Publisher() { super(); // TODO Auto-generated constructor stub } /** * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse * response) */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter out = response.getWriter(); try { // get the initial context InitialContext ctx = new InitialContext(); // lookup the topic object Topic topic = (Topic) ctx.lookup("java:comp/env/topic/topic0"); // lookup the topic connection factory TopicConnectionFactory connFactory = (TopicConnectionFactory) ctx .lookup("java:comp/env/topic/connectionFactory"); // create a topic connection TopicConnection topicConn = connFactory.createTopicConnection(); // create a topic session TopicSession topicSession = topicConn.createTopicSession(false, Session.AUTO_ACKNOWLEDGE); // create a topic publisher TopicPublisher topicPublisher = topicSession.createPublisher(topic); topicPublisher.setDeliveryMode(DeliveryMode.NON_PERSISTENT); // create the "Hello World" message TextMessage message = topicSession.createTextMessage(); message.setText("Hello World"); // publish the messages topicPublisher.publish(message); // print what we did out.write("Message published: " + message.getText()); // close the topic connection topicConn.close(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse * response) */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub } } 新建一个订阅者Servlet package pubSub; import java.io.IOException; import java.io.PrintWriter; import javax.jms.Session; import javax.jms.TextMessage; import javax.jms.Topic; import javax.jms.TopicConnection; import javax.jms.TopicConnectionFactory; import javax.jms.TopicSession; import javax.jms.TopicSubscriber; import javax.naming.InitialContext; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * Servlet implementation class Receive */ @WebServlet("/Subscribe") public class Subscriber extends HttpServlet { private static final long serialVersionUID = 1L; /** * @see HttpServlet#HttpServlet() */ public Subscriber() { super(); // TODO Auto-generated constructor stub } /** * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse * response) */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter out = response.getWriter(); try { // get the initial context InitialContext ctx = new InitialContext(); // lookup the topic object Topic topic = (Topic) ctx.lookup("java:comp/env/topic/topic0"); // lookup the topic connection factory TopicConnectionFactory connFactory = (TopicConnectionFactory) ctx .lookup("java:comp/env/topic/connectionFactory"); // create a topic connection TopicConnection topicConn = connFactory.createTopicConnection(); // create a topic session TopicSession topicSession = topicConn.createTopicSession(false, Session.AUTO_ACKNOWLEDGE); // create a topic subscriber TopicSubscriber topicSubscriber = topicSession .createSubscriber(topic); // start the connection topicConn.start(); // receive the message TextMessage message = (TextMessage) topicSubscriber.receive(); // print the message out.write("Message received: " + message.getText()); // close the topic connection topicConn.close(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse * response) */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub } } 运行Web工程,分别打开多个标签访问订阅servlet,然后访问发布servlet,结果如下: 在订阅者订阅消息的时候,一开始没接收到消息,一旦发布者发布消息后,订阅者马上收到消息。 本文转自风一样的码农博客园博客,原文链接:http://www.cnblogs.com/chenpi/p/5566983.html,如需转载请自行联系原作者
(1)选择最有效率的表名顺序(只在基于规则的优化器中有效) ORACLE 的解析器按照从右到左的顺序处理FROM子句中的表名,FROM子句中写在最后的表(基础表 driving table)将被最先处理,在FROM子句中包含多个表的情况下,你必须选择记录条数最少的表作为基础表。如果有3个以上的表连接查询, 那就需要选择交叉表(intersection table)作为基础表, 交叉表是指那个被其他表所引用的表. 回到顶部 (2)WHERE子句中的连接顺序 ORACLE采用自下而上的顺序解析WHERE子句,根据这个原理,表之间的连接必须写在其他WHERE条件之前, 那些可以过滤掉最大数量记录的条件必须写在WHERE子句的末尾. 回到顶部 (3)SELECT子句中避免使用 ‘ * ‘ ORACLE在解析的过程中, 会将'*' 依次转换成所有的列名, 这个工作是通过查询数据字典完成的, 这意味着将耗费更多的时间 回到顶部 (4)减少访问数据库的次数 ORACLE在内部执行了许多工作: 解析SQL语句, 估算索引的利用率, 绑定变量 , 读数据块等; 回到顶部 (5)在SQL*Plus , SQL*Forms和Pro*C中重新设置ARRAYSIZE参数, 可以增加每次数据库访问的检索数据量 ,建议值为200 回到顶部 (6)使用DECODE函数来减少处理时间 使用DECODE函数可以避免重复扫描相同记录或重复连接相同的表. 回到顶部 (7) 整合简单,无关联的数据库访问 如果你有几个简单的数据库查询语句,你可以把它们整合到一个查询中(即使它们之间没有关系) 回到顶部 (8) 删除重复记录 最高效的删除重复记录方法 ( 因为使用了ROWID)例子: DELETE FROM EMP E WHERE E.ROWID > (SELECT MIN(X.ROWID) FROM EMP X WHERE X.EMP_NO = E.EMP_NO); 回到顶部 (9) 用TRUNCATE替代DELETE 当删除表中的记录时,在通常情况下, 回滚段(rollback segments ) 用来存放可以被恢复的信息. 如果你没有COMMIT事务,ORACLE会将数据恢复到删除之前的状态(准确地说是恢复到执行删除命令之前的状况) 而当运用TRUNCATE时, 回滚段不再存放任何可被恢复的信息.当命令运行后,数据不能被恢复.因此很少的资源被调用,执行时间也会很短. (译者按: TRUNCATE只在删除全表适用,TRUNCATE是DDL不是DML) 回到顶部 (10)尽量多使用COMMIT 只要有可能,在程序中尽量多使用COMMIT, 这样程序的性能得到提高,需求也会因为COMMIT所释放的资源而减少: COMMIT所释放的资源: a. 回滚段上用于恢复数据的信息. b. 被程序语句获得的锁 c. redo log buffer 中的空间 d. ORACLE为管理上述3种资源中的内部花费 回到顶部 (11) 用Where子句替换HAVING子句 避免使用HAVING子句, HAVING 只会在检索出所有记录之后才对结果集进行过滤. 这个处理需要排序,总计等操作. 如果能通过WHERE子句限制记录的数目,那就能减少这方面的开销. (非oracle中)on、where、having这三个都可以加条件的子句中,on是最先执行,where次之,having最后,因为on是先把不 符合条件的记录过滤后才进行统计,它就可以减少中间运算要处理的数据,按理说应该速度是最快的,where也应该比having快点的,因为它过滤数据后 才进行sum,在两个表联接时才用on的,所以在一个表的时候,就剩下where跟having比较了。在这单表查询统计的情况下,如果要过滤的条件没有涉及到要计算字段,那它们的结果是一样的,只是where可以使用rushmore技术,而having就不能,在速度上后者要慢如果要涉及到计算的字 段,就表示在没计算之前,这个字段的值是不确定的,根据上篇写的工作流程,where的作用时间是在计算之前就完成的,而having就是在计算后才起作 用的,所以在这种情况下,两者的结果会不同。在多表联接查询时,on比where更早起作用。系统首先根据各个表之间的联接条件,把多个表合成一个临时表 后,再由where进行过滤,然后再计算,计算完后再由having进行过滤。由此可见,要想过滤条件起到正确的作用,首先要明白这个条件应该在什么时候起作用,然后再决定放在那里 回到顶部 (12) 减少对表的查询 在含有子查询的SQL语句中,要特别注意减少对表的查询.例子: SELECT TAB_NAME FROM TABLES WHERE (TAB_NAME,DB_VER) = ( SELECT TAB_NAME,DB_VER FROM TAB_COLUMNS WHERE VERSION = 604) 回到顶部 (13) 通过内部函数提高SQL效率 复杂的SQL往往牺牲了执行效率. 能够掌握上面的运用函数解决问题的方法在实际工作中是非常有意义的 回到顶部 (14) 使用表的别名(Alias) 当在SQL语句中连接多个表时, 请使用表的别名并把别名前缀于每个Column上.这样一来,就可以减少解析的时间并减少那些由Column歧义引起的语法错误. 回到顶部 (15) 用EXISTS替代IN、用NOT EXISTS替代NOT IN 在许多基于基础表的查询中,为了满足一个条件,往往需要对另一个表进行联接.在这种情况下, 使用EXISTS(或NOT EXISTS)通常将提高查询的效率. 在子查询中,NOT IN子句将执行一个内部的排序和合并. 无论在哪种情况下,NOT IN都是最低效的(因为它对子查询中的表执行了一个全表遍历). 为了避免使用NOT IN ,我们可以把它改写成外连接(Outer Joins)或NOT EXISTS. 例子: (高效)SELECT * FROM EMP (基础表) WHERE EMPNO > 0 AND EXISTS (SELECT ‘X' FROM DEPT WHERE DEPT.DEPTNO = EMP.DEPTNO AND LOC = ‘MELB') (低效)SELECT * FROM EMP (基础表) WHERE EMPNO > 0 AND DEPTNO IN(SELECT DEPTNO FROM DEPT WHERE LOC = ‘MELB') 回到顶部 (16) 识别'低效执行'的SQL语句 虽然目前各种关于SQL优化的图形化工具层出不穷,但是写出自己的SQL工具来解决问题始终是一个最好的方法: SELECT EXECUTIONS , DISK_READS, BUFFER_GETS, ROUND((BUFFER_GETS-DISK_READS)/BUFFER_GETS,2) Hit_radio, ROUND(DISK_READS/EXECUTIONS,2) Reads_per_run, SQL_TEXT FROM V$SQLAREA WHERE EXECUTIONS>0 AND BUFFER_GETS > 0 AND (BUFFER_GETS-DISK_READS)/BUFFER_GETS < 0.8 ORDER BY 4 DESC; 回到顶部 (17) 用索引提高效率 索引是表的一个概念部分,用来提高检索数据的效率,ORACLE使用了一个复杂的自平衡B-tree结构. 通常,通过索引查询数据比全表扫描要快. 当ORACLE找出执行查询和Update语句的最佳路径时, ORACLE优化器将使用索引. 同样在联结多个表时使用索引也可以提高效率. 另一个使用索引的好处是,它提供了主键(primary key)的唯一性验证.。那些LONG或LONG RAW数据类型, 你可以索引几乎所有的列. 通常, 在大型表中使用索引特别有效. 当然,你也会发现, 在扫描小表时,使用索引同样能提高效率. 虽然使用索引能得到查询效率的提高,但是我们也必须注意到它的代价. 索引需要空间来存储,也需要定期维护, 每当有记录在表中增减或索引列被修改时, 索引本身也会被修改. 这意味着每条记录的INSERT , DELETE , UPDATE将为此多付出4 , 5 次的磁盘I/O . 因为索引需要额外的存储空间和处理,那些不必要的索引反而会使查询反应时间变慢.。定期的重构索引是有必要的.: ALTER INDEX <INDEXNAME> REBUILD <TABLESPACENAME> 回到顶部 (18) 用EXISTS替换DISTINCT 当提交一个包含一对多表信息(比如部门表和雇员表)的查询时,避免在SELECT子句中使用DISTINCT. 一般可以考虑用EXIST替换, EXISTS 使查询更为迅速,因为RDBMS核心模块将在子查询的条件一旦满足后,立刻返回结果. 例子: (低效): SELECT DISTINCT DEPT_NO,DEPT_NAME FROM DEPT D , EMP E WHERE D.DEPT_NO = E.DEPT_NO (高效): SELECT DEPT_NO,DEPT_NAME FROM DEPT D WHERE EXISTS ( SELECT ‘X' FROM EMP E WHERE E.DEPT_NO = D.DEPT_NO); 回到顶部 (19) sql语句用大写的;因为oracle总是先解析sql语句,把小写的字母转换成大写的再执行 回到顶部 (20) 在java代码中尽量少用连接符“+”连接字符串! 回到顶部 (21) 避免在索引列上使用NOT 通常 我们要避免在索引列上使用NOT, NOT会产生在和在索引列上使用函数相同的影响. 当ORACLE”遇到”NOT,他就会停止使用索引转而执行全表扫描. 回到顶部 (22) 避免在索引列上使用计算 WHERE子句中,如果索引列是函数的一部分.优化器将不使用索引而使用全表扫描. 举例: 低效: SELECT … FROM DEPT WHERE SAL * 12 > 25000; 高效: SELECT … FROM DEPT WHERE SAL > 25000/12; 回到顶部 (23) 用>=替代> 高效: SELECT * FROM EMP WHERE DEPTNO >=4 低效: SELECT * FROM EMP WHERE DEPTNO >3 两者的区别在于, 前者DBMS将直接跳到第一个DEPT等于4的记录而后者将首先定位到DEPTNO=3的记录并且向前扫描到第一个DEPT大于3的记录. 回到顶部 (24) 用UNION替换OR (适用于索引列) 通常情况下, 用UNION替换WHERE子句中的OR将会起到较好的效果. 对索引列使用OR将造成全表扫描. 注意, 以上规则只针对多个索引列有效. 如果有column没有被索引, 查询效率可能会因为你没有选择OR而降低. 在下面的例子中, LOC_ID 和REGION上都建有索引. 高效: SELECT LOC_ID , LOC_DESC , REGION FROM LOCATION WHERE LOC_ID = 10 UNION SELECT LOC_ID , LOC_DESC , REGION FROM LOCATION WHERE REGION = “MELBOURNE” 低效: SELECT LOC_ID , LOC_DESC , REGION FROM LOCATION WHERE LOC_ID = 10 OR REGION = “MELBOURNE” 如果你坚持要用OR, 那就需要返回记录最少的索引列写在最前面. 回到顶部 (25) 用IN来替换OR 这是一条简单易记的规则,但是实际的执行效果还须检验,在ORACLE8i下,两者的执行路径似乎是相同的. 低效: SELECT…. FROM LOCATION WHERE LOC_ID = 10 OR LOC_ID = 20 OR LOC_ID = 30 高效 SELECT… FROM LOCATION WHERE LOC_IN IN (10,20,30); 回到顶部 (26) 避免在索引列上使用IS NULL和IS NOT NULL 避免在索引中使用任何可以为空的列,ORACLE将无法使用该索引.对于单列索引,如果列包含空值,索引中将不存在此记录. 对于复合索引,如果每个列都为空,索引中同样不存在此记录. 如果至少有一个列不为空,则记录存在于索引中.举例: 如果唯一性索引建立在表的A列和B列上, 并且表中存在一条记录的A,B值为(123,null) , ORACLE将不接受下一条具有相同A,B值(123,null)的记录(插入). 然而如果所有的索引列都为空,ORACLE将认为整个键值为空而空不等于空. 因此你可以插入1000 条具有相同键值的记录,当然它们都是空! 因为空值不存在于索引列中,所以WHERE子句中对索引列进行空值比较将使ORACLE停用该索引. 低效: (索引失效) SELECT … FROM DEPARTMENT WHERE DEPT_CODE IS NOT NULL; 高效: (索引有效) SELECT … FROM DEPARTMENT WHERE DEPT_CODE >=0; 回到顶部 (27) 总是使用索引的第一个列 如果索引是建立在多个列上, 只有在它的第一个列(leading column)被where子句引用时,优化器才会选择使用该索引. 这也是一条简单而重要的规则,当仅引用索引的第二个列时,优化器使用了全表扫描而忽略了索引 回到顶部 (28) 用UNION-ALL 替换UNION ( 如果有可能的话) 当SQL 语句需要UNION两个查询结果集合时,这两个结果集合会以UNION-ALL的方式被合并, 然后在输出最终结果前进行排序. 如果用UNION ALL替代UNION, 这样排序就不是必要了. 效率就会因此得到提高. 需要注意的是,UNION ALL 将重复输出两个结果集合中相同记录. 因此各位还是要从业务需求分析使用UNION ALL的可行性. UNION 将对结果集合排序,这个操作会使用到SORT_AREA_SIZE这块内存. 对于这块内存的优化也是相当重要的. 下面的SQL可以用来查询排序的消耗量 低效: SELECT ACCT_NUM, BALANCE_AMT FROM DEBIT_TRANSACTIONS WHERE TRAN_DATE = '31-DEC-95' UNION SELECT ACCT_NUM, BALANCE_AMT FROM DEBIT_TRANSACTIONS WHERE TRAN_DATE = '31-DEC-95' 高效: SELECT ACCT_NUM, BALANCE_AMT FROM DEBIT_TRANSACTIONS WHERE TRAN_DATE = '31-DEC-95' UNION ALL SELECT ACCT_NUM, BALANCE_AMT FROM DEBIT_TRANSACTIONS WHERE TRAN_DATE = '31-DEC-95' (29) 用WHERE替代ORDER BY: ORDER BY 子句只在两种严格的条件下使用索引. ORDER BY中所有的列必须包含在相同的索引中并保持在索引中的排列顺序. ORDER BY中所有的列必须定义为非空. WHERE子句使用的索引和ORDER BY子句中所使用的索引不能并列. 例如: 表DEPT包含以下列: DEPT_CODE PK NOT NULL DEPT_DESC NOT NULL DEPT_TYPE NULL 低效: (索引不被使用) SELECT DEPT_CODE FROM DEPT ORDER BY DEPT_TYPE 高效: (使用索引) SELECT DEPT_CODE FROM DEPT WHERE DEPT_TYPE > 0 回到顶部 (30) 避免改变索引列的类型 当比较不同数据类型的数据时, ORACLE自动对列进行简单的类型转换. 假设 EMPNO是一个数值类型的索引列. SELECT … FROM EMP WHERE EMPNO = ‘123' 实际上,经过ORACLE类型转换, 语句转化为: SELECT … FROM EMP WHERE EMPNO = TO_NUMBER(‘123') 幸运的是,类型转换没有发生在索引列上,索引的用途没有被改变. 现在,假设EMP_TYPE是一个字符类型的索引列. SELECT … FROM EMP WHERE EMP_TYPE = 123 这个语句被ORACLE转换为: SELECT … FROM EMP WHERETO_NUMBER(EMP_TYPE)=123 因为内部发生的类型转换, 这个索引将不会被用到! 为了避免ORACLE对你的SQL进行隐式的类型转换, 最好把类型转换用显式表现出来. 注意当字符和数值比较时, ORACLE会优先转换数值类型到字符类型 回到顶部 (31) 需要当心的WHERE子句 某些SELECT 语句中的WHERE子句不使用索引. 这里有一些例子. 在下面的例子里, (1)‘!=' 将不使用索引. 记住, 索引只能告诉你什么存在于表中, 而不能告诉你什么不存在于表中. (2) ‘ | |'是字符连接函数. 就象其他函数那样, 停用了索引. (3) ‘+'是数学函数. 就象其他数学函数那样, 停用了索引. (4)相同的索引列不能互相比较,这将会启用全表扫描. 回到顶部 (32) a. 如果检索数据量超过30%的表中记录数.使用索引将没有显著的效率提高 b. 在特定情况下, 使用索引也许会比全表扫描慢, 但这是同一个数量级上的区别. 而通常情况下,使用索引比全表扫描要块几倍乃至几千倍! 回到顶部 (33) 避免使用耗费资源的操作 带有DISTINCT,UNION,MINUS,INTERSECT,ORDER BY的SQL语句会启动SQL引擎 执行耗费资源的排序(SORT)功能. DISTINCT需要一次排序操作, 而其他的至少需要执行两次排序. 通常, 带有UNION, MINUS , INTERSECT的SQL语句都可以用其他方式重写. 如果你的数据库的SORT_AREA_SIZE调配得好, 使用UNION , MINUS, INTERSECT也是可以考虑的, 毕竟它们的可读性很强 回到顶部 (34) 优化GROUP BY 提高GROUP BY 语句的效率, 可以通过将不需要的记录在GROUP BY 之前过滤掉.下面两个查询返回相同结果但第二个明显就快了许多. 低效: SELECT JOB , AVG(SAL) FROM EMP GROUP by JOB HAVING JOB = ‘PRESIDENT' OR JOB = ‘MANAGER' 高效: SELECT JOB , AVG(SAL) FROM EMP WHERE JOB = ‘PRESIDENT' OR JOB = ‘MANAGER' GROUP by JOB 回到顶部 (35)Select Count (*)和Select Count(1) 区别 一般情况下,Select Count (*)和Select Count(1)两着返回结果是一样的 假如表沒有主键(Primary key), 那么count(1)比count(*)快, 如果有主键的話,那主键作为count的条件时候count(主键)最快 如果你的表只有一个字段的话那count(*)就是最快的 count(*) 跟 count(1) 的结果一样,都包括对NULL的统计,而count(column) 是不包括NULL的统计 selelct 常量 from ... 对应所有行,返回的永远只有一个值,即常量 。所以正常只会用来判断是否有还是没有(比如exists子句)。而select * from ... 是返回所有行的所有列。 性能上的差异,关键看你的from和where子句。比如说如果你的where条件中可以通过索引,那显然 select 1 from ... 的性能比 select * from ... 好。 select count(*)返回所有满足条件的记录数,此时同select sum(1) 但是sum()可以传任意数字,负数、浮点数都可以,返回的值是传入值n*满足条件记录数m 回到顶部 (36) IS NULL 与 IS NOT NULL 不能用null作索引,任何包含null值的列都将不会被包含在索引中。即使索引有多列这样的情况下,只要这些列中有一列含有null,该列就会从索引中排除。也就是说如果某列存在空值,即使对该列建索引也不会提高性能。 任何在where子句中使用is null或is not null的语句优化器是不允许使用索引的。 回到顶部 (37) 联接列 对于有联接的列,即使最后的联接值为一个静态值,优化器是不会使用索引的。我们一起来看一个例子,假定有一个职工表(employee),对于一个职工的姓和名分成两列存放(FIRST_NAME和LAST_NAME),现在要查询一个叫比尔.克林顿(Bill Cliton)的职工。 下面是一个采用联接查询的SQL语句, select * from employss where first_name||''||last_name ='Beill Cliton'; 上面这条语句完全可以查询出是否有Bill Cliton这个员工,但是这里需要注意,系统优化器对基于last_name创建的索引没有使用。 当采用下面这种SQL语句的编写,Oracle系统就可以采用基于last_name创建的索引。 *** where first_name ='Beill' and last_name ='Cliton'; . 带通配符(%)的like语句 同样以上面的例子来看这种情况。目前的需求是这样的,要求在职工表中查询名字中包含cliton的人。可以采用如下的查询SQL语句: select * from employee where last_name like '%cliton%'; 这里由于通配符(%)在搜寻词首出现,所以Oracle系统不使用last_name的索引。在很多情况下可能无法避免这种情况,但是一定要心中有底,通配符如此使用会降低查询速度。然而当通配符出现在字符串其他位置时,优化器就能利用索引。在下面的查询中索引得到了使用: select * from employee where last_name like 'c%'; 回到顶部 (38)Order by语句 ORDER BY语句决定了Oracle如何将返回的查询结果排序。Order by语句对要排序的列没有什么特别的限制,也可以将函数加入列中(象联接或者附加等)。任何在Order by语句的非索引项或者有计算表达式都将降低查询速度。 仔细检查order by语句以找出非索引项或者表达式,它们会降低性能。解决这个问题的办法就是重写order by语句以使用索引,也可以为所使用的列建立另外一个索引,同时应绝对避免在order by子句中使用表达式。 回到顶部 (39) NOT 我们在查询时经常在where子句使用一些逻辑表达式,如大于、小于、等于以及不等于等等,也可以使用and(与)、or(或)以及not(非)。NOT可用来对任何逻辑运算符号取反。下面是一个NOT子句的例子: ... where not (status ='VALID') 如果要使用NOT,则应在取反的短语前面加上括号,并在短语前面加上NOT运算符。NOT运算符包含在另外一个逻辑运算符中,这就是不等于(<>)运算符。换句话说,即使不在查询where子句中显式地加入NOT词,NOT仍在运算符中,见下例: ... where status <>'INVALID'; 对这个查询,可以改写为不使用NOT: select * from employee where salary<3000 or salary>3000; 虽然这两种查询的结果一样,但是第二种查询方案会比第一种查询方案更快些。第二种查询允许Oracle对salary列使用索引,而第一种查询则不能使用索引。 虽然这两种查询的结果一样,但是第二种查询方案会比第一种查询方案更快些。第二种查询允许Oracle对salary列使用索引,而第一种查询则不能使用索引。 本文转自Orson博客园博客,原文链接:http://www.cnblogs.com/java-class/p/4555767.html,如需转载请自行联系原作者
今天和一个哥们学习了ghost系统备份。一般装系统总会为驱动而犯愁。但是有了ghost寻找驱动就不是问题啦! ghost可以备份已有的硬盘或分区,做成系统镜像,在另一台需要装系统的机子上,还原镜像,就可以完美的移植过去。 如果ghost C盘,那么所有C盘已有的东西都可以显示在另一台机子上。省去了装系统的麻烦,和装软件的时间。 ghost之前需要一个ghost盘,我用的是WinPE+ghost。 下面介绍一下如何ghost: 使用Ghost有整个硬盘(Disk)和分区硬盘(Partition)两种方式。 在菜单中点击 Local(本地)项,在右面弹出的菜单中有3个子项, 其中 Disk表示备份整个硬盘(即克隆)、Partition 表示备份硬盘的单个分区、Check 表示检查硬盘或备份的文件,查看是否可能因分区、硬盘被破坏等造成备份或还原失败。 分区备份作为个人用户来保存系统数据,特别是在恢复和复制系统分区时具有实用价值。 选 Local→Partition→To Image 菜单,弹出硬盘选择窗口,开始分区备份操作。 点击该窗口中白色的硬盘信息条,选择硬盘,进入窗口,选择要操作的分区(若没有鼠标,可用键盘进行操作:TAB键进行切换,回车键进行确认,方向键进行选择)。 在弹出的窗口中选择备份储存的目录路径并输入备份文件名称,注意备份文件的名称带有 GHO 的后缀名。 接下来,程序会询问是否压缩备份数据,并给出3个选择:No 表示不压缩,Fast表示压缩比例小而执行备份速度较快,High 就是压缩比例高但执行备份速度相当慢。 最后选择 Yes 按钮即开始进行分区硬盘的备份。 Ghost 备份的速度比较快,一般二十分钟之内。不用久等就可以完成,备份的文件以 GHO 后缀名储存在设定的目录中。 之后在需要装系统的机子上用相同的手段。只不过是选择Local→Partition→from image。然后选择.gho镜像文件,开始解压。一般十分钟之内。 本文转自施杨博客园博客,原文链接:http://www.cnblogs.com/shiyangxt/archive/2008/09/29/1301780.html,如需转载请自行联系原作者
1. java.sql.SQLException: ORA-01000: 超出打开游标的最大数 step 1: 查看数据库当前的游标数配置slqplus:show parameter open_cursors; step 2: 查看游标使用情况: select o.sid, osuser, machine, count(*) num_curs from v$open_cursor o, v$session s where user_name = 'user' and o.sid=s.sid group by o.sid, osuser, machine order by num_curs desc; 此处的user_name='user'中,user代表占用数据库资源的数据库用户名. step 3: 查看游标执行的sql情况: select o.sid q.sql_text from v$open_cursor o, v$sql q where q.hash_value=o.hash_value and o.sid = 123; step 4: 根据游标占用情况分析访问数据库的程序在资源释放上是否正常,如果程序释放资源没有问题,则加大游标数。 alter system set open_cursors=2000 scope=both; 补充:在java代码中,执行conn.createStatement()和conn.prepareStatement()的时候,实际上都是相当与在数据库中打开了一个cursor。尤其是,如果你的createStatement和prepareStatement是在一个循环里面的话,就会非常容易出现这个问题。因为游标一直在不停的打开,而且没有关闭。 一般来说,我们在写Java代码的时候,createStatement和prepareStatement都应该要放在循环外面,而且使用了这些Statment后,及时关闭。最好是在执行了一次executeQuery、executeUpdate等之后,如果不需要使用结果集(ResultSet)的数据,就马上将Statment关闭,调用close()方法。 本文转自Orson博客园博客,原文链接:http://www.cnblogs.com/java-class/p/4555782.html,如需转载请自行联系原作者
整体流程 每次SVN上代码有变动,触发自动构建动作,并部署到服务器的tomcat上,具体流程: 1、SVN上提交代码修改 2、maven执行Goals 3、将web工程打成war包 4、关闭服务器的tomcat,拷贝war包到服务器tomcat的webapps目录,启动tomcat 项目相关配置 因为我们使用的是maven工程,所以配置下pom.xml就可以啦,重点关注build的配置,如下: <build> <finalName>dss</finalName> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.0</version> <configuration> <source>1.7</source> <target>1.7</target> <encoding>UTF-8</encoding> </configuration> </plugin> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.1</version> </plugin> <plugin> <artifactId>maven-antrun-plugin</artifactId> <version>1.8</version> <configuration> <target> <ant antfile="./deploy/deploy.xml"> <target name="deploy" /> </ant> </target> </configuration> </plugin> </plugins> </build> 使用maven-antrun-plugin插件,可以执行ant target,方便拷贝文件,执行sh脚本、linux命令等。这里的target被定义在deploy.xml文件里,如下: <?xml version="1.0" ?> <project name="deploy_war" default="deploy" basedir="."> <property environment="env"/> <target name="deploy"> <echo>start deploy</echo> <path id="deploy.classpath"> <fileset dir="./deploy/lib"> <include name="**/*.jar"/> </fileset> </path> <taskdef name="sshexec" classname="org.apache.tools.ant.taskdefs.optional.ssh.SSHExec" classpathref="deploy.classpath" /> <taskdef name="scp" classname="org.apache.tools.ant.taskdefs.optional.ssh.Scp" classpathref="deploy.classpath" /> <echo>start stopping tomcat</echo> <scp file="./deploy/stopdsstomcat.sh" todir="root:123.com@10.0.10.53:/usr/local/dss-tomcat-7.0.47/bin" trust="true"/> <sshexec host="10.0.10.53" username="root" password="123.com" trust="true" command="cd /usr/local/dss-tomcat-7.0.47/bin;chmod 777 *.sh; ./stopdsstomcat.sh" /> <echo>start copying war</echo> <scp file="./target/dss.war" todir="root:123.com@10.0.10.53:/usr/local/dss-tomcat-7.0.47/webapps" trust="true"/> <echo>start starting tomcat</echo> <sshexec host="10.0.10.53" username="root" password="123.com" trust="true" command="cd /usr/local/dss-tomcat-7.0.47/bin;./startup.sh;rm -rf stopdsstomcat.sh;"/> </target> </project> 几点说明: 1、指定依赖的jar包路径 //path 2、定义第三方任务 //taskdef 3、将sh脚本传到远程服务器上,执行该脚本(该脚本主要是关闭tomcat,并删除webapps下的war包) //scp sshexec 4、拷贝war包到远程服务器 5、启动tomcat,部署完成。 sh脚本如下,用于关闭tomcat,删除war包,这里之所以采用杀掉进程的方式,而不使用tomcat自带的shutdown脚本,是因为当tomcat已经关闭的时候,执行shutdown.sh会报错: #!/bin/bash #kill tomcat pid USER=`whoami` pidlist=`ps -ef|grep apache-tomcat-7.0.47|grep $USER|grep -v "grep"|awk '{print $2}'` #ps -u $USER|grep "tomcat"|grep -v "grep" if [ "$pidlist" = "" ] then echo "no tomcat pid alive" else for pid in ${pidlist} { kill -9 $pid echo "KILL $pid:" echo "service stop success" } fi cd /usr/local/dss-tomcat-7.0.47/webapps/; rm -rf dss.war; echo "remove dss.war" rm -rf dss; echo "remove dss folder" 以上就是我们工程的配置了。 jenkins配置 首先,下载部署jenkins,其实就是一个java web工程; 至于jenkins的配置,主要在界面上配配即可: 我们新建一个maven的job,在配置界面: 配置jdk版本,svn相关信息 配置触发构建的条件 配置maven相关信息 配置结束!!! 本文转自风一样的码农博客园博客,原文链接:http://www.cnblogs.com/chenpi/p/5715029.html,如需转载请自行联系原作者
JAVA的时间日期处理一直是一个比较复杂的问题,大多数程序员都不能很轻松的来处理这些问题。首先Java中关于时间的类,从 JDK 1.1 开始,Date的作用很有限,相应的功能已由Calendar与DateFormat代替。使用Calendar类实现日期和时间字段之间转换,使用 DateFormat 类来格式化和分析日期字符串。 Calendar 类是一个抽象类,它为特定瞬间与一组诸如 YEAR、MONTH、DAY_OF_MONTH、HOUR 等 日历字段之间的转换提供了一些方法,并为操作日历字段(例如获得下星期的日期)提供了一些方法。 TimeZone : 表示时区偏移量。 Locale : 表示了特定的地理、政治和文化地区。需要 Locale 来执行其任务的操作称为语言环境敏感的操作,它使用 Locale 为用户量身定制信息。 SimpleDateFormat : 主要是用来格式化Date,用过之后就会发现,它其实不完善,对Calendar提供的支持很少. 经历几个项目发现apache提供的第三方扩展类库,org.apache.commons.lang.time包比较好用,可以将程序中时间处理变的简单一点,提高你的开发效率,下面介绍下常用的方法和具体使用。 org.apache.commons.lang.time 包括以下几个类: a. DateFormatUtils 【格式化Calendar与Date并依赖于 FastDateFormat】 b. DateUtils 【围绕Calendar与Date的实用方法】 c. DurationFormatUtils 【毫秒数格式化Calendar与Date】 d. FastDateFormat 【线程安全的SimpleDateFormat】 e. StopWatch 【提供一个方便的定时的API 】 回到顶部 1. DateFormatUtils 和 FastDateFormat DateFormatUtils相对来说比较简单,它的方法全部都是static的,所以不需要用构造器创建新的实例,但它构造器却是public的,这并不是说我们应该在程序中使用它,官方文档已说明,它是为了与其它工具的集成的准备的。它包括的方法主要就是format。主要用途就是根据传入的pattern格式化Date或Calendar。也可以有一些附加的参数,如Locale,TimeZone。 函数定义: format(java.util.Calendar calendar, java.lang.String pattern, java.util.TimeZone timeZone, java.util.Locale locale) 原来项目中是这样写的: SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String nowDay = sdf.format(new Date()); 现在可以这样写: String nowDay = DateFormatUtils.format(nowDay,"yyyy-MM-dd HH:mm:ss"); DateFormatUtils预定义的日期格式有9种之多,基本能满足项目时间格式化的要求,当然也可以自己自定义格式: public static final FastDateFormat ISO_DATETIME_FORMAT = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss"); public static final FastDateFormat ISO_DATETIME_TIME_ZONE_FORMAT = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ssZZ"); public static final FastDateFormat ISO_DATE_FORMAT = FastDateFormat.getInstance("yyyy-MM-dd"); public static final FastDateFormat ISO_DATE_TIME_ZONE_FORMAT = FastDateFormat.getInstance("yyyy-MM-ddZZ"); public static final FastDateFormat ISO_TIME_FORMAT = FastDateFormat.getInstance("'T'HH:mm:ss"); public static final FastDateFormat ISO_TIME_TIME_ZONE_FORMAT = FastDateFormat.getInstance("'T'HH:mm:ssZZ"); public static final FastDateFormat ISO_TIME_NO_T_FORMAT = FastDateFormat.getInstance("HH:mm:ss"); public static final FastDateFormat ISO_TIME_NO_T_TIME_ZONE_FORMAT = FastDateFormat.getInstance("HH:mm:ssZZ"); 所以上面的也可以这样写: DateFormatUtils.ISO_DATETIME_FORMAT.format(date); DateFormatUtils 预定义的日期格式,通过公有的静态final字段来暴露这些常量简化了类的使用,但是却有一些地方是需要注意的,在这方面FastDateFormat做得还是相当不错的。要使final字段真正的不可变, a. 这些字段是基本类型的值(int,String等), b. 指向一个不可变对象的引用。 c. 需要注意的特殊情况是,长度为零的数组总是可变的,要么通过不可变的Collection来包装, 要么使数组变成私有的,并添加一个公有的方法,返回一个数组的备份,这在effective java上的Item 15上有详细的介绍。上面的final 字段代表一个不可变的FastDateFormat,然而要让FastDateFormat字段真正的不可变, FastDateFormat内部必须遵循相应的规则才可以。 a. 不要提供能修改对象状态的方法 b. 确保类不会被继承 c. 让所有字段都成为static final字段 d. 确保所有可变的组件不能被访问 详细介绍可参考Effective java第二版的Item 15。仔细查看,会发现FastDateFormat都遵循这些规则,而Effective java 2rd 的出版也在commons-lang 2.4 开发之后,这说明 commons-lang的代码质量还是相当值得肯定的。 其实DateFormateUtils内部细节实现完全依靠FastDateFormat,DateFormateUtils只是把一些常用的格式化功能单独组织起来,让日期时间的使用变得简单,毕竟大多数时候用户查看API时,如果有太多的方法,会给他们纷繁复杂的感觉。如果需要更加强大灵活的日期格式化功能,可以直接使用FastDateFormat,FastDateFormat这个类编写比较复杂,它有自己的一套解析规则,同时又使用了DateFormat类的一些规则,如Pattern。与SimpleDateFormat不同,FastDateFormat是线程安全,所以这个类在多线程的服务环境中特别有用。 虽然它们都继承自java.text.Format,其实FastDateFormat相当于DateFormat与SimpleDateFormat的合并,只是功能更强大而已。如果是从DateFormat迁移到FastDateFormat的话,还是有一些地方需要注意的,比如,Date(String date)被DateFormat.parse(String s) 取代了,仔细查看,FastDateFormat并没有类似的方法,其实准确的说,把日期解析放在DateFormat本身就不太合理,不看文档的话,大多数人都会认为Date应该提供该功能。所以commons-lang把它放在了DateUtils 类中,对应方法则更加的强大: eg: public static Date parseDate(String str, String parsePatterns[]); String partterns[] = new String[5]; partterns[0] = "yyyy-MM-dd"; DateUtils.parseDate("2015-09-28", partterns); 因parsePatterns可以包括多种pattern,只要满足其中的一种即可。如果使用SimpleDateFormat,则先要通过SimpleDateFormat(String str)创建实例,然后通过applyPattern(String pattern)来变换不同的pattern。 回到顶部 2. DateUtils DateUtils提供了很多很方便的功能,减轻了使用Date的复杂性。把原来需用Calendar才能完成的功能统一集中了起来,也就是说没有对应的CalendarUtils类。在JDK中,Date与Calendar概念本身就有些混淆,只是为了保持兼容性才引入的Calendar。相对于Calendar提供的方法,DateUtils提供了更加合理的方法, 对时间的单个字段操作变得更加的容易。如需要修改时间Date的某个字段,必须先获得Date对象实例,再传入Calendar,才能修改,如: eg: //方法已经过时了 public static Date add(Date date, int calendarField, int amount) { if (date == null) { throw new IllegalArgumentException("The date must not be null"); } Calendar c = Calendar.getInstance(); c.setTime(date); c.add(calendarField, amount); return c.getTime(); } 在这方面commons-lang的确做得很完善,现在可以这样写: eg: View Code 方法名也非常的直观,使用也更加方便了。但有一些方法不是很好理解,就像下面这个函数: eg: 1 public static long getFragmentInSeconds(Date date,int fragment) 2 3 这个方法的作用是:返回一个指定时间的秒数。关键的是参数fragment,它的作用非常重要。它的值必须是Calendar的时间常量字段。如Calendar.MONTH , 4 5 需要注意的是,小时必须用24小时制的,即Calendar.HOUR_OF_DAY ,而不能用Calendar.HOUR字段。 6 /* 7 * 如现在是2014-10-23 13:27:00,那么 8 * DateUtils.getFragmentInDays(new Date(), Calendar.MONTH)返回23,表示从当月起已经过去23天了, 9 * DateUtils.getFragmentInDays(new Date(), Calendar.YEAR)返回296,表示从当年起已经过去296天了, 10 * DateUtils.getFragmentInHours(new Date(), Calendar.DATE)返回13,表示从今天起已经过去13个小时了 11 */ 当然在DateUtils也有一些一看就知道是做什么的函数,我在这里列一下,不具体给例子了: @Description: 判断是否是同一天 public static boolean isSameDay(Calendar cal1, Calendar cal2) @Description: Date 转 Calendar public static Calendar toCalendar(Date date); @Description: 从日期特定字段开始四舍五入 public static Calendar round(Calendar date, int field); * @Description: 格式化截取日期,从给定字段开始 public static Date truncate(Date date, int field); @Description: 从日期特定字段开始向上舍入 public static Date ceiling(Date date, int field); eg : 假设当前时间 2015-09-14 ceiling(new Date(),Calendar.MONTH); 输出 2015-10-01 round(new Date(), Calendar.MONTH); 输出 2015-09-01 假设当前时间 2015-09-16,超过半月,上面输出结果是一致的 @Description: 比较日历对应字段是否相等 public static boolean truncatedEquals(Calendar cal1, Calendar cal2, int field); 回到顶部 3. DurationFormatUtils 和 StopWatch 相对于这些增强已有功能的类,还有一些对常用功能进行补充的类,如DurationFormatUtils ,这个类主要的作用就是处理时间的片断,主要包括两种方法:formatDuration和formatPeriod。如: eg: 1 formatDuration(long durationMillis, java.lang.String format) 2 4 通过传入一个毫秒数与日期格式(如:yyyy-MM-dd HH:mm:ss),它会返回一个对应日期的字符串形式。 5 6 当然Date类本身有一个与这类似的方法,即Date(String s)方法,用于创建一个Date实例,但它只是创建一个Date实例,如果要转换成相应的String,还要经过一些步骤才行。 7 8 需要注意的是,此日期片断能表示的最大单位为天,用法也很简单: 9 11 String pattern = "yyyy-MM-dd HH:mm:ss"; 12 long durationMillis = (10+20*60+13*3600+4*24*3600) * 1000; 13 String formatDate = DurationFormatUtils.formatDuration(durationMillis, pattern); 14 System.out.println(formatDate); 需要注意的是日期格式的小时必须用HH,而不能用hh 。 formatPeriod方法用于计算两个时间之间的片断,然后转化成相应的日期字符串类型,即能表示的最大单位 1 public static java.lang.String formatPeriod(long startMillis, long endMillis, java.lang.String format) 2 3 4 String[] parsePatterns = {"yyyy-MM-dd HH:mm:ss"}; 5 String str = "2009-09-29 15:30:12"; 6 String str2 = "2010-09-30 15:40:18"; 7 Date date = DateUtils.parseDate(str, parsePatterns); 8 Date date2 = DateUtils.parseDate(str2, parsePatterns); 9 long durationMillis = DateUtils.getFragmentInMilliseconds(date, Calendar.YEAR); 10 long durationMillis2 =DateUtils.getFragmentInMilliseconds(date2,Calendar.YEAR); 11 12 String s = DurationFormatUtils.formatPeriod(durationMillis, durationMillis2,"yyyy-MM-dd HH:mm:ss") 13 14 其中s的值为:0000-00-01 00:10:06 StopWatch 是一个计时器,具体举个简单的例子: StopWatch stWatch = new StopWatch(); stWatch.start(); /** * 从周日开始,获取一周的 Calendar 对象 */ Iterator<?> itr = iterator(new Date(),RANGE_MONTH_MONDAY); while (itr.hasNext()) { Calendar gCal = (Calendar) itr.next(); System.out.println(formatDateObject(gCal.getTime(),"yyyy-MM-dd HH:mm:ss")); } stWatch.stop(); System.out.println("花费时间 >>" + stWatch.getTime() + ",毫秒"); 本文转自Orson博客园博客,原文链接:http://www.cnblogs.com/java-class/p/4845962.html,如需转载请自行联系原作者
前期准备 搭建solr服务 参考上一篇,搭建solr搜索服务。 添加依赖 maven工程的话,添加如下依赖, <!-- https://mvnrepository.com/artifact/org.apache.solr/solr-solrj --> <dependency> <groupId>org.apache.solr</groupId> <artifactId>solr-solrj</artifactId> <version>5.5.3</version> </dependency> 也可以自己导入jar包 在solr安装目录下,找到solr-5.5.3\dist\solrj-lib路径,添加里面所有的jar包到自己的工程,别忘了在外面的文件夹还有个solr-solrj-5.5.3.jar包,一起拷贝。 编写调用代码 这里简单给个示例(包含分页),Spring mvc工程的: @ResponseBody @RequestMapping(value = "/searchByKeyWord", method = { RequestMethod.GET }, produces = "application/json; charset=utf-8") @ApiOperation(value = "搜索接口", notes = "", httpMethod = "GET") public BaseResponse<Map<String, Object>> search(HttpServletRequest request, HttpServletResponse response, @RequestParam(value = "keyWord", defaultValue = "") String keyWord, @RequestParam(value = "pageSize", defaultValue = "10") long pageSize, @RequestParam(value = "pageOffset", defaultValue = "0") long pageOffset) { System.out.println(pageSize + "," + pageOffset); try { SolrClient solr = new HttpSolrClient(solrServiceUrl); SolrQuery query = new SolrQuery(); String kw = URLDecoder.decode(keyWord, "UTF-8"); query.set("q", "title:" + kw + " and content:" + kw); query.set("start", String.valueOf(pageOffset * pageSize)); query.set("rows", String.valueOf(pageSize)); query.addHighlightField("content"); query.setHighlight(true).setHighlightSnippets(1); query.setParam("hl.fl", "title,content"); query.setHighlightFragsize(300); QueryResponse queryResponse = solr.query(query); Map<String, Map<String, List<String>>> highlightMap = queryResponse.getHighlighting(); Map<String, Object> result = new HashMap<String, Object>(); List<SearchModel> rstList = queryResponse.getBeans(SearchModel.class); for (SearchModel obj : rstList) { Map<String, List<String>> hiStr = highlightMap.get(obj.getUrl()); List<String> contentList = hiStr.get("content"); if (contentList != null && contentList.size() > 0) obj.setContentHighlight(contentList.get(0)); List<String> titleList = hiStr.get("title"); if (titleList != null && titleList.size() > 0) obj.setTitleHighlight(titleList.get(0)); } result.put("success", true); result.put("key", URLDecoder.decode(keyWord, "UTF-8")); result.put("list", rstList); result.put("totalCount", queryResponse.getResults().getNumFound()); solr.close(); return BaseResponse.buildSuccessResponse(result); } catch (Exception e) { LOGGER.error(e.toString(), e); return BaseResponse .buildFailResponse(HttpStatus.INTERNAL_SERVER_ERROR.value(), "搜索异常."); } } 以下是SearchModel类,注意要与data-config.xml配置的字段对应起来,还有就是不要忘了在字段前面加上@Field注解(org.apache.solr.client.solrj.beans.Field)。 package com.cetiti.eppsas.model; import org.apache.solr.client.solrj.beans.Field; public class SearchModel { private String origin; private String title; private String content; private String linkUrl; private String keyWord; private long postTime; private String url; private String titleHighlight; private String contentHighlight; /** * @return the origin */ public String getOrigin() { return origin; } /** * @param origin the origin to set */ @Field("origin") public void setOrigin(String origin) { this.origin = origin; } /** * @return the title */ public String getTitle() { return title; } /** * @param title the title to set */ @Field("title") public void setTitle(String title) { this.title = title; } /** * @return the content */ public String getContent() { return content; } /** * @param content the content to set */ @Field("content") public void setContent(String content) { this.content = content; } /** * @return the linkUrl */ public String getLinkUrl() { return linkUrl; } /** * @param linkUrl the linkUrl to set */ @Field("linkUrl") public void setLinkUrl(String linkUrl) { this.linkUrl = linkUrl; } /** * @return the keyWord */ public String getKeyWord() { return keyWord; } /** * @param keyWord the keyWord to set */ @Field("keyWord") public void setKeyWord(String keyWord) { this.keyWord = keyWord; } /** * @return the url */ public String getUrl() { return url; } /** * @param url the url to set */ @Field("url") public void setUrl(String url) { this.url = url; } /** * @return the postTime */ public long getPostTime() { return postTime; } /** * @param postTime the postTime to set */ @Field("postTime") public void setPostTime(long postTime) { this.postTime = postTime; } /** * @return the titleHighlight */ public String getTitleHighlight() { return titleHighlight; } /** * @param titleHighlight the titleHighlight to set */ public void setTitleHighlight(String titleHighlight) { this.titleHighlight = titleHighlight; } /** * @return the contentHighlight */ public String getContentHighlight() { return contentHighlight; } /** * @param contentHighlight the contentHighlight to set */ public void setContentHighlight(String contentHighlight) { this.contentHighlight = contentHighlight; } } 前端示例 其它的根据业务需求具体扩展吧,在前端可以对查询到的数据进行一些自定义展示(关键字标红高亮,每条记录分类,点击跳转到记录详情页面)。 本文转自风一样的码农博客园博客,原文链接:http://www.cnblogs.com/chenpi/p/6055061.html,如需转载请自行联系原作者
在设计和实现的过程之后,你永远不知道部署上去的程序会已什么样的姿势运行。 本篇借一次生成二维码逻辑的不同实现,阐述 Web 项目中二维码生成的正确姿势。 文中如有批量,欢迎各位看客老爷拍砖。试运行前5天实现的逻辑是这样的: 客户 ajax 请求生成二维码,后端服务洞悉这一请求,生成二维码(可参照我博客:Google Zxing 二维码生成与解析)。 并将二维码已用户 ID 进行命名存储在项目工程 /webcontent/qrcode/AAAAAAAAAAAAAA.png 当中。 使用用户 ID 是想减少服务器存储压力,一个用户有且只有一张二维码,无论他点击了多少次,项目目录下只保存一张(Java IO 会在写文件前判断,如果存在相同名称的文件,会直接覆盖)。 将生成的 二维码 名称返回给前端,Jquery 将图片路径属性精准的放入 Dom 元素中。 var qrcode = $("#qrcode"); qrcode.removeAttr("src"); qrcode.attr("src","${pageContext.request.contextPath}/qrcode/" + data.qrcodeFileName); 实现后出现的问题: 当用户在一个业务点击了多次生成二维码时,因为有时效性,导致后续生成的二维码都失效,一边点马上扫都会是失效。 查看日志发现,后续的请求链接都是第一次生成的时间戳,查看项目目录发现,二维码确实是实时生成,但扫码后的链接却是第一次的。 我想问题应该在浏览器上面,经过反复实验,几乎所有的浏览器在第一次引入相同路径相同名称的图片时,后续如果还需要引入,会读取浏览器缓存当中的图片。 你不可能对用户说“每次点生成二维码的时候先清除一下你的浏览器缓存图片”,对吧?。 其实项目目录下的图片内容已经发生变化,只是名称和引入路径没变,但并没有被浏览器发现,这确实也不能怪浏览器太笨。 那就每张二维码都给一个唯一的ID? 项目路径下的文件肯定会爆炸,到时候会被项目经理叫去喝茶。 那就在想想办法,反复搜索和实践,第二种实现逻辑就出现了: 用户 ajax 请求生成二维码内容,返回给页面,前端使用第三方生成二维码类库 qrcode.js 在前端生成二维码。 new QRCode(document.getElementById("qrcode"), data.Link); 想了解 qrcode.js 的到官网:http://davidshimjs.github.io/qrcodejs/。 这种流程的实现方式,完全摒弃了后端生成二维码的部分代码、将生成二维码图片放入项目路径的两个过程。 前端随用随生成,需要注意的是返回给前端的跳转链接中的参数需要加密处理,毕竟前端是个是非之地。 大公司的前端二维码生成,估计和第二种解决方案差不了多少。 本文转自Orson博客园博客,原文链接:http://www.cnblogs.com/java-class/p/5607348.html,如需转载请自行联系原作者
这个东东安装和切换ruby的版本非常方便.在这里强烈推荐一下.Mac系统自带的ruby版本比较陈旧.所以俺需要一个工具来管理,RVM的安装 需要借助bash和git,这两个工具安装很简单,不多做介绍了. 安装RVM命令: bash < <( curl http://rvm.beginrescueend.com/releases/rvm-install-head ) 然后修改用户配置文件/Users/username/.profile,你也可以写到别的全局配置里. 添加一行代码: [[ -s "$HOME/.rvm/scripts/rvm" ]] && . "$HOME/.rvm/scripts/rvm" # This loads RVM into a shell session. 重启Terminal,RVM就安装好了. RVM的使用过程中不需要使用sudo,gem也不需要. 然后就可以安装ruby了,例如: rvm install ruby-1.8.7 --head --docs --docs是编译安装后自动生成文档,--head是最新的版本 删除某个版本的ruby,并且把文档和gems都删除,则: rvm remove ruby-1.8.7 --docs --gems 选择版本: rvm use ruby-1.8.7 --default 查询当前版本: rvm info 列出版本: rvm list 本文转自施杨博客园博客,原文链接:http://www.cnblogs.com/shiyangxt/archive/2010/11/23/1885379.html,如需转载请自行联系原作者
`rabbitmq`支持一对多的模式,一般称为发布/订阅。也就是说,生产者产生一条消息后,`rabbitmq`会把该消息分发给所有的消费者。 Exchanges 之前的教程中,仅仅使用了基本的消息模型: 生产者产生消息 把消息添加到消息队列 消费者接收消息 而在`rabbitmq完整的消息模型`中,并不是这样的。事实上,生产者并不知道消息是否发送到队列,而是把消息直接发送给`Exchanges`。 `Exchanges`的功能理解起来非常简单,它只负责接收生产者发送的数据并把这些数据添加到消息队列。但是,在存在多个消息队列的情况下,`Exchanges`必须知道每条消息要添加到哪一个消息队列。 `rabbitmq`提供了几种`Exchanges`,包括:`direct`, `topic`, `headers` and `fanout`。 这里,仅仅介绍fanout的使用。 channel.exchange_declare(exchange='news', type='fanout') 那么,发布消息: channel.basic_publish(exchange='news', routing_key='', body=message) Temporary queues 由于在生产者和消费者中需要指定相同的消息队列才能实现消息通信,那么如果不特殊指定某个消息队列会如何呢? 那么需要使用默认参数让系统给生成一个特定的消息队列。 result = channel.queue_declare() Bindings 为了发送指定发送的消息队列,必须创建exchange和消息队列之间的关系: channel.queue_bind(exchange='news', queue=result.method.queue) 例子 作为生产者的publish: #!/usr/bin/env python # coding=utf-8 import pika import sys connection = pika.BlockingConnection(pika.ConnectionParameters( host='localhost')) channel = connection.channel() channel.exchange_declare(exchange='news', type='fanout') for i in range(100): message = str(i) + 'Hello World!' channel.basic_publish(exchange='news', routing_key='', body=message) print " [x] Sent %r" % (message,) import time time.sleep(2) connection.close() 作为消费者的subscribe: #!/usr/bin/env python # coding=utf-8 import pika connection = pika.BlockingConnection(pika.ConnectionParameters( host='localhost')) channel = connection.channel() channel.exchange_declare(exchange='news', type='fanout') result = channel.queue_declare(exclusive=True) queue_name = result.method.queue channel.queue_bind(exchange='news', queue=queue_name) print ' [*] Waiting for news. To exit press CTRL+C' def callback(ch, method, properties, body): print " [x] %r" % (body,) channel.basic_consume(callback, queue=queue_name, no_ack=True) channel.start_consuming() 本文转自cococo点点博客园博客,原文链接:http://www.cnblogs.com/coder2012/p/4342927.html,如需转载请自行联系原作者
众所周知,opencv下有自带的供人脸识别以及行人检测的分类器,也就是说已经有现成的xml文件供你用。如果我们不做人脸识别或者行人检测,而是想做点其他的目标检测该怎么做呢?答案自然是自己训练一个特定的训练器。opencv里面比较常用的分类器有svm以及级联分类器,svm的训练以及分类很简单,这里不再赘述,这里谈谈级联分类器的训练。级联分类器可是好东西,opencv已经封装了多尺度检测方法(multiScaleDetector)以及绘制外接矩形的方法,这两个方法为目标检测提供了非常大的便利性。以下是本系列教程的总体流程: 1.训练前的准备:opencv_createsamples.exe和 opencv_traincascade.exe程序的编译 2.级联分类器的训练 3.级联分类器的应用 =================== 1.训练的准备在开始训练分类器前需要先获取到opencv_createsamples.exe以及opencv_traincascade.exe,有两种方式:第一自己编译,可以按照博客的内容自己编译,不过按照该方法最后运行出来的结果没有这两个可执行程序产生;第二拿别人编译好的文件,从链接http://en.pudn.com/downloads204/sourcecode/graph/texture_mapping/detail958471_en.html处下载整个工程,在bin目录下有这两个文件,不过是opencv2.2版本的不影响,要用的时候把整个工程的文件(exe+dll)同时拷出来,否则会报“找不到dll”的错误。 2.分类器的训练: 按照帖子http://jingyan.baidu.com/article/4dc40848f50689c8d946f197.html的操作一步步做下去,注意事项见: http://blog.csdn.net/xidianzhimeng/article/details/10470839 结合自己的经验补充如下: 1.在train时,numPos是指每个stage上用的正样本数,一般取vec里面的正样本数的90%; 目前卡在错误: ===== TRAINING 0-stage ===== <BEGIN POS count : consumed 16 : 16 Train dataset for temp stage can not be filled. Branch training terminated. 找了半天资料试了之后仍旧无解,准备换台机子试试,to be continue... 本文转自编程小翁博客园博客,原文链接:http://www.cnblogs.com/wengzilin/p/3478865.html,如需转载请自行联系原作者
jQuery.post( url, [data], [callback], [type] ) :使用POST方式来进行异步请求 参数: url (String) : 发送请求的URL地址. data (Map) : (可选) 要发送给服务器的数据,以 Key/value 的键值对形式表示。 callback (Function) : (可选) 载入成功时回调函数(只有当Response的返回状态是success才是调用该方法)。 type (String) : (可选)官方的说明是:Type of data to be sent。其实应该为客户端请求的类型(JSON,XML,等等) 这是一个简单的 POST 请求功能以取代复杂 .ajax。请求成功时可调用回调函数。如果需要在出错时执行函数,请使用.ajax。请求成功时可调用回调函数。如果需要在出错时执行函数,请使用.ajax。示例代码: $.post("/myurl", { uname: "change", password: "me" }, function (data, textStatus){ // data 可以是 xmlDoc, jsonObj, html, text, 等等. //this; // 这个Ajax请求的选项配置信息,请参考jQuery.get()说到的this console.log(data);//object console.log(JSON.stringify(data));//json }, "json"); 本文转自Ryan.Miao博客园博客,原文链接:http://www.cnblogs.com/woshimrf/p/4583719.html,如需转载请自行联系原作者
修改很简单,通过参数判断,然后返回下promise对象 (function() { var timeout = setTimeout; window.setTimeout = function(fn, time) { if (!time) { time = fn; return $.Deferred(function(dfd) { timeout(function() { dfd.resolvel(dfd); }, time); }).promise(); } timeout.apply(window, arguments); } })(); 调用 setTimeout(1000).done(function(dnt) { console.log(dnt) }) 本文转自艾伦 Aaron博客园博客,原文链接:http://www.cnblogs.com/aaronjs/p/3951732.html,如需转载请自行联系原作者