社区服务 统计排行 帮助
  • 517阅读
  • 39回复

植发有害吗?

楼层直达
级别: 圣骑士
只看该作者 30楼 发表于: 10-13
Java Web 开发平台 WebBuilder 7 发布
WebBuilder是一款开源的可视化Web应用开发和运行平台 java培训。基于浏览器的集成开发环境,可视化和智能化的设计,能轻松完成Web应用和面向移动设备的应用开发;高效、稳定和可扩展的特点,适合复杂企业级应用的运行;跨平台、数据库和浏览器的架构,适应复杂的服务器和客户端环境 西安it培训;包含多项先进技术,使应用系统的开发更快捷和简单。

官网:http://www.putdb.com
西安网页培训
WebBuilder 7 更新内容:

相关的主题文章:

  
   [url=http://www.sjwlip.com/bbs/read.php?tid=2
级别: 圣骑士
只看该作者 31楼 发表于: 10-13
Spring思维导图,让Spring不再难懂(aop篇)
什么是aop
AOP(Aspect-OrientedProgramming,面向方面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。这种散布在各处的无关的代码被称为横切(cross-cutting)代码,在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。
而AOP技术则恰恰相反,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。
aop使用场景
aop框架种类
使用aop可以做的事情有很多
观察一下传统编码方式与使用aop的区别
核心概念
描述AOP常用的一些术语有通知(Adivce)、切点(Pointcut)、连接点(Join point)、切面(Aspect)、引入(Introduction)、织入(Weaving)、通知(Advice)等。
简单例子
相比xml配置,基于注解的方式更加简洁方便。
@Aspectpublic class TransactionDemo { @Pointcut(value="execution(* com.yangxin.core.service.*.*.*(..))") public void point(){ } @Before(value="point()") public void before(){ System.out.println("transaction begin"); } 疯狂极客 @AfterReturning(value = "point()") public void after(){ System.out.println("transaction commit"); } @Around("point()") public void around(ProceedingJoinPoint joinPoint) throws Throwable{ System.out.println("transaction begin"); joinPoint.proceed(); System.out.println("transaction commit"); } }
在applicationContext.xml中配置。
<aop:aspectj-autoproxy /> <bean id = "transactionDemo" class = "com.yangxin.core.transaction.TransactionDemo" />
spring aop原理
通过前面介绍可以知道:AOP 代理其实是由 AOP 框架动态生成的一个对象,该对象可作为目标对象使用。AOP 代理包含了目标对象的全部方法,但 AOP 代理中的方法与目标对象的方法存在差异:AOP 方法在特定切入点添加了增强处理,并回调了目标对象的方法。
Spring 的 AOP 代理由 Spring 的 IoC 容器负责生成、管理,其依赖关系也由 西安网页培训 IoC 容器负责管理。因此,AOP 代理可以直接使用容器中的其他 Bean 西安软件培训 实例作为目标,这种关系可由 IoC 容器的依赖注入提供。
aop开发时,其中需要程序员参与的只有 3 个部分:
为了理清关系,先来个类关系图。
两种动态代理方式
Spring默认采取的动态代理机制实现AOP,当动态代理不可用时(代理类无接口)会使用CGlib机制。
Spring提供了两种方式来生成代理对象: JDKProxy和Cglib,具体使用哪种方式生成由AopProxyFactory根据AdvisedSupport对象的配置来决定。默认的策略是如果目标类是接口,则使用JDK动态代理技术,否则使用Cglib来生成代理。
JDK动态代理
JDK动态代理主要涉及到java.lang.reflect包中的两个类:Proxy和InvocationHandler。InvocationHandler是一个接口,通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编制在一起。
Proxy利用InvocationHandler动态创建一个符合某一接口的实例,生成目标类的代理对象。
CGLib动态代理
CGLib全称为Code Generation Library,是一个强大的高性能,高质量的代码生成类库,可以在运行期扩展Java类与实现Java接口,CGLib封装了asm,可以再运行期动态生成新的class。和JDK动态代理相比较:JDK创建代理有一个限制,就是只能为接口创建代理实例,而对于没有通过接口定义业务方法的类,则可以通过CGLib创建动态代理。
知识拓展
通过上面的分析,大家是否有种熟悉的感觉,似乎和拦截器、过滤器的功能相似。那么问题来了,aop与拦截器、过滤器是什么关系。
先来回顾一下拦截器与过滤器。如下图一网友的测试,在web.xml中注册了TestFilter1和TestFilter2。然后在spring的配置文件中配置了BaseInterceptor和TestInterceptor。得到的结果如下图所示。从图中可以看出,拦截器和过滤器都横切了业务方法,看似符合aop的思想。
ilter过滤器:拦截web访问url地址。
Interceptor拦截器:拦截以 .action结尾的url,拦截Action的访问。
Spring AOP拦截器:只能拦截Spring管理Bean的访问(业务层Service)
写在最后
下篇文章将会写Spring cache的内容,同样以思维导图的方式编写。可视化学习,让java不再难懂。
相关的主题文章:

  
   建筑装修网怎么样 弱电系统工程如何
  
   竞价和seo的对比:seo和竞价哪个好
级别: 圣骑士
只看该作者 32楼 发表于: 10-13
ITeye新闻热点月刊2014年03月总第73期发布了! - 互联网 - ITeye资讯
ITeye新闻热点月刊总第73期(2014年03月版)发布了!制作精美,内容丰富,为您总结一个月最精彩的技术新闻。
【点击下载2014年03月ITeye新闻热点月刊】
精彩内容推荐:
>> 西安seo培训 Java 8正式发布,新特性全搜罗
甲骨文3月18日正式发布了Java 8,带来了诸多新特性,比如Lambda表达式、Nashorn引擎、日期时间API等,以及GC、并发等方面的改进。
>> Google公布Ara样机视频,手机DIY将成现实
Ara项目的目的是构建高度模块化的智能手机,用户可以购买不同的模块然后自己组装手机,这就像DIY电脑一样。
>> 西安java培训机构哪家好 高效能程序员的七个习惯
高效能程序员应该具备的七个习惯,你占了几个 西安java培训机构
【点击下载2014年03月ITeye新闻热点月刊】
【查看所有新闻热点月刊】
相关的主题文章:

  
   [url=http://www.sjwlip.com/bbs/read.php?tid=2
级别: 圣骑士
只看该作者 33楼 发表于: 10-13
关于Java你可能不知道的10件事
呃,你是不是写Java已经有些年头了?还依稀记得这些吧: 那些年,它还叫做Oak;那些年,OO还是个热门话题;那些年,C  同学们觉得Java是没有出路的;那些年,Applet还风头正劲……
但我打赌下面的这些事中至少有一半你还不知道。这周我们来聊聊这些会让你有些惊讶的Java内部的那些事儿吧。
1. 其实没有受检异常(checked exception)
是的!JVM才不知道这类事情,只有Java语言才会知道。
今天,大家都赞同受检异常是个设计失误,一个Java语言中的设计失误。正如 Bruce Eckel 在布拉格的GeeCON会议上演示的总结中说的, Java之后的其它语言都没有再涉及受检异常了,甚至Java 8的新式流API(Streams API)都不再拥抱受检异常 (以lambda的方式使用IO和JDBC,这个API用起来还是有些痛苦的。)
想证明JVM不理会受检异常?试试下面的这段代码:
public class Test { // 方法没有声明throws public static void main(String[] args) { doThrow(new SQLException()); } static void doThrow(Exception e) { Test.<RuntimeException> doThrow0(e); } @SuppressWarnings("unchecked") static <E extends Exception> void doThrow0(Exception e) throws E { throw (E) e; } }
不仅可以编译通过,并且也抛出了SQLException,你甚至都不需要用上Lombok的@SneakyThrows。
更多细节,可以再看看这篇文章,或Stack Overflow上的这个问题。
2. 可以有只是返回类型不同的重载方法
下面的代码不能编译,是吧?
class Test { Object x() { return "abc"; } String x() { return "123"; } }
是的!Java语言不允许一个类里有2个方法是『重载一致』的,而不会关心这2个方法的throws子句或返回类型实际是不同的。
但是等一下!来看看Class.getMethod(String, Class...)方法的Javadoc:
引用注意,可能在一个类中会有多个匹配的方法,因为尽管Java语言禁止在一个类中多个方法签名相同只是返回类型不同,但是JVM并不禁止。 这让JVM可以更灵活地去实现各种语言特性。比如,可以用桥方法(bridge method)来实现方法的协变返回类型;桥方法和被重载的方法可以有相同的方法签名,但返回类型不同。
嗯,这个说的通。实际上,当写了下面的代码时,就发生了这样的情况:
abstract class Parent<T> { abstract T x(); } class Child extends Parent<String> { @Override String x() { return "abc"; } }
查看一下Child类所生成的字节码:
// Method descriptor #15 ()Ljava/lang/String; // Stack: 1, Locals: 1 java.lang.String x(); 0 ldc <String "abc"> [16] 2 areturn Line numbers: [pc: 0, line: 7] Local variable table: [pc: 0, pc: 3] local: this index: 0 西安java培训机构 type: Child // Method descriptor #18 ()Ljava/lang/Object; // Stack: 1, Locals: 1 bridge synthetic java.lang.Object x(); 0 aload_0 [this] 1 invokevirtual Child.x() : java.lang.String [19] 4 areturn Line numbers: [pc: 0, line: 1]
在字节码中,T实际上就是Object类型。这很好理解。
合成的桥方法实际上是由编译器生成的,因为在一些调用场景下,Parent.x()方法签名的返回类型期望是Object。 添加泛型而不生成这个桥方法,不可能做到二进制兼容。 所以,让JVM允许这个特性,可以愉快解决这个问题(实际上可以允许协变重载的方法包含有副作用的逻辑)。 聪明不?呵呵~
你是不是想要扎入语言规范和内核看看?可以在这里找到更多有意思的细节。
3. 所有这些写法都是二维数组!
class Test { int[][] a() { return new int[0][]; } int[] b() [] { return new int[0][]; } int c() [][] { return new int[0][]; } }
是的,这是真的。尽管你的人肉解析器不能马上理解上面这些方法的返回类型,但都是一样的!下面的代码也类似:
class Test { int[][] a = {{}}; int[] b[] = {{}}; int c[][] = {{}}; }
是不是觉得这个很2B?想象一下在上面的代码中使用JSR-308/Java 8的类型注解。 语法糖的数目要爆炸了吧!
@Target(ElementType.TYPE_USE) @interface Crazy {} class Test { @Crazy int[][] a1 = {{}}; int @Crazy [][] a2 = {{}}; int[] @Crazy [] a3 = {{}}; @Crazy int[] b1[] = {{}}; int @Crazy [] b2[] = {{}}; int[] b3 @Crazy [] = {{}}; @Crazy int c1[][] = {{}}; int c2 @Crazy [][] = {{}}; int c3[] @Crazy [] = {{}}; }
类型注解。这个设计引入的诡异在程度上仅仅被它解决问题的能力超过。
或换句话说:
在我4周休假前的最后一个提交里,我写了这样的代码,然后。。。
【译注:然后,亲爱的同事你,就有得火救啦,哼,哼哼,哦哈哈哈哈~】
请找出上面用法合适的使用场景,还是留给你作为一个练习吧。
4. 你没有掌握条件表达式
呃,你认为自己知道什么时候该使用条件表达式?面对现实吧,你还不知道。大部分人会下面的2个代码段是等价的:
Object o1 = true ? new Integer(1) : new Double(2.0);
等同于:
Object o2; if (true) o2 = new Integer(1); else o2 = new Double(2.0);
让你失望了。来做个简单的测试吧:
System.out.println(o1); System.out.println(o2);
打印结果是:
1.0 1
哦!如果『需要』,条件运算符会做数值类型的类型提升,这个『需要』有非常非常非常强的引号。因为,你觉得下面的程序会抛出NullPointerException吗?
Integer i = new Integer(1); if (i.equals(1)) i = null; Double d = new Double(2.0); Object o = true ? i : d; // NullPointerException! System.out.println(o);
关于这一条的更多的信息可以在这里找到。
5. 你没有掌握复合赋值运算符
是不是觉得不服?来看看下面的2行代码:
i  = j; i = i   j;
直觉上认为,2行代码是等价的,对吧?但结果即不是!JLS(Java语言规范)指出:
引用复合赋值运算符表达式 E1 op= E2 等价于 E1 = (T)((E1) op (E2)) 其中T是E1的类型,但E1只会被求值一次。
这个做法太漂亮了,请允许我引用Peter Lawrey在Stack Overflow上的回答:
使用*=或/=作为例子可以方便说明其中的转型问题:
byte b = 10; b *= 5.7; System.out.println(b); // prints 57 byte b = 100; b /= 2.5; System.out.println(b); // prints 40 char ch = '0'; ch *= 1.1; System.out.println(ch); // prints '4' char ch = 'A'; ch *= 1.5; System.out.println(ch); // prints 'a'
为什么这个真是太有用了?如果我要在代码中,就地对字符做转型和乘法。然后,你懂的……
6. 随机Integer
这条其实是一个迷题,先不要看解答。看看你能不能自己找出解法。运行下面的代码:
for (int i = 0; i < 10; i  ) { System.out.println((Integer) i); }
…… 然后要得到类似下面的输出(每次输出是随机结果):
引用92
221
45
48
236
183
39
193
33
84
这怎么可能?!
.
.
.
.
.
.
. 我要剧透了…… 解答走起……
.
.
.
.
.
.
好吧,解答在这里(http://blog.jooq.org/2013/10/17/add-some-entropy-to-your-jvm/), 和用反射覆盖JDK的Integer缓存,然后使用自动打包解包(auto-boxing/auto-unboxing)有关。 同学们请勿模仿!或换句话说,想想会有这样的状况,再说一次:
在我4周休假前的最后一个提交里,我写了这样的代码,然后。。。
【译注:然后,亲爱的同事你,就有得火救啦,哼,哼哼,哦哈哈哈哈~】
7. GOTO
这条是我的最爱。Java是有GOTO的!打上这行代码:
int goto = 1;
结果是:
Test.java:44: error: <identifier> expected int goto = 1; ^
这是因为goto是个还未使用的关键字,保留了为以后可以用……
但这不是我要说的让你兴奋的内容。让你兴奋的是,你是可以用break、continue和有标签的代码块来实现goto的:
向前跳:
label: { // do stuff if (check) break label; // do more stuff }
对应的字节码是:
2 iload_1 [check] 3 ifeq 6 // 向前跳 6 ..
向后跳:
label: do { // do stuff if (check) continue label; // do more stuff break label; } while(true);
对应的字节码是:
2 iload_1 [check] 3 ifeq 9 6 goto 2 // 向后跳 9 ..
8. Java是有类型别名的
在别的语言中(比如,Ceylon), 可以方便地定义类型别名:
interface People => Set<Person>;
这样定义的People可以和Set<Person>互换地使用:
People? p1 = null; Set<Person>? p2 = p1; People? p3 = p2;
在Java中不能在顶级(top level)定义类型别名。但可以在类级别、或方法级别定义。 如果对Integer、Long这样名字不满意,想更短的名字:I和L。很简单:
class Test<I extends Integer> { <L extends Long> void x(I i, L l) { System.out.println( i.intValue()   ", "   l.longValue() ); } }
上面的代码中,在Test类级别中I是Integer的『别名』,在x方法级别,L是Long的『别名』。可以这样来调用这个方法:
new Test().x(1, 2L);
当然这个用法不严谨。在例子中,Integer、Long都是final类型,结果I和L 效果上是个别名 (大部分情况下是。赋值兼容性只是单向的)。如果用非final类型(比如,Object),还是要使用原来的泛型参数类型。
玩够了这些恶心的小把戏。现在要上干货了!
9. 有些类型的关系是不确定的
好,这条会很稀奇古怪,你先来杯咖啡,再集中精神来看。看看下面的2个类型:
// 一个辅助类。也可以直接使用List interface Type<T> {} class C implements Type<Type<? super C>> {} class D<P> implements Type<Type<? super D<D<P>>>> {}
类型C和D是啥意思呢?
这2个类型声明中包含了递归,和java.lang.Enum的声明类似 (但有微妙的不同):
public abstract class Enum<E extends Enum<E>> { ... }
有了上面的类型声明,一个实际的enum实现只是语法糖:
// 这样的声明 enum MyEnum {} // 实际只是下面写法的语法糖: class MyEnum extends Enum<MyEnum> { ... }
记住上面的这点后,回到我们的2个类型声明上。下面的代码可以编译通过吗?
class Test { Type<? super C> c = new C(); Type<? super D<Byte>> d = new D<Byte>(); }
很难的问题,Ross Tate回答过这个问题。答案实际上是不确定的:
C是Type<? super C>的子类吗? 步骤 0) C <?: Type<? super C> 步骤 1) Type<Type<? super C>> <?: Type (继承) 步骤 2) C (检查通配符 ? super C) 步骤 . . . (进入死循环)
然后:
D是Type<? super D<Byte>>的子类吗? 步骤 0) D<Byte> <?: Type<? super C<Byte>> 步骤 1) Type<Type<? super D<D<Byte>>>> <?: Type<? super D<Byte>> 步骤 2) D<Byte> <?: Type<? super D<D<Byte>>> 步骤 3) List<List<? super C<C>>> <?: List<? super C<C>> 步骤 4) 西安web前端培训机构 D<D<Byte>> <?: Type<? super D<D<Byte>>> 步骤 . . . (进入永远的展开中)
试着在你的Eclipse中编译上面的代码,会Crash!(别担心,我已经提交了一个Bug。)
我们继续深挖下去……
在Java中有些类型的关系是不确定的!
如果你有兴趣知道更多古怪Java行为的细节,可以读一下Ross Tate的论文『驯服Java类型系统的通配符』 (由Ross Tate、Alan Leung和Sorin Lerner合著),或者也可以看看我们在子类型多态和泛型多态的关联方面的思索。
10. 类型交集(Type intersections)
Java有个很古怪的特性叫类型交集。你可以声明一个(泛型)类型,这个类型是2个类型的交集。比如:
class Test<T 西安java培训 extends Serializable
级别: 圣骑士
只看该作者 34楼 发表于: 10-13
Java线程的5个使用技巧
Java线程有哪些不太为人所知的技巧与用法?
萝卜白菜各有所爱。像我就喜欢Java。学无止境,这也是我喜欢它的一个原因。日常工作中你所用到的工具,通常都有些你从来没有了解过的东西,比方说某个方法或者是一些有趣的用法。比如说线程。没错,就是线程。或者确切说是Thread这个类。当我们在构建高可扩展性系统的时候,通常会面临各种各样的并发编程的问题,不过我们现在所要讲的可能会略有不同。
从本文中你将会看到线程提供的一些不太常用的方法及技术。不管你是初学者还是高级用户或者是Java专家,希望都能看一下哪些是你已经知道的,而哪些是刚了解的。如果你认为关于线程还有什么值得分享给大家的,希望能在下面积极回复。那我们就先开始吧。
初学者
1.线程名
程序中的每个线程都有一个名字,创建线程的时候会给它分配一个简单的Java字符串来作为线程名。默认的名字是”Thread-0″, “Thread-1″, “Thread-2″等等。现在有趣的事情来了——Thread提供了两种方式来设置线程名:

class SuchThread extends Thread { Public void run() { System.out.println ("Hi Mom! "   getName()); } } SuchThread wow = new SuchThread("much-name");

wow.setName(“Just another thread name”);
没错,线程名是可变的。因此我们可以在运行时修改它的名字,而不用在初始化的时候就指定好。name字段其实就是一个简单的字符串对象。也就是说它能达到2??-1个字符那么长(Integer.MAX_VALUE)。这足够用了。注意这个名字并不是一个唯一性的标识,因此不同的线程也可以拥有同样的线程名。还有一点就是,不要把null用作线程名,否则会抛出异常(当然了,”null”还是可以的)。
使用线程名来调试问题
既然可以设置线程名,那么如果遵循一定的命名规则的话,出了问题的时候排查起来就能更容易一些。“Thread-6″这样的名字看起来就太没心没肺了,肯定有比它更好的名字。在处理用户请求的时候,可以将事务ID追加到线程名后面,这样能显著减少你排查问题的时间。
“pool-1-thread-1″ #17 prio=5 os_prio=31 tid=0x00007f9d620c9800 nid=0x6d03 in Object.wait() [0x000000013ebcc000]
“pool-1-thread-1″,这也太严肃了吧。我们来看下这是什么情况,给它起一个好点的名字:
Thread.currentThread().setName(Context   TID   Params   current Time, ...);
现在我们再来运行下jstack,情况便豁然开朗了:
”Queue Processing Thread, MessageID: AB5CAD, type: AnalyzeGraph, queue: ACTIVE_PROD, Transaction_ID: 5678956, 西安网络营销培训 Start Time: 30/12/2014 17:37″ #17 prio=5 os_prio=31 tid=0x00007f9d620c9800 nid=0x6d03 in Object.wait() [0x000000013ebcc000]
如果我们能知道线程在做什么,这样当它出问题的时候,至少可以拿到事务ID来开始排查。你可以回溯这个问题,复现它,然后定位问题并搞定它。如果你想知道jstack有什么给力的用法,可以看下这篇文章。
2. 线程优先级
线程还有一个有意思的属性就是它的优先级。线程的优先级介于1 (MINPRIORITY)到10 (MAXPRIORITY)之间,主线程默认是5(NORM_PRIORITY)。每个新线程都默认继承父线程的优先级,因此如果你没有设置过的话,所有线程的优先级都是5。这个是通常被忽视的属性,我们可以通过getPriority()与setPriority()方法来获取及修改它的值。线程的构造函数里是没有这个功能的。
什么地方会用到优先级?
当然并不是所有的线程都是平等的,有的线程需要立即引起CPU的重视,而有些线程则只是后台任务而已。优先级就是用来把这些告诉给操作系统的线程调度器的。在Takipi中,这是我们开发的一错误跟踪及排查的工具,负责处理用户异常的线程的优先级是MAX_PRIORITY,而那些只是在上报新的部署情况的线程,它们的优先级就要低一些。你可能会觉得优先级高的线程从JVM的线程调度器那得到的时间会多一些。但其实并都是这样的。
在操作系统层面,每一个新线程都会对应一个本地线程,你所设置的Java线程的优先级会被转化成本地线程的优先级,这个在各个平台上是不一样的。在Linux上,你可以打开“-XX: UseThreadPriorities”选项来启用这项功能。正如前面所说的,线程优先级只是你所提供的一个建议。和Linux本地的优先级相比,Java线程的优先级并不能覆盖全所有的级别(Linux共有1到99个优先级,线程的优先级在是-20到20之间)。最大的好处就是你所设定的优先级能在每个线程获得的CPU时间上有所体现,不过完全依赖于线程优先级的做法是不推荐的。
进阶篇
3.线程本地存储
这个和前面提到的两个略有不同。ThreadLocal是在Thread类之外实现的一个功能(java.lang.ThreadLocal),但它会为每个线程分别存储一份唯一的数据。正如它的名字所说的,它为线程提供了本地存储,也就是说你所创建出来变量对每个线程实例来说都是唯一的。和线程名,线程优先级类似,你可以自定义出一些属性,就好像它们是存储在Thread线程内部一样,是不是觉得酷?不过先别高兴得太早了,有几句丑话得先说在前头。
创建ThreadLocal有两种推荐方式:要么是静态变量,要么是单例实例中的属性,这样可以是非静态的。注意,它的作用域是全局的,只不过对访问它的线程而言好像是本地的而已。在下面这个例子中,ThreadLocal里面存储了一个数据结构,这样我们可以很容易地访问到它:
public static class CriticalData { public int transactionId; public int username; } public static final ThreadLocal<CriticalData> globalData = new ThreadLocal<CriticalData>();
一旦获取到了ThreadLocal对象,就可以通过 globalData.set()和globalData.get()方法来对它进行操作了。
全局变量?这不是什么好事
也尽然。ThreadLocal可以用来存储事务ID。如果代码中出现未捕获异常的时候它就相当有用了。最佳实践是设置一个UncaughtExceptionHandler,这个是Thread类本身就支持的,但是你得自己去实现一下这个接口。一旦执行到了UncaughtExceptionHandler里,就几乎没有任何线索能够知道到底发生了什么事情了。这会儿你能获取到的就只有Thread对象,之前导致异常发生的所有变量都无法再访问了,因为那些栈帧都已经被弹出了。一旦到了UncaughtExceptionHandler里,这个线程就只剩下最后一口气了,唯一能抓住的最后一根稻草就是ThreadLocal。
我们来试下这么做:
System.err.println("Transaction ID "   globalData.get().transactionId);
我们可以将一些与错误相关的有价值的上下文信息给存储到里面添。ThreadLocal还有一个更有创意的用法,就是用它来分配一块特定的内存,这样工作线程可以把它当作缓存来不停地使用。当然了,这有没有用得看你在CPU和内存之间是怎么权衡的了。没错,ThreadLocal需要注意的就是会造成内存空间的浪费。只要线程还活着,那么它就会一直存在,除非你主动释放否则它是不会被回收的。因此如果使用它的话你最好注意一下,尽量保持简单。
4. 用户线程及守护线程
我们再回到Thread类。程序中的每个线程都会有一个状态,要么是用户状态,要么是守护状态。换句话说,要么是前台线程要么是后台线程。主线程默认是用户线程,每个新线程都会从创建它的线程中继承线程状态。因此如果你把一个线程设置成守护线程,那么它所创建的所有线程都会被标记成守护线程。如果程序中的所有线程都是守护线程的话,那么这个进程便会终止。我们可以通过Boolean .setDaemon(true)和.isDaemon()方法来查看及设置线程状态。
什么时候会用到守护线程?
如果进程不必等到某个线程结束才能终止,那么这个线程就可以设置成守护线程。这省掉了正常关闭线程的那些麻烦事,可以立即将线程结束掉。换个角度来说,如果一个正在执行某个操作的线程必须要正确地关闭掉否则就会出现不好的后果的话,那么这个线程就应该是用户线程。通常都是些关键的事务,比方说,数据库录入或者更新,这些操作都是不能中断的。
专家级
5. 处理器亲和性(Processor Affinity)
这里要讲的会更靠近硬件 西安软件开发培训,也就是说,当软件遇上了硬件。处理器亲和性使得你能够将线程或者进程绑定到特定的CPU核上。这意味着只要是某个特定的线程,它就肯定只会在某个特定的CPU核上执行。通常来讲如何绑定是由操作系统的线程调度器根据它自己的逻辑来决定的,它很可能会将我们前面提到的线程优先级也一并考虑进来。
这么做的好处在于CPU缓存。如果某个线程只会在某个核上运行,那么它的数据恰好在缓存里的概率就大大提高了。如果数据正好就在CPU缓存里,那么就没有必要重新再从内存里加载了。你所节省的这几毫秒时间就能用在刀刃上,在这段时间里代码可以马上开始执行,也就能更好地利用所分配给它的CPU时间。当然了,操作系统层面可能会存在某种优化,硬件架构当然也是个很重要的因素,但利用了处理器的亲和性至少能够减小线程切换CPU的机率。
由于这里掺杂着多种因素,处理器亲和性到底对吞吐量有多大的影响,最好还是通过测试的方式来进行证明。也许这个方法并不是总能显著地提升性能,但至少有一个好处就是吞吐量会相对稳定。亲和策略可以细化到非常细的粒度上,这取决于你具体想要什么。高频交易行业便是这一策略最能大显身手的场景之一。
处理器亲和性的测试
Java对处理器的亲和性并没有原生的支持,当然了,故事也还没有就此结束。在Linux上,我们可以通过taskset命令来设置进程的亲和性。假设我们现在有一个Java进程在运行,而我们希望将它绑定到某个特定的CPU上:
taskset -c 1 “java 西安前端培训 AboutToBePinned”
如果是一个已经在运行了的进程:
taskset -c 1 <PID>
要想深入到线程级别还得再加些代码才行。所幸的是,有一个开源库能完成这样的功能:Java-Thread-Affinity。这个库是由OpenHFT的Peter Lawrey开发的,实现这一功能最简单直接的方式应该就是使用这个库了。我们通过一个例子来快速看下如何绑定某个线程,关于该库的更多细节请参考它在Github上的文档:
AffinityLock al = AffinityLock.acquireLock();
这样就可以了。关于获取锁的一些更高级的选项——比如说根据不同的策略来选择CPU——在Github上都有详细的说明。
结论
本文我们介绍了关于线程的5点知识:线程名,线程本地存储,优先级,守护线程以及处理器亲和性。希望这能为你日常工作中所用到的内容打开一扇新的窗户,期待你们的反馈!还有什么有关线程处理的方法可以分享给大家的吗,请不吝赐教。
相关的主题文章:

  
   [/url]
  
   [url=http://www.oo.ht/zhidao/question.php?qid=821]Java中处理异常的9个最佳实践
级别: 圣骑士
只看该作者 35楼 发表于: 10-13
Spring思维导图,让Spring不再难懂(mvc篇)
写在前面
生活就像海洋,只有意志坚强的人才能到达彼岸。已经很久没有发文章了呀,想必大家都挂念我了,哈哈。温故而知新,今天一起来复习一下spring mvc的内容吧。
spring mvc简介与运行原理
Spring的模型-视图-控制器(MVC)框架是围绕一个DispatcherServlet来设计的,这个Servlet会把请求分发给各个处理器,并支持可配置的处理器映射、视图渲染、本地化、时区与主题渲染等,甚至还能支持文件上传。
主要注解
ContextLoaderListener
在讲ContextLoaderListener之前,首先来了解一下web.xml的作用。
而spring mvc启动过程大致分为两个过程:
其中ContextLoaderListener监听器它实现了ServletContextListener这个接口,在web.xml配置这个监听器,启动容器时,就会默认执行它实现的方法。在ContextLoaderListener中关联了ContextLoader这个类,所以整个加载配置过程由ContextLoader来完成。
ContextLoaderListener在web.xml中的配置
<!-- 配置contextConfigLocation初始化参数 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/applicationContext.xml</param-value> </context-param> <!-- 配置ContextLoaderListerner --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
ServletContextListener 接口有两个方法:contextInitialized,contextDestroyed
DispatcherServlet
Spring MVC框架,与其他很多web的MVC框架一样:请求驱动;所有设计都围绕着一个中央Servlet来展开,它负责把所有请求分发到控制器;同时提供其他web应用开发所需要的功能。不过Spring的中央处理器,DispatcherServlet,能做的比这更多。
下图展示了Spring Web MVC的DispatcherServlet处理请求的工作流。熟悉设计模式的朋友会发现,DispatcherServlet应用的其实就是一个“前端控制器”的设计模式(其他很多优秀的web框架也都使用了这个设计模式)。
流程图
spring mvc处理请求的流程.jpg
在web.xml中的配置
<!-- servlet定义 --> <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
其中
在Spring MVC中,每个DispatcherServlet都持有一个自己的上下文对象WebApplicationContext,它又继承了根(root)WebApplicationContext对象中已经定义的所有bean。这些继承的bean可以在具体的Servlet实例中被重载,在每个Servlet实例中你也可以定义其scope下的新bean。
WebApplicationContext继承自ApplicationContext,它提供了一些web应用经常需要用到的特性。它与普通的ApplicationContext不同的地方在于,它支持主题的解析,并且知道它关联到的是哪个servlet(它持有一个该ServletContext的引用)
spring mvc同时提供了很多特殊的注解,用于处理请求和渲染视图等。DispatcherServlet初始化的过程中会默认使用这些特殊bean进行配置。如果你想指定使用哪个特定的bean,你可以在web应用上下文WebApplicationContext中简单地配置它们。
其中,常用的ViewResolver的配置。以jsp作为视图为例
<!-- 对模型视图名称的解析,即在模型视图名称添加前后缀 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/" /> <property name="suffix" value=".jsp" /> </bean>
配置上传文件限制MultipartResolver
<!-- 上传限制 --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!-- 上传文件大小限制为31M,31*1024*1024 --> <property name="maxUploadSize" value="32505856"/> </bean>
applicationContext.xml中的标签
文件上传
前面说到DispatcherServlet中有个特殊的Bean叫MultipartResolver,可用于限制文件的上传大小等。当解析器MultipartResolver完成处理时,请求便会像其他请求一样被正常流程处理。
表单
<form method="post" action="/form" enctype="multipart/form-data"> <input type="text" name="name"/> <input type="file" 西安前端培训 name="file"/> <input type="submit"/> </form>
控制器
@RequestMapping(path = "/form", method = RequestMethod.POST) public String handleFormUpload(@RequestParam("name") String name, @RequestParam("file") MultipartFile file) { if (!file.isEmpty()) { byte[] bytes = file.getBytes(); // store 西安sem培训 the bytes somewhere return "redirect:uploadSuccess"; } return "redirect:uploadFailure"; }
异常处理
先来说下常见的异常处理有几种方式,如下图:
Spring的处理器异常解析器HandlerExceptionResolver接口的实现负责处理各类控制器执行过程中出现的异常。也是上面提到的,是DispatcherServlet中的特殊bean,可以自定义配置处理。
某种程度上讲,HandlerExceptionResolver与你在web应用描述符web.xml文件中能定义的异常映射(exception mapping)很相像,不过它比后者提供了更灵活的方式。比如它能提供异常被抛出时正在执行的是哪个处理器这样的信息。
HandlerExceptionResolver 提供resolveException接口
public interface HandlerExceptionResolver { ModelAndView resolveException( HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex); }
在BaseController中使用 @ExceptionHandler注解处理异常
@ExceptionHandler(Exception.class) public Object exceptionHandler(Exception ex, HttpServletResponse response, HttpServletRequest request) throws IOException { String url = ""; String msg = ex.getMessage(); Object resultModel = null; try { if (ex.getClass() == HttpRequestMethodNotSupportedException.class) { url = "admin/common/500"; System.out.println("--------毛有找到对应方法---------"); } else if (ex.getClass() == ParameterException.class) {//自定义的异常 } else if (ex.getClass() == UnauthorizedException.class) { url = "admin/common/unauth"; System.out.println("--------毛有权限---------"); } String header = req.getHeader("X-Requested-With"); boolean isAjax = "XMLHttpRequest".equalsIgnoreCase(header); String method = req.getMethod(); boolean isPost = "POST".equalsIgnoreCase(method); if (isAjax || isPost) { return Message.error(msg); } else { ModelAndView view = new ModelAndView(url); view.addObject("error", msg); view.addObject("class", ex.getClass()); view.addObject("method", request.getRequestURI()); return view; } } catch (Exception exception) { logger.error(exception.getMessage(), exception); return resultModel; } finally { logger.error(msg, ex); ex.printStackTrace(); } }
在web.xml中处理异常
<!-- 默认的错误处理页面 --> <error-page> <error-code>403</error-code> <location>/403.html</location> </error-page> <error-page> <error-code>404</error-code> 疯狂极客培训怎么样 <location>/404.html</location> </error-page> <!-- 仅仅在调试的时候注视掉,在正式部署的时候不能注释 --><!-- 这样配置也是可以的,表示发生500错误的时候,转到500.jsp页面处理。 --> <error-page> <error-code>500</error-code> <location>/500.html</location> </error-page> <!-- 这样的配置表示如果jsp页面或者servlet发生java.lang.Exception类型(当然包含子类)的异常就会转到500.jsp页面处理。 --> <error-page> <exception-type>java.lang.Exception</exception-type> <location>/500.jsp</location> </error-page> <error-page> <exception-type>java.lang.Throwable</exception-type> <location>/500.jsp</location> </error-page> <!-- 当error-code和exception-type都配置时,exception-type配置的页面优先级高及出现500错误,发生异常Exception时会跳转到500.jsp-->
来一个问题:HandlerExceptionResolver和web.xml中配置的error-page会有冲突吗?
解答:如果resolveException返回了ModelAndView,会优先根据返回值中的页面来显示。不过,resolveException可以返回null,此时则展示web.xml中的error-page的500状态码配置的页面。
当web.xml中有相应的error-page配置,则可以在实现resolveException方法时返回null。
API文档中对返回值的解释:
return a corresponding ModelAndView to forward to, or null for default processing.
写在最后
下篇文章将会写Spring aop的内容,同样以思维导图的方式编写。可视化学习,让java不再难懂。
相关的主题文章:

  
   “Java之父”如何看待甲骨文接手后的Sun产品
  
   几级脱发不能植发
级别: 圣骑士
只看该作者 36楼 发表于: 10-13
以Java、Kotlin和Scala编写基本Spring Web应用的比较
引用原文:Basic Spring web application in Java, Kotlin and Scala - comparison
作者:Rados?aw Skupnik
翻译:Vincent
译者注:现在可以用来开发web应用的语言五花八门,每种语言都各有千秋,本文作者挑选了Java、Kotlin 、Scala这三种语言,开发同一个基础的Spring web应用,从而比对出他们之间的差别。以下为译文。
我一直在想,在JVM语言中选择一个(如Scala和Kotlin )用来实现同一个基础的Spring Boot应用程序是多么的困难,所以我决定试试。
源代码可以这个地址看到:https://github.com/rskupnik/pet-clinic-jvm
这款应用程序是非常基础的,因为它只包含以下元素:
我将用三种语言来做代码比较:
实体
这个应用里面涉及到了两个实体:Customer 和 Pet
Java
@Entity public class Customer { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String firstName, lastName; @JsonIgnore @OneToMany(mappedBy = "owner") private List<Pet> pets; protected Customer() { } public Customer(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } // A whole lot of getters and setters here... // Ommited for the sake of brevity @Override public String toString() { return firstName " " lastName; } }
@Entity public class Pet { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String name; @ManyToOne @JoinColumn(name = "ownerId", nullable = false) private Customer owner; protected Pet() { } public Pet(String name) { this.name = name; } // A whole lot of getters and setters here... // Ommited for the sake of brevity @Override public String toString() { return name; } }
这里无需多言——因为很显然Java是很冗长的,即使去掉getter和setter方法之后,还是会有很多的代码。除了使用Lombok可以帮助用户生成模板文件以外,或者类似的工具,我们也没有什么更好的办法。
Kotlin
在Kotlin语言中有好几种方法可以定义一个实体类,我已经试过两种了。尽管作用都是一样的,但是后者可能更受用户欢迎,因为前者只是简单地在做一些Java里面也能做的事情。
// Implementation using a regular class, mimicking regular Java @Entity class Pet { constructor() { } constructor(name: String) { this.name = name } @Id @GeneratedValue(strategy = GenerationType.AUTO) var id: Long = 0 var name: String = "" @ManyToOne @JoinColumn(name = "ownerId", nullable = false) var owner: Customer? = null override fun toString(): String = "$name" }
// Implementation using a data class (preferred) @Entity data class Customer( @Id @GeneratedValue(strategy = GenerationType.AUTO) var id: Long = 0, var firstName: 西安前端培训 String = "", var lastName: String = "", @JsonIgnore @OneToMany(mappedBy = "owner") var pets: List<Pet>? = null ) { override fun toString(): String = "$firstName $lastName" }
尽管第一眼看上去,它不像Java代码那样比较直观,但是用数据类实现的话,代码量就要短得多,而且也不需要大量的模板文件。这里的大部分冗余代码都是因为需要做必要的注释。
注意,实体类需要一个默认的没有参数的构造函数——它在常规类的情况下显式提供,而数据类通过为单个构造函数中的每个参数定义 默认值 来提供的 - 包括一个默认值,而没有参数 ,它只是将默认值分配给每个变量。
由于需要将override关键字显示的定义出来,这样做代码更容易阅读,出现错误的概率也会降低,所以我挺喜欢这种做法的。
Scala
@Entity class Customer { // Need to specify a parameterized constructor explicitly def this(firstName: String, lastName: String) { this() this.firstName = firstName this.lastName = lastName } // BeanProperty needed to generate getters and setters @Id @GeneratedValue(strategy = GenerationType.AUTO) @BeanProperty var id: Long = _ @BeanProperty var firstName: String = _ @BeanProperty var lastName: String = _ @JsonIgnore @OneToMany(mappedBy = "owner") @BeanProperty var pets: java.util.List[Pet] = _ override def toString(): String = s"$firstName $lastName" }
@Entity class Pet { def this(name: String, owner: Customer) { this() this.name = name this.owner = owner } @Id @GeneratedValue(strategy = GenerationType.AUTO) @BeanProperty var id: Long = _ @BeanProperty var name: String = _ @ManyToOne @JoinColumn(name = "ownerId", nullable = false) @BeanProperty var owner: Customer = _ }
实际上仅针对这种情况,我对Scala感到失望——它的实现几乎和Java一样冗长,它们的区别就在于Scala不需要显示的定义好getter和setter方法,它只需要使用额外的字段注释(@beanproperty)就可以了。
我试图使用一个case class来减少代码实现的行数,这在理论上是可以行的通的,但是我不能让它运行起来(也许这根本原因就是因为我使用Scala不熟)。
至少它提供了字符串插值(String interpolation),允许在一行中使用大括号,并且需要显式的
override关键字,这与Kotlin是一致的。
Repositories
Java
@Repository public interface CustomerRepository extends CrudRepository<Customer, Long> { List<Customer> findByLastName(String lastName); }
@Repository public interface PetRepository extends CrudRepository<Pet, Long> { }
注意,findByLastName函数实际上并没有在其它地方进行调用,我定义它只是用来提供一个示例的。
Kotlin
@Repository interface CustomerRepository : CrudRepository<Customer, Long> { fun findByLastName(name: String): List<Customer> }
`@Repository interface PetRepository : CrudRepository<Pet, Long>`
这里没有太大的区别,代码基本上是一样的。Kotlin版本的代码稍微短一点,这是因为Kotlin的默认修饰符是public的,而且有一个:符号而不是extends关键字。此外,也有可能是如果没有在body中定义任何内容的话,就有可能可能会忽略花括号。
Scala
@Repository trait CustomerRepository extends CrudRepository[Customer, java.lang.Long] { def findByLastName(lastName: String): List[Customer] }
@Repository trait PetRepository extends CrudRepository[Pet, java.lang.Long]
Scala使用的是traits,而不是interfaces,但在大部分情况下它们都是相同的概念,或者至少针对我们这个简单的例子而言它们是一样的。
由于某些原因,需要将Long类明确定义为java.lang.Long以避免编译错误(我再次对Scala感到失望)。
Controllers控制器
Java
@RestController @RequestMapping("/customers") public class CustomerController { private CustomerRepository customerRepository; @Autowired public CustomerController(CustomerRepository customerRepository) { this.customerRepository = customerRepository; } @GetMapping(value = "/{id}", produces = "application/json") public Customer getCustomer(@PathVariable("id") Long id) { return customerRepository.findOne(id); } @GetMapping(produces = "application/json") public List<Customer> getAllCustomers() { return (List<Customer>) customerRepository.findAll(); } @GetMapping(value = "/formatted", produces = "application/json") public List<String> getAllCustomersFormatted() { return ((List<Customer>) customerRepository.findAll()) .stream() .map( customer -> customer.getFirstName() " " customer.getLastName() ) .collect(Collectors.toList()); } @PostMapping(produces = "application/json", consumes = "application/json") public Customer addCustomer(@RequestBody Customer customer) { return customerRepository.save(customer); } }
@RestController @RequestMapping("/pets") public class PetController { @Autowired private PetRepository petRepository; @GetMapping(produces = "application/json") public List<Pet> getAllPets() { return (List<Pet>) petRepository.findAll(); } @PostMapping(produces = "application/json", consumes = "application/json") public Pet addPet(@RequestBody Pet pet) { return petRepository.save(pet); } }
Scala
@RestController @RequestMapping(Array("/customers")) class CustomerController ( private val customerRepository: CustomerRepository ) { @GetMapping(value = Array("/{id}"), produces = Array("application/json")) def getCustomer(@PathVariable("id") id: Long) = customerRepository.findOne(id) @GetMapping(produces = Array("application/json")) def getAllCustomers() = customerRepository.findAll() @GetMapping(value = Array("/formatted"), produces = Array("application/json")) def getAllCustomersFormatted() = { customerRepository .findAll() .asScala .map(_.toString()) .asJava } @PostMapping(produces = Array("application/json"), consumes = Array("application/json")) def addCustomer(@RequestBody customer: Customer) = customerRepository.save(customer) }
@RestController @RequestMapping(Array("/pets")) class PetController { @Autowired var petRepository: PetRepository = null @GetMapping(produces = Array("application/json")) def getAllPets = petRepository.findAll() @PostMapping(produces = Array("application/json"), consumes = Array("application/json")) def addPet(@RequestBody pet: Pet) = petRepository.save(pet) }
CustomerController是通过构造函数注入的,而PetController则是通过字段注入的,这么做是为了提供出两种不同的方式——Kotlin和Scala也是同样的处理逻辑。
同样,Java的话,代码还是显得很冗长,尽管其中很大一部分来自于健壮的注释(使用@get/PostMapping代替@requestmapping来减少注释的大小)。值得注意的是,Java 8将会解决这个问题,因为由于缺少lambda函数,getAllCustomersFormatted()函数在Java 7中会变得更加臃肿。
Kotlin
@RestController @RequestMapping("/customers") class CustomerController(val customerRepository: CustomerRepository) { @GetMapping(value = "/{id}", produces = arrayOf("application/json")) fun getCustomer(@PathVariable("id") id: Long): Customer? = customerRepository.findOne(id) @GetMapping(value = "/formatted", produces = arrayOf("application/json")) fun getAllCustomersFormatted() = customerRepository.findAll().map { it.toString() } @GetMapping(produces = arrayOf("application/json")) fun getAllCustomers() = customerRepository.findAll() @PostMapping(produces = arrayOf("application/json"), consumes = arrayOf("application/json")) fun addCustomer(@RequestBody customer: Customer): Customer? = customerRepository.save(customer) }
@RestController @RequestMapping("/pets") class PetController { // When using Autowired like this we need to make the variable lateinit @Autowired lateinit var petRepository: PetRepository @GetMapping(produces = arrayOf("application/json")) fun getAllPets() = petRepository.findAll() @PostMapping(produces = arrayOf("application/json"), consumes = arrayOf("application/json")) fun addPet(@RequestBody pet: Pet): Pet? = petRepository.save(pet) }
乍一看,这似乎和Java一样冗长,这很让人吃惊,但我们必须注意到 西安软件培训,这种冗长的代码大部分来自于所需的注释。除去这些,控制器的主体仅仅只有4行。
当然,如果我要将@requestmapping注释写在一行中,那么它就不会那么简单了,但是在博客文章中,可读性就会首先出现。
使用@get/PostMapping注释可以让我们至少跳过方法参数,以减少注释的大小。理论上,我们可以去掉produces和consumes,但这也会使XML成为一个可行的选择——所以这些params并不是多余的。
需要指出的一件令人讨厌的事情是,如果需要使用多个参数(除了默认值以外),那么在注解中使用arrayif()是必要的。这将在Kotlin 1.2中得到修复。
我喜欢这个构造函数注入芬兰湾的科特林提供了(我们甚至不需要一个@ autowired注解出于某种原因[这是原因])虽然看起来令人困惑如果类更大,更依赖项注入,我想说这是一个机会,在这种情况下适当的格式。
我喜欢这个构造函数注入芬兰湾的科特林提供了(我们甚至不需要一个@ autowired注解出于某种原因[这是原因])虽然看起来令人困惑如果类更大,更依赖项注入,我想说这是一个机会,在这种情况下适当的格式。
Scala
@RestController @RequestMapping(Array("/customers")) class CustomerController ( private val customerRepository: CustomerRepository ) { @GetMapping(value = Array("/{id}"), produces = Array("application/json")) def getCustomer(@PathVariable("id") id: Long) = customerRepository.findOne(id) @GetMapping(produces = Array("application/json")) def getAllCustomers() = customerRepository.findAll() @GetMapping(value = Array("/formatted"), produces = Array("application/json")) def getAllCustomersFormatted() = { customerRepository .findAll() .asScala .map(_.toString()) .asJava } @PostMapping(produces = Array("application/json"), consumes = Array("application/json")) def addCustomer(@RequestBody customer: Customer) = 西安java培训 customerRepository.save(customer) }
@RestController @RequestMapping(Array("/pets")) class PetController { @Autowired var petRepository: PetRepository = null @GetMapping(produces = Array("application/json")) def getAllPets = petRepository.findAll() @PostMapping(produces = Array("application/json"), consumes = Array("application/json")) def addPet(@RequestBody pet: Pet) = petRepository.save(pet) }
Scala还需要在提供参数时使用Array关键字,即使是默认的参数也需要。
getAllCustomersFormatted()函数,这是一种暴行,但我不能让Java集合正确地使用Scala集合——所以,对不起,我的眼睛(划痕,代码在Teemu P?ntelin的帮助下得到了改进,谢谢:))。
请注意,必须在构造函数中包含@autowired(),这可能在Kotlin中跳过(如果您只有一个构造函数,那么实际上根本不需要@autowired),如这里所解释的那样)。
总结
尽管这个应用程序非常简单,但是对于我来说,这足以让我对如何在每一门特色语言中做一些更深入的了解有一个基本的感觉。
如果需要在 Kotlin 和 Scala 之间做个选择,毫无疑问我的选择是Kotlin。
为什么呢?
首先,我觉得Scala就好像是IntelliJ IDEA中的二等公民一样,而Kotlin无疑是一等公民。这是显而易见的,因为创建IDE(Jetbrains)的公司和创建Kotlin语言的公司是同一家的——所以他们当然非常支持这门语言。另一方面,Scala是通过一个插件集成的。两者的区别是显而易见的,至少对我个人来说,这种区别是非常重要的。
其次,如果我想用Scala为web应用程序开发框架,我就会选择 Play Framework,原因很简单,就是因为它设计的思维是基于Scala 的,而且开发语言能使得某些事情变得更容易,而不是妨碍你(就像在这个小应用程序的情况下)。
这些都是我个人的原因,但也有更多、更普遍的原因。
我觉得Scala比Kotlin更脱离Java,因为后者基本上算是一种扩展,旨在解决Java最初存在的问题,而前者的目标是将命令式编程和函数式编程混合在一起。尽管如此,我相信Scala在其他领域更好地使用,比如大数据,而Kotlin在它应该做的事情上做得很好——取代Java解决一些比较常见的问题,并提供紧密的互操作性。
此外,Spring本身似乎对Kotlin 的支持远远超过了对 Scala的支持。
最后,我相信,从Java程序员的角度来看,Kotlin比Scala更容易学习。这主要是因为Kotlin被设计为基于Java进行的改进,并没有像Scala那样重视函数式编程。在Kotlin中,与Java的互操作性也更加紧密,这使得调试问题更加容易。
最后,但同样重要的是——我想明确地声明我不会以任何方式抨击Scala。就我个人而言,我认为 如果用一门非Java的JVM 语言去开发一个Spring Boot的web应用程序——Kotlin会是更好的选择。粗体部分是很重要的:)正如前面提到的,在其他领域,Scala是很优秀的,比如前面提到的大数据,但想要取代Java目前估计还有一段很长的路要走。
相关的主题文章:

  
   [url=http://www.sjwlip.com/bbs/read.php?tid=2
级别: 圣骑士
只看该作者 37楼 发表于: 10-20
Eclipse 4.7 Oxygen 正式发布!带来大量更新
Eclipse 4.7 Oxygen 正式版已发布。更新内容十分多,来看看值得关注的更新:
Eclipse Platform
编辑器

编辑器方面,除此之外,还包括调试、视图、对话框、工具栏、首选项、主题和样式等诸多内容的更新
Java 开发工具
Java 9
支持构建 Java 9 应用。Java 9 规范尚未发布,所以对它的支持还没集成到标准下载包中,Eclipse 西安it培训 对 Java 9 的支持(BETA)包括以下内容:
Java 西安网页设计培训 编辑器

const 的放置
除此之外,还包括 Java 视图和对话框、Java 编译器、调试、jUnit 等内容的更新。
C/C   开发工具
代码分析
支持 C   西安seo 14 新的语言特性:
本次更新内容比较多,详情请参阅发布主页。
相关的主题文章:

  
   Java反射机制,速度提高1000倍
  
   Iodine:一个优秀的Java语言工具链
级别: 圣骑士
只看该作者 38楼 发表于: 10-20
Java 8:不要再用循环了
正如我之前所写的,Java 8中的新功能特性改变了游戏规则。对Java开发者来说这是一个全新的世界,并且是时候去适应它了。
在这篇文章里,我们将会去了解传统循环的一些替代方案。在Java 8的新功能特性中,最棒的特性就是允许我们去表达我们想要完成什么而不是要怎样做。这正是循环的不足之处。要确保循环的灵活性是需要付出代价的。return、break 或者 continue都会显著地改变循环的实际表现。这迫使我们不仅要清楚我们要实现怎样的代码,还要了解循环是怎样工作的。
在介绍Java 8的流(Stream)时,我们学会了一些集合操作的实用技巧。现在我们要看看怎样把这些循环转换为更简洁,可读性更高的代码。
开始编码!
好吧,讲的够多了,是时候展示一些例子了!
这次我们要以文章为例子。一篇文章拥有一个标题,一个作者和几个标签。
private class Article { private final String title; private final String author; private final List<String> tags; private Article(String title, String author, List<String> tags) { this.title = title; this.author = author; this.tags = tags; 西安网站建设培训 } public String getTitle() { return title; } public String getAuthor() { return author; } public List<String> getTags() { return tags; } }
每个例子都会包含一个使用传统循环的方案和一个使用Java 8新特性的方案。
在第一个例子里,我们要在集合中查找包含“Java”标签的第一篇文章。
看一下使用for循环的解决方案。
public Article getFirstJavaArticle() { for (Article article : articles) { if (article.getTags().contains("Java")) { return article; } } return null; }
现在我们使用Stream API的相关操作来解决这个问题。
public Optional<Article> getFirstJavaArticle() { return articles.stream() .filter(article -> article.getTags().contains("Java")) .findFirst(); }
是不是很酷?我们首先使用 filter 操作去找到所有包含Java标签的文章,然后使用 findFirst() 操作去获取第一次出现的文章。因为Stream是“延迟计算”(lazy)的并且filter返回一个流对象,所以这个方法仅在找到第一个匹配元素时才会处理元素。
现在,让我们获取所有匹配的元素而不是仅获取第一个。
首先使用for循环方案。
public List<Article> getAllJavaArticles() { List<Article> result = new ArrayList<>(); for (Article article : articles) { if (article.getTags().contains("Java")) { result.add(article); } } return result; }
使用Stream操作的方案。
public List<Article> getAllJavaArticles() { return articles.stream() .filter(article -> article.getTags().contains("Java")) .collect(Collectors.toList()); }
在这个例子里我们使用 collection 操作在返回流上执行少量代码而不是手动声明一个集合并显式地添加匹配的文章到集合里。
到目前为止还不错。是时候举一些突出Stream API强大的例子了。
根据作者来把所有的文章分组。
照旧,我们使用循环方案。
public java培训 Map<String, List<Article>> groupByAuthor() { Map<String, List<Article>> result = new HashMap<>(); for (Article article : articles) { if (result.containsKey(article.getAuthor())) { result.get(article.getAuthor()).add(article); } else { ArrayList<Article> articles = new ArrayList<>(); articles.add(article); result.put(article.getAuthor(), articles); } } return result; }
我们能否找到一个使用流操作的简洁方案来解决这个问题?
public Map<String, List<Article>> groupByAuthor() { return articles.stream() .collect(Collectors.groupingBy(Article::getAuthor)); 西安java培训 }
很好!使用 groupingBy 操作和 getAuthor 方法,我们得到了更简洁、可读性更高的代码。
现在,我们查找集合中所有不同的标签。
我们从使用循环的例子开始。
public Set<String> getDistinctTags() { Set<String> result = new HashSet<>(); for (Article article : articles) { result.addAll(article.getTags()); } return result; }
好,我们来看看如何使用Stream操作来解决这个问题。
public Set<String> getDistinctTags() { return articles.stream() .flatMap(article -> article.getTags().stream()) .collect(Collectors.toSet()); }
棒极了!flatmap 帮我把标签列表转为一个返回流,然后我们使用 collect 去创建一个集合作为返回值。
一切皆有可能
以上的就是如何使用可读性更高的代码代替循环的例子。务必仔细看看Stream API,因为这篇文章仅仅提到它的一些皮毛而已。
更新
收到solarfuse和dhruvgairola的评论后,更新了getDistinctTags()例子,使用集合(Set)作为返回集合。
原文来自:deadcoderising
相关的主题文章:

  
   Java中处理异常的9个最佳实践
  
   FUE植发效果好吗
级别: 圣骑士
只看该作者 39楼 发表于: 11-07
最新植发技术
核心提示: 每个人的头发都是从一出生就有的,头发会随着人的年龄的增长而增长的,有些人的头发比较多,有的人的头发比较少,每个人的发量是不一样的,发量少的人是可以植发的,那么最新的植发技术是什么呢。
       造成头发脱落的原因是很多的,不过大部分都是跟自己不良的生活习惯有关系,大部分脱发少发的患者都是因为自己的精神压力太大 植发,而且内分泌失调,还有使用不当的洗护用品造成的,那么现在最新的植发技术是什么呢,多少钱左右。
       1现在国内最新的植发技术就是无痕植发了,无痕植发后是看不出来植发的痕迹的,植发其实就是取人的后脑部位的毛囊来作为种植体,然后再经过分离或者单柱的毛囊单位,通过手术以后移植到患者需要种植头发的部位,像头发啊,眉毛,睫毛,都是可以的,让这些毛囊在别的地方存活,达到植发的目的。
       2无痕植发的技术在国内是非常的成熟的,不单单有无痕植发,还有有痕的植发,无痕的植发效果要比有痕的效果好,无痕植发的价格也是比有痕植发要贵的,无痕植发是在十块钱一个毛囊的,而有痕植发是在五块钱一个毛囊的,出来的效果是不一样的,不过都是可以让头发或者别的部位的毛发变茂密的。
       3植发的手术是很安全的,创伤也比较小,植发的过程中是要进行局部的麻醉,患者是感受不到痛苦的,植发的手术是需要七八个小时左右,患者是可以在这个过程中看看书跟杂志的,植完发以后也可以直接回家不需要住院 碧莲盛植发,只不过刚植完发以后不可以洗头的,也不可以去用力的抓头发摸头皮的。
    
快速回复

限100 字节
如果您在写长篇帖子又不马上发表,建议存为草稿
 
上一个 下一个