做用在代碼上的功能註解(部分):java
註解名稱 | 功能描述 |
---|---|
@Override | 檢查該方法是不是重寫方法。若是發現其父類,或者是引用的接口中並無該方法時,會報編譯錯誤 |
@Deprecated | 標記過期方法。若是使用該方法,會報編譯警告 |
@SuppressWarnings | 指示編譯器去忽略註釋解中聲明的警告 |
@FunctionalInterface | java8支持,標識一個匿名函數或函數式接口 |
讓給程序員開發自定義註解的元註解(和關鍵字@interface配合使用的註解)python
元註解名稱 | 功能描述 |
---|---|
@Retention | 標識這個註釋解怎麼保存,是隻在代碼中,仍是編入類文件中,或者是在運行時能夠經過反射訪問 |
@Documented | 標識這些註解是否包含在用戶文檔中 |
@Target | 標識這個註解的做用範圍 |
@Inherited | 標識註解可被繼承類獲取 |
@Repeatable | 標識某註解能夠在同一個聲明上使用屢次 |
package java.lang.annotation; public interface Annotation { boolean equals(Object obj); int hashCode(); String toString(); // 返回定義的註解類型,你在代碼聲明的@XXX,至關於該類型的一實例 Class<? extends Annotation> annotationType(); } -----自定義示例----- @Retention( value = RetentionPolicy.RUNTIME) @Target(value = ElementType.TYPE) public @interface ATest { String hello() default "siting"; }
ATest的字節碼文件,編譯器讓自定義註解實現了Annotation接口程序員
public abstract @interface com/ATest implements java/lang/annotation/Annotation { // compiled from: ATest.java @Ljava/lang/annotation/Retention;(value=Ljava/lang/annotation/RetentionPolicy;.RUNTIME) @Ljava/lang/annotation/Target;(value={Ljava/lang/annotation/ElementType;.TYPE}) // access flags 0x401 public abstract hello()Ljava/lang/String; default="siting" }
System.setProperty("sum.misc.ProxyGenerator.saveGeneratedFiles","true");
可生成註解相應的代理類public enum RetentionPolicy { /** 註解將被編譯器丟棄,生成的class不包含註解信息 */ SOURCE, /** 註解在class文件中可用,但會被JVM丟棄;當註解未定義Retention值時,默認值是CLASS */ CLASS, /** 註解信息在運行期(JVM)保留(.class也有),能夠經過反射機制讀取註解的信息, * 操做方法看AnnotatedElement(全部被註釋類的父類) */ RUNTIME }
public enum ElementType { /** 適用範圍:類、接口、註解類型,枚舉類型enum */ TYPE, /** 做用於類屬性 (includes enum constants) */ FIELD, /** 做用於方法 */ METHOD, /** 做用於參數聲明 */ PARAMETER, /** 做用於構造函數聲明 */ CONSTRUCTOR, /** 做用於局部變量聲明 */ LOCAL_VARIABLE, /** 做用於註解聲明 */ ;, /** 做用於包聲明 */ PACKAGE, /** 做用於類型參數(泛型參數)聲明 */ TYPE_PARAMETER, /** 做用於使用類型的任意語句(不包括class) */ TYPE_USE }
TYPE_PARAMETER的用法示例web
class D<@PTest T> { } // 註解@PTest做用於泛型T
TYPE_USE的用法示例spring
//用於父類或者接口 class Test implements @Parent TestP {} //用於構造函數 new @Test String("/usr/data") //用於強制轉換和instanceof檢查,注意這些註解中用於外部工具 //它們不會對類型轉換或者instanceof的檢查行爲帶來任何影響 String path=(@Test String)input; if(input instanceof @Test String) //註解不會影響 //用於指定異常 public Person read() throws @Test IOException. //用於通配符綁定 List<@Test ? extends Data> List<? extends @Test Data> @Test String.class //非法,不能標註class
@Inherited @Retention( value = RetentionPolicy.RUNTIME) @Target(value = ElementType.TYPE) public @interface ATest { } ----被ATest註解的父類PTest---- @ATest public class PTest{ } ---Main是PTest的子類---- public class Main extends PTest { public static void main(String[] args){ Annotation an = Main.class.getAnnotations()[0]; //Main能夠拿到父類的註解ATest,由於ATest被元註解@Inherited修飾 System.out.println(an); } } ---result-- @com.ATest()
//Java8前沒法重複使用註解 @FilterPath("/test/v2") @FilterPath("/test/v1") public class Test {}
--- 做用於註解的註解---- @Inherited @Retention( value = RetentionPolicy.RUNTIME) @Target(value = {ElementType.ANNOTATION_TYPE}) public @interface AnnotationTest { String value() default "AnnotationTest"; } ------父類------- public class PTest {} ------被註解修飾的package-info.java------ //package-info.java @AnTest("com-package-info") package com; ------------- @AnnotationTest("AnnotationTest") @Inherited @Retention( value = RetentionPolicy.RUNTIME) @Target(value = {ElementType.TYPE_USE,ElementType.PACKAGE,ElementType.FIELD, ElementType.TYPE_PARAMETER,ElementType.CONSTRUCTOR,ElementType.LOCAL_VARIABLE}) public @interface AnTest { String value() default "siting"; }
運行示例編程
//註解類 @AnTest("mainClass") //註解泛型參數 //註解繼承父類 public class Main<@AnTest("parameter") T > extends @AnTest("parent") PTest { @AnTest("constructor") //註解構造函數 Main(){ } //註解字段域 private @AnTest("name") String name; //註解泛型字段域 private @AnTest("value") T value; //註解通配符 private @AnTest("list")List<@AnTest("generic") ?>list; //註解方法 @AnTest("method") //註解方法參數 public String hello(@AnTest("methodParameter") String name) throws @AnTest("Exception") Exception { // 註解拋出異常 //註解局部變量,如今運行時暫時沒法獲取(忽略) @AnTest("result") String result; result = "siting"; System.out.println(name); return result; } public static void main(String[] args) throws Exception { Main<String> main = new Main<> (); Class<Main<Object>> clazz = (Class<Main<Object>>) main.getClass(); //class的註解 Annotation[] annotations = clazz.getAnnotations(); AnTest testTmp = (AnTest) annotations[0]; System.out.println("修飾Main.class註解value: "+testTmp.value()); //構造器的註解 Constructor<Main<Object>> constructor = (Constructor<Main<Object>>) clazz.getDeclaredConstructors()[0]; testTmp = constructor.getAnnotation(AnTest.class); System.out.println("修飾構造器的註解value: "+testTmp.value()); //繼承父類的註解 AnnotatedType annotatedType = clazz.getAnnotatedSuperclass(); testTmp = annotatedType.getAnnotation(AnTest.class); System.out.println("修飾繼承父類的註解value: "+testTmp.value()); //註解的註解 AnnotationTest annotationTest = testTmp.annotationType().getAnnotation(AnnotationTest.class); System.out.println("修飾註解的註解AnnotationTest-value: "+annotationTest.value()); //泛型參數 T 的註解 TypeVariable<Class<Main<Object>>> variable = clazz.getTypeParameters()[0]; testTmp = variable.getAnnotation(AnTest.class); System.out.println("修飾泛型參數T註解value: "+testTmp.value()); //普通字段域 的註解 Field[] fields = clazz.getDeclaredFields(); Field nameField = fields[0]; testTmp = nameField.getAnnotation(AnTest.class); System.out.println("修飾普通字段域name註解value: "+testTmp.value()); //泛型字段域 的註解 Field valueField = fields[1]; testTmp = valueField.getAnnotation(AnTest.class); System.out.println("修飾泛型字段T註解value: "+testTmp.value()); //通配符字段域 的註解 Field listField = fields[2]; AnnotatedParameterizedType annotatedPType = (AnnotatedParameterizedType)listField.getAnnotatedType(); testTmp = annotatedPType.getAnnotation(AnTest.class); System.out.println("修飾泛型註解value: "+testTmp.value()); //通配符註解 的註解 AnnotatedType[] annotatedTypes = annotatedPType.getAnnotatedActualTypeArguments(); AnnotatedWildcardType annotatedWildcardType = (AnnotatedWildcardType) annotatedTypes[0]; testTmp = annotatedWildcardType.getAnnotation(AnTest.class); System.out.println("修飾通配符註解value: "+testTmp.value()); //方法的註解 Method method = clazz.getDeclaredMethod("hello", String.class); annotatedType = method.getAnnotatedReturnType(); testTmp = annotatedType.getAnnotation(AnTest.class); System.out.println("修飾方法的註解value: "+testTmp.value()); //異常的註解 annotatedTypes = method.getAnnotatedExceptionTypes(); testTmp = annotatedTypes[0].getAnnotation(AnTest.class); System.out.println("修飾方法拋出錯誤的註解value: "+testTmp.value()); //方法參數的註解 annotatedTypes = method.getAnnotatedParameterTypes(); testTmp = annotatedTypes[0].getAnnotation(AnTest.class); System.out.println("修飾方法參數註解value: "+testTmp.value()); //包的註解 Package p = Package.getPackage("com"); testTmp = p.getAnnotation(AnTest.class); System.out.println("修飾package註解value: "+testTmp.value()); main.hello("hello"); } }
結果app
修飾Main.class註解value: mainClass 修飾構造器的註解value: constructor 修飾繼承父類的註解value: parent 修飾註解的註解AnnotationTest-value: AnnotationTest 修飾泛型參數T註解value: parameter 修飾普通字段域name註解value: name 修飾泛型字段T註解value: value 修飾泛型註解value: list 修飾通配符註解value: generic 修飾方法的註解value: method 修飾方法拋出錯誤的註解value: Exception 修飾方法參數註解value: methodParameter 修飾package註解value: com-package-info hello
spring.AOP至關於動態代理和註解機制在spring框架的結合實現框架
概念 | 描述 |
---|---|
通知(Advice) | 須要切入的增長代碼邏輯被稱爲通知 |
切點(Pointcut) | 定義加強代碼在何處執行 |
切面(Aspect) | 切面是通知和切點的集合 |
鏈接點(JoinPoint) | 在切點基礎上,指定加強代碼在切點執行的時機(在切點前,切點後,拋出異常後等) |
目標(target) | 被加強目標類 |
切面編程相關注解 | 功能描述 |
---|---|
@Aspect | 做用於類,聲明當前方法類是加強代碼的切面類 |
@Pointcut | 做用於方法,指定須要被攔截的其餘方法。當前方法則做爲攔截集合名使用 |
spring通知(Advice)註解 | 功能描述 |
---|---|
@After | 加強代碼在@Pointcut指定的方法以後執行 |
@Before | 加強代碼在@Pointcut指定的方法以前執行 |
@AfterReturning | 加強代碼在@Pointcut指定的方法 return返回以後執行 |
@Around | 加強代碼能夠在被攔截方法先後執行 |
@AfterThrowing | 加強代碼在@Pointcut指定的方法拋出異常以後執行 |
新建spring-web + aop 項目;新建以下class ------ 目標Controller ------ @RestController public class TestController { @STAnnotation @RequestMapping("/hello") public String hello(){ return "hello@csc"; } } ------ Controller註解 ------- @Retention( value = RetentionPolicy.RUNTIME) @Target(value = ElementType.METHOD) public @interface STAnnotation { String value() default "註解hello!"; } ------ Controller切面 ------ @Aspect @Component public class ControllerAspect { //切點:註解指定關聯 (對註解進行切面) @Pointcut("@annotation(STAnnotation)") public void controllerX(){} //切點:路徑指定關聯 @Pointcut("execution(public * com.example.demo.TestController.*(..))") public void controllerY(){} //在controllerY()切點執行以前的鏈接點加入通知 @Before("controllerY()") public void yBefore(JoinPoint joinPoint) throws Throwable { //能夠加入加強代碼 MethodSignature methodS = (MethodSignature)joinPoint.getSignature(); Method method = methodS.getMethod(); if (method.isAnnotationPresent(STAnnotation.class)) { STAnnotation annotation = method.getAnnotation(STAnnotation.class); System.out.println(annotation.value()); } System.out.println("controllerY"); } //controllerX()切點執行以後的鏈接點加入通知 @After("controllerX()") public void xBefore(JoinPoint joinPoint) throws Throwable { //能夠加入加強代碼 System.out.println("controllerX"); } }
啓動項目;執行curl http://127.0.0.1:8080/hello
,控制檯輸出以下
curl
@FunctionalInterface public interface Func { void hello(String name); } --------------------- public static void main(String[] args) { Func func = (name) -> System.out.println(name); func.hello("siting"); }
查看對應的Main.class字節碼文件 javap.exe -p -v -c Main.class
jvm
Constant pool: #1 = Methodref #8.#28 // java/lang/Object."<init>":()V //常量值中前面的#0表示引導方法取BootstrapMethods屬性表的第0項(字節碼在最下面) #2 = InvokeDynamic #0:#33 // #0:hello:()Lcom/Func; #3 = String #34 // siting #4 = InterfaceMethodref #35.#36 // com/Func.hello:(Ljava/lang/String;)V #5 = Fieldref #37.#38 // java/lang/System.out:Ljava/io/PrintStream; #6 = Methodref #39.#40 // java/io/PrintStream.println:(Ljava/lang/String;)V #7 = Class #41 // com/Main .... // main執行字節碼 public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=2, args_size=1 // 動態得到一個CallSite對象,該對象是一個內部類,實現了Func接口 0: invokedynamic #2, 0 // InvokeDynamic #0:hello:()Lcom/Func; 5: astore_1 6: aload_1 7: ldc #3 // String siting // 調用CallSite對象的hello方法 9: invokeinterface #4, 2 // InterfaceMethod com/Func.hello:(Ljava/lang/String;)V 14: return .... //lambda表達式 會編譯出私有靜態類 private static void lambda$main$0(java.lang.String); descriptor: (Ljava/lang/String;)V flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC Code: stack=2, locals=1, args_size=1 0: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 3: aload_0 4: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 7: return .... //lambda表達式 會編譯出一個對應的內部類 SourceFile: "Main.java" InnerClasses: public static final #59= #58 of #62; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles BootstrapMethods: 0: #30 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lan g/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; Method arguments: #31 (Ljava/lang/String;)V //調用Main方法裏的lambda$main$0靜態方法(真正執行lambda的邏輯的方法) #32 invokestatic com/Main.lambda$main$0:(Ljava/lang/String;)V #31 (Ljava/lang/String;)V
從上面的字節碼可看出,1:lambda表達式會被編譯成一個私有靜態方法和一個內部類;2:內部類實現了函數式接口,而實現方法會調用一個Main.class裏一靜態方法 3:靜態方法lambda$main$0裏是咱們本身寫的代碼邏輯。運行參數加上-Djdk.internal.lambda.dumpProxyClasses
能夠查看lambda對應內部類的具體信息
接口 | 描述 |
---|---|
Predicate | 判斷:傳入一個參數,返回一個bool結果, 方法爲boolean test(T t) |
Consumer | 消費:傳入一個參數,無返回值, 方法爲void accept(T t) |
Function | 轉化處理:傳入一個參數,返回一個結果,方法爲R apply(T t) |
Supplier | 生產:無參數傳入,返回一個結果,方法爲T get() |
BiFunction | 轉化處理:傳入兩個個參數,返回一個結果,方法R apply(T t, U u) |
BinaryOperator | 二元操做符, 傳入的兩個參數的類型和返回類型相同, 繼承 BiFunction |