
暂无个人介绍
能力说明:
了解变量作用域、Java类的结构,能够创建带main方法可执行的java应用,从命令行运行java程序;能够使用Java基本数据类型、运算符和控制结构、数组、循环结构书写和运行简单的Java程序。
暂时未有相关云产品技术能力~
阿里云技能认证
详细说明首先贴出原文地址,尊重原作者http://blog.csdn.net/zxygww/article/details/47045055注意:下面方法已验证通过。HTTP请求中的是字符串数据: //字符串读取 void charReader(HttpServletRequest request) { BufferedReader br = request.getReader(); String str, wholeStr = ""; while((str = br.readLine()) != null){ wholeStr += str; } System.out.println(wholeStr); } //二进制读取 void binaryReader(HttpServletRequest request) { int len = request.getContentLength(); ServletInputStream iii = request.getInputStream(); byte[] buffer = new byte[len]; iii.read(buffer, 0, len); } 注意: request.getInputStream(); request.getReader();和request.getParameter("key");这三个函数中任何一个函数执行一次后(可正常读取body数据),之后再执行就无效了。 从HttpServletRequest中得到完整的请求URL String getFullURL(HttpRequest request) { StringBuffer url = request.getRequestURL(); if (request.getQueryString() != null) { url.append(’?’); url.append(request.getQueryString()); } return url.toString(); }
1、@Autowired与@Resource都可以用来装配bean. 都可以写在字段上,或写在setter方法上。 2、@Autowired默认按类型装配(这个注解是属业spring的),默认情况下必须要求依赖对象必须存在,如果要允许null 值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用,如下: Java代码 @Autowired() @Qualifier("baseDao") private BaseDao baseDao; 3、@Resource(这个注解属于J2EE的),默认安照名称进行装配,名称可以通过name属性进行指定,如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,如果注解写在setter方法上默认取属性名进行装配。 当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。 Java代码 @Resource(name="baseDao") private BaseDao baseDao; 他们的主要区别就是@Autowired是默认按照类型装配的 @Resource默认是按照名称装配的byName 通过参数名 自动装配,如果一个bean的name 和另外一个bean的 property 相同,就自动装配。byType 通过参数的数据类型自动自动装配,如果一个bean的数据类型和另外一个bean的property属性的数据类型兼容,就自动装配 @Resource注解属于J2EE的,减少了与spring的耦合。
【常规】 Ctrl+Shift + Enter,语句完成 “!”,否定完成,输入表达式时按 “!”键 Ctrl+E,最近的文件 Ctrl+Shift+E,最近更改的文件 Shift+Click,可以关闭文件 Ctrl+[ OR ],可以跑到大括号的开头与结尾 Ctrl+F12,可以显示当前文件的结构 Ctrl+F7,可以查询当前元素在当前文件中的引用,然后按 F3 可以选择 Ctrl+N,可以快速打开类 Ctrl+Shift+N,可以快速打开文件 Alt+Q,可以看到当前方法的声明 Ctrl+P,可以显示参数信息 Ctrl+Shift+Insert,可以选择剪贴板内容并插入 Alt+Insert,可以生成构造器/Getter/Setter等 Ctrl+Alt+V,可以引入变量。例如:new String(); 自动导入变量定义 Ctrl+Alt+T,可以把代码包在一个块内,例如:try/catch Ctrl+Enter,导入包,自动修正 Ctrl+Alt+L,格式化代码 Ctrl+Alt+I,将选中的代码进行自动缩进编排,这个功能在编辑 JSP 文件时也可以工作 Ctrl+Alt+O,优化导入的类和包 Ctrl+R,替换文本 Ctrl+F,查找文本 Ctrl+Shift+Space,自动补全代码 Ctrl+空格,代码提示(与系统输入法快捷键冲突) Ctrl+Shift+Alt+N,查找类中的方法或变量 Alt+Shift+C,最近的更改 Alt+Shift+Up/Down,上/下移一行 Shift+F6,重构 - 重命名 Ctrl+X,删除行 Ctrl+D,复制行 Ctrl+/或Ctrl+Shift+/,注释(//或者/**/) Ctrl+J,自动代码(例如:serr) Ctrl+Alt+J,用动态模板环绕 Ctrl+H,显示类结构图(类的继承层次) Ctrl+Q,显示注释文档 Alt+F1,查找代码所在位置 Alt+1,快速打开或隐藏工程面板 Ctrl+Alt+left/right,返回至上次浏览的位置 Alt+left/right,切换代码视图 Alt+Up/Down,在方法间快速移动定位 Ctrl+Shift+Up/Down,向上/下移动语句 F2 或 Shift+F2,高亮错误或警告快速定位 Tab,代码标签输入完成后,按 Tab,生成代码 Ctrl+Shift+F7,高亮显示所有该文本,按 Esc 高亮消失 Alt+F3,逐个往下查找相同文本,并高亮显示 Ctrl+Up/Down,光标中转到第一行或最后一行下 Ctrl+B/Ctrl+Click,快速打开光标处的类或方法(跳转到定义处) Ctrl+Alt+B,跳转到方法实现处 Ctrl+Shift+Backspace,跳转到上次编辑的地方 Ctrl+O,重写方法 Ctrl+Alt+Space,类名自动完成 Ctrl+Alt+Up/Down,快速跳转搜索结果 Ctrl+Shift+J,整合两行 Alt+F8,计算变量值 Ctrl+Shift+V,可以将最近使用的剪贴板内容选择插入到文本 Ctrl+Alt+Shift+V,简单粘贴 Shift+Esc,不仅可以把焦点移到编辑器上,而且还可以隐藏当前(或最后活动的)工具窗口 F12,把焦点从编辑器移到最近使用的工具窗口 Shift+F1,要打开编辑器光标字符处使用的类或者方法 Java 文档的浏览器 Ctrl+W,可以选择单词继而语句继而行继而函数 Ctrl+Shift+W,取消选择光标所在词 Alt+F7,查找整个工程中使用地某一个类、方法或者变量的位置 Ctrl+I,实现方法 Ctrl+Shift+U,大小写转化 Ctrl+Y,删除当前行 Shift+Enter,向下插入新行 psvm/sout,main/System.out.println(); Ctrl+J,查看更多 Ctrl+Shift+F,全局查找 Ctrl+F,查找/Shift+F3,向上查找/F3,向下查找 Ctrl+Shift+S,高级搜索 Ctrl+U,转到父类 Ctrl+Alt+S,打开设置对话框 Alt+Shift+Inert,开启/关闭列选择模式 Ctrl+Alt+Shift+S,打开当前项目/模块属性 Ctrl+G,定位行 Alt+Home,跳转到导航栏 Ctrl+Enter,上插一行 Ctrl+Backspace,按单词删除 Ctrl+"+/-",当前方法展开、折叠 Ctrl+Shift+"+/-",全部展开、折叠 【调试部分、编译】 Ctrl+F2,停止 Alt+Shift+F9,选择 Debug Alt+Shift+F10,选择 Run Ctrl+Shift+F9,编译 Ctrl+Shift+F10,运行 Ctrl+Shift+F8,查看断点 F8,步过 F7,步入 Shift+F7,智能步入 Shift+F8,步出 Alt+Shift+F8,强制步过 Alt+Shift+F7,强制步入 Alt+F9,运行至光标处 Ctrl+Alt+F9,强制运行至光标处 F9,恢复程序 Alt+F10,定位到断点 Ctrl+F8,切换行断点 Ctrl+F9,生成项目 Alt+1,项目 Alt+2,收藏 Alt+6,TODO Alt+7,结构 Ctrl+Shift+C,复制路径 Ctrl+Alt+Shift+C,复制引用,必须选择类名 Ctrl+Alt+Y,同步 Ctrl+~,快速切换方案(界面外观、代码风格、快捷键映射等菜单) Shift+F12,还原默认布局 Ctrl+Shift+F12,隐藏/恢复所有窗口 Ctrl+F4,关闭 Ctrl+Shift+F4,关闭活动选项卡 Ctrl+Tab,转到下一个拆分器 Ctrl+Shift+Tab,转到上一个拆分器 【重构】 Ctrl+Alt+Shift+T,弹出重构菜单 Shift+F6,重命名 F6,移动 F5,复制 Alt+Delete,安全删除 Ctrl+Alt+N,内联 【查找】 Ctrl+F,查找 Ctrl+R,替换 F3,查找下一个 Shift+F3,查找上一个 Ctrl+Shift+F,在路径中查找 Ctrl+Shift+R,在路径中替换 Ctrl+Shift+S,搜索结构 Ctrl+Shift+M,替换结构 Alt+F7,查找用法 Ctrl+Alt+F7,显示用法 Ctrl+F7,在文件中查找用法 Ctrl+Shift+F7,在文件中高亮显示用法 【VCS】 Alt+~,VCS 操作菜单 Ctrl+K,提交更改 Ctrl+T,更新项目 Ctrl+Alt+Shift+D,显示变化【常规】 Ctrl+Shift + Enter,语句完成 “!”,否定完成,输入表达式时按 “!”键 Ctrl+E,最近的文件 Ctrl+Shift+E,最近更改的文件 Shift+Click,可以关闭文件 Ctrl+[ OR ],可以跑到大括号的开头与结尾 Ctrl+F12,可以显示当前文件的结构 Ctrl+F7,可以查询当前元素在当前文件中的引用,然后按 F3 可以选择 Ctrl+N,可以快速打开类 Ctrl+Shift+N,可以快速打开文件 Alt+Q,可以看到当前方法的声明 Ctrl+P,可以显示参数信息 Ctrl+Shift+Insert,可以选择剪贴板内容并插入 Alt+Insert,可以生成构造器/Getter/Setter等 Ctrl+Alt+V,可以引入变量。例如:new String(); 自动导入变量定义 Ctrl+Alt+T,可以把代码包在一个块内,例如:try/catch Ctrl+Enter,导入包,自动修正 Ctrl+Alt+L,格式化代码 Ctrl+Alt+I,将选中的代码进行自动缩进编排,这个功能在编辑 JSP 文件时也可以工作 Ctrl+Alt+O,优化导入的类和包 Ctrl+R,替换文本 Ctrl+F,查找文本 Ctrl+Shift+Space,自动补全代码 Ctrl+空格,代码提示(与系统输入法快捷键冲突) Ctrl+Shift+Alt+N,查找类中的方法或变量 Alt+Shift+C,最近的更改 Alt+Shift+Up/Down,上/下移一行 Shift+F6,重构 - 重命名 Ctrl+X,删除行 Ctrl+D,复制行 Ctrl+/或Ctrl+Shift+/,注释(//或者/**/) Ctrl+J,自动代码(例如:serr) Ctrl+Alt+J,用动态模板环绕 Ctrl+H,显示类结构图(类的继承层次) Ctrl+Q,显示注释文档 Alt+F1,查找代码所在位置 Alt+1,快速打开或隐藏工程面板 Ctrl+Alt+left/right,返回至上次浏览的位置 Alt+left/right,切换代码视图 Alt+Up/Down,在方法间快速移动定位 Ctrl+Shift+Up/Down,向上/下移动语句 F2 或 Shift+F2,高亮错误或警告快速定位 Tab,代码标签输入完成后,按 Tab,生成代码 Ctrl+Shift+F7,高亮显示所有该文本,按 Esc 高亮消失 Alt+F3,逐个往下查找相同文本,并高亮显示 Ctrl+Up/Down,光标中转到第一行或最后一行下 Ctrl+B/Ctrl+Click,快速打开光标处的类或方法(跳转到定义处) Ctrl+Alt+B,跳转到方法实现处 Ctrl+Shift+Backspace,跳转到上次编辑的地方 Ctrl+O,重写方法 Ctrl+Alt+Space,类名自动完成 Ctrl+Alt+Up/Down,快速跳转搜索结果 Ctrl+Shift+J,整合两行 Alt+F8,计算变量值 Ctrl+Shift+V,可以将最近使用的剪贴板内容选择插入到文本 Ctrl+Alt+Shift+V,简单粘贴 Shift+Esc,不仅可以把焦点移到编辑器上,而且还可以隐藏当前(或最后活动的)工具窗口 F12,把焦点从编辑器移到最近使用的工具窗口 Shift+F1,要打开编辑器光标字符处使用的类或者方法 Java 文档的浏览器 Ctrl+W,可以选择单词继而语句继而行继而函数 Ctrl+Shift+W,取消选择光标所在词 Alt+F7,查找整个工程中使用地某一个类、方法或者变量的位置 Ctrl+I,实现方法 Ctrl+Shift+U,大小写转化 Ctrl+Y,删除当前行 Shift+Enter,向下插入新行 psvm/sout,main/System.out.println(); Ctrl+J,查看更多 Ctrl+Shift+F,全局查找 Ctrl+F,查找/Shift+F3,向上查找/F3,向下查找 Ctrl+Shift+S,高级搜索 Ctrl+U,转到父类 Ctrl+Alt+S,打开设置对话框 Alt+Shift+Inert,开启/关闭列选择模式 Ctrl+Alt+Shift+S,打开当前项目/模块属性 Ctrl+G,定位行 Alt+Home,跳转到导航栏 Ctrl+Enter,上插一行 Ctrl+Backspace,按单词删除 Ctrl+"+/-",当前方法展开、折叠 Ctrl+Shift+"+/-",全部展开、折叠 【调试部分、编译】 Ctrl+F2,停止 Alt+Shift+F9,选择 Debug Alt+Shift+F10,选择 Run Ctrl+Shift+F9,编译 Ctrl+Shift+F10,运行 Ctrl+Shift+F8,查看断点 F8,步过 F7,步入 Shift+F7,智能步入 Shift+F8,步出 Alt+Shift+F8,强制步过 Alt+Shift+F7,强制步入 Alt+F9,运行至光标处 Ctrl+Alt+F9,强制运行至光标处 F9,恢复程序 Alt+F10,定位到断点 Ctrl+F8,切换行断点 Ctrl+F9,生成项目 Alt+1,项目 Alt+2,收藏 Alt+6,TODO Alt+7,结构 Ctrl+Shift+C,复制路径 Ctrl+Alt+Shift+C,复制引用,必须选择类名 Ctrl+Alt+Y,同步 Ctrl+~,快速切换方案(界面外观、代码风格、快捷键映射等菜单) Shift+F12,还原默认布局 Ctrl+Shift+F12,隐藏/恢复所有窗口 Ctrl+F4,关闭 Ctrl+Shift+F4,关闭活动选项卡 Ctrl+Tab,转到下一个拆分器 Ctrl+Shift+Tab,转到上一个拆分器 【重构】 Ctrl+Alt+Shift+T,弹出重构菜单 Shift+F6,重命名 F6,移动 F5,复制 Alt+Delete,安全删除 Ctrl+Alt+N,内联 【查找】 Ctrl+F,查找 Ctrl+R,替换 F3,查找下一个 Shift+F3,查找上一个 Ctrl+Shift+F,在路径中查找 Ctrl+Shift+R,在路径中替换 Ctrl+Shift+S,搜索结构 Ctrl+Shift+M,替换结构 Alt+F7,查找用法 Ctrl+Alt+F7,显示用法 Ctrl+F7,在文件中查找用法 Ctrl+Shift+F7,在文件中高亮显示用法 【VCS】 Alt+~,VCS 操作菜单 Ctrl+K,提交更改 Ctrl+T,更新项目 Ctrl+Alt+Shift+D,显示变化 后续陆续添加 来自:http://blog.csdn.net/djcken/article/details/16362629
首先,说说什么事务(Transaction)。事务,就是一组操作数据库的动作集合。事务是现代数据库理论中的核心概念之一。如果一组处理步骤或者全部发生或者一步也不执行,我们称该组处理步骤为一个事务。当所有的步骤像一个操作一样被完整地执行,我们称该事务被提交。由于其中的一部分或多步执行失败,导致没有步骤被提交,则事务必须回滚到最初的系统状态。其中spring七个事物传播属性: PROPAGATION_REQUIRED -- 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。 PROPAGATION_SUPPORTS -- 支持当前事务,如果当前没有事务,就以非事务方式执行。 PROPAGATION_MANDATORY -- 支持当前事务,如果当前没有事务,就抛出异常。 PROPAGATION_REQUIRES_NEW -- 新建事务,如果当前存在事务,把当前事务挂起。 PROPAGATION_NOT_SUPPORTED -- 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 PROPAGATION_NEVER -- 以非事务方式执行,如果当前存在事务,则抛出异常。 PROPAGATION_NESTED -- 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。 五个隔离级别:ISOLATION_DEFAULT 这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别.另外四个与JDBC的隔离级别相对应;ISOLATION_READ_UNCOMMITTED 这是事务最低的隔离级别,它充许别外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读。 ISOLATION_READ_COMMITTED 保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻像读。 ISOLATION_REPEATABLE_READ 这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。 ISOLATION_SERIALIZABLE 这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻像读。 关键词:1)幻读:事务1读取记录时事务2增加了记录并提交,事务1再次读取时可以看到事务2新增的记录;2)不可重复读取:事务1读取记录时,事务2更新了记录并提交,事务1再次读取时可以看到事务2修改后的记录;3)脏读:事务1更新了记录,但没有提交,事务2读取了更新后的行,然后事务T1回滚,现在T2读取无效。 Spring事务配置的五种方式前段时间对Spring的事务配置做了比较深入的研究,在此之间对Spring的事务配置虽说也配置过,但是一直没有一个清楚的认识。通过这次的学习发觉Spring的事务配置只要把思路理清,还是比较好掌握的。 总结如下: Spring配置文件中关于事务配置总是由三个组成部分,分别是DataSource、TransactionManager和代理机制这三部分,无论哪种配置方式,一般变化的只是代理机制这部分。 DataSource、TransactionManager这两部分只是会根据数据访问方式有所变化,比如使用Hibernate进行数据访问时,DataSource实际为SessionFactory,TransactionManager的实现为HibernateTransactionManager。 具体如下图: 根据代理机制的不同,总结了五种Spring事务的配置方式,配置文件如下: 第一种方式:每个Bean都有一个代理 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="configLocation" value="classpath:hibernate.cfg.xml" /> <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" /> </bean> <!-- 定义事务管理器(声明式的事务) --> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <!--配置DAO --> <bean id="userDaoTarget" class="com.bluesky.spring.dao.UserDaoImpl"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <bean id="userDao" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <!--配置事务管理器 --> <property name="transactionManager" ref="transactionManager" /> <property name="target" ref="userDaoTarget" /> <property name="proxyInterfaces" value="com.bluesky.spring.dao.GeneratorDao" /> <!-- 配置事务属性 --> <property name="transactionAttributes"> <props> <prop key="*">PROPAGATION_REQUIRED</prop> </props> </property> </bean> </beans> 第二种方式:所有Bean共享一个代理基类 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="configLocation" value="classpath:hibernate.cfg.xml" /> <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" /> </bean> <!--定义事务管理器(声明式的事务)--> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <bean id="transactionBase" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" lazy-init="true" abstract="true"> <!-- 配置事务管理器 --> <property name="transactionManager" ref="transactionManager" /> <!-- 配置事务属性 --> <property name="transactionAttributes"> <props> <prop key="*">PROPAGATION_REQUIRED</prop> </props> </property> </bean> <!-- 配置DAO --> <bean id="userDaoTarget" class="com.bluesky.spring.dao.UserDaoImpl"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <bean id="userDao" parent="transactionBase" > <property name="target" ref="userDaoTarget" /> </bean> </beans> 第三种方式:使用拦截器 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="configLocation" value="classpath:hibernate.cfg.xml" /> <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" /> </bean> <!--定义事务管理器(声明式的事务)--> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor"> <property name="transactionManager" ref="transactionManager" /> <!--配置事务属性--> <property name="transactionAttributes"> <props> <prop key="*">PROPAGATION_REQUIRED</prop> </props> </property> </bean> <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="beanNames"> <list> <value>*Dao</value> </list> </property> <property name="interceptorNames"> <list> <value>transactionInterceptor</value> </list> </property> </bean> <!-- 配置DAO--> <bean id="userDao" class="com.bluesky.spring.dao.UserDaoImpl"> <property name="sessionFactory" ref="sessionFactory" /> </bean> </beans> 第四种方式:使用tx标签配置的拦截器 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> <context:annotation-config /> <context:component-scan base-package="com.bluesky" /> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="configLocation" value="classpath:hibernate.cfg.xml" /> <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" /> </bean> <!--定义事务管理器(声明式的事务)--> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="*" propagation="REQUIRED" /> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="interceptorPointCuts" expression="execution(* com.bluesky.spring.dao.*.*(..))" /> <aop:advisor advice-ref="txAdvice" pointcut-ref="interceptorPointCuts" /> </aop:config> </beans> 第五种方式:全注解 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> <context:annotation-config /> <context:component-scan base-package="com.bluesky" /> <tx:annotation-driven transaction-manager="transactionManager"/> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="configLocation" value="classpath:hibernate.cfg.xml" /> <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" /> </bean> <!-- 定义事务管理器(声明式的事务) --> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> </beans> 此时在DAO上需加上@Transactional注解,如下: package com.bluesky.spring.dao; import java.util.List; import org.hibernate.SessionFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.orm.hibernate3.support.HibernateDaoSupport; import org.springframework.stereotype.Component; import com.bluesky.spring.domain.User; @Transactional @Component("userDao") public class UserDaoImpl extends HibernateDaoSupport implements UserDao { public List<User> listUsers() { return this.getSession().createQuery("from User").list(); } } spring里面事务的传播属性和事务隔离级别 一、Propagation (事务的传播属性)Propagation : key属性确定代理应该给哪个方法增加事务行为。这样的属性最重要的部份是传播行为。有以下选项可供使用: PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。PROPAGATION_SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起。PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。PROPAGATION_NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。1: PROPAGATION_REQUIRED加入当前正要执行的事务不在另外一个事务里,那么就起一个新的事务比如说,ServiceB.methodB的事务级别定义为PROPAGATION_REQUIRED, 那么由于执行ServiceA.methodA的时候,ServiceA.methodA已经起了事务,这时调用ServiceB.methodB,ServiceB.methodB看到自己已经运行在ServiceA.methodA的事务内部,就不再起新的事务。而假如ServiceA.methodA运行的时候发现自己没有在事务中,他就会为自己分配一个事务。这样,在ServiceA.methodA或者在ServiceB.methodB内的任何地方出现异常,事务都会被回滚。即使ServiceB.methodB的事务已经被提交,但是ServiceA.methodA在接下来fail要回滚,ServiceB.methodB也要回滚2: PROPAGATION_SUPPORTS如果当前在事务中,即以事务的形式运行,如果当前不再一个事务中,那么就以非事务的形式运行3: PROPAGATION_MANDATORY必须在一个事务中运行。也就是说,他只能被一个父事务调用。否则,他就要抛出异常4: PROPAGATION_REQUIRES_NEW这个就比较绕口了。 比如我们设计ServiceA.methodA的事务级别为PROPAGATION_REQUIRED,ServiceB.methodB的事务级别为PROPAGATION_REQUIRES_NEW,那么当执行到ServiceB.methodB的时候,ServiceA.methodA所在的事务就会挂起,ServiceB.methodB会起一个新的事务,等待ServiceB.methodB的事务完成以后,他才继续执行。他与PROPAGATION_REQUIRED 的事务区别在于事务的回滚程度了。因为ServiceB.methodB是新起一个事务,那么就是存在两个不同的事务。如果ServiceB.methodB已经提交,那么ServiceA.methodA失败回滚,ServiceB.methodB是不会回滚的。如果ServiceB.methodB失败回滚,如果他抛出的异常被ServiceA.methodA捕获,ServiceA.methodA事务仍然可能提交。5: PROPAGATION_NOT_SUPPORTED当前不支持事务。比如ServiceA.methodA的事务级别是PROPAGATION_REQUIRED ,而ServiceB.methodB的事务级别是PROPAGATION_NOT_SUPPORTED ,那么当执行到ServiceB.methodB时,ServiceA.methodA的事务挂起,而他以非事务的状态运行完,再继续ServiceA.methodA的事务。6: PROPAGATION_NEVER不能在事务中运行。假设ServiceA.methodA的事务级别是PROPAGATION_REQUIRED, 而ServiceB.methodB的事务级别是PROPAGATION_NEVER ,那么ServiceB.methodB就要抛出异常了。7: PROPAGATION_NESTED理解Nested的关键是savepoint。他与PROPAGATION_REQUIRES_NEW的区别是,PROPAGATION_REQUIRES_NEW另起一个事务,将会与他的父事务相互独立,而Nested的事务和他的父事务是相依的,他的提交是要等和他的父事务一块提交的。也就是说,如果父事务最后回滚,他也要回滚的。而Nested事务的好处是他有一个savepoint。 ServiceA {/** 事务属性配置为 PROPAGATION_REQUIRED*/ void methodA() {try {//savepointServiceB.methodB(); //PROPAGATION_NESTED 级别} catch (SomeException) {// 执行其他业务, 如 ServiceC.methodC();}}} 也就是说ServiceB.methodB失败回滚,那么ServiceA.methodA也会回滚到savepoint点上,ServiceA.methodA可以选择另外一个分支,比如ServiceC.methodC,继续执行,来尝试完成自己的事务。但是这个事务并没有在EJB标准中定义。二、Isolation Level(事务隔离等级):1、Serializable:最严格的级别,事务串行执行,资源消耗最大;2、REPEATABLE READ:保证了一个事务不会修改已经由另一个事务读取但未提交(回滚)的数据。避免了“脏读取”和“不可重复读取”的情况,但是带来了更多的性能损失。3、READ COMMITTED:大多数主流数据库的默认事务等级,保证了一个事务不会读到另一个并行事务已修改但未提交的数据,避免了“脏读取”。该级别适用于大多数系统。4、Read Uncommitted:保证了读取过程中不会读取到非法数据。隔离级别在于处理多事务的并发问题。我们知道并行可以提高数据库的吞吐量和效率,但是并不是所有的并发事务都可以并发运行,这需要查看数据库教材的可串行化条件判断了。这里就不阐述。我们首先说并发中可能发生的3中不讨人喜欢的事情1: Dirty reads--读脏数据。也就是说,比如事务A的未提交(还依然缓存)的数据被事务B读走,如果事务A失败回滚,会导致事务B所读取的的数据是错误的。2: non-repeatable reads--数据不可重复读。比如事务A中两处读取数据-total-的值。在第一读的时候,total是100,然后事务B就把total的数据改成 200,事务A再读一次,结果就发现,total竟然就变成200了,造成事务A数据混乱。3: phantom reads--幻象读数据,这个和non-repeatable reads相似,也是同一个事务中多次读不一致的问题。但是non-repeatable reads的不一致是因为他所要取的数据集被改变了(比如total的数据),但是phantom reads所要读的数据的不一致却不是他所要读的数据集改变,而是他的条件数据集改变。比如Select account.id where account.name="ppgogo*",第一次读去了6个符合条件的id,第二次读取的时候,由于事务b把一个帐号的名字由"dd"改成"ppgogo1",结果取出来了7个数据。 Dirty reads non-repeatable reads phantom reads Serializable 不会 不会 不会REPEATABLE READ 不会 不会 会READ COMMITTED 不会 会 会Read Uncommitted 会 会 会 三、readOnly事务属性中的readOnly标志表示对应的事务应该被最优化为只读事务。这是一个最优化提示。在一些情况下,一些事务策略能够起到显著的最优化效果,例如在使用Object/Relational映射工具(如:Hibernate或TopLink)时避免dirty checking(试图“刷新”)。四、Timeout在事务属性中还有定义“timeout”值的选项,指定事务超时为几秒。在JTA中,这将被简单地传递到J2EE服务器的事务协调程序,并据此得到相应的解释 spring里面事务的传播属性和事务隔离级别 一、Propagation (事务的传播属性)Propagation : key属性确定代理应该给哪个方法增加事务行为。这样的属性最重要的部份是传播行为。有以下选项可供使用:PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。PROPAGATION_SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起。PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。PROPAGATION_NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。1: PROPAGATION_REQUIRED加入当前正要执行的事务不在另外一个事务里,那么就起一个新的事务比如说,ServiceB.methodB的事务级别定义为PROPAGATION_REQUIRED, 那么由于执行ServiceA.methodA的时候,ServiceA.methodA已经起了事务,这时调用ServiceB.methodB,ServiceB.methodB看到自己已经运行在ServiceA.methodA的事务内部,就不再起新的事务。而假如ServiceA.methodA运行的时候发现自己没有在事务中,他就会为自己分配一个事务。这样,在ServiceA.methodA或者在ServiceB.methodB内的任何地方出现异常,事务都会被回滚。即使ServiceB.methodB的事务已经被提交,但是ServiceA.methodA在接下来fail要回滚,ServiceB.methodB也要回滚2: PROPAGATION_SUPPORTS如果当前在事务中,即以事务的形式运行,如果当前不再一个事务中,那么就以非事务的形式运行3: PROPAGATION_MANDATORY必须在一个事务中运行。也就是说,他只能被一个父事务调用。否则,他就要抛出异常4: PROPAGATION_REQUIRES_NEW这个就比较绕口了。 比如我们设计ServiceA.methodA的事务级别为PROPAGATION_REQUIRED,ServiceB.methodB的事务级别为PROPAGATION_REQUIRES_NEW,那么当执行到ServiceB.methodB的时候,ServiceA.methodA所在的事务就会挂起,ServiceB.methodB会起一个新的事务,等待ServiceB.methodB的事务完成以后,他才继续执行。他与PROPAGATION_REQUIRED 的事务区别在于事务的回滚程度了。因为ServiceB.methodB是新起一个事务,那么就是存在两个不同的事务。如果ServiceB.methodB已经提交,那么ServiceA.methodA失败回滚,ServiceB.methodB是不会回滚的。如果ServiceB.methodB失败回滚,如果他抛出的异常被ServiceA.methodA捕获,ServiceA.methodA事务仍然可能提交。5: PROPAGATION_NOT_SUPPORTED当前不支持事务。比如ServiceA.methodA的事务级别是PROPAGATION_REQUIRED ,而ServiceB.methodB的事务级别是PROPAGATION_NOT_SUPPORTED ,那么当执行到ServiceB.methodB时,ServiceA.methodA的事务挂起,而他以非事务的状态运行完,再继续ServiceA.methodA的事务。6: PROPAGATION_NEVER不能在事务中运行。假设ServiceA.methodA的事务级别是PROPAGATION_REQUIRED, 而ServiceB.methodB的事务级别是PROPAGATION_NEVER ,那么ServiceB.methodB就要抛出异常了。7: PROPAGATION_NESTED理解Nested的关键是savepoint。他与PROPAGATION_REQUIRES_NEW的区别是,PROPAGATION_REQUIRES_NEW另起一个事务,将会与他的父事务相互独立,而Nested的事务和他的父事务是相依的,他的提交是要等和他的父事务一块提交的。也就是说,如果父事务最后回滚,他也要回滚的。而Nested事务的好处是他有一个savepoint。 ServiceA {/** 事务属性配置为 PROPAGATION_REQUIRED*/ void methodA() {try {//savepointServiceB.methodB(); //PROPAGATION_NESTED 级别} catch (SomeException) {// 执行其他业务, 如 ServiceC.methodC();}}} 也就是说ServiceB.methodB失败回滚,那么ServiceA.methodA也会回滚到savepoint点上,ServiceA.methodA可以选择另外一个分支,比如ServiceC.methodC,继续执行,来尝试完成自己的事务。但是这个事务并没有在EJB标准中定义。二、Isolation Level(事务隔离等级):1、Serializable:最严格的级别,事务串行执行,资源消耗最大;2、REPEATABLE READ:保证了一个事务不会修改已经由另一个事务读取但未提交(回滚)的数据。避免了“脏读取”和“不可重复读取”的情况,但是带来了更多的性能损失。3、READ COMMITTED:大多数主流数据库的默认事务等级,保证了一个事务不会读到另一个并行事务已修改但未提交的数据,避免了“脏读取”。该级别适用于大多数系统。4、Read Uncommitted:保证了读取过程中不会读取到非法数据。隔离级别在于处理多事务的并发问题。我们知道并行可以提高数据库的吞吐量和效率,但是并不是所有的并发事务都可以并发运行,这需要查看数据库教材的可串行化条件判断了。这里就不阐述。我们首先说并发中可能发生的3中不讨人喜欢的事情1: Dirty reads--读脏数据。也就是说,比如事务A的未提交(还依然缓存)的数据被事务B读走,如果事务A失败回滚,会导致事务B所读取的的数据是错误的。2: non-repeatable reads--数据不可重复读。比如事务A中两处读取数据-total-的值。在第一读的时候,total是100,然后事务B就把total的数据改成 200,事务A再读一次,结果就发现,total竟然就变成200了,造成事务A数据混乱。3: phantom reads--幻象读数据,这个和non-repeatable reads相似,也是同一个事务中多次读不一致的问题。但是non-repeatable reads的不一致是因为他所要取的数据集被改变了(比如total的数据),但是phantom reads所要读的数据的不一致却不是他所要读的数据集改变,而是他的条件数据集改变。比如Select account.id where account.name="ppgogo*",第一次读去了6个符合条件的id,第二次读取的时候,由于事务b把一个帐号的名字由"dd"改成"ppgogo1",结果取出来了7个数据。 Dirty reads non-repeatable reads phantom reads Serializable 不会 不会 不会REPEATABLE READ 不会 不会 会READ COMMITTED 不会 会 会Read Uncommitted 会 会 会 三、readOnly事务属性中的readOnly标志表示对应的事务应该被最优化为只读事务。这是一个最优化提示。在一些情况下,一些事务策略能够起到显著的最优化效果,例如在使用Object/Relational映射工具(如:Hibernate或TopLink)时避免dirty checking(试图“刷新”)。四、Timeout在事务属性中还有定义“timeout”值的选项,指定事务超时为几秒。在JTA中,这将被简单地传递到J2EE服务器的事务协调程序,并据此得到相应的解释 20110112数据库提供了四种事务隔离级别, 不同的隔离级别采用不同的锁类开来实现. 在四种隔离级别中, Serializable的级别最高, Read Uncommited级别最低. 大多数数据库的默认隔离级别为: Read Commited,如Sql Server , Oracle. 少数数据库默认的隔离级别为Repeatable Read, 如MySQL InnoDB存储引擎 即使是最低的级别,也不会出现 第一类 丢失 更新问题 . 脏读(事务没提交,提前读取) :脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。 不可重复读(两次读的不一致) :是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。例如,一个编辑人员两次读取同一文档,但在两次读取之间,作者重写了该文档。当编辑人员第二次读取文档时,文档已更改。原始读取不可重复。如果只有在作者全部完成编写后编辑人员才可以读取文档,则可以避免该问题。 幻读 : 是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。例如,一个编辑人员更改作者提交的文档,但当生产部门将其更改内容合并到该文档的主复本时,发现作者已将未编辑的新材料添加到该文档中。如果在编辑人员和生产部门完成对原始文档的处理之前,任何人都不能将新材料添加到文档中,则可以避免该问题。 第一类更新丢失(回滚丢失) : 当2个事务更新相同的数据源,如果第一个事务被提交,而另外一个事务却被撤销,那么会连同第一个事务所做的跟新也被撤销。也就是说第一个事务做的跟新丢失了。 第二类更新丢失(覆盖丢失) : 第二类更新丢失实在实际应用中经常遇到的并发问题,他和不可重复读本质上是同一类并发问题,通常他被看做不可重复读的特例:当2个或这个多个事务查询同样的记录然后各自基于最初的查询结果更新该行时,会造成第二类丢失更新。因为每个事务都不知道不知道其他事务的存在,最后一个事务对记录做的修改将覆盖其他事务对该记录做的已提交的跟新... 补充 : 基于元数据的 Spring 声明性事务 : Isolation 属性一共支持五种事务设置,具体介绍如下: l DEFAULT 使用数据库设置的隔离级别 ( 默认 ) ,由 DBA 默认的设置来决定隔离级别 . l READ_UNCOMMITTED 会出现脏读、不可重复读、幻读 ( 隔离级别最低,并发性能高 ) l READ_COMMITTED 会出现不可重复读、幻读问题(锁定正在读取的行) l REPEATABLE_READ 会出幻读(锁定所读取的所有行) l SERIALIZABLE 保证所有的情况不会发生(锁表) 不可重复读的重点是修改 : 同样的条件 , 你读取过的数据 , 再次读取出来发现值不一样了 幻读的重点在于新增或者删除 同样的条件 , 第 1 次和第 2 次读出来的记录数不一样 转自:http://www.cnblogs.com/yuanfy008/p/4174340.html转自:http://blog.csdn.net/it_man/article/details/5074371
1.snowflake简介 互联网快速发展的今天,分布式应用系统已经见怪不怪,在分布式系统中,我们需要各种各样的ID,既然是ID那么必然是要保证全局唯一,除此之外,不同当业务还需要不同的特性,比如像并发巨大的业务要求ID生成效率高,吞吐大;比如某些银行类业务,需要按每日日期制定交易流水号;又比如我们希望用户的ID是随机的,无序的,纯数字的,且位数长度是小于10位的。等等,不同的业务场景需要的ID特性各不一样,于是,衍生了各种ID生成器,但大多数利用数据库控制ID的生成,性能受数据库并发能力限制,那么有没有一款不需要依赖任何中间件(如数据库,分布式缓存服务等)的ID生成器呢?本着取之于开源,用之于开源的原则,今天,特此介绍Twitter开源的一款分布式自增ID算法snowflake,并附上算法原理推导和演算过程!snowflake算法是一款本地生成的(ID生成过程不依赖任何中间件,无网络通信),保证ID全局唯一,并且ID总体有序递增,性能每秒生成300w+。 2.snowflake算法原理 snowflake生产的ID二进制结构表示如下(每部分用-分开):0 - 00000000 00000000 00000000 00000000 00000000 0 - 00000 - 00000 - 00000000 0000 第一位未使用,接下来的41位为毫秒级时间(41位的长度可以使用69年,从1970-01-01 08:00:00),然后是5位datacenterId(最大支持2^5=32个,二进制表示从00000-11111,也即是十进制0-31),和5位workerId(最大支持2^5=32个,原理同datacenterId),所以datacenterId*workerId最多支持部署1024个节点,最后12位是毫秒内的计数(12位的计数顺序号支持每个节点每毫秒产生2^12=4096个ID序号). 所有位数加起来共64位,恰好是一个Long型(转换为字符串长度为18). 单台机器实例,通过时间戳保证前41位是唯一的,分布式系统多台机器实例下,通过对每个机器实例分配不同的datacenterId和workerId避免中间的10位碰撞。最后12位每毫秒从0递增生产ID,再提一次:每毫秒最多生成4096个ID,每秒可达4096000个。理论上,只要CPU计算能力足够,单机每秒可生产400多万个,实测300w+,效率之高由此可见。 (该节改编自:http://www.cnblogs.com/relucent/p/4955340.html) 3.snowflake算法源码(java版) @ToString @Slf4j public class SnowflakeIdFactory { private final long twepoch = 1288834974657L; private final long workerIdBits = 5L; private final long datacenterIdBits = 5L; private final long maxWorkerId = -1L ^ (-1L << workerIdBits); private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); private final long sequenceBits = 12L; private final long workerIdShift = sequenceBits; private final long datacenterIdShift = sequenceBits + workerIdBits; private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; private final long sequenceMask = -1L ^ (-1L << sequenceBits); private long workerId; private long datacenterId; private long sequence = 0L; private long lastTimestamp = -1L; public SnowflakeIdFactory(long workerId, long datacenterId) { if (workerId > maxWorkerId || workerId < 0) { throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId)); } if (datacenterId > maxDatacenterId || datacenterId < 0) { throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId)); } this.workerId = workerId; this.datacenterId = datacenterId; } public synchronized long nextId() { long timestamp = timeGen(); if (timestamp < lastTimestamp) { //服务器时钟被调整了,ID生成器停止服务. throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp)); } if (lastTimestamp == timestamp) { sequence = (sequence + 1) & sequenceMask; if (sequence == 0) { timestamp = tilNextMillis(lastTimestamp); } } else { sequence = 0L; } lastTimestamp = timestamp; return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence; } protected long tilNextMillis(long lastTimestamp) { long timestamp = timeGen(); while (timestamp <= lastTimestamp) { timestamp = timeGen(); } return timestamp; } protected long timeGen() { return System.currentTimeMillis(); } public static void testProductIdByMoreThread(int dataCenterId, int workerId, int n) throws InterruptedException { List<Thread> tlist = new ArrayList<>(); Set<Long> setAll = new HashSet<>(); CountDownLatch cdLatch = new CountDownLatch(10); long start = System.currentTimeMillis(); int threadNo = dataCenterId; Map<String,SnowflakeIdFactory> idFactories = new HashMap<>(); for(int i=0;i<10;i++){ //用线程名称做map key. idFactories.put("snowflake"+i,new SnowflakeIdFactory(workerId, threadNo++)); } for(int i=0;i<10;i++){ Thread temp =new Thread(new Runnable() { @Override public void run() { Set<Long> setId = new HashSet<>(); SnowflakeIdFactory idWorker = idFactories.get(Thread.currentThread().getName()); for(int j=0;j<n;j++){ setId.add(idWorker.nextId()); } synchronized (setAll){ setAll.addAll(setId); log.info("{}生产了{}个id,并成功加入到setAll中.",Thread.currentThread().getName(),n); } cdLatch.countDown(); } },"snowflake"+i); tlist.add(temp); } for(int j=0;j<10;j++){ tlist.get(j).start(); } cdLatch.await(); long end1 = System.currentTimeMillis() - start; log.info("共耗时:{}毫秒,预期应该生产{}个id, 实际合并总计生成ID个数:{}",end1,10*n,setAll.size()); } public static void testProductId(int dataCenterId, int workerId, int n){ SnowflakeIdFactory idWorker = new SnowflakeIdFactory(workerId, dataCenterId); SnowflakeIdFactory idWorker2 = new SnowflakeIdFactory(workerId+1, dataCenterId); Set<Long> setOne = new HashSet<>(); Set<Long> setTow = new HashSet<>(); long start = System.currentTimeMillis(); for (int i = 0; i < n; i++) { setOne.add(idWorker.nextId());//加入set } long end1 = System.currentTimeMillis() - start; log.info("第一批ID预计生成{}个,实际生成{}个<<<<*>>>>共耗时:{}",n,setOne.size(),end1); for (int i = 0; i < n; i++) { setTow.add(idWorker2.nextId());//加入set } long end2 = System.currentTimeMillis() - start; log.info("第二批ID预计生成{}个,实际生成{}个<<<<*>>>>共耗时:{}",n,setTow.size(),end2); setOne.addAll(setTow); log.info("合并总计生成ID个数:{}",setOne.size()); } public static void testPerSecondProductIdNums(){ SnowflakeIdFactory idWorker = new SnowflakeIdFactory(1, 2); long start = System.currentTimeMillis(); int count = 0; for (int i = 0; System.currentTimeMillis()-start<1000; i++,count=i) { /** 测试方法一: 此用法纯粹的生产ID,每秒生产ID个数为300w+ */ idWorker.nextId(); /** 测试方法二: 在log中打印,同时获取ID,此用法生产ID的能力受限于log.error()的吞吐能力. * 每秒徘徊在10万左右. */ //log.error("{}",idWorker.nextId()); } long end = System.currentTimeMillis()-start; System.out.println(end); System.out.println(count); } public static void main(String[] args) { /** case1: 测试每秒生产id个数? * 结论: 每秒生产id个数300w+ */ //testPerSecondProductIdNums(); /** case2: 单线程-测试多个生产者同时生产N个id,验证id是否有重复? * 结论: 验证通过,没有重复. */ //testProductId(1,2,10000);//验证通过! //testProductId(1,2,20000);//验证通过! /** case3: 多线程-测试多个生产者同时生产N个id, 全部id在全局范围内是否会重复? * 结论: 验证通过,没有重复. */ try { testProductIdByMoreThread(1,2,100000);//单机测试此场景,性能损失至少折半! } catch (InterruptedException e) { e.printStackTrace(); } } } 测试用例: /** case1: 测试每秒生产id个数? * 结论: 每秒生产id个数300w+ */ //testPerSecondProductIdNums(); /** case2: 单线程-测试多个生产者同时生产N个id,验证id是否有重复? * 结论: 验证通过,没有重复. */ //testProductId(1,2,10000);//验证通过! //testProductId(1,2,20000);//验证通过! /** case3: 多线程-测试多个生产者同时生产N个id, 全部id在全局范围内是否会重复? * 结论: 验证通过,没有重复. */ try { testProductIdByMoreThread(1,2,100000);//单机测试此场景,性能损失至少折半! } catch (InterruptedException e) { e.printStackTrace(); } 4.snowflake算法推导和演算过程说明:演算使用的对象实例:SnowflakeIdFactory idWorker = new SnowflakeIdFactory(1, 2);运行时数据workerId=1,datacenterId=2,分别表示机器实例的生产者编号,数据中心编号;sequence=0表示每毫秒生产ID从0开始计数递增;以下演算基于时间戳=1482394743339时刻进行推导。 一句话描述:以下演算模拟了1482394743339这一毫秒时刻,workerId=1,datacenterId=2的id生成器,生产第一个id的过程。 我自己弄的一个 import java.text.SimpleDateFormat; import java.util.Date; /** * Created by yinx on 2017/10/27 0027. * Twitter_Snowflake<br> * SnowFlake的结构如下(每部分用-分开):<br> * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 <br> * 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0<br> * 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截) * 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的startTime属性)。41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69<br> * 10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId<br> * 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号<br> * 加起来刚好64位,为一个Long型。<br> * SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。 */ //@RunWith(SpringJUnit4ClassRunner.class) public class SnowflakeIdWorker { // ==============================Fields=========================================== /** 开始时间截 (2015-01-01) */ private final long twepoch = 1420041600000L; /** 机器id所占的位数 */ private final long workerIdBits = 5L; /** 数据标识id所占的位数 */ private final long datacenterIdBits = 5L; /** 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) */ private final long maxWorkerId = -1L ^ (-1L << workerIdBits); /** 支持的最大数据标识id,结果是31 */ private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); /** 序列在id中占的位数 */ private final long sequenceBits = 12L; /** 机器ID向左移12位 */ private final long workerIdShift = sequenceBits; /** 数据标识id向左移17位(12+5) */ private final long datacenterIdShift = sequenceBits + workerIdBits; /** 时间截向左移22位(5+5+12) */ private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; /** 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095) */ private final long sequenceMask = -1L ^ (-1L << sequenceBits); /** 工作机器ID(0~31) */ private long workerId; /** 数据中心ID(0~31) */ private long datacenterId; /** 毫秒内序列(0~4095) */ private long sequence = 0L; /** 上次生成ID的时间截 */ private long lastTimestamp = -1L; //==============================Constructors===================================== /** * 构造函数 * @param workerId 工作ID (0~31) * @param datacenterId 数据中心ID (0~31) */ public SnowflakeIdWorker(long workerId, long datacenterId) { if (workerId > maxWorkerId || workerId < 0) { throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId)); } if (datacenterId > maxDatacenterId || datacenterId < 0) { throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId)); } this.workerId = workerId; this.datacenterId = datacenterId; } // ==============================Methods========================================== /** * 获得下一个ID (该方法是线程安全的) * @return SnowflakeId */ public synchronized long nextId() { long timestamp = timeGen(); //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常 if (timestamp < lastTimestamp) { throw new RuntimeException( String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp)); } //如果是同一时间生成的,则进行毫秒内序列 if (lastTimestamp == timestamp) { sequence = (sequence + 1) & sequenceMask; //毫秒内序列溢出 if (sequence == 0) { //阻塞到下一个毫秒,获得新的时间戳 timestamp = tilNextMillis(lastTimestamp); } } //时间戳改变,毫秒内序列重置 else { sequence = 0L; } //上次生成ID的时间截 lastTimestamp = timestamp; //移位并通过或运算拼到一起组成64位的ID return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence; } /** * 阻塞到下一个毫秒,直到获得新的时间戳 * @param lastTimestamp 上次生成ID的时间截 * @return 当前时间戳 */ protected long tilNextMillis(long lastTimestamp) { long timestamp = timeGen(); while (timestamp <= lastTimestamp) { timestamp = timeGen(); } return timestamp; } /** * 返回以毫秒为单位的当前时间 * @return 当前时间(毫秒) */ protected long timeGen() { return System.currentTimeMillis(); } //==============================Test============================================= /** 测试 */ public static void main(String[] args) { SimpleDateFormat sdf=new SimpleDateFormat("yyyyMMddHHmmss"); SnowflakeIdWorker idWorker = new SnowflakeIdWorker(0, 0); for (int i = 0; i < 10000; i++) { long id = idWorker.nextId(); String orderNo = sdf.format(new Date()); System.out.println(Long.toBinaryString(id)); orderNo = orderNo + "_" + id; System.out.println(orderNo + "=====================" + orderNo.length()); } } } end!参考https://github.com/twitter/snowflake http://www.cnblogs.com/relucent/p/4955340.html 转自:http://blog.csdn.net/li396864285/article/details/54668031
1,管理类 import org.quartz.CronTrigger; import org.quartz.JobDetail; import org.quartz.Scheduler; /** * Quartz调度管理器 * Created by yinx on 2017/9/25 0025. */ public class QuartzManager { private static String JOB_GROUP_NAME = "EXTJWEB_JOBGROUP_NAME"; private static String TRIGGER_GROUP_NAME = "EXTJWEB_TRIGGERGROUP_NAME"; /** * @Description: 添加一个定时任务,使用默认的任务组名,触发器名,触发器组名 * * @param sched 调度器 * @param jobName 任务名 * @param cls 任务 * @param time 时间设置,参考quartz说明文档 * * @Title: QuartzManager.java */ public static void addJob(Scheduler sched, String jobName, @SuppressWarnings("rawtypes") Class cls, String time) { try { JobDetail jobDetail = new JobDetail(jobName, JOB_GROUP_NAME, cls);// 任务名,任务组,任务执行类 // 触发器 CronTrigger trigger = new CronTrigger(jobName, TRIGGER_GROUP_NAME);// 触发器名,触发器组 trigger.setCronExpression(time);// 触发器时间设定 sched.scheduleJob(jobDetail, trigger); // 启动 if (!sched.isShutdown()) { sched.start(); } } catch (Exception e) { throw new RuntimeException(e); } } /** * @Description: 添加一个定时任务 * @param sched 调度器 * @param jobName 任务名 * @param jobGroupName 任务组名 * @param triggerName 触发器名 * @param triggerGroupName 触发器组名 * @param jobClass 任务 * @param time 时间设置,参考quartz说明文档 * @Title: QuartzManager.java */ public static void addJob(Scheduler sched, String jobName, String jobGroupName, String triggerName, String triggerGroupName, @SuppressWarnings("rawtypes") Class jobClass, String time) { try { JobDetail jobDetail = new JobDetail(jobName, jobGroupName, jobClass);// 任务名,任务组,任务执行类 // 触发器 CronTrigger trigger = new CronTrigger(triggerName, triggerGroupName);// 触发器名,触发器组 trigger.setCronExpression(time);// 触发器时间设定 sched.scheduleJob(jobDetail, trigger); } catch (Exception e) { throw new RuntimeException(e); } } /** * @Description: 修改一个任务的触发时间(使用默认的任务组名,触发器名,触发器组名) * * @param sched 调度器 * @param jobName * @param time * * @Title: QuartzManager.java */ @SuppressWarnings("rawtypes") public static void modifyJobTime(Scheduler sched, String jobName, String time) { try { CronTrigger trigger = (CronTrigger) sched.getTrigger(jobName, TRIGGER_GROUP_NAME); if (trigger == null) { return; } String oldTime = trigger.getCronExpression(); if (!oldTime.equalsIgnoreCase(time)) { JobDetail jobDetail = sched.getJobDetail(jobName, JOB_GROUP_NAME); Class objJobClass = jobDetail.getJobClass(); removeJob(sched, jobName); addJob(sched, jobName, objJobClass, time); } } catch (Exception e) { throw new RuntimeException(e); } } /** * @Description: 修改一个任务的触发时间 * * @param sched 调度器 * @param triggerName * @param triggerGroupName * @param time * * @Title: QuartzManager.java */ public static void modifyJobTime(Scheduler sched, String triggerName, String triggerGroupName, String time) { try { CronTrigger trigger = (CronTrigger) sched.getTrigger(triggerName, triggerGroupName); if (trigger == null) { return; } String oldTime = trigger.getCronExpression(); if (!oldTime.equalsIgnoreCase(time)) { CronTrigger ct = (CronTrigger) trigger; // 修改时间 ct.setCronExpression(time); // 重启触发器 sched.resumeTrigger(triggerName, triggerGroupName); } } catch (Exception e) { throw new RuntimeException(e); } } /** * @Description: 移除一个任务(使用默认的任务组名,触发器名,触发器组名) * * @param sched 调度器 * @param jobName * * @Title: QuartzManager.java */ public static void removeJob(Scheduler sched, String jobName) { try { sched.pauseTrigger(jobName, TRIGGER_GROUP_NAME);// 停止触发器 sched.unscheduleJob(jobName, TRIGGER_GROUP_NAME);// 移除触发器 sched.deleteJob(jobName, JOB_GROUP_NAME);// 删除任务 } catch (Exception e) { throw new RuntimeException(e); } } /** * @Description: 移除一个任务 * * @param sched 调度器 * @param jobName * @param jobGroupName * @param triggerName * @param triggerGroupName * * @Title: QuartzManager.java */ public static void removeJob(Scheduler sched, String jobName, String jobGroupName, String triggerName, String triggerGroupName) { try { sched.pauseTrigger(triggerName, triggerGroupName);// 停止触发器 sched.unscheduleJob(triggerName, triggerGroupName);// 移除触发器 sched.deleteJob(jobName, jobGroupName);// 删除任务 } catch (Exception e) { throw new RuntimeException(e); } } /** * @Description:启动所有定时任务 * * @param sched 调度器 * * @Title: QuartzManager.java */ public static void startJobs(Scheduler sched) { try { sched.start(); } catch (Exception e) { throw new RuntimeException(e); } } /** * @Description:关闭所有定时任务 * * @param sched 调度器 * * @Title: QuartzManager.java */ public static void shutdownJobs(Scheduler sched) { try { if (!sched.isShutdown()) { sched.shutdown(); } } catch (Exception e) { throw new RuntimeException(e); } } } 2,dingshi定时类 import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import java.text.SimpleDateFormat; import java.util.Date; /** * quartz示例定时器类 * Created by Administrator on 2017/9/25 0025. */ public class QuartzJobExample implements Job{ @Override public void execute(JobExecutionContext arg0) throws JobExecutionException { System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + ""); } } 3,test类 import org.junit.Test; import org.junit.runner.RunWith; import org.quartz.Scheduler; import org.quartz.SchedulerFactory; import org.quartz.impl.StdSchedulerFactory; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** * Created by yinx on 2017/9/25 0025. */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { "classpath:META-INF/test-config.xml" }) public class QuartzTest { @Test public void quartzTest(){ System.out.println("========================" + QuartzTest.class); } @Test public void quartz() { try { SchedulerFactory gSchedulerFactory = new StdSchedulerFactory(); Scheduler sche = gSchedulerFactory.getScheduler(); String job_name = "动态任务调度"; System.out.println("【系统启动】开始(每1秒输出一次)..."); QuartzManager.addJob(sche, job_name, QuartzJobExample.class, "0/1 * * * * ?"); Thread.sleep(3000); System.out.println("【修改时间】开始(每2秒输出一次)..."); QuartzManager.modifyJobTime(sche, job_name, "10/2 * * * * ?"); Thread.sleep(4000); System.out.println("【移除定时】开始..."); QuartzManager.removeJob(sche, job_name); System.out.println("【移除定时】成功"); System.out.println("【再次添加定时任务】开始(每10秒输出一次)..."); QuartzManager.addJob(sche, job_name, QuartzJobExample.class, "10 * * * * ?"); Thread.sleep(30000); System.out.println("【移除定时】开始..."); QuartzManager.removeJob(sche, job_name); System.out.println("【移除定时】成功"); } catch (Exception e) { e.printStackTrace(); } } } 这也是我在别的博客看见的,地址忘了,写的挺好的这个
在操作json的数据格式的时候,如果没有指明数据类型,那么只能是基本类型或者是String类型,不能出现复杂数据类型。 for(Map<String, Object> map : datas){ String gw_id = map.get("GW_ID").toString(); gw_id = gw_id.substring(0, 14)+"1"+gw_id.substring(15); map.put("GW_ID", gw_id); map.put("UPDATE_TIME", new Date().getTime()); map.put("CREATE_TIME", map.get("CREATE_TIME")); map.put("LAST_COLLECT_TIME", map.get("LAST_COLLECT_TIME")); Set<String> keys = map.keySet(); for(String key:keys){ if(null == map.get(key)){ map.put(key, ""); } } } String jsondata = JSONArray.fromObject(datas).toString(); JSONArray jsonarray = JSONArray.fromObject(jsondata ); List<Map<String, Object>> objs = (List<Map<String, Object>>)JSONArray.toCollection(jsonarray, Map.class); 在这里,因为得到json字符串的时候用的是 JSONArray.fromObject(collenction),所有,在讲json字符串转换成json对象的时候,也只能用JSONArray.toCollection,并且,一定要带上后面的class参数。 将json字符串转换成对应的json对象以后,在遍历json对象时 for(Map<String, Object> data : objs) data.get("LAST_COLLECT_NUM") 此时,get()方法得到的是对象,并且是只能转换成基本数据类型或者是String类型的,如果强制转化成复杂的类型,会提示这个错误。 再如: Teacher类中,有三个属性。 private String teaId; private String teaName; private List<Student> stus; Teacher teacher_1 = new Teacher("编号1", "教师1", stus); JSONObject obj = JSONObject.fromObject(teacher_1); 这一步,将Teacher 对象转换成json字符串的时候,没有任何的问题。 下面,将JSONObject 转换成Teacher 对象,如果,不加后面的class参数,也会报这儿错误。加了后,这一步也正常。 Teacher teacherBean = (Teacher) JSONObject.toBean(obj, Teacher.class);Student studentBean = teacherBean.getStus().get(0); 当从teacherBean 对象中取出stus属性的值时候,就会提示: java.lang.ClassCastException:net.sf.ezmorph.bean.MorphDynaBean cannot be cast to com.edu.xukai.Student 用这种方式可以解决: map.put("stus", Student.class); Teacher teacherBean = (Teacher) JSONObject.toBean(obj, Teacher.class, map); 其中map对象是teacherBean对象中各个属性的类型,map额key是属性每次,value是属性的类型。 JSONArray.toArray(jsonArray, objectClass, classMap) JSONArray.toList(jsonArray, objectClass, classMap) 也支持这样的方式。 顺便贴一下我自己写的一个公共方法,后续再继续优化 /** * 封装返回参数 * @param requestBody * @param beanClass * @param <T> * @return * @throws IllegalAccessException * @throws InstantiationException */ public <T> T result(String requestBody, Class<T> beanClass, Map<String,Object> map) throws IllegalAccessException, InstantiationException { T t = beanClass.newInstance(); JSONObject jsonObject = null; JSONArray jsonarr= null; if(null != requestBody){ jsonObject = JSONObject.fromObject(requestBody); if(jsonObject.has("status")){ if(jsonObject.getString("status").equalsIgnoreCase("0")){ jsonarr= jsonObject.getJSONArray("data"); if(jsonarr.size() > 0){ //json对象转换成java对象 if(null == map){ t = (T) JSONObject.toBean(jsonarr.getJSONObject(0),beanClass); }else{ t = (T) JSONObject.toBean(jsonarr.getJSONObject(0),beanClass,map); } } }else{ System.out.println("==================================================================获取返回失败信息=========》》》》》》"+jsonObject.getString("msg")); return t; } }else{ System.out.println("==================================================================获取返回失败信息=========》》》》》》"+requestBody); return null; //jsonObject= JSONObject.fromObject(requestBody); //json对象转换成java对象 //t = (T) JSONObject.toBean(jsonObject,beanClass); //jsonarr= JSONArray.fromObject(requestBody); //json对象转换成java对象 //t = (T) JSONObject.toBean(jsonarr.getJSONObject(0),beanClass); } } return t; }
本文参考java 泛型详解、Java中的泛型方法、 java泛型详解 概述 泛型在java中有很重要的地位,在面向对象编程及各种设计模式中有非常广泛的应用。 什么是泛型?为什么要使用泛型? 泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。 泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。2.一个栗子 一个被举了无数次的例子: List arrayList = new ArrayList(); arrayList.add("aaaa"); arrayList.add(100); for(int i = 0; i< arrayList.size();i++){ String item = (String)arrayList.get(i); Log.d("泛型测试","item = " + item); } 毫无疑问,程序的运行结果会以崩溃结束: java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String ArrayList可以存放任意类型,例子中添加了一个String类型,添加了一个Integer类型,再使用时都以String的方式使用,因此程序崩溃了。为了解决类似这样的问题(在编译阶段就可以解决),泛型应运而生。 我们将第一行声明初始化list的代码更改一下,编译器会在编译阶段就能够帮我们发现类似这样的问题。 List<String> arrayList = new ArrayList<String>(); ... //arrayList.add(100); 在编译阶段,编译器就会报错 3.特性 泛型只在编译阶段有效。看下面的代码: List<String> stringArrayList = new ArrayList<String>(); List<Integer> integerArrayList = new ArrayList<Integer>(); Class classStringArrayList = stringArrayList.getClass(); Class classIntegerArrayList = integerArrayList.getClass(); if(classStringArrayList.equals(classIntegerArrayList)){ Log.d("泛型测试","类型相同"); } 输出结果:D/泛型测试: 类型相同。 通过上面的例子可以证明,在编译之后程序会采取去泛型化的措施。也就是说Java中的泛型,只在编译阶段有效。在编译过程中,正确检验泛型结果后,会将泛型的相关信息擦出,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。也就是说,泛型信息不会进入到运行时阶段。 对此总结成一句话:泛型类型在逻辑上看以看成是多个不同的类型,实际上都是相同的基本类型。 4.泛型的使用 泛型有三种使用方式,分别为:泛型类、泛型接口、泛型方法 4.3 泛型类 泛型类型用于类的定义中,被称为泛型类。通过泛型可以完成对一组类的操作对外开放相同的接口。最典型的就是各种容器类,如:List、Set、Map。 泛型类的最基本写法(这么看可能会有点晕,会在下面的例子中详解): class 类名称 <泛型标识:可以随便写任意标识号,标识指定的泛型的类型>{ private 泛型标识 /*(成员变量类型)*/ var; } } 一个最普通的泛型类: //此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型 //在实例化泛型类时,必须指定T的具体类型 public class Generic<T>{ //key这个成员变量的类型为T,T的类型由外部指定 private T key; public Generic(T key) { //泛型构造方法形参key的类型也为T,T的类型由外部指定 this.key = key; } public T getKey(){ //泛型方法getKey的返回值类型为T,T的类型由外部指定 return key; } } //泛型的类型参数只能是类类型(包括自定义类),不能是简单类型 //传入的实参类型需与泛型的类型参数类型相同,即为Integer. Generic<Integer> genericInteger = new Generic<Integer>(123456); //传入的实参类型需与泛型的类型参数类型相同,即为String. Generic<String> genericString = new Generic<String>("key_vlaue"); Log.d("泛型测试","key is " + genericInteger.getKey()); Log.d("泛型测试","key is " + genericString.getKey()); 12-27 09:20:04.432 13063-13063/? D/泛型测试: key is 123456 12-27 09:20:04.432 13063-13063/? D/泛型测试: key is key_vlaue 定义的泛型类,就一定要传入泛型类型实参么?并不是这样,在使用泛型的时候如果传入泛型实参,则会根据传入的泛型实参做相应的限制,此时泛型才会起到本应起到的限制作用。如果不传入泛型类型实参的话,在泛型类中使用泛型的方法或成员变量定义的类型可以为任何的类型。 看一个例子: Generic generic = new Generic("111111"); Generic generic1 = new Generic(4444); Generic generic2 = new Generic(55.55); Generic generic3 = new Generic(false); Log.d("泛型测试","key is " + generic.getKey()); Log.d("泛型测试","key is " + generic1.getKey()); Log.d("泛型测试","key is " + generic2.getKey()); Log.d("泛型测试","key is " + generic3.getKey()); D/泛型测试: key is 111111 D/泛型测试: key is 4444 D/泛型测试: key is 55.55 D/泛型测试: key is false 注意: 泛型的类型参数只能是类类型,不能是简单类型。不能对确切的泛型类型使用instanceof操作。如下面的操作是非法的,编译时会出错。 if(ex_num instanceof Generic<Number>){ } 4.4 泛型接口 泛型接口与泛型类的定义及使用基本相同。泛型接口常被用在各种类的生产器中,可以看一个例子: //定义一个泛型接口 public interface Generator<T> { public T next(); } 当实现泛型接口的类,未传入泛型实参时: /** * 未传入泛型实参时,与泛型类的定义相同,在声明类的时候,需将泛型的声明也一起加到类中 * 即:class FruitGenerator<T> implements Generator<T>{ * 如果不声明泛型,如:class FruitGenerator implements Generator<T>,编译器会报错:"Unknown class" */ class FruitGenerator<T> implements Generator<T>{ @Override public T next() { return null; } } 当实现泛型接口的类,传入泛型实参时: /** * 传入泛型实参时: * 定义一个生产器实现这个接口,虽然我们只创建了一个泛型接口Generator<T> * 但是我们可以为T传入无数个实参,形成无数种类型的Generator接口。 * 在实现类实现泛型接口时,如已将泛型类型传入实参类型,则所有使用泛型的地方都要替换成传入的实参类型 * 即:Generator<T>,public T next();中的的T都要替换成传入的String类型。 */ public class FruitGenerator implements Generator<String> { private String[] fruits = new String[]{"Apple", "Banana", "Pear"}; @Override public String next() { Random rand = new Random(); return fruits[rand.nextInt(3)]; } } 4.5 泛型通配符 我们知道Ingeter是Number的一个子类,同时在特性章节中我们也验证过Generic与Generic实际上是相同的一种基本类型。那么问题来了,在使用Generic作为形参的方法中,能否使用Generic的实例传入呢?在逻辑上类似于Generic和Generic是否可以看成具有父子关系的泛型类型呢? 为了弄清楚这个问题,我们使用Generic这个泛型类继续看下面的例子: public void showKeyValue1(Generic<Number> obj){ Log.d("泛型测试","key value is " + obj.getKey()); } Generic<Integer> gInteger = new Generic<Integer>(123); Generic<Number> gNumber = new Generic<Number>(456); showKeyValue(gNumber); // showKeyValue这个方法编译器会为我们报错:Generic<java.lang.Integer> // cannot be applied to Generic<java.lang.Number> // showKeyValue(gInteger); 通过提示信息我们可以看到Generic不能被看作为`Generic的子类。由此可以看出:同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。 回到上面的例子,如何解决上面的问题?总不能为了定义一个新的方法来处理Generic类型的类,这显然与java中的多台理念相违背。因此我们需要一个在逻辑上可以表示同时是Generic和Generic父类的引用类型。由此类型通配符应运而生。 我们可以将上面的方法改一下: public void showKeyValue1(Generic<?> obj){ Log.d("泛型测试","key value is " + obj.getKey()); } 类型通配符一般是使用?代替具体的类型实参,注意了,此处’?’是类型实参,而不是类型形参 。重要说三遍!此处’?’是类型实参,而不是类型形参 ! 此处’?’是类型实参,而不是类型形参 !再直白点的意思就是,此处的?和Number、String、Integer一样都是一种实际的类型,可以把?看成所有类型的父类。是一种真实的类型。 可以解决当具体类型不确定的时候,这个通配符就是 ? ;当操作类型时,不需要使用类型的具体功能时,只使用Object类中的功能。那么可以用 ? 通配符来表未知类型。 4.6 泛型方法 在java中,泛型类的定义非常简单,但是泛型方法就比较复杂了。 尤其是我们见到的大多数泛型类中的成员方法也都使用了泛型,有的甚至泛型类中也包含着泛型方法,这样在初学者中非常容易将泛型方法理解错了。泛型类,是在实例化类的时候指明泛型的具体类型;泛型方法,是在调用方法的时候指明泛型的具体类型 。 /** * 泛型方法的基本介绍 * @param tClass 传入的泛型实参 * @return T 返回值为T类型 * 说明: * 1)public 与 返回值中间<T>非常重要,可以理解为声明此方法为泛型方法。 * 2)只有声明了<T>的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。 * 3)<T>表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。 * 4)与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型。 */ public <T> T genericMethod(Class<T> tClass)throws InstantiationException , IllegalAccessException{ T instance = tClass.newInstance(); return instance; } Object obj = genericMethod(Class.forName("com.test.test")); 4.6.1 泛型方法的基本用法 光看上面的例子有的同学可能依然会非常迷糊,我们再通过一个例子,把我泛型方法再总结一下。 public class GenericTest { //这个类是个泛型类,在上面已经介绍过 public class Generic<T>{ private T key; public Generic(T key) { this.key = key; } //我想说的其实是这个,虽然在方法中使用了泛型,但是这并不是一个泛型方法。 //这只是类中一个普通的成员方法,只不过他的返回值是在声明泛型类已经声明过的泛型。 //所以在这个方法中才可以继续使用 T 这个泛型。 public T getKey(){ return key; } /** * 这个方法显然是有问题的,在编译器会给我们提示这样的错误信息"cannot reslove symbol E" * 因为在类的声明中并未声明泛型E,所以在使用E做形参和返回值类型时,编译器会无法识别。 public E setKey(E key){ this.key = keu } */ } /** * 这才是一个真正的泛型方法。 * 首先在public与返回值之间的<T>必不可少,这表明这是一个泛型方法,并且声明了一个泛型T * 这个T可以出现在这个泛型方法的任意位置. * 泛型的数量也可以为任意多个 * 如:public <T,K> K showKeyName(Generic<T> container){ * ... * } */ public <T> T showKeyName(Generic<T> container){ System.out.println("container key :" + container.getKey()); //当然这个例子举的不太合适,只是为了说明泛型方法的特性。 T test = container.getKey(); return test; } //这也不是一个泛型方法,这就是一个普通的方法,只是使用了Generic<Number>这个泛型类做形参而已。 public void showKeyValue1(Generic<Number> obj){ Log.d("泛型测试","key value is " + obj.getKey()); } //这也不是一个泛型方法,这也是一个普通的方法,只不过使用了泛型通配符? //同时这也印证了泛型通配符章节所描述的,?是一种类型实参,可以看做为Number等所有类的父类 public void showKeyValue2(Generic<?> obj){ Log.d("泛型测试","key value is " + obj.getKey()); } /** * 这个方法是有问题的,编译器会为我们提示错误信息:"UnKnown class 'E' " * 虽然我们声明了<T>,也表明了这是一个可以处理泛型的类型的泛型方法。 * 但是只声明了泛型类型T,并未声明泛型类型E,因此编译器并不知道该如何处理E这个类型。 public <T> T showKeyName(Generic<E> container){ ... } */ /** * 这个方法也是有问题的,编译器会为我们提示错误信息:"UnKnown class 'T' " * 对于编译器来说T这个类型并未项目中声明过,因此编译也不知道该如何编译这个类。 * 所以这也不是一个正确的泛型方法声明。 public void showkey(T genericObj){ } */ public static void main(String[] args) { } } 4.6.2 类中的泛型方法 当然这并不是泛型方法的全部,泛型方法可以出现杂任何地方和任何场景中使用。但是有一种情况是非常特殊的,当泛型方法出现在泛型类中时,我们再通过一个例子看一下 public class GenericFruit { class Fruit{ @Override public String toString() { return "fruit"; } } class Apple extends Fruit{ @Override public String toString() { return "apple"; } } class Person{ @Override public String toString() { return "Person"; } } class GenerateTest<T>{ public void show_1(T t){ System.out.println(t.toString()); } //在泛型类中声明了一个泛型方法,使用泛型E,这种泛型E可以为任意类型。可以类型与T相同,也可以不同。 //由于泛型方法在声明的时候会声明泛型<E>,因此即使在泛型类中并未声明泛型,编译器也能够正确识别泛型方法中识别的泛型。 public <E> void show_3(E t){ System.out.println(t.toString()); } //在泛型类中声明了一个泛型方法,使用泛型T,注意这个T是一种全新的类型,可以与泛型类中声明的T不是同一种类型。 public <T> void show_2(T t){ System.out.println(t.toString()); } } public static void main(String[] args) { Apple apple = new Apple(); Person person = new Person(); GenerateTest<Fruit> generateTest = new GenerateTest<Fruit>(); //apple是Fruit的子类,所以这里可以 generateTest.show_1(apple); //编译器会报错,因为泛型类型实参指定的是Fruit,而传入的实参类是Person //generateTest.show_1(person); //使用这两个方法都可以成功 generateTest.show_2(apple); generateTest.show_2(person); //使用这两个方法也都可以成功 generateTest.show_3(apple); generateTest.show_3(person); } } 4.6.3 泛型方法与可变参数 再看一个泛型方法和可变参数的例子: public <T> void printMsg( T... args){ for(T t : args){ Log.d("泛型测试","t is " + t); } } printMsg("111",222,"aaaa","2323.4",55.55); 4.6.4 静态方法与泛型 静态方法有一种情况需要注意一下,那就是在类中的静态方法使用泛型:静态方法无法访问类上定义的泛型;如果静态方法操作的引用数据类型不确定的时候,必须要将泛型定义在方法上。 即:如果静态方法要使用泛型的话,必须将静态方法也定义成泛型方法 。 public class StaticGenerator<T> { .... .... /** * 如果在类中定义使用泛型的静态方法,需要添加额外的泛型声明(将这个方法定义成泛型方法) * 即使静态方法要使用泛型类中已经声明过的泛型也不可以。 * 如:public static void show(T t){..},此时编译器会提示错误信息: "StaticGenerator cannot be refrenced from static context" */ public static <T> void show(T t){ } } 4.6.5 泛型方法总结 泛型方法能使方法独立于类而产生变化,以下是一个基本的指导原则: 无论何时,如果你能做到,你就该尽量使用泛型方法。也就是说,如果使用泛型方法将整个类泛型化,那么就应该使用泛型方法。另外对于一个static的方法而已,无法访问泛型类型的参数。所以如果static方法要使用泛型能力,就必须使其成为泛型方法。4.6 泛型上下边界 在使用泛型的时候,我们还可以为传入的泛型类型实参进行上下边界的限制,如:类型实参只准传入某种类型的父类或某种类型的子类。 为泛型添加上边界,即传入的类型实参必须是指定类型的子类型 public void showKeyValue1(Generic<? extends Number> obj){ Log.d("泛型测试","key value is " + obj.getKey()); } Generic<String> generic1 = new Generic<String>("11111"); Generic<Integer> generic2 = new Generic<Integer>(2222); Generic<Float> generic3 = new Generic<Float>(2.4f); Generic<Double> generic4 = new Generic<Double>(2.56); //这一行代码编译器会提示错误,因为String类型并不是Number类型的子类 //showKeyValue1(generic1); showKeyValue1(generic2); showKeyValue1(generic3); showKeyValue1(generic4); 如果我们把泛型类的定义也改一下: public class Generic<T extends Number>{ private T key; public Generic(T key) { this.key = key; } public T getKey(){ return key; } } //这一行代码也会报错,因为String不是Number的子类 Generic<String> generic1 = new Generic<String>("11111"); 再来一个泛型方法的例子: //在泛型方法中添加上下边界限制的时候,必须在权限声明与返回值之间的<T>上添加上下边界,即在泛型声明的时候添加 //public <T> T showKeyName(Generic<T extends Number> container),编译器会报错:"Unexpected bound" public <T extends Number> T showKeyName(Generic<T> container){ System.out.println("container key :" + container.getKey()); T test = container.getKey(); return test; } 通过上面的两个例子可以看出:泛型的上下边界添加,必须与泛型的声明在一起 。 4.7 关于泛型数组要提一下 看到了很多文章中都会提起泛型数组,经过查看sun的说明文档,在java中是”不能创建一个确切的泛型类型的数组”的。 也就是说下面的这个例子是不可以的: List<String>[] ls = new ArrayList<String>[10]; 而使用通配符创建泛型数组是可以的,如下面这个例子: List<?>[] ls = new ArrayList<?>[10]; 这样也是可以的: List<String>[] ls = new ArrayList[10]; 下面使用Sun的一篇文档的一个例子来说明这个问题: List<String>[] lsa = new List<String>[10]; // Not really allowed. Object o = lsa; Object[] oa = (Object[]) o; List<Integer> li = new ArrayList<Integer>(); li.add(new Integer(3)); oa[1] = li; // Unsound, but passes run time store check String s = lsa[1].get(0); // Run-time error: ClassCastException. 这种情况下,由于JVM泛型的擦除机制,在运行时JVM是不知道泛型信息的,所以可以给oa[1]赋上一个ArrayList而不会出现异常,但是在取出数据的时候却要做一次类型转换,所以就会出现ClassCastException,如果可以进行泛型数组的声明,上面说的这种情况在编译期将不会出现任何的警告和错误,只有在运行时才会出错。 而对泛型数组的声明进行限制,对于这样的情况,可以在编译期提示代码有类型安全问题,比没有任何提示要强很多。下面采用通配符的方式是被允许的:数组的类型不可以是类型变量,除非是采用通配符的方式,因为对于通配符的方式,最后取出数据是要做显式的类型转换的。 List<?>[] lsa = new List<?>[10]; // OK, array of unbounded wildcard type. Object o = lsa; Object[] oa = (Object[]) o; List<Integer> li = new ArrayList<Integer>(); li.add(new Integer(3)); oa[1] = li; // Correct. Integer i = (Integer) lsa[1].get(0); // OK 5.最后 本文中的例子主要是为了阐述泛型中的一些思想而简单举出的,并不一定有着实际的可用性。另外,一提到泛型,相信大家用到最多的就是在集合中,其实,在实际的编程过程中,自己可以使用泛型去简化开发,且能很好的保证代码质量。 转自:http://blog.csdn.net/s10461/article/details/53941091
一.问题描述:在SprinvMVC的Web程序中,我在页面发送Ajax 的POST请求,然后在服务器端利用@requestBody接收请求body中的参数,当时运行过程中,我想服务器发送Ajax请求,浏览器一直反馈415 Unsupported Media Type或者400的状态码,以为是Ajax写的有问题。便查找了半天资料,才发现spring-mvc.config文件的配置中少了东西,当然也有可能是你真的在Ajax中缺少了对Content-Type参数的设置。分析后应该是我springMVC-config.xml文件配置有问题。(注):400:(错误请求) 服务器不理解请求的语法。 415:(不支持的媒体类型) 请求的格式不受请求页面的支持。二.解决方法: 在springMVC-config.xml文件中,增加了一个StringHttpMessageConverter请求信息转换器,配置片段如下: < bean id = "stringHttpMessageConverter" class = "org.springframework.http.converter.StringHttpMessageConverter"/> < ref bean= "stringHttpMessageConverter" /> < ref bean= "jsonHttpMessageConverter" /> < ref bean= "formHttpMessageConverter" /> list> property> bean> 三.HttpMessageConverter请求信息转换器简介:HttpMessageConverter接口指定了一个可以把Http request信息和Http response信息进行格式转换的转换器。通常实现HttpMessageConverter接口的转换器有以下几种:ByteArrayHttpMessageConverter: 负责读取二进制格式的数据和写出二进制格式的数据;StringHttpMessageConverter: 负责读取字符串格式的数据和写出二进制格式的数据; ResourceHttpMessageConverter:负责读取资源文件和写出资源文件数据; FormHttpMessageConverter: 负责读取form提交的数据(能读取的数据格式为 application/x-www-form-urlencoded,不能读取multipart/form-data格式数据);负责写入application/x-www-from-urlencoded和multipart/form-data格式的数据;MappingJacksonHttpMessageConverter: 负责读取和写入json格式的数据;SourceHttpMessageConverter: 负责读取和写入 xml 中javax.xml.transform.Source定义的数据;Jaxb2RootElementHttpMessageConverter: 负责读取和写入xml 标签格式的数据;AtomFeedHttpMessageConverter: 负责读取和写入Atom格式的数据;RssChannelHttpMessageConverter: 负责读取和写入RSS格式的数据;更多关于HttpMessageConverter的信息请看:http://docs.spring.io/spring/docs/3.0.x/api/org/springframework/http/converter/HttpMessageConverter.html四.HttpMessageConverter请求信息转换器执行流程:当用户发送请求后,@Requestbody 注解会读取请求body中的数据,默认的请求转换器HttpMessageConverter通过获取请求头Header中的Content-Type来确认请求头的数据格式,从而来为请求数据适配合适的转换器。例如contentType:applicatin/json,那么转换器会适配MappingJacksonHttpMessageConverter。响应时候的时候同理,@Responsebody注解会启用HttpMessageConverter,通过检测Header中Accept属性来适配的响应的转换器。 总结:当在使用SpringMVC做服务器数据接收时,尤其是在做Ajax请求的时候,尤其要注意contentType属性,和accepte 属性的设置,在springmvc-config.xml中配置好相应的转换器。当我们在用SpringMVC做 Ajax 请求的时候,有的做法用response.getWriter().print()的方法,还有更好的方法就是添加@Responsebody注解,直接返回Map类型的数据,转换器自动转换为JSON数据类型。 转自:http://www.jb51.net/article/88091.htm
package cn.xxx; import javax.net.ssl.*; import java.io.*; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.cert.X509Certificate; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; /** * Created by yinx on 2017/9/19 0019. */ public class HttpUtils { public static final String UTF8 = "utf-8"; public static final String GBK = "gbk"; public static final String GB2312 = "gb2312"; public static final String ISO88591 = "ISO-8859-1"; public static int READ_TIMEOUT = 30000; public static int CONNECT_TIMEOUT = 30000; /** * post请求数据 * * @param connectURL * @param param * @param charset * @return */ public static String doPost(String connectURL, String param, String charset) { byte[] bytes = null; ByteArrayOutputStream byteArrayOut = null; URL url = null; HttpURLConnection httpPost = null; OutputStream out = null; InputStream in = null; try { url = new URL(connectURL); httpPost = (HttpURLConnection) url.openConnection(); httpPost.setRequestMethod("POST"); httpPost.setDoInput(true); httpPost.setDoOutput(true); httpPost.setUseCaches(false); httpPost.setConnectTimeout(CONNECT_TIMEOUT); httpPost.setReadTimeout(READ_TIMEOUT); httpPost.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); httpPost.connect(); out = httpPost.getOutputStream(); out.write(param.getBytes(charset)); out.flush(); in = httpPost.getInputStream(); byteArrayOut = new ByteArrayOutputStream(); byte[] buf = new byte[512]; int l = 0; while ((l = in.read(buf)) != -1) { byteArrayOut.write(buf, 0, l); } bytes = byteArrayOut.toByteArray(); return new String(bytes, charset); } catch (Exception e) { e.printStackTrace(); } finally { close(byteArrayOut); close(out); close(in); close(httpPost); } return null; } public static String doPostSSL(String connectURL, Map<String, String> params, String charset) throws MalformedURLException, IOException, UnsupportedEncodingException { _ignoreSSL(); return doPost(connectURL, params, charset); } /** * post请求数据 * * @param connectURL * @param params * @param charset * @return */ public static String doPost(String connectURL, Map<String, String> params, String charset) { String param = ""; if (params != null && !params.isEmpty()) { StringBuffer paramBuf = new StringBuffer(); for (Iterator<String> it = params.keySet().iterator(); it.hasNext();) { String key = it.next(); String value = params.get(key); paramBuf.append("&").append(key).append("=").append(value); } param = paramBuf.substring(1); } System.out.println("post url:" + connectURL); System.out.println("post data:" + param); byte[] bytes = null; ByteArrayOutputStream byteArrayOut = null; URL url = null; HttpURLConnection httpPost = null; OutputStream out = null; BufferedInputStream in = null; try { url = new URL(connectURL); httpPost = (HttpURLConnection) url.openConnection(); httpPost.setRequestMethod("POST"); httpPost.setDoInput(true); httpPost.setDoOutput(true); httpPost.setUseCaches(false); httpPost.setConnectTimeout(CONNECT_TIMEOUT); httpPost.setReadTimeout(READ_TIMEOUT); httpPost.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); httpPost.connect(); out = httpPost.getOutputStream(); out.write(param.getBytes(charset)); out.flush(); try { in = new BufferedInputStream(httpPost.getInputStream()); } catch (IOException e) { in = new BufferedInputStream(httpPost.getErrorStream()); } byteArrayOut = new ByteArrayOutputStream(); byte[] buf = new byte[512]; int l = 0; while ((l = in.read(buf)) != -1) { byteArrayOut.write(buf, 0, l); } bytes = byteArrayOut.toByteArray(); return new String(bytes, charset); } catch (Exception e) { e.printStackTrace(); } finally { close(byteArrayOut); close(in); close(out); close(httpPost); } return null; } /** * Get请求数据 * * @param connectURL * @param charset * @return */ public static String doGet(String connectURL, String charset) { byte[] bytes = null; ByteArrayOutputStream byteArrayOut = null; URL url = null; HttpURLConnection httpGet = null; InputStream in = null; try { url = new URL(connectURL); httpGet = (HttpURLConnection) url.openConnection(); httpGet.setConnectTimeout(CONNECT_TIMEOUT); httpGet.setReadTimeout(READ_TIMEOUT); httpGet.connect(); in = httpGet.getInputStream(); byteArrayOut = new ByteArrayOutputStream(); byte[] buf = new byte[512]; int l = 0; while ((l = in.read(buf)) != -1) { byteArrayOut.write(buf, 0, l); } bytes = byteArrayOut.toByteArray(); return bytes != null ? new String(bytes, charset) : null; } catch (Exception e) { e.printStackTrace(); } finally { close(byteArrayOut); close(in); close(httpGet); } return null; } private static void close(Closeable stream) { if (stream != null) { try { stream.close(); stream = null; } catch (IOException e) { e.printStackTrace(); } } } private static void close(HttpURLConnection httpConn){ if(httpConn != null){ httpConn.disconnect(); } } private static HostnameVerifier ignoreHostnameVerifier = new HostnameVerifier() { @Override public boolean verify(String s, SSLSession sslsession) { return true; } }; /** * 忽略SSL */ private static void _ignoreSSL() { try { // Create a trust manager that does not validate certificate chains TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { @Override public X509Certificate[] getAcceptedIssuers() { return null; } @Override public void checkClientTrusted(X509Certificate[] certs, String authType) { } @Override public void checkServerTrusted(X509Certificate[] certs, String authType) { } } }; // Install the all-trusting trust manager SSLContext sc = SSLContext.getInstance("TLS"); sc.init(null, trustAllCerts, new SecureRandom()); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); HttpsURLConnection.setDefaultHostnameVerifier(ignoreHostnameVerifier); } catch (KeyManagementException ex) { Logger.getLogger(HttpUtils.class.getName()).log(Level.SEVERE, null, ex); } catch (NoSuchAlgorithmException ex) { Logger.getLogger(HttpUtils.class.getName()).log(Level.SEVERE, null, ex); } } public String doPost(String actionURL, HashMap<String, String> parameters){ String response = ""; try{ URL url = new URL(actionURL); HttpURLConnection connection = (HttpURLConnection)url.openConnection(); //发送post请求需要下面两行 connection.setDoInput(true); connection.setDoOutput(true); connection.setUseCaches(false); connection.setRequestMethod("POST"); connection.setRequestProperty("Connection", "Keep-Alive"); connection.setRequestProperty("Charset", "UTF-8");; connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); //设置请求数据内容 String requestContent = ""; Set<String> keys = parameters.keySet(); for(String key : keys){ requestContent = requestContent + key + "=" + parameters.get(key) + "&"; } requestContent = requestContent.substring(0, requestContent.lastIndexOf("&")); DataOutputStream ds = new DataOutputStream(connection.getOutputStream()); //使用write(requestContent.getBytes())是为了防止中文出现乱码 ds.write(requestContent.getBytes()); ds.flush(); try{ //获取URL的响应 BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), "utf-8")); String s = ""; String temp = ""; while((temp = reader.readLine()) != null){ s += temp; } response = s; reader.close(); }catch(IOException e){ e.printStackTrace(); System.out.println("No response get!!!"); } ds.close(); }catch(IOException e){ e.printStackTrace(); System.out.println("Request failed!"); } return response; } /** * @author Johnson * @method singleFileUploadWithParameters * @description 集上传单个文件与传递参数于一体的方法 * @param actionURL 上传文件的URL地址包括URL * @param fileType 文件类型(枚举类型) * @param uploadFile 上传文件的路径字符串 * @param parameters 跟文件一起传输的参数(HashMap) * @return String("" if no response get) * @attention 上传文件name为file(服务器解析) * */ /*public String singleFileUploadWithParameters(String actionURL, String uploadFile, MIME_FileType fileType, HashMap<String, String> parameters){ String end = "\r\n"; String twoHyphens = "--"; String boundary = "---------------------------7e0dd540448"; String response = ""; try{ URL url = new URL(actionURL); HttpURLConnection connection = (HttpURLConnection)url.openConnection(); //发送post请求需要下面两行 connection.setDoInput(true); connection.setDoOutput(true); //设置请求参数 connection.setUseCaches(false); connection.setRequestMethod("POST"); connection.setRequestProperty("Connection", "Keep-Alive"); connection.setRequestProperty("Charset", "UTF-8"); connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary); //获取请求内容输出流 DataOutputStream ds = new DataOutputStream(connection.getOutputStream()); String fileName = uploadFile.substring(uploadFile.lastIndexOf(this.PathSeparator) + 1); //开始写表单格式内容 //写参数 Set<String> keys = parameters.keySet(); for(String key : keys){ ds.writeBytes(twoHyphens + boundary + end); ds.writeBytes("Content-Disposition: form-data; name=\""); ds.write(key.getBytes()); ds.writeBytes("\"" + end); ds.writeBytes(end); ds.write(parameters.get(key).getBytes()); ds.writeBytes(end); } //写文件 ds.writeBytes(twoHyphens + boundary + end); ds.writeBytes("Content-Disposition: form-data; " + "name=\"file\"; " + "filename=\""); //防止中文乱码 ds.write(fileName.getBytes()); ds.writeBytes("\"" + end); ds.writeBytes("Content-Type: " + fileType.getValue() + end); ds.writeBytes(end); //根据路径读取文件 FileInputStream fis = new FileInputStream(uploadFile); byte[] buffer = new byte[1024]; int length = -1; while((length = fis.read(buffer)) != -1){ ds.write(buffer, 0, length); } ds.writeBytes(end); fis.close(); ds.writeBytes(twoHyphens + boundary + twoHyphens + end); ds.writeBytes(end); ds.flush(); try{ //获取URL的响应 BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), "utf-8")); String s = ""; String temp = ""; while((temp = reader.readLine()) != null){ s += temp; } response = s; reader.close(); }catch(IOException e){ e.printStackTrace(); System.out.println("No response get!!!"); } ds.close(); }catch(IOException e){ e.printStackTrace(); System.out.println("Request failed!"); } return response; }*/ }
引言: 在Http请求中,我们每天都在使用Content-type来指定不同格式的请求信息,但是却很少有人去全面了解content-type中允许的值有多少,这里将讲解Content-Type的可用值,以及在Spring MVC中如何使用它们来映射请求信息。 Content-Type MediaType,即是Internet Media Type,互联网媒体类型;也叫做MIME类型,在Http协议消息头中,使用Content-Type来表示具体请求中的媒体类型信息。[html] view plain copy类型格式:type/subtype(;parameter)? type 主类型,任意的字符串,如text,如果是*号代表所有; subtype 子类型,任意的字符串,如html,如果是*号代表所有; parameter 可选,一些参数,如Accept请求头的q参数, Content-Type的 charset参数。 例如: Content-Type: text/html;charset:utf-8; 常见的媒体格式类型如下: text/html : HTML格式 text/plain :纯文本格式 text/xml : XML格式 image/gif :gif图片格式 image/jpeg :jpg图片格式 image/png:png图片格式 以application开头的媒体格式类型: application/xhtml+xml :XHTML格式 application/xml : XML数据格式 application/atom+xml :Atom XML聚合格式 application/json : JSON数据格式 application/pdf :pdf格式 application/msword : Word文档格式 application/octet-stream : 二进制流数据(如常见的文件下载) application/x-www-form-urlencoded : 中默认的encType,form表单数据被编码为key/value格式发送到服务器(表单默认的提交数据的格式) 另外一种常见的媒体格式是上传文件之时使用的:multipart/form-data : 需要在表单中进行文件上传时,就需要使用该格式 以上就是我们在日常的开发中,经常会用到的若干content-type的内容格式。 Spring MVC中关于关于Content-Type类型信息的使用 首先我们来看看RequestMapping中的Class定义: [html] view plain copy @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Mapping public @interface RequestMapping { String[] value() default {}; RequestMethod[] method() default {}; String[] params() default {}; String[] headers() default {}; String[] consumes() default {}; String[] produces() default {}; } value: 指定请求的实际地址, 比如 /action/info之类。method: 指定请求的method类型, GET、POST、PUT、DELETE等consumes: 指定处理请求的提交内容类型(Content-Type),例如application/json, text/html;produces: 指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回params: 指定request中必须包含某些参数值是,才让该方法处理headers: 指定request中必须包含某些指定的header值,才能让该方法处理请求其中,consumes, produces使用content-typ信息进行过滤信息;headers中可以使用content-type进行过滤和判断。 使用示例 3.1 headers[html] view plain copy@RequestMapping(value = "/test", method = RequestMethod.GET, headers="Referer=http://www.ifeng.com/") public void testHeaders(@PathVariable String ownerId, @PathVariable String petId) { // implementation omitted } 这里的Headers里面可以匹配所有Header里面可以出现的信息,不局限在Referer信息。 示例2[html] view plain copy@RequestMapping(value = "/response/ContentType", headers = "Accept=application/json") public void response2(HttpServletResponse response) throws IOException { //表示响应的内容区数据的媒体类型为json格式,且编码为utf-8(客户端应该以utf-8解码) response.setContentType("application/json;charset=utf-8"); //写出响应体内容 String jsonData = "{\"username\":\"zhang\", \"password\":\"123\"}"; response.getWriter().write(jsonData); } 服务器根据请求头“Accept=application/json”生产json数据。当你有如下Accept头,将遵守如下规则进行应用:①Accept:text/html,application/xml,application/json 将按照如下顺序进行produces的匹配 ①text/html ②application/xml ③application/json ②Accept:application/xml;q=0.5,application/json;q=0.9,text/html 将按照如下顺序进行produces的匹配 ①text/html ②application/json ③application/xml 参数为媒体类型的质量因子,越大则优先权越高(从0到1) ③Accept:/,text/*,text/html 将按照如下顺序进行produces的匹配 ①text/html ②text/* ③*/* 即匹配规则为:最明确的优先匹配。Requests部分Header 解释 示例Accept 指定客户端能够接收的内容类型 Accept: text/plain, text/htmlAccept-Charset 浏览器可以接受的字符编码集。 Accept-Charset: iso-8859-5Accept-Encoding 指定浏览器可以支持的web服务器返回内容压缩编码类型。 Accept-Encoding: compress, gzipAccept-Language 浏览器可接受的语言 Accept-Language: en,zhAccept-Ranges 可以请求网页实体的一个或者多个子范围字段 Accept-Ranges: bytesAuthorization HTTP授权的授权证书 Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==Cache-Control 指定请求和响应遵循的缓存机制 Cache-Control: no-cacheConnection 表示是否需要持久连接。(HTTP 1.1默认进行持久连接) Connection: closeCookie HTTP请求发送时,会把保存在该请求域名下的所有cookie值一起发送给web服务器。 Cookie: $Version=1; Skin=new;Content-Length 请求的内容长度 Content-Length: 348Content-Type 请求的与实体对应的MIME信息 Content-Type: application/x-www-form-urlencodedDate 请求发送的日期和时间 Date: Tue, 15 Nov 2010 08:12:31 GMTExpect 请求的特定的服务器行为 Expect: 100-continueFrom 发出请求的用户的Email From: user@email.comHost 指定请求的服务器的域名和端口号 Host: www.zcmhi.comIf-Match 只有请求内容与实体相匹配才有效 If-Match: “737060cd8c284d8af7ad3082f209582d”If-Modified-Since 如果请求的部分在指定时间之后被修改则请求成功,未被修改则返回304代码 If-Modified-Since: Sat, 29 Oct 2010 19:43:31 GMTIf-None-Match 如果内容未改变返回304代码,参数为服务器先前发送的Etag,与服务器回应的Etag比较判断是否改变 If-None-Match: “737060cd8c284d8af7ad3082f209582d”If-Range 如果实体未改变,服务器发送客户端丢失的部分,否则发送整个实体。参数也为Etag If-Range: “737060cd8c284d8af7ad3082f209582d”If-Unmodified-Since 只在实体在指定时间之后未被修改才请求成功 If-Unmodified-Since: Sat, 29 Oct 2010 19:43:31 GMTMax-Forwards 限制信息通过代理和网关传送的时间 Max-Forwards: 10Pragma 用来包含实现特定的指令 Pragma: no-cacheProxy-Authorization 连接到代理的授权证书 Proxy-Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==Range 只请求实体的一部分,指定范围 Range: bytes=500-999Referer 先前网页的地址,当前请求网页紧随其后,即来路 Referer: http://www.zcmhi.com/archives/71.htmlTE 客户端愿意接受的传输编码,并通知服务器接受接受尾加头信息 TE: trailers,deflate;q=0.5Upgrade 向服务器指定某种传输协议以便服务器进行转换(如果支持) Upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11User-Agent User-Agent的内容包含发出请求的用户信息 User-Agent: Mozilla/5.0 (Linux; X11)Via 通知中间网关或代理服务器地址,通信协议 Via: 1.0 fred, 1.1 nowhere.com (Apache/1.1)Warning 关于消息实体的警告信息 Warn: 199 Miscellaneous warningResponses 部分 Header 解释 示例Accept-Ranges 表明服务器是否支持指定范围请求及哪种类型的分段请求 Accept-Ranges: bytesAge 从原始服务器到代理缓存形成的估算时间(以秒计,非负) Age: 12Allow 对某网络资源的有效的请求行为,不允许则返回405 Allow: GET, HEADCache-Control 告诉所有的缓存机制是否可以缓存及哪种类型 Cache-Control: no-cacheContent-Encoding web服务器支持的返回内容压缩编码类型。 Content-Encoding: gzipContent-Language 响应体的语言 Content-Language: en,zhContent-Length 响应体的长度 Content-Length: 348Content-Location 请求资源可替代的备用的另一地址 Content-Location: /index.htmContent-MD5 返回资源的MD5校验值 Content-MD5: Q2hlY2sgSW50ZWdyaXR5IQ==Content-Range 在整个返回体中本部分的字节位置 Content-Range: bytes 21010-47021/47022Content-Type 返回内容的MIME类型 Content-Type: text/html; charset=utf-8Date 原始服务器消息发出的时间 Date: Tue, 15 Nov 2010 08:12:31 GMTETag 请求变量的实体标签的当前值 ETag: “737060cd8c284d8af7ad3082f209582d”Expires 响应过期的日期和时间 Expires: Thu, 01 Dec 2010 16:00:00 GMTLast-Modified 请求资源的最后修改时间 Last-Modified: Tue, 15 Nov 2010 12:45:26 GMTLocation 用来重定向接收方到非请求URL的位置来完成请求或标识新的资源 Location: http://www.zcmhi.com/archives/94.htmlPragma 包括实现特定的指令,它可应用到响应链上的任何接收方 Pragma: no-cacheProxy-Authenticate 它指出认证方案和可应用到代理的该URL上的参数 Proxy-Authenticate: Basicrefresh 应用于重定向或一个新的资源被创造,在5秒之后重定向(由网景提出,被大部分浏览器支持) Refresh: 5; url=http://www.zcmhi.com/archives/94.htmlRetry-After 如果实体暂时不可取,通知客户端在指定时间之后再次尝试 Retry-After: 120Server web服务器软件名称 Server: Apache/1.3.27 (Unix) (Red-Hat/Linux)Set-Cookie 设置Http Cookie Set-Cookie: UserID=JohnDoe; Max-Age=3600; Version=1Trailer 指出头域在分块传输编码的尾部存在 Trailer: Max-ForwardsTransfer-Encoding 文件传输编码 Transfer-Encoding:chunkedVary 告诉下游代理是使用缓存响应还是从原始服务器请求 Vary: *Via 告知代理客户端响应是通过哪里发送的 Via: 1.0 fred, 1.1 nowhere.com (Apache/1.1)Warning 警告实体可能存在的问题 Warning: 199 Miscellaneous warningWWW-Authenticate 表明客户端请求实体应该使用的授权方案 WWW-Authenticate: Basic3.2 params的示例 [html] view plain copy@RequestMapping(value = "/test/{userId}", method = RequestMethod.GET, params="myParam=myValue") public void findUser(@PathVariable String userId) { // implementation omitted } 仅处理请求中包含了名为“myParam”,值为“myValue”的请求,起到了一个过滤的作用。3.3 consumes/produces [html] view plain copy@Controller @RequestMapping(value = "/users", method = RequestMethod.POST, consumes="application/json", produces="application/json") @ResponseBody public List addUser(@RequestBody User userl) { // implementation omitted return List<User> users; } 方法仅处理request Content-Type为“application/json”类型的请求. produces标识==>处理request请求中Accept头中包含了"application/json"的请求,同时暗示了返回的内容类型为application/json; 总结 在本文中,首先介绍了Content-Type主要支持的格式内容,然后基于@RequestMapping标注的内容介绍了主要的使用方法,其中,headers, consumes,produces,都是使用Content-Type中使用的各种媒体格式内容,可以基于这个格式内容来进行访问的控制和过滤。参考资料: HTTP中支持的Content-Type: http://tool.oschina.net/commons Media Type介绍。 http://www.iteye.com/topic/1127120 来自:http://blog.csdn.net/blueheart20/article/details/45174399
【常规】 Ctrl+Shift + Enter,语句完成 “!”,否定完成,输入表达式时按 “!”键 Ctrl+E,最近的文件 Ctrl+Shift+E,最近更改的文件 Shift+Click,可以关闭文件 Ctrl+[ OR ],可以跑到大括号的开头与结尾 Ctrl+F12,可以显示当前文件的结构 Ctrl+F7,可以查询当前元素在当前文件中的引用,然后按 F3 可以选择 Ctrl+N,可以快速打开类 Ctrl+Shift+N,可以快速打开文件 Alt+Q,可以看到当前方法的声明 Ctrl+P,可以显示参数信息 Ctrl+Shift+Insert,可以选择剪贴板内容并插入 Alt+Insert,可以生成构造器/Getter/Setter等 Ctrl+Alt+V,可以引入变量。例如:new String(); 自动导入变量定义 Ctrl+Alt+T,可以把代码包在一个块内,例如:try/catch Ctrl+Enter,导入包,自动修正 Ctrl+Alt+L,格式化代码 Ctrl+Alt+I,将选中的代码进行自动缩进编排,这个功能在编辑 JSP 文件时也可以工作 Ctrl+Alt+O,优化导入的类和包 Ctrl+R,替换文本 Ctrl+F,查找文本 Ctrl+Shift+Space,自动补全代码 Ctrl+空格,代码提示(与系统输入法快捷键冲突) Ctrl+Shift+Alt+N,查找类中的方法或变量 Alt+Shift+C,最近的更改 Alt+Shift+Up/Down,上/下移一行 Shift+F6,重构 - 重命名 Ctrl+X,删除行 Ctrl+D,复制行 Ctrl+/或Ctrl+Shift+/,注释(//或者/**/) Ctrl+J,自动代码(例如:serr) Ctrl+Alt+J,用动态模板环绕 Ctrl+H,显示类结构图(类的继承层次) Ctrl+Q,显示注释文档 Alt+F1,查找代码所在位置 Alt+1,快速打开或隐藏工程面板 Ctrl+Alt+left/right,返回至上次浏览的位置 Alt+left/right,切换代码视图 Alt+Up/Down,在方法间快速移动定位 Ctrl+Shift+Up/Down,向上/下移动语句 F2 或 Shift+F2,高亮错误或警告快速定位 Tab,代码标签输入完成后,按 Tab,生成代码 Ctrl+Shift+F7,高亮显示所有该文本,按 Esc 高亮消失 Alt+F3,逐个往下查找相同文本,并高亮显示 Ctrl+Up/Down,光标中转到第一行或最后一行下 Ctrl+B/Ctrl+Click,快速打开光标处的类或方法(跳转到定义处) Ctrl+Alt+B,跳转到方法实现处 Ctrl+Shift+Backspace,跳转到上次编辑的地方 Ctrl+O,重写方法 Ctrl+Alt+Space,类名自动完成 Ctrl+Alt+Up/Down,快速跳转搜索结果 Ctrl+Shift+J,整合两行 Alt+F8,计算变量值 Ctrl+Shift+V,可以将最近使用的剪贴板内容选择插入到文本 Ctrl+Alt+Shift+V,简单粘贴 Shift+Esc,不仅可以把焦点移到编辑器上,而且还可以隐藏当前(或最后活动的)工具窗口 F12,把焦点从编辑器移到最近使用的工具窗口 Shift+F1,要打开编辑器光标字符处使用的类或者方法 Java 文档的浏览器 Ctrl+W,可以选择单词继而语句继而行继而函数 Ctrl+Shift+W,取消选择光标所在词 Alt+F7,查找整个工程中使用地某一个类、方法或者变量的位置 Ctrl+I,实现方法 Ctrl+Shift+U,大小写转化 Ctrl+Y,删除当前行 Shift+Enter,向下插入新行 psvm/sout,main/System.out.println(); Ctrl+J,查看更多 Ctrl+Shift+F,全局查找 Ctrl+F,查找/Shift+F3,向上查找/F3,向下查找 Ctrl+Shift+S,高级搜索 Ctrl+U,转到父类 Ctrl+Alt+S,打开设置对话框 Alt+Shift+Inert,开启/关闭列选择模式 Ctrl+Alt+Shift+S,打开当前项目/模块属性 Ctrl+G,定位行 Alt+Home,跳转到导航栏 Ctrl+Enter,上插一行 Ctrl+Backspace,按单词删除 Ctrl+"+/-",当前方法展开、折叠 Ctrl+Shift+"+/-",全部展开、折叠 【调试部分、编译】 Ctrl+F2,停止 Alt+Shift+F9,选择 Debug Alt+Shift+F10,选择 Run Ctrl+Shift+F9,编译 Ctrl+Shift+F10,运行 Ctrl+Shift+F8,查看断点 F8,步过 F7,步入 Shift+F7,智能步入 Shift+F8,步出 Alt+Shift+F8,强制步过 Alt+Shift+F7,强制步入 Alt+F9,运行至光标处 Ctrl+Alt+F9,强制运行至光标处 F9,恢复程序 Alt+F10,定位到断点 Ctrl+F8,切换行断点 Ctrl+F9,生成项目 Alt+1,项目 Alt+2,收藏 Alt+6,TODO Alt+7,结构 Ctrl+Shift+C,复制路径 Ctrl+Alt+Shift+C,复制引用,必须选择类名 Ctrl+Alt+Y,同步 Ctrl+~,快速切换方案(界面外观、代码风格、快捷键映射等菜单) Shift+F12,还原默认布局 Ctrl+Shift+F12,隐藏/恢复所有窗口 Ctrl+F4,关闭 Ctrl+Shift+F4,关闭活动选项卡 Ctrl+Tab,转到下一个拆分器 Ctrl+Shift+Tab,转到上一个拆分器 【重构】 Ctrl+Alt+Shift+T,弹出重构菜单 Shift+F6,重命名 F6,移动 F5,复制 Alt+Delete,安全删除 Ctrl+Alt+N,内联 【查找】 Ctrl+F,查找 Ctrl+R,替换 F3,查找下一个 Shift+F3,查找上一个 Ctrl+Shift+F,在路径中查找 Ctrl+Shift+R,在路径中替换 Ctrl+Shift+S,搜索结构 Ctrl+Shift+M,替换结构 Alt+F7,查找用法 Ctrl+Alt+F7,显示用法 Ctrl+F7,在文件中查找用法 Ctrl+Shift+F7,在文件中高亮显示用法 【VCS】 Alt+~,VCS 操作菜单 Ctrl+K,提交更改 Ctrl+T,更新项目 Ctrl+Alt+Shift+D,显示变化【常规】 Ctrl+Shift + Enter,语句完成 “!”,否定完成,输入表达式时按 “!”键 Ctrl+E,最近的文件 Ctrl+Shift+E,最近更改的文件 Shift+Click,可以关闭文件 Ctrl+[ OR ],可以跑到大括号的开头与结尾 Ctrl+F12,可以显示当前文件的结构 Ctrl+F7,可以查询当前元素在当前文件中的引用,然后按 F3 可以选择 Ctrl+N,可以快速打开类 Ctrl+Shift+N,可以快速打开文件 Alt+Q,可以看到当前方法的声明 Ctrl+P,可以显示参数信息 Ctrl+Shift+Insert,可以选择剪贴板内容并插入 Alt+Insert,可以生成构造器/Getter/Setter等 Ctrl+Alt+V,可以引入变量。例如:new String(); 自动导入变量定义 Ctrl+Alt+T,可以把代码包在一个块内,例如:try/catch Ctrl+Enter,导入包,自动修正 Ctrl+Alt+L,格式化代码 Ctrl+Alt+I,将选中的代码进行自动缩进编排,这个功能在编辑 JSP 文件时也可以工作 Ctrl+Alt+O,优化导入的类和包 Ctrl+R,替换文本 Ctrl+F,查找文本 Ctrl+Shift+Space,自动补全代码 Ctrl+空格,代码提示(与系统输入法快捷键冲突) Ctrl+Shift+Alt+N,查找类中的方法或变量 Alt+Shift+C,最近的更改 Alt+Shift+Up/Down,上/下移一行 Shift+F6,重构 - 重命名 Ctrl+X,删除行 Ctrl+D,复制行 Ctrl+/或Ctrl+Shift+/,注释(//或者/**/) Ctrl+J,自动代码(例如:serr) Ctrl+Alt+J,用动态模板环绕 Ctrl+H,显示类结构图(类的继承层次) Ctrl+Q,显示注释文档 Alt+F1,查找代码所在位置 Alt+1,快速打开或隐藏工程面板 Ctrl+Alt+left/right,返回至上次浏览的位置 Alt+left/right,切换代码视图 Alt+Up/Down,在方法间快速移动定位 Ctrl+Shift+Up/Down,向上/下移动语句 F2 或 Shift+F2,高亮错误或警告快速定位 Tab,代码标签输入完成后,按 Tab,生成代码 Ctrl+Shift+F7,高亮显示所有该文本,按 Esc 高亮消失 Alt+F3,逐个往下查找相同文本,并高亮显示 Ctrl+Up/Down,光标中转到第一行或最后一行下 Ctrl+B/Ctrl+Click,快速打开光标处的类或方法(跳转到定义处) Ctrl+Alt+B,跳转到方法实现处 Ctrl+Shift+Backspace,跳转到上次编辑的地方 Ctrl+O,重写方法 Ctrl+Alt+Space,类名自动完成 Ctrl+Alt+Up/Down,快速跳转搜索结果 Ctrl+Shift+J,整合两行 Alt+F8,计算变量值 Ctrl+Shift+V,可以将最近使用的剪贴板内容选择插入到文本 Ctrl+Alt+Shift+V,简单粘贴 Shift+Esc,不仅可以把焦点移到编辑器上,而且还可以隐藏当前(或最后活动的)工具窗口 F12,把焦点从编辑器移到最近使用的工具窗口 Shift+F1,要打开编辑器光标字符处使用的类或者方法 Java 文档的浏览器 Ctrl+W,可以选择单词继而语句继而行继而函数 Ctrl+Shift+W,取消选择光标所在词 Alt+F7,查找整个工程中使用地某一个类、方法或者变量的位置 Ctrl+I,实现方法 Ctrl+Shift+U,大小写转化 Ctrl+Y,删除当前行 Shift+Enter,向下插入新行 psvm/sout,main/System.out.println(); Ctrl+J,查看更多 Ctrl+Shift+F,全局查找 Ctrl+F,查找/Shift+F3,向上查找/F3,向下查找 Ctrl+Shift+S,高级搜索 Ctrl+U,转到父类 Ctrl+Alt+S,打开设置对话框 Alt+Shift+Inert,开启/关闭列选择模式 Ctrl+Alt+Shift+S,打开当前项目/模块属性 Ctrl+G,定位行 Alt+Home,跳转到导航栏 Ctrl+Enter,上插一行 Ctrl+Backspace,按单词删除 Ctrl+"+/-",当前方法展开、折叠 Ctrl+Shift+"+/-",全部展开、折叠 【调试部分、编译】 Ctrl+F2,停止 Alt+Shift+F9,选择 Debug Alt+Shift+F10,选择 Run Ctrl+Shift+F9,编译 Ctrl+Shift+F10,运行 Ctrl+Shift+F8,查看断点 F8,步过 F7,步入 Shift+F7,智能步入 Shift+F8,步出 Alt+Shift+F8,强制步过 Alt+Shift+F7,强制步入 Alt+F9,运行至光标处 Ctrl+Alt+F9,强制运行至光标处 F9,恢复程序 Alt+F10,定位到断点 Ctrl+F8,切换行断点 Ctrl+F9,生成项目 Alt+1,项目 Alt+2,收藏 Alt+6,TODO Alt+7,结构 Ctrl+Shift+C,复制路径 Ctrl+Alt+Shift+C,复制引用,必须选择类名 Ctrl+Alt+Y,同步 Ctrl+~,快速切换方案(界面外观、代码风格、快捷键映射等菜单) Shift+F12,还原默认布局 Ctrl+Shift+F12,隐藏/恢复所有窗口 Ctrl+F4,关闭 Ctrl+Shift+F4,关闭活动选项卡 Ctrl+Tab,转到下一个拆分器 Ctrl+Shift+Tab,转到上一个拆分器 【重构】 Ctrl+Alt+Shift+T,弹出重构菜单 Shift+F6,重命名 F6,移动 F5,复制 Alt+Delete,安全删除 Ctrl+Alt+N,内联 【查找】 Ctrl+F,查找 Ctrl+R,替换 F3,查找下一个 Shift+F3,查找上一个 Ctrl+Shift+F,在路径中查找 Ctrl+Shift+R,在路径中替换 Ctrl+Shift+S,搜索结构 Ctrl+Shift+M,替换结构 Alt+F7,查找用法 Ctrl+Alt+F7,显示用法 Ctrl+F7,在文件中查找用法 Ctrl+Shift+F7,在文件中高亮显示用法 【VCS】 Alt+~,VCS 操作菜单 Ctrl+K,提交更改 Ctrl+T,更新项目 Ctrl+Alt+Shift+D,显示变化 后续陆续添加 来自:http://blog.csdn.net/djcken/article/details/16362629
一、背景 最近由于项目的需要,我们把log4j 1.x的版本全部迁移成log4j 2.x 的版本,那随之而来的slf4j整合log4j的配置(使用Slf4j集成Log4j2构建项目日志系统的完美解决方案)以及log4j2配置文件的详解,就需要我们来好好聊一聊了。本文就专门来讲解下log4j2.xml配置文件的各项标签的意义。 二、配置全解 1.关于配置文件的名称以及在项目中的存放位置 log4j 2.x版本不再支持像1.x中的.properties后缀的文件配置方式,2.x版本配置文件后缀名只能为”.xml”,”.json”或者”.jsn”. 系统选择配置文件的优先级(从先到后)如下: (1).classpath下的名为log4j2-test.json 或者log4j2-test.jsn的文件. (2).classpath下的名为log4j2-test.xml的文件. (3).classpath下名为log4j2.json 或者log4j2.jsn的文件. (4).classpath下名为log4j2.xml的文件. 我们一般默认使用log4j2.xml进行命名。如果本地要测试,可以把log4j2-test.xml放到classpath,而正式环境使用log4j2.xml,则在打包部署的时候不要打包log4j2-test.xml即可。 2.缺省默认配置文件 1 <?xml version="1.0" encoding="UTF-8"?> 2 <Configuration status="WARN"> 3 <Appenders> 4 <Console name="Console" target="SYSTEM_OUT"> 5 <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> 6 </Console> 7 </Appenders> 8 <Loggers> 9 <Root level="error"> 10 <AppenderRef ref="Console"/> 11 </Root> 12 </Loggers> 13 </Configuration> 3.配置文件节点解析 (1).根节点Configuration有两个属性:status和monitorinterval,有两个子节点:Appenders和Loggers(表明可以定义多个Appender和Logger). status用来指定log4j本身的打印日志的级别. monitorinterval用于指定log4j自动重新配置的监测间隔时间,单位是s,最小是5s. (2).Appenders节点,常见的有三种子节点:Console、RollingFile、File. Console节点用来定义输出到控制台的Appender. name:指定Appender的名字. target:SYSTEM_OUT 或 SYSTEM_ERR,一般只设置默认:SYSTEM_OUT. PatternLayout:输出格式,不设置默认为:%m%n. File节点用来定义输出到指定位置的文件的Appender. name:指定Appender的名字. fileName:指定输出日志的目的文件带全路径的文件名. PatternLayout:输出格式,不设置默认为:%m%n. RollingFile节点用来定义超过指定大小自动删除旧的创建新的的Appender. name:指定Appender的名字. fileName:指定输出日志的目的文件带全路径的文件名. PatternLayout:输出格式,不设置默认为:%m%n. filePattern:指定新建日志文件的名称格式. Policies:指定滚动日志的策略,就是什么时候进行新建日志文件输出日志. TimeBasedTriggeringPolicy:Policies子节点,基于时间的滚动策略,interval属性用来指定多久滚动一次,默认是1 hour。modulate=true用来调整时间:比如现在是早上3am,interval是4,那么第一次滚动是在4am,接着是8am,12am…而不是7am. SizeBasedTriggeringPolicy:Policies子节点,基于指定文件大小的滚动策略,size属性用来定义每个日志文件的大小. DefaultRolloverStrategy:用来指定同一个文件夹下最多有几个日志文件时开始删除最旧的,创建新的(通过max属性)。 (3).Loggers节点,常见的有两种:Root和Logger. Root节点用来指定项目的根日志,如果没有单独指定Logger,那么就会默认使用该Root日志输出 level:日志输出级别,共有8个级别,按照从低到高为:All < Trace < Debug < Info < Warn < Error < Fatal < OFF. AppenderRef:Root的子节点,用来指定该日志输出到哪个Appender. Logger节点用来单独指定日志的形式,比如要为指定包下的class指定不同的日志级别等。 level:日志输出级别,共有8个级别,按照从低到高为:All < Trace < Debug < Info < Warn < Error < Fatal < OFF. name:用来指定该Logger所适用的类或者类所在的包全路径,继承自Root节点. AppenderRef:Logger的子节点,用来指定该日志输出到哪个Appender,如果没有指定,就会默认继承自Root.如果指定了,那么会在指定的这个Appender和Root的Appender中都会输出,此时我们可以设置Logger的additivity=”false”只在自定义的Appender中进行输出。 (4).关于日志level. 共有8个级别,按照从低到高为:All < Trace < Debug < Info < Warn < Error < Fatal < OFF. All:最低等级的,用于打开所有日志记录. Trace:是追踪,就是程序推进以下,你就可以写个trace输出,所以trace应该会特别多,不过没关系,我们可以设置最低日志级别不让他输出. Debug:指出细粒度信息事件对调试应用程序是非常有帮助的. Info:消息在粗粒度级别上突出强调应用程序的运行过程. Warn:输出警告及warn以下级别的日志. Error:输出错误信息日志. Fatal:输出每个严重的错误事件将会导致应用程序的退出的日志. OFF:最高等级的,用于关闭所有日志记录. 程序会打印高于或等于所设置级别的日志,设置的日志等级越高,打印出来的日志就越少。 4.比较完整的log4j2.xml配置模板 1 <?xml version="1.0" encoding="UTF-8"?> 2 <!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL --> 3 <!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出--> 4 <!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数--> 5 <configuration status="WARN" monitorInterval="30"> 6 <!--先定义所有的appender--> 7 <appenders> 8 <!--这个输出控制台的配置--> 9 <console name="Console" target="SYSTEM_OUT"> 10 <!--输出日志的格式--> 11 <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> 12 </console> 13 <!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,这个也挺有用的,适合临时测试用--> 14 <File name="log" fileName="log/test.log" append="false"> 15 <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/> 16 </File> 17 <!-- 这个会打印出所有的info及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档--> 18 <RollingFile name="RollingFileInfo" fileName="${sys:user.home}/logs/info.log" 19 filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log"> 20 <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)--> 21 <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/> 22 <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> 23 <Policies> 24 <TimeBasedTriggeringPolicy/> 25 <SizeBasedTriggeringPolicy size="100 MB"/> 26 </Policies> 27 </RollingFile> 28 <RollingFile name="RollingFileWarn" fileName="${sys:user.home}/logs/warn.log" 29 filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log"> 30 <ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/> 31 <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> 32 <Policies> 33 <TimeBasedTriggeringPolicy/> 34 <SizeBasedTriggeringPolicy size="100 MB"/> 35 </Policies> 36 <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件,这里设置了20 --> 37 <DefaultRolloverStrategy max="20"/> 38 </RollingFile> 39 <RollingFile name="RollingFileError" fileName="${sys:user.home}/logs/error.log" 40 filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log"> 41 <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/> 42 <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> 43 <Policies> 44 <TimeBasedTriggeringPolicy/> 45 <SizeBasedTriggeringPolicy size="100 MB"/> 46 </Policies> 47 </RollingFile> 48 </appenders> 49 <!--然后定义logger,只有定义了logger并引入的appender,appender才会生效--> 50 <loggers> 51 <!--过滤掉spring和mybatis的一些无用的DEBUG信息--> 52 <logger name="org.springframework" level="INFO"></logger> 53 <logger name="org.mybatis" level="INFO"></logger> 54 <root level="all"> 55 <appender-ref ref="Console"/> 56 <appender-ref ref="RollingFileInfo"/> 57 <appender-ref ref="RollingFileWarn"/> 58 <appender-ref ref="RollingFileError"/> 59 </root> 60 </loggers> 61 </configuration> 转自:http://blog.csdn.net/wenwenxiong/article/details/70815051
1、生成的字符串每个位置都有可能是str中的一个字母或数字,需要导入的包是import java.util.Random;//length用户要求产生字符串的长度 public static String getRandomString(int length){ String str="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; Random random=new Random(); StringBuffer sb=new StringBuffer(); for(int i=0;i<length;i++){ int number=random.nextInt(62); sb.append(str.charAt(number)); } return sb.toString(); } 2、可以指定某个位置是a-z、A-Z或是0-9,需要导入的包是import java.util.Random;//可以指定字符串的某个位置是什么范围的值 public static String getRandomString2(int length){ Random random=new Random(); StringBuffer sb=new StringBuffer(); for(int i=0;i<length;i++){ int number=random.nextInt(3); long result=0; switch(number){ case 0: result=Math.round(Math.random()*25+65); sb.append(String.valueOf((char)result)); break; case 1: result=Math.round(Math.random()*25+97); sb.append(String.valueOf((char)result)); break; case 2: sb.append(String.valueOf(new Random().nextInt(10))); break; } } return sb.toString(); } 3、org.apache.commons.lang包下有一个RandomStringUtils类,其中有一个randomAlphanumeric(int length)函数,可以随机生成一个长度为length的字符串。 String filename=RandomStringUtils.randomAlphanumeric(10); 4、GUID是一个128位长的数字,一般用16进制表示。算法的核心思想是结合机器的网卡、当地时间、一个随机数来生成GUID。从理论上讲,如果一台机器每秒产生10000000个GUID,则可以保证(概率意义上)3240年不重复。 UUID是1.5中新增的一个类,在java.util下,用它可以产生一个号称全球唯一的ID: package com.mytest; import java.util.UUID; public class UTest { public static void main(String[] args) { UUID uuid = UUID.randomUUID(); System.out.println(uuid); } } UUID由以下几部分的组合: (1)当前日期和时间,UUID的第一个部分与时间有关,如果你在生成一个UUID之后,过几秒又生成一个UUID,则第一个部分不同,其余相同。 (2)时钟序列 (3)全局唯一的IEEE机器识别号,如果有网卡,从网卡MAC地址获得,没有网卡以其他方式获得。 UUID的唯一缺陷在于生成的结果串会比较长。关于UUID这个标准使用最普遍的是微软的GUID(Globals Unique Identifiers)。在ColdFusion中可以用CreateUUID()函数很简单的生成UUID,其格式为:xxxxxxxx-xxxx- xxxx-xxxxxxxxxxxxxxxx(8-4-4-16),其中每个 x 是 0-9 或 a-f 范围内的一个十六进制的数字。而标准的UUID格式为:xxxxxxxx-xxxx-xxxx-xxxxxx-xxxxxxxxxx (8-4-4-4-12),可以从cflib 下载CreateGUID() UDF进行转换。 使用UUID的好处在分布式的软件系统中(比如:DCE/RPC, COM+,CORBA)就能体现出来,它能保证每个节点所生成的标识都不会重复,并且随着WEB服务等整合技术的发展,UUID的优势将更加明显。根据使用的特定机制,UUID不仅需要保证是彼此不相同的,或者最少也是与公元3400年之前其他任何生成的通用惟一标识符有非常大的区别。 通用惟一标识符还可以用来指向大多数的可能的物体。微软和其他一些软件公司都倾向使用全球惟一标识符(GUID),这也是通用惟一标识符的一种类型,可用来指向组建对象模块对象和其他的软件组件。第一个通用惟一标识符是在网罗计算机系统(NCS)中创建,并且随后成为开放软件基金会(OSF)的分布式计算环境(DCE)的组件。以下是具体生成UUID的例子: package test; import java.util.UUID; public class UUIDGenerator { public UUIDGenerator() { } public static String getUUID() { UUID uuid = UUID.randomUUID(); String str = uuid.toString(); // 去掉"-"符号 String temp = str.substring(0, 8) + str.substring(9, 13) + str.substring(14, 18) + str.substring(19, 23) + str.substring(24); return str+","+temp; } //获得指定数量的UUID public static String[] getUUID(int number) { if (number < 1) { return null; } String[] ss = new String[number]; for (int i = 0; i < number; i++) { ss[i] = getUUID(); } return ss; } public static void main(String[] args) { String[] ss = getUUID(10); for (int i = 0; i < ss.length; i++) { System.out.println("ss["+i+"]====="+ss[i]); } } } 控制台输出: view plaincopy to clipboardprint? ss[0]=====4cdbc040-657a-4847-b266-7e31d9e2c3d9,4cdbc040657a4847b2667e31d9e2c3d9 ss[1]=====72297c88-4260-4c05-9b05-d28bfb11d10b,72297c8842604c059b05d28bfb11d10b ss[2]=====6d513b6a-69bd-4f79-b94c-d65fc841ea95,6d513b6a69bd4f79b94cd65fc841ea95 ss[3]=====d897a7d3-87a3-4e38-9e0b-71013a6dbe4c,d897a7d387a34e389e0b71013a6dbe4c ss[4]=====5709f0ba-31e3-42bd-a28d-03485b257c94,5709f0ba31e342bda28d03485b257c94 ss[5]=====530fbb8c-eec9-48d1-ae1b-5f792daf09f3,530fbb8ceec948d1ae1b5f792daf09f3 ss[6]=====4bf07297-65b2-45ca-b905-6fc6f2f39158,4bf0729765b245cab9056fc6f2f39158 ss[7]=====6e5a0e85-b4a0-485f-be54-a758115317e1,6e5a0e85b4a0485fbe54a758115317e1 ss[8]=====245accec-3c12-4642-967f-e476cef558c4,245accec3c124642967fe476cef558c4 ss[9]=====ddd4b5a9-fecd-446c-bd78-63b70bb500a1,ddd4b5a9fecd446cbd7863b70bb500a1 ss[0]=====4cdbc040-657a-4847-b266-7e31d9e2c3d9,4cdbc040657a4847b2667e31d9e2c3d9 ss[1]=====72297c88-4260-4c05-9b05-d28bfb11d10b,72297c8842604c059b05d28bfb11d10b ss[2]=====6d513b6a-69bd-4f79-b94c-d65fc841ea95,6d513b6a69bd4f79b94cd65fc841ea95 ss[3]=====d897a7d3-87a3-4e38-9e0b-71013a6dbe4c,d897a7d387a34e389e0b71013a6dbe4c ss[4]=====5709f0ba-31e3-42bd-a28d-03485b257c94,5709f0ba31e342bda28d03485b257c94 ss[5]=====530fbb8c-eec9-48d1-ae1b-5f792daf09f3,530fbb8ceec948d1ae1b5f792daf09f3 ss[6]=====4bf07297-65b2-45ca-b905-6fc6f2f39158,4bf0729765b245cab9056fc6f2f39158 ss[7]=====6e5a0e85-b4a0-485f-be54-a758115317e1,6e5a0e85b4a0485fbe54a758115317e1 ss[8]=====245accec-3c12-4642-967f-e476cef558c4,245accec3c124642967fe476cef558c4 ss[9]=====ddd4b5a9-fecd-446c-bd78-63b70bb500a1,ddd4b5a9fecd446cbd7863b70bb500a1
刚开始学习写Java的时候,用的eclipse,正式工作后,主要用的myeclipse,去年初在前辈的推荐下,在2折的时候买了正版的 IntelliJ IDEA 和 Pycharm,12.0版终生使用,一年更新。使用前早就久闻其名,据说是最好的Java开发工具。起先用的非常不习惯,但是花了钱的,硬着头皮用了下去。短短一个礼拜,彻底爱上了它!就是辣么酸爽!--------------------------------------------------------------谨以此文献给对于拥抱IDEA还存在疑虑的筒子们 JetBrains(这个公司的名称翻译过来就是“大脑喷射?”) 公司的宣传语是这么说的:Develop with pleasure!(带着快乐开发!) Why?他们咋这么有信心呢?这到底是怎样的一个工具? 跟我一起来数数IDEA特别长的地方: 一.黑色主题 Darcula 眼睛舒服,最重要的是酷!设置方式:FILE--Settings--Edit--Colors&Fonts--Scheme name 二.智能提示 创建或引入项目的时候,会自动引入缺少的包,真找不着的还可以search in the net键入代码的时候,区分大小写的智能提示,自动引入包,如果有重名的会列出所有选择,但是比 myeclipse 的更聪明,至于怎么个聪明法,你自个儿试试就知道了 三.工具集成 基本上正式开发的常用工具基本都集成了,而且基本都在你非常容易触到的位置。说说我比较常用的: 1.ant 你懂的 2.maven你也懂的 3.SVN相比之下,IDEA的SVN的提交提供了更多的选项和功能提交的界面两侧会显示当前文件和修改的部分对比,可以非常方便地检查和修改文件。提交前:(1).可选择自动格式化(2).可选择自动忽略没有使用的imports(3).可选择分析代码(4).检查是否有TODO提交后甚至可以选择将改变上传到特定的目录 4.系统终端有了这个东西,你就不必频繁地切换窗口了打开方式:Tools--Open Terminal 5.SSH工具打开方式:Tools--Start SSH session 6.数据库连接工具打开方式:View--Tool Windows--Database 7.IDEA talk神奇的东西,你可以联系局域网内其它的IDEA使用者,可以方便地把你的代码show给你的同事打开方式:View--Tool Windows--IDEA talk 8.Changes非常方便的changes视图,它会变色显示你所有改动过的文件,而且可以方便的与 本地历史 或 线上历史 做对比打开方式:View--Tool Windows--Changes 还有git、github、cvs、groovy consle以及等等,真没有的你还可以:FILE--Settings--Plugins 四.强大的绘图工具 读源码看不懂项目结构?写文章画类图好麻烦?右键diagram帮助你 上个例图: 右键选择 Show Categories 有惊喜,什么值域、方法、构造器、内部类统统有 五.无缝接入eclipse和myeclipse IDEA可以直接打开有.project文件的项目,也可以将自身项目导出成eclipse的项目。而且因为IDEA的项目配置文件为1个.iml文件加一个独立文件夹.idea,所以只要你将两者放入ignore目录,即使在你个人使用IDEA而团队使用其他IDE的情况下,也并不会对其他人造成麻烦。 导出方法:FILE--Export to eclipse 六.更小的体积,更快的速度 用过myeclipse的你明白的 综上,我想你应该能够明白,为啥用IDEA写代码,就是那么愉快,就是那么酸爽,就是那么令人停不下来! 接着分享开发中我比较常用的一些快捷键: 一.找文件找代码找引用相关 1.双击shift 在项目的所有目录查找,就是你想看到你不想看到的和你没想过你能看到的都给你找出来 2.ctrl+f 当前文件查找特定内容 3.ctrl+shift+f当前项目查找包含特定内容的文件 4.ctrl+n 查找类 5.ctrl+shift+n查找文件 6.ctrl+e 最近的文件 7.alt+F7非常非常频繁使用的一个快捷键,可以帮你找到你的函数或者变量或者类的所有引用到的地方 二.编辑相关 1.shift+enter另起一行 2.ctrl+r当前文件替换特定内容 3.ctrl+shift+r当前项目替换特定内容 4.shift+F6非常非常省心省力的一个快捷键,可以重命名你的类、方法、变量等等,而且这个重命名甚至可以选择替换掉注释中的内容 5.ctrl+d复制当前行到下一行 6.ctrl+x剪切当前行 7.ctrl+c ctrl+v 大家都懂的 8.ctrl+z撤销 9.ctrl+shift+z取消撤销 10.ctrl+k提交代码到SVN 11.ctrl+t更新代码 12.alt+insert非常非常以及相当方便的一个组合键,不信您往下看在类中使用: 可以自动生成构造器、getter/setter等等常用方法在项目目录上使用: 可用于新建各种文件。 13.alt+enter又是一个大杀器,有多杀?例1:发现代码很“黄”怎么办? 鼠标移上去喵一下: 试试alt+enter: 选择simplify看看 我勒个去,代码简洁了有木有啊! PS:黄色警告编译可以通过,不影响程序的正常运行,一般都是一些对于代码优化的建议,我遇到过的有:(1).can be simplify代码可以简化(2).Variable is never use声明的变量从未被使用(3)unnecessary boxing不必要的声明包装 以及等等等,多留意下,对于编码的某些细节能够更了解。 例2:写个类实现某个接口,加上 implements Runnable 后你就会发现这条红色的波浪线: 咱试试在这行上来个 alt+enter: 感动地哭了有木有?你想到的没想到的IDEA都帮你想到了。看到代码里提示的各种红XX,就试试alt+enter吧!什么未抛异常啊没有try catch啊都能搞得定。 14.ctrl+alt+L 自动格式化代码,我已经养成了写完代码就来一发的习惯。你可以个性化设置你自己的代码风格:File--Settings--CodeStyle ——————————切——————————切——————————切——————————切——————————切—————————— 凡事有利有弊,IDEA同样是把双刃剑,最后说说它不好的地方 1.最重要的就是“贵”!专业版个人许可2折可以接受,5折小贵,不打折桑不起!!!囧RZ~美刀的消费水准令国内大多在屌丝线上挣扎的猿类们望而却步。好在JetBrains是有社区版的,最近对于学生也有免费的Key。对于使用破解版的小伙伴,建议自己用用就好了,不必太声张。 用户养成良好的软件付费习惯,才能让我们的工作变得越来越有价值。 2.集成太完善了集成的工具完全能够应付大多数的工作需求,对于不求甚解又偷懒的筒子们,直接使用,可能很多工具的细节,你都不会了解,也不会再去了解了。比如:maven,ant 3.不自带JDK不是缺点的缺点,myeclipse是集成的 差不多了,该撸代码了。以上这些,就是我使用IDEA一年半来几乎所有的心得了。能力有限,砖头轻拍。如果你有杀伤力更强大的手段,欢迎交流。 来自:http://www.blogjava.net/rockblue1988/archive/2014/10/24/418994.html
虽然MyBatis很方便,但是想要手写全部的mapper还是很累人的,好在MyBatis官方推出了自动化工具,可以根据数据库和定义好的配置直接生成DAO层及以下的全部代码,非常方便.首先wom我们自己建一个maven项目,我这里就不详细写mybatis-generator使用配置 打开pom.xml文件,添加3个依赖和mybatis-generator插件,分别是1.mybatis3.xjar包 2.逆向工程核心包 3.数据库连接包 4.log4j.jar用于输出日志 <build> <plugins> <plugin> <!-- 用maven mybatis插件 如果不在plugin里面添加依赖包得引用的话,会找不到相关得jar包, 在plugin外部得jar包,他不会去找到并执行, 所以要把plugin运行依赖得jar配置都放在里面 --> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-maven-plugin</artifactId> <version>1.3.5</version> <dependencies> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.2.6</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.30</version> </dependency> </dependencies> </plugin> </plugins> </build> 在src/main/resources包下创建逆向工程配置文件generatorConfig.xml,内容如下: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"> <generatorConfiguration> <context id="testTables" targetRuntime="MyBatis3"> <commentGenerator> <!-- 是否去除自动生成的注释 true:是 : false:否 --> <property name="suppressAllComments" value="true" /> </commentGenerator> <!--数据库连接的信息:驱动类、连接地址、用户名、密码 --> <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/mybatis" userId="root" password="123qwe"> </jdbcConnection> <!-- <jdbcConnection driverClass="oracle.jdbc.OracleDriver" connectionURL="jdbc:oracle:thin:@127.0.0.1:1521:yycg" userId="yycg" password="yycg"> </jdbcConnection> --> <!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL 和 NUMERIC 类型解析为java.math.BigDecimal --> <javaTypeResolver> <property name="forceBigDecimals" value="false" /> </javaTypeResolver> <!-- targetProject:生成PO类的位置 --> <javaModelGenerator targetPackage="po" targetProject="src"> <!-- enableSubPackages:是否让schema作为包的后缀 --> <property name="enableSubPackages" value="false" /> <!-- 从数据库返回的值被清理前后的空格 --> <property name="trimStrings" value="true" /> </javaModelGenerator> <!-- targetProject:mapper映射文件生成的位置 --> <sqlMapGenerator targetPackage="mapper" targetProject="src"> <!-- enableSubPackages:是否让schema作为包的后缀 --> <property name="enableSubPackages" value="false" /> </sqlMapGenerator> <!-- targetPackage:mapper接口生成的位置 --> <javaClientGenerator type="XMLMAPPER" targetPackage="mapper" targetProject="src"> <!-- enableSubPackages:是否让schema作为包的后缀 --> <property name="enableSubPackages" value="false" /> </javaClientGenerator> <!-- 指定数据库表 --> <table tableName="items"></table> <table tableName="orders"></table> <table tableName="orderdetail"></table> <!-- <table schema="" tableName="sys_user"></table> <table schema="" tableName="sys_role"></table> <table schema="" tableName="sys_permission"></table> <table schema="" tableName="sys_user_role"></table> <table schema="" tableName="sys_role_permission"></table> --> <table tableName="t_warningSetting" domainObjectName="WarningSetting" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"></table> <!-- 有些表的字段需要指定java类型 <table schema="" tableName=""> <columnOverride column="" javaType="" /> </table> --> </context> </generatorConfiguration> 需要修改的地方: javaModelGenerator,生成PO类的位置 sqlMapGenerator,mapper映射文件生成的位置 javaClientGenerator,mapper接口生成的位置 table,其tableName属性对应数据库中相应表 点击IDEA右边的maven projects标签,按下图进行操作 这里写图片描述 运行插件后最后的工程目录结构如下: 这里写图片描述 mybatis-generator的应用 mybatis-generator往往是单独的建立一个普通工程如A,通过运行逆向工程生成相应的mapper和po后然后再将这两个包拷贝到我们使用到ssm框架创建的web项目,而不是直接在web项目中使用逆向工程。 通过运行上述的程序,我们便通过数据库中的表快速的生成了相应的po类和mapper,而不用我们程序员自己再编写相应的po类和mapper,为我们带来了很大的方便,所以这个一定要学会,在后续开发中只要使用到mybatis的地方我们都会通过mybatis的逆向工程自动为我们生成mapper和po类。
前言 Swagger 是一款RESTFUL接口的文档在线自动生成+功能测试功能软件。本文简单介绍了在项目中集成swagger的方法和一些常见问题。如果想深入分析项目源码,了解更多内容,见参考资料。 Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。总体目标是使客户端和文件系统作为服务器以同样的速度来更新。文件的方法,参数和模型紧密集成到服务器端的代码,允许API来始终保持同步。Swagger 让部署管理和使用功能强大的API从未如此简单。一、使用介绍 什么是 Swagger? Swagger™的目标是为REST APIs 定义一个标准的,与语言无关的接口,使人和计算机在看不到源码或者看不到文档或者不能通过网络流量检测的情况下能发现和理解各种服务的功能。当服务通过Swagger定义,消费者就能与远程的服务互动通过少量的实现逻辑。类似于低级编程接口,Swagger去掉了调用服务时的很多猜测。 浏览 Swagger-Spec 去了解更多关于Swagger 项目的信息,包括附加的支持其他语言的库。 如何集成Swagger-springmvc到我们的项目中? 依赖:Maven <dependency> <groupId>com.mangofactory</groupId> <artifactId>swagger-springmvc</artifactId> <version>0.9.4</version> </dependency> Gradle repositories { jcenter() } compile "com.mangofactory:swagger-springmvc:0.9.4"使用:要最快捷地启动swagger-springmvc项目并且使用默认设置,推荐的方式是使用SwaggerSpringMvc插件 Spring Java Configuration @Configuration @EnableWebMvc @EnableSwagger @ComponentScan("com.myapp.packages") public class WebAppConfig { ... } Spring xml Configuration SwaggerSpringMvcPlugin XML Configuration 为了使用这个插件,你需要创造一个spring java配置类。使用spring的 @Configuration ,这个配置类必须被定义到你的xml上下文 <mvc:annotation-driven/> <bean class="com.yourapp.configuration.MySwaggerConfig"/> @Configuration @EnableSwagger //Loads the spring beans required by the framework public class MySwaggerConfig { private SpringSwaggerConfig springSwaggerConfig; /** * Required to autowire SpringSwaggerConfig */ @Autowired public void setSpringSwaggerConfig(SpringSwaggerConfig springSwaggerConfig) { this.springSwaggerConfig = springSwaggerConfig; } /** * Every SwaggerSpringMvcPlugin bean is picked up by the swagger-mvc framework - allowing for multiple * swagger groups i.e. same code base multiple swagger resource listings. */ @Bean public SwaggerSpringMvcPlugin customImplementation(){ return new SwaggerSpringMvcPlugin(this.springSwaggerConfig) .includePatterns(".*pet.*"); } } SwaggerSpringMvcPlugin Spring Java Configuration 使用@EnableSwagger注解 自动注入SpringSwaggerConfig 定义一个或多个SwaggerSpringMvcPlugin实例,通过springs @Bean注解 @Configuration @EnableWebMvc @EnableSwagger @ComponentScan("com.myapp.controllers") public class CustomJavaPluginConfig { private SpringSwaggerConfig springSwaggerConfig; @Autowired public void setSpringSwaggerConfig(SpringSwaggerConfig springSwaggerConfig) { this.springSwaggerConfig = springSwaggerConfig; } @Bean //Don't forget the @Bean annotation public SwaggerSpringMvcPlugin customImplementation(){ return new SwaggerSpringMvcPlugin(this.springSwaggerConfig) .apiInfo(apiInfo()) .includePatterns(".*pet.*"); } private ApiInfo apiInfo() { ApiInfo apiInfo = new ApiInfo( "My Apps API Title", "My Apps API Description", "My Apps API terms of service", "My Apps API Contact Email", "My Apps API Licence Type", "My Apps API License URL" ); return apiInfo; } } 二、碰到的问题 关于@API注解 在Swagger Annotation中: API表示一个开放的API,可以通过description简要描述该API的功能。 在一个@API下,可有多个@ApiOperation,表示针对该API的CRUD操作。在ApiOperation Annotation中可以通过value,notes描述该操作的作用,response描述正常情况下该请求的返回对象类型。 在一个ApiOperation下,可以通过ApiResponses描述该API操作可能出现的异常情况。 ApiParam用于描述该API操作接受的参数类型再接着,为项目的Model对象添加Swagger Annotation,这样Swagger Scanner可以获取更多关于Model对象的信息。 @ApiModel(value = "A SayingRepresentation is a representation of greeting") @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) public class SayingRepresentation { private long id; @ApiModelProperty(value = "greeting content", required = true) private String content; public SayingRepresentation(long id, String content) { this.id = id; this.content = content; } public long getId() { return id; } public String getContent() { return content; } 通过上面的步骤,项目已经具备了提供Swagger格式的API信息的能力,接下来,我们把这些信息和Swagger UI集成,以非常美观,实用的方式把这些API信息展示出来。 和Swagger UI的集成 首先,从github(https://github.com/wordnik/swagger-ui)上下载Swagger-UI, 把该项目dist目录下的内容拷贝到项目的resources的目录assets下。 然后,修改index.html, 把Swagger UI对象中的URL替换为自己的API路径。 window.swaggerUi = new SwaggerUi({ url: "/api/api-docs", dom_id: "swagger-ui-container", 最后,为了能访问到该页面,还需要在Service的Initialize方法中,添加AssetsBundle public void initialize(Bootstrap<HelloWorldConfiguration> bootstrap) { //指定配置文件的名字 bootstrap.setName("helloWorld"); bootstrap.addBundle(new AssetsBundle("/assets", "/", "index.html")); } 最后的效果图: 三、评价 Swagger可以充当前后端交流的重要桥梁,方便快捷。很实用。 Swagger项目允许你生产,显示和消费你自己的RESTful服务。不需要代理和第三方服务。是一个依赖自由的资源集合,它能通过Swagger API动态的生成漂亮的文档和沙盒,因为Swagger UI没有依赖,你可以把他部署到任何服务器环境或者是你自己的机器。 四、参考资料 官网:http://swagger.io/ GitHub:swagger-springmvc:https://github.com/martypitt/swagger-springmvc swagger-ui:https://github.com/swagger-api/swagger-ui swagger-core:https://github.com/swagger-api/swagger-core swagger-spec:https://github.com/swagger-api/swagger-spec