spring cloud feign 拦截器配置

问题 公司大规模推全链路,要求所有应用都接入。手头一个新的项目,采用spring cloud框架。通过 feign 调用rest接口。但是支持提供的支持 HttpClient4 的方式无法对 feign 起作用。 思路 想着 feign 也是采用 HttpClient 的。按理对 HttpClient4 起作用的也应该同样有效。然后去看了下架构组全链路配置代码,原来他们是在初始化 HttpCLient 实例时候,加入了拦截器。通过拦截器,在 header 中加入链路信息。但是这个方式似乎对 feign 没啥作用。那我就自己实现添加拦截器呗。 查了下 feign 添加拦截器,网络上提供的方式都是通过自定义 FeignConfiguration 的方式来实现。方法如下: 新建 FeignConfiguration类 public class FeignConfiguration { @Bean public FeignBasicAuthRequestInterceptor basicAuthRequestInterceptor() { return new FeignBasicAuthRequestInterceptor(); } } 然后在配置客户端调用时候加入配置中 @FeignClient(value = "xxx-service", path = "/xxx", configuration = FeignConfiguration.class) 只是这种方法无法做到全局。找了好久,看了文档,都没有办法配置全局。 无奈,去翻源码吧。找到了 FeignAutoConfiguration 类。看到了初始化配置。 protected static class HttpClientFeignConfiguration { @Autowired( required = false ) private HttpClient httpClient; protected HttpClientFeignConfiguration() { } @Bean @ConditionalOnMissingBean({Client....

January 25, 2018

javabean字段文字拼接

问题 接了一个需求,从数据库拿出患者的记录,这些记录都是一些吸烟年份、饮酒年份、是否糖尿病等,不是数字就是布尔值,需要通过代码转化为普通人能理解的文字。 思前想后,总不能使用大量 if-else 判断来拼接字符串吧。可以采用模板,但是那样除了必要判空,代码和模板也将分离,不方便维护。而且有些字段存在依赖关系。 解决 刚好之前看了使用注解来替代枚举的方式。想着是否可以在每个需要拼接的字段上打上注解,通过判断注解的方式拼接。注解中也可以放入模板、依赖关系等配置。 @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface StringAssemble { /** * 模板 */ String template(); /** * 分类 */ String type() default ""; /** * 上级依赖 */ String dependency() default ""; /** * 相互依赖的内容之间的分割符号 */ String dependencySplit() default ","; } 上面注解中,templete 主要是字段值需要填入的文字模板,type 用于不同的字段组合,dependency 主要是有依赖关系的字段之间的内容组合,最后一个是有依赖关系的字段字段拼接后用于隔离的标点。 实现代码如下: private static String stringAssemble(Object data, String type, String splitPunctuation) { final Map<String, String> infoMap = new HashMap<>(); final Map<String, String> dependencyMap = new HashMap<>(); try { Field[] fields = data....

January 20, 2018

SpringMVC没有拦截根路径的问题

在开发tomcat + spring mvc的web工程时候,发现在controller中配置的根路劲不会被拦截到,或者说前段返回的根本就不是controller中所配置的。比如,我在controller中配置根路径的返回为一个欢迎页面: @RequestMapping(value = "/", method = RequestMethod.GET) public ModelAndView main() { ModelAndView mv = new ModelAndView("main"); return mv; } 但访问“http://localhost:8080/project_name/”,返回的却是404. 在对该单元测试中,对根路径的访问等得到了正确的结果,这证明spring的配置是正确的。于是乎想起来web.xml中的配置可能存在为题,自然最有可能有问题的是web.xml对默认页面的设置了。及welcome-file-list。但是自己并没有配置这个。后来看了tomcat官网对tomcat目录下conf/web.xml的说明才知道,原来此处的web.xml中的配置将被当作默认配置,而部署的工程内的web.xml中的配置会覆盖默认配置,但是如果向我那样没有配置welcome-file-list,那么就会使用默认的了。为此当访问根目录的时候,tomcat试图去寻找默认的index.html等资源,而不是把请求交给spring的拦截器。 知道了这些,解决问的就简单了,直接修改工程下的web.xml,加入如下配置: <welcome-file-list> <welcome-file></welcome-file> </welcome-file-list> 这样就配置后,tomcat就知道不需要对根路径的访问做处理了,而将请求交给了spring的拦截器。

December 2, 2014

Spring4定时任务的注解配置

不想写繁琐的xml文件,所以试着用Spring的注解来配置定时任务。按网上好多写的那样,使用@Scheduled注解: @Scheduled(fixedRate=5000) public void doSomething() { // something that should execute periodically } 但是怎么也无法让定时任务跑起来。最后无奈,只有去官网看英语了。 原来,还需要有一个允许定时任务的注解,放在一个有@configuration注解类中。 @Configuration @EnableAsync @EnableScheduling public class AppConfig { } 其中,@EnableAsysnc是允许异步任务。至于@Configuration的说明,见Spring的最新文档。

November 3, 2014

Clone使用方法详解

Java语言的一个优点就是取消了指针的概念,但也导致了许多程序员在编程中常常忽略了对象与引用的区别。和C语言一样,当把Java的基本数据类型(如int,char,double等)作为 入口参数传给函数体的时候,传入的参数在函数体内部变成了局部变量,这个局部变量是输入参数的一个拷贝,所有的函数体内部的操作都是针对这个拷贝的操作, 函数执行结束后,这个局部变量也就完成了它的使命,它影响不到作为输入参数的变量。这种方式的参数传递被称为"值传递"。而在Java中用对象的作为入口 参数的传递则缺省为"引用传递",也就是说仅仅传递了对象的一个"引用",这个"引用"的概念同C语言中的指针引用是一样的。当函数体内部对输入变量改变 时,实质上就是在对这个对象的直接操作。 java中的clone: 在实际编程过程中,我们常常要遇到这种情况:有一个对象A,在某一时刻A中已经包含了一些有效值,此时可能会需要一个和A完全相同新对象B,并且此后对B 任何改动都不会影响到A中的值,也就是说,A与B是两个独立的对象,但B的初始值是由A对象确定的。在Java语言中,用简单的赋值语句是不能满足这种需 求的。要满足这种需求虽然有很多途径,但实现clone()方法是其中最简单,也是最高效的手段。 Java的所有类都默认继承java.lang.Object类,在java.lang.Object类中有一个方法clone()。JDK API的说明文档解释这个方法将返回Object对象的一个拷贝。要说明的有两点: ** 一是拷贝对象返回的是一个新对象,而不是一个引用。** ** 二是拷贝对象与用 new操作符返回的新对象的区别就是这个拷贝已经包含了一些原来对象的信息,而不是对象的初始信息。** 有三个值得注意的地方, 一是希望能实现clone功能的CloneClass类实现了Cloneable接口,这个接口属于java.lang包, java.lang包已经被缺省的导入类中,所以不需要写成java.lang.Cloneable。 另一个值得请注意的是重载了clone()方法。最 后在clone()方法中调用了super.clone(),这也意味着无论clone类的继承结构是什么样的,super.clone()直接或间接调 用了java.lang.Object类的clone()方法。下面再详细的解释一下这几点。 应该说第三点是最重要的,仔细观察一下Object类的clone()一个native方法,native方法的效率一般来说都是远高于java中的非 native方法。这也解释了为什么要用Object中clone()方法而不是先new一个类,然后把原始对象中的信息赋到新对象中,虽然这也实现了 clone功能。对于第二点,也要观察Object类中的clone()还是一个protected属性的方法。这也意味着如果要应用clone()方 法,必须继承Object类,在Java中所有的类是缺省继承Object类的,也就不用关心这点了。然后重载clone()方法。还有一点要考虑的是为 了让其它类能调用这个clone类的clone()方法,重载之后要把clone()方法的属性设置为public。那么clone类为什么还要实现Cloneable接口呢? 稍微注意一下,Cloneable接口是不包含任何方法的!其实这个接口仅仅是一个标志,而且 这个标志也仅仅是针对Object类中clone()方法的,如果clone类没有实现Cloneable接口,并调用了Object的clone()方法(也就是调用了super.Clone()方法),那么Object的clone()方法就会抛出 CloneNotSupportedException异常。 什么是影子clone? 调用Object类中clone()方法产生的效果是:先在内存中开辟一块和原始对象一样的空间,然后原样拷贝原始对象中的内容。对基本数据类型,这样的操作是没有问题的,但对非基本类型变量,我们知道它们保存的仅仅是对象的引用,这也导致clone后的非基本类型变量和原始对象中相应的变量指向的是同一个对象。 怎么进行深度clone? 需要两个改变,一是让对象内的非基本类型变量也实现Clone功能(实现Cloneable接 口,重载clone()方法)。二是在对象的Clone方法中加一句o.unCA = (UnCloneA)unCA.clone(); 要知道不是所有的类都能实现深度clone的。例如,如果把上面的CloneB类中的UnCloneA类型变量改成StringBuffer类型,看一下 JDK API中关于StringBuffer的说明,StringBuffer没有重载clone()方法,更为严重的是StringBuffer还是一个 final类,这也是说我们也不能用继承的办法间接实现StringBuffer的clone。 还要知道的是除了基本数据类型能自动实现深度clone以外,String对象,Integer,Double等是一个例外,它clone后的表现好象也实现了深度clone,虽然这只是一个假象,但却大大方便了我们的编程。 Clone中String和StringBuffer的区别,这个区别主要原因来自String和StringBuffer的区别。

September 5, 2013

Java静态代码块(static{})

静态代码块:即在类中的标有static修饰符的代码块,如: static { } 与一般代码块的异同: 相同点:都是在JVM加载类时且在构造方法执行之前执行,在类中都可以定义多个,一般在代码块中对一些static变量进行赋值。 不同点:静态代码块在非静态代码块之前执行。静态代码块只在第一次new执行一次,之后不再执行,而非静态代码块在每new一次就执行一次。非静态代码块可在普通方法中定义;而静态代码块不行。 与静态方法的区别: 静态代码块在第一次初始化的时候被执行,静态方法在类加载的时候被加载,且必须需要主动调用才会执行。 执行顺序相关: 1、单个类的初始化顺序如下:为静态属性分配内存并赋值->执行静态代码块->为非静态属性分配内存并赋值->执行非静态代码块->构造函数。 2、有继承关系的类的初始化顺序如下:父静属->父静块->子静属->子静块->父非静属->父非静块->父构造->子非静属->子非静块->子构造。 static的一些使用注意事项: 1、静态方法只能调用同类中的其他静态成员(静态方法或是静态变量)。 2、静态方法不能使用this和super。因为静态方法不属于任何实例。 3、静态变量属于整个类,在类的方法中不能定义静态方法。

August 21, 2013

Java源码解读——ArrayList

为了提高自己的Java开发能力,我也向高手、牛人学习,去解读源码。自己底子差了点,不过看个源码还是没问题的。第一站ArrayList。 源码为Java 1.7的源码 ArrayList是一个实现可变长数组,继承AbstractList类,实现所有的List接口,还实现了RandomAccess、Cloneable、Serializable接口。ArrayList不进行同步,除此之外基本和Vector等同。 1、成员变量 private transient Object[] elementData; elementData用于保存数据的数组。 private int size; size为ArrayList内的数据数量,但并不是elementData的长度。 2、构造方法 public ArrayList(int initialCapacity) { super(); if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); this.elementData = new Object[initialCapacity]; } public ArrayList() { this(10); } public ArrayList(Collection<? extends E> c) { elementData = c.toArray(); size = elementData.length; if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); } 第一个是根据指定长度建立List,第二个是根据默认长度建立List,第三个根据传入Collection子类实例创建List。通过第三个实例看到Collection子类都会实现toArray()方法,可以看出至少很多Collection子类依赖于数组来实现(还没看过其他的集合实现,这只是我的妄言而已)。 3、add方法 public boolean add(E e) { ensureCapacityInternal(size + 1); elementData[size++] = e; return true; } 该方法在数组最后添加一个元素。ensureCapacityInternal方法提供了ArrayList的自增长实现,以确保elementData有足够的长度来容纳新进入的元素(后面介绍自增长的实现)。...

August 14, 2013

Mybatis返回特殊Map

想返回个特殊实体,一个Map,key是一个表的一个字段的值,value是另一个表的所有记录。参考了下网上的“攻略”,不过和攻略里面讲的有点不同,那里面key的值value的实体实在同一个表内,如果不同表,会报缺少set方法的异常。解决方法很简单,就是在实体里面加个字段对应的属性。但是并不愿意为了一个查询语句就要去改变实体。 无聊看了下session的select,提供了通过 ResultHandler 来处理返回的结果集。所以尝试着解决这个问题。 mapper.xml里面的配置如下: <resultMap type="HashMap" id="testMap"> <result column="UA_INFO" property="key" /> <association property="value" resultMap="com.xxx.xxx.BaseResultMap"> </association> </resultMap> <select id="getUaMapByTimestamp" parameterType="Map" resultMap="testMap"> SQL语句 </select> com.xxx.xxx.BaseResultMap是另一个实体的mapper的resultMap。这个查询,返回的每一条记录都是 {key=..., value=...} 这个结果集并不符合要求。不过通过ResultHandler来处理每一条记录就可以达到要求了。 看下Mybatis源码里面有关继承 ResultHandler 的 DefaultMapResultHandler类。 public class DefaultMapResultHandler<K, V> implements ResultHandler { private final Map<K, V> mappedResults; private final String mapKey; @SuppressWarnings("unchecked") public DefaultMapResultHandler(String mapKey, ObjectFactory objectFactory) { this.mappedResults = objectFactory.create(Map.class); this.mapKey = mapKey; } public void handleResult(ResultContext context) { // TODO is that assignment always true?...

August 4, 2012

Java可变参数方法及它的重载

JAVA支持方法的可变参数。类似如下 public void method1(String... arg1); 这样,调用method1时候,参数的数量是可变的,如method1(“1”,“2”,“3”)。注意可变参数只能是最后一个参数。 JAVA内部是把这样的参数看成一个数组来处理。在获取多个参数的时候可以用数组遍历来完成。 for (String temp : arg1) { System.out.println("one of the arguments is " + temp); } 可能有人会说,这样的循环遍历像List那样的集合也可以,凭什么说是数组。那好,再看下面代码: public void method1(String... arg1); public void method1(String[] arg1); 这样的重载,在编译时候会报错,JAVA不允许这样的重载,其实理由很简单,String… 和String[]对编译器来说是一样的。参数一样自然无法重载。 下面是可变参数方法的重载: public void method1(String... arg1) { } public void method1(String a, String... arg1) { } 我在eclipse中测试,IDE并没有提示错误。但是在实例后调用就不行了,IDE报 The method method1(String[]) is ambiguous for the type ...,大致是说这个方法有混淆,无法正确的调用。话说类编译的时候没问题,到实例后调用就会提示错误。 public void method1(String... arg1) { System.out.println("1"); } public void method1(String a, String b) { System....

April 28, 2012

Hibernate获取返回值报XXX connot be cast to [Ljava.lang.Object

今天在测试时候发现在获取Hibernate返回数据时候报 java.math.BigDecimal cannot be cast to \[Ljava.lang.Object 异常,但是查看了代码,很简单也很平常,createNativeQuery执行,getResultList获取结果集,每行都用Object[]类型。调试时候发现返回结果集为[2],也就是就一行且一个。也就是说,每行的类型不再是 Object[]。 按以往, getResultList 的返回是一个 List(Object\[\]),当如果返回只有一个,比如1或2是,类型就变成了该数据库字段所对应的类型,在我这里就是BigDecimal。 真是不明白,多个就是Object[],一个就变成不是数组了。 Hibernate莫名其妙啊。发下牢骚。。。

November 3, 2011