👊 【Spring技術特性】帶你看看那些可能你還不知道的特性技巧哦!

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"前提介紹","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文主要介紹相關Spring框架的一些新特性問題機制,包含了一些特定註解方面的認識。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"@Lazy可以延遲依賴注入","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"@Lazy註解修飾在類層面!","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Lazy\n@Service\npublic class UserService extends BaseService { }\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以把@Lazy放在@Autowired之上,即依賴注入也是延遲的;當我們調用userService時纔會注入。即延遲依賴注入到使用時。同樣適用於@Bean。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Lazy\n@Autowired\nprivate UserService userService;\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"@Conditional","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"@Conditional類似於@Profile","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一般用於如有開發環境、測試環境、正式機環境,爲了方便切換不同的環境可以使用@Profile指定各個環境的配置。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過某個配置來開啓某個環境,方便切換,但是@Conditional的優點是允許自己定義規則,可以指定在如@Component、@Bean、@Configuration等註解的類上,以絕對Bean是否創建等。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先來看看使用@Profile的用例,假設我們有個用戶模塊:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"在測試/開發期間調用本機的模擬接口方便開發;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"在部署到正式機時換成調用遠程接口;","attrs":{}}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public abstract class UserService extends BaseService { }\n\n@Profile(\"local\")\n@Service\npublic class LocalUserService extends UserService {}\n\n@Profile(\"remote\")\n@Service\npublic class RemoteUserService extends UserService {}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們在寫測試用例時,可以指定我們使用哪個Profile:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@ActiveProfiles(\"remote\")\n@RunWith(SpringJUnit4ClassRunner.class)\n@ContextConfiguration(locations = \"classpath:spring-config.xml\")\npublic class ServiceTest {\n @Autowired\n private UserService userService;\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果想自定義如@Profile之類的註解等,那麼@Conditional就派上用場了,假設我們系統中有好多本地/遠程接口,那麼我們定義兩個註解@Local和@Remote註解要比使用@Profile方便的多;如:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":" @Retention(RetentionPolicy.RUNTIME)\n@Target({ElementType.TYPE, ElementType.METHOD})\n@Conditional(CustomCondition.class)\npublic @interface Local { }\n\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ElementType.TYPE, ElementType.METHOD})\n@Conditional(CustomCondition.class)\npublic @interface Remote {}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class CustomCondition implements Condition {\n @Override\n public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {\n boolean isLocalBean = metadata.isAnnotated(\"com.xxx.Local\");\n boolean isRemoteBean = metadata.isAnnotated(\"com.xxx.Remote\");\n //如果bean沒有註解@Local或@Remote,返回true,表示創建Bean\n if(!isLocalBean && !isRemoteBean) {\n return true;\n }\n boolean isLocalProfile = context.getEnvironment().acceptsProfiles(\"local\");\n //如果profile=local 且 bean註解了@Local,則返回true 表示創建bean\n if(isLocalProfile) {\n return isLocalBean;\n }\n // 否則默認返回註解了@Remote或沒有註解@Remote的Bean\n return isRemoteBean;\n }\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然後我們使用這兩個註解分別註解我們的Service:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Local\n@Service\npublic class LocalUserService extends UserService { }\n@Remote\n@Service\npublic class RemoteUserService extends UserService {}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先在@Local和@Remote註解上使用@Conditional(CustomCondition.class)指定條件。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然後使用@Local和@Remote註解我們的Service,這樣當加載Service時,會先執行條件然後判斷是否加載爲Bean。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"@Profile實現的Condition是:org.springframework.context.annotation.ProfileCondition。","attrs":{}}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"AsyncRestTemplate非阻塞異步(已廢棄WebClient代替之)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"提供AsyncRestTemplate用於客戶端非阻塞異步支持。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","text":"服務器端","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@RestController\npublic class UserController {\n private UserService userService;\n @Autowired\n public UserController(UserService userService) {\n this.userService = userService;\n }\n @RequestMapping(\"/api\")\n public Callable api() {\n return new Callable() {\n @Override\n public User call() throws Exception {\n Thread.sleep(10L * 1000); //暫停兩秒\n User user = new User();\n user.setId(1L);\n user.setName(\"haha\");\n return user;\n }\n };\n }\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"非常簡單,服務器端暫停10秒再返回結果(但是服務器也是非阻塞的)。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"客戶端","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public static void main(String[] args) {\n AsyncRestTemplate template = new AsyncRestTemplate();\n //調用完後立即返回(沒有阻塞)\n ListenableFuture> future = template.getForEntity(\"http://localhost:9080/rest/api\", User.class);\n //設置異步回調\n future.addCallback(new ListenableFutureCallback>() {\n @Override\n public void onSuccess(ResponseEntity result) {\n System.out.println(\"======client get result : \" + result.getBody());\n }\n @Override\n public void onFailure(Throwable t) {\n System.out.println(\"======client failure : \" + t);\n }\n });\n System.out.println(\"==no wait\");\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"承接上面的內容:Future增強,提供了一個ListenableFuture,其是jdk的Future的封裝,用來支持回調(成功/失敗),借鑑了com.google.common.util.concurrent.ListenableFuture。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Test\npublic void test() throws Exception {\n ListenableFutureTask task = new ListenableFutureTask(new Callable() { \n @Override \n public Object call() throws Exception { \n Thread.sleep(10 * 1000L); \n System.out.println(\"=======task execute\"); \n return \"hello\"; \n } \n }); \n task.addCallback(new ListenableFutureCallback() { \n @Override \n public void onSuccess(String result) { \n System.out.println(\"===success callback 1\"); \n } \n \n @Override \n public void onFailure(Throwable t) { \n } \n }); \n task.addCallback(new ListenableFutureCallback() { \n @Override \n public void onSuccess(String result) { \n System.out.println(\"===success callback 2\"); \n } \n \n @Override \n public void onFailure(Throwable t) { \n } \n }); \n \n ExecutorService executorService = Executors.newSingleThreadExecutor(); \n executorService.submit(task); \n String result = task.get(); \n System.out.println(result); \n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以通過addCallback添加一些回調,當執行成功/失敗時會自動調用。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"此處使用Future來完成非阻塞,這樣的話我們也需要給它一個回調接口來拿結果;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Future和Callable是一對,一個消費結果,一個產生結果。調用完模板後會立即返回,不會阻塞;有結果時會調用其回調。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"horizontalrule","attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"AsyncRestTemplate默認使用SimpleClientHttpRequestFactory,即通過java.net.HttpURLConnection實現;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"另外可以使用apache的http components,使用template.setAsyncRequestFactory(new HttpComponentsAsyncClientHttpRequestFactory()),設置即可。","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"Spring對Java8的時間類型支持","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對jsr310的支持,只要能發現java.time.LocalDate,DefaultFormattingConversionService就會自動註冊對jsr310的支持,只需要在實體/Bean上使用DateTimeFormat註解:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@DateTimeFormat(pattern = \"yyyy-MM-dd HH:mm:ss\")\nprivate LocalDateTime dateTime;\n\n@DateTimeFormat(pattern = \"yyyy-MM-dd\")\nprivate LocalDate date;\n\n@DateTimeFormat(pattern = \"HH:mm:ss\")\nprivate LocalTime time;\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"比如我們在springmvc中:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@RequestMapping(\"/test\")\npublic String test(@ModelAttribute(\"entity\") Entity entity) {\n return \"test\";\n}\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","text":"當前端頁面請求:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"localhost:9080/spring4/test?dateTime=2013-11-11 11:11:11&date=2013-11-11&time=12:12:12\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","text":"會自動進行類型轉換","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"另外spring4也提供了對TimeZone的支持,比如在springmvc中註冊了LocaleContextResolver相應實現的話(如CookieLocaleResolver),我們就可以使用如下兩種方式得到相應的TimeZone:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"RequestContextUtils.getTimeZone(request)\nLocaleContextHolder.getTimeZone()\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不過目前的缺點是不能像Local那樣自動的根據當前請求得到相應的TimeZone,如果需要這種功能需要覆蓋相應的如CookieLocaleResolver中的如下方法來得到:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"protected TimeZone determineDefaultTimeZone(HttpServletRequest request) { \n return getDefaultTimeZone(); \n} \n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"另外還提供了DateTimeContextHolder,其用於線程綁定DateTimeContext;而DateTimeContext提供瞭如:Chronology、ZoneId、DateTimeFormatter等上下文數據,如果需要這種上下文信息的話,可以使用這個API進行綁定。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"比如在進行日期格式化時,就會去查找相應的DateTimeFormatter,因此如果想自定義相應的格式化格式,那麼使用DateTimeContextHolder綁定即可。","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"泛型操作控制","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"隨着泛型用的越來越多,獲取泛型實際類型信息的需求也會出現,如果用原生API,需要很多步操作才能獲取到泛型,比如:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"ParameterizedType parameterizedType = \n (ParameterizedType) ABService.class.getGenericInterfaces()[0]; \nType genericType = parameterizedType.getActualTypeArguments()[1]; \n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Spring提供的ResolvableType API,提供了更加簡單易用的泛型操作支持,如:","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","text":"接口層的泛型處理","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"ResolvableType resolvableType1 = ResolvableType.forClass(ABService.class);\nresolvableType1.as(Service.class).getGeneric(1).resolve();\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於獲取更復雜的泛型操作ResolvableType更加簡單。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"假設我們的API是:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public interface Service { }\[email protected]\npublic class ABService implements Service { }\[email protected]\npublic class CDService implements Service {}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"得到類型的泛型信息","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"ResolvableType resolvableType1 = ResolvableType.forClass(ABService.class);\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過如上API,可以得到類型的ResolvableType,如果類型被Spring AOP進行了CGLIB代理,請使用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ClassUtils.getUserClass(ABService.class)","attrs":{}}],"attrs":{}},{"type":"text","text":"得到原始類型,可以通過如下得到泛型參數的第1個位置(從0開始)的類型信息","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"resolvableType1.getInterfaces()[0].getGeneric(1).resolve()\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"泛型信息放在 Service 上,所以需要resolvableType1.getInterfaces()[0]得到;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過getGeneric(泛型參數索引)得到某個位置的泛型;","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"resolve()把實際泛型參數解析出來","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","text":"得到字段級別的泛型信息","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"假設我們的字段如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Autowired\n private Service abService;\n @Autowired\n private Service cdService;\n private List> list;\n private Map> map;\n private List[] array;\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過如下API可以得到字段級別的ResolvableType","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"ResolvableType resolvableType2 = \n ResolvableType.forField(ReflectionUtils.findField(GenricInjectTest.class, \"cdService\")); \n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然後通過如下API得到Service的第0個位置上的泛型實參類型,即C","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"resolvableType2.getGeneric(0).resolve()\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"比如 List> list;是一種嵌套的泛型用例,我們可以通過如下操作獲取String類型:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"ResolvableType resolvableType3 =\n ResolvableType.forField(ReflectionUtils.findField(GenricInjectTest.class, \"list\"));\nresolvableType3.getGeneric(0).getGeneric(0).resolve();\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"更簡單的寫法","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"resolvableType3.getGeneric(0, 0).resolve(); //List> 即String\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"比如,Map> map;我們想得到Integer,可以使用:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"ResolvableType resolvableType4 =\n ResolvableType.forField(ReflectionUtils.findField(GenricInjectTest.class, \"map\"));\nresolvableType4.getGeneric(1).getGeneric(1).resolve();\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"更簡單的寫法","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"resolvableType4.getGeneric(1, 1).resolve() \n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"得到方法返回值的泛型信息","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"private HashMap> method() { \n return null; \n} \n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"得到Map中的List中的String泛型實參:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"ResolvableType resolvableType5 = ResolvableType.forMethodReturnType(ReflectionUtils.findMethod(GenricInjectTest.class, \"method\"));\nresolvableType5.getGeneric(1, 0).resolve();\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"得到構造器參數的泛型信息","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"假設我們的構造器如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public Const(List> list, Map> map) { }\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們可以通過如下方式得到第1個參數( Map>)中的Integer:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"ResolvableType resolvableType6 = ResolvableType.forConstructorParameter(ClassUtils.getConstructorIfAvailable(Const.class, List.class, Map.class), 1);\nresolvableType6.getGeneric(1, 0).resolve();\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"得到數組組件類型的泛型信息","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如對於private List[] array; 可以通過如下方式獲取List的泛型實參String:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"ResolvableType resolvableType7 = ResolvableType.forField(ReflectionUtils.findField(GenricInjectTest.class, \"array\"));\nresolvableType7.isArray();//判斷是否是數組 \nresolvableType7.getComponentType().getGeneric(0).resolve(); \n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"自定義泛型類型","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"ResolvableType resolvableType8 = ResolvableType.forClassWithGenerics(List.class, String.class);\n ResolvableType resolvableType9 = ResolvableType.forArrayComponent(resolvableType8); \nresolvableType9.getComponentType().getGeneric(0).resolve(); \nResolvableType.forClassWithGenerics(List.class, String.class)相當於創建一個List類型;\nResolvableType.forArrayComponent(resolvableType8);:相當於創建一個List[]數組;\nresolvableType9.getComponentType().getGeneric(0).resolve():得到相應的泛型信息;\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"泛型等價比較:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"resolvableType7.isAssignableFrom(resolvableType9)\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如下創建一個List[]數組,與之前的List[]數組比較,將返回false。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"ResolvableType resolvableType10 = ResolvableType.forClassWithGenerics(List.class, Integer.class);\nResolvableType resolvableType11= ResolvableType.forArrayComponent(resolvableType10); \nresolvableType11.getComponentType().getGeneric(0).resolve(); \nresolvableType7.isAssignableFrom(resolvableType11); \n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從如上操作可以看出其泛型操作功能十分完善,尤其在嵌套的泛型信息獲取上相當簡潔。目前整個Spring環境都使用這個API來操作泛型信息。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"註解方面的改進","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Spring對註解API和ApplicationContext獲取註解Bean做了一點改進,取註解的註解,如@Service是被@Compent註解的註解,可以通過如下方式獲取@Componet註解實例:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"Annotation service = AnnotationUtils.findAnnotation(ABService.class, org.springframework.stereotype.Service.class); \nAnnotation component = AnnotationUtils.getAnnotation(service, org.springframework.stereotype.Component.class); \n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","text":"獲取重複註解:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"比如在使用hibernate validation時,我們想在一個方法上加相同的註解多個,需要使用如下方式:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Length.List( \n value = { \n @Length(min = 1, max = 2, groups = A.class), \n @Length(min = 3, max = 4, groups = B.class) \n } \n) \npublic void test() {}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以通過如下方式獲取@Length:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"Method method = ClassUtils.getMethod(AnnotationUtilsTest.class, \"test\"); \nSet set = AnnotationUtils.getRepeatableAnnotation(method, Length.List.class, Length.class);\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當然,如果你使用Java8,那麼本身就支持重複註解,比如spring的任務調度註解,","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) \n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Repeatable(Schedules.class)\npublic @interface Scheduled {}\n\n@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) \n@Retention(RetentionPolicy.RUNTIME) \n@Documented\npublic @interface Schedules {\n Scheduled[] value();\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這樣的話,我們可以直接同時註解相同的多個註解:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Scheduled(cron = \"123\")\n@Scheduled(cron = \"234\")\npublic void test\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但是獲取的時候還是需要使用如下方式:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"AnnotationUtils.getRepeatableAnnotation(ClassUtils.getMethod(TimeTest.class, \"test\"), Schedules.class, Scheduled.class)\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ApplicationContext和BeanFactory提供了直接通過註解獲取Bean的方法:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Test\npublic void test() {\n AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();\n ctx.register(GenericConfig.class);\n ctx.refresh();\n Map beans = ctx.getBeansWithAnnotation(org.springframework.stereotype.Service.class); \n System.out.println(beans);\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"另外和提供了一個AnnotatedElementUtils用於簡化java.lang.reflect.AnnotatedElement的操作。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"ScriptEvaluator腳本的支持","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"spring也提供了類似於javax.script的簡單封裝,用於支持一些腳本語言,核心接口是:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public interface ScriptEvaluator {\n Object evaluate(ScriptSource script) throws ScriptCompilationException;\n Object evaluate(ScriptSource script, Map arguments) throws ScriptCompilationException;\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"比如我們使用groovy腳本的話,可以這樣:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Test\npublic void test() throws ExecutionException, InterruptedException {\n ScriptEvaluator scriptEvaluator = new GroovyScriptEvaluator();\n //ResourceScriptSource 外部的\n ScriptSource source = new StaticScriptSource(\"i+j\");\n Map args = new HashMap<>();\n args.put(\"i\", 1);\n args.put(\"j\", 2);\n System.out.println(scriptEvaluator.evaluate(source, args));\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"另外還提供了BeanShell(BshScriptEvaluator)和javax.script(StandardScriptEvaluator)的簡單封裝。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"MvcUriComponentsBuilder","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"MvcUriComponentsBuilder類似於ServletUriComponentsBuilder,但是可以直接從控制器獲取URI信息,如下所示:假設我們的控制器是:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Controller\n@RequestMapping(\"/user\")\npublic class UserController {\n @RequestMapping(\"/{id}\")\n public String view(@PathVariable(\"id\") Long id) {\n return \"view\";\n }\n @RequestMapping(\"/{id}\")\n public A getUser(@PathVariable(\"id\") Long id) {\n return new A();\n }\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"注:如果在真實mvc環境,存在兩個@RequestMapping(\"/{id}\")是錯誤的。當前只是爲了測試。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"需要靜態導入 import static org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder.*;","attrs":{}}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Test \npublic void test() { \n MockHttpServletRequest req = new MockHttpServletRequest(); \n RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(req)); \n \n //MvcUriComponentsBuilder類似於ServletUriComponentsBuilder,但是直接從控制器獲取 \n //類級別的 \n System.out.println( \n fromController(UserController.class).build().toString() \n ); \n \n //方法級別的 \n System.out.println( \n fromMethodName(UserController.class, \"view\", 1L).build().toString() \n ); \n \n //通過Mock方法調用得到 \n System.out.println( \n fromMethodCall(on(UserController.class).getUser(2L)).build() \n ); \n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"注意:當前MvcUriComponentsBuilder實現有問題,只有JDK環境支持,大家可以複製一份,然後修改:method.getParameterCount() (Java 8才支持)到method.getParameterTypes().length","attrs":{}}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"Socket支持","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"提供了獲取Socket TCP/UDP可用端口的工具,如","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"SocketUtils.findAvailableTcpPort()\nSocketUtils.findAvailableTcpPort(min, max) \nSocketUtils.findAvailableUdpPort()\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章