文章來源于Java官方文檔,原文請點這里
一,Classes
Java中的類型,要么是引用類型,要么是原始數(shù)據(jù)類型。原始數(shù)據(jù)類型是一個固定的集合,包括:boolean, byte, short, int, long, char, float, and double。其它都是引用類型,例如:arrays, string, enum 等。
對于任意一類的對象,在JVM中都會為其實例化一個不變的 Class 對象,該對象提供了可以檢查該對象運行時屬性、方法和類型信息的方法。同時,Class 對象也具備可以創(chuàng)建一個新的 class 或 對象的方法。更重要的是,Class 對象是反射API發(fā)揮作用的入口點。
1,Retrieving Class Objects
第一種,使用 Object.getClass()
當一個類是繼承自 Object 類的時候,那么該類的實例則可以通過調(diào)用 .getClass() 方法來獲取到這個類的 Class 對象。例如:
Class c = "foo".getClass(); //返回字符串的 Class 對象
enum E {
A, B
}
Class c = E.A.getClass(); //返回枚舉類 E 的 Class 對象
byte[] bytes = new byte[1024];
Class c = bytes.getClass(); //返回 byte 類型的 Class 對象
Set<String> s = new HashSet<String>();
Class c = s.getClass(); //返回 HashSet 類型的 Class 對象
第二種,使用 .class
當沒有類的實例時,可以使用 .class 語法來獲取類的 Class 對象,同時,使用 .class 也可以獲取原始數(shù)據(jù)類型的 Class 對象。例如:
boolean b;
Class c = b.getClass(); // compile-time error
Class c = boolean.class; // 正確返回 boolean 類型的 Class 對象
Class c = java.io.PrintStream.class; //返回 PrintStream 類的 Class 對象
Class c = int[][][].class; //返回 int 三維數(shù)組的 Class 對象
第三種,使用 Class.forName()
如果有類的完全限定名稱,則可以使用 Class.forName() 來獲取對應類的 Class 對象。這種方式不能應用到原始數(shù)據(jù)類型上。例如:
Class c = Class.forName("com.duke.MyLocaleServiceProvider"); //返回 MyLocaleServiceProvider 類的 Class 對象
//返回一個表示 double 類型一維數(shù)組的 Class 對象
//等價于 double[].class 或者 double[] darr = new double[9]; darr.getClass()
Class cDoubleArray = Class.forName("[D");
//返回一個表示 String 類型的二維數(shù)組的 Class 對象
Class cStringArray = Class.forName("[[Ljava.lang.String;");
2,原始包裝類型的 TYPE 字段
對于原始數(shù)據(jù)類型 .class 這種方式是一種非常合適的方式來獲取它的 Class 對象。但是這里還有另外一種方式,對于每一種原始數(shù)據(jù)類型和 void 類型,在 java.lang 包中都有其一個對應的包裝類型。每個包裝類型都有一個 TYPE 字段可以用來獲取對應原始數(shù)據(jù)類型的 Class 對象。例如:
//以下都表示獲取 double 類型的 Class 對象
Class c = Double.TYPE;
Class c1 = double.class;
3,返回 Class 對象的方法
下面舉例的這些反射API都可以返回一個 Class 對象,但是前提是,你必須首先得直接或間接的已經(jīng)得到一個 Class 對象。
Class.getSuperclass() 獲取指定類的父類的 Class 對象
Class c = javax.swing.JButton.class.getSuperclass();
獲取定義成員類的 Class 對象
Class.getDeclaringClass()
java.lang.reflect.Field.getDeclaringClass()
java.lang.reflect.Method.getDeclaringClass()
java.lang.reflect.Constructor.getDeclaringClass()
class T {
public String name; //這里必須是 public 類型
}
Class c = T.class.getField("name").getDeclaringClass(); //返回 T 類的 Class 對象
4,Examining Class Modifiers and Types
一個類可以聲明一個或多個修飾符(Modifiers),這些修飾符將影響它運行時的行為。
- Access modifiers: public, protected, and private
- Modifier requiring override: abstract
- Modifier restricting to one instance: static
- Modifier prohibiting value modification: final
- Annotations
java.lang.reflect.Modifier 類包含了所有可用的修飾符,同時它包含了一些方法用來解析由 Class.getModifiers() 類返回的修飾符集合。
以下代碼示例展示了如何獲取一個類的相關聲明的組件信息,包括類修飾符,泛型類型參數(shù),實現(xiàn)的接口和繼承路徑。同時因為 Class 類實現(xiàn)了 java.lang.reflect.AnnotatedElement 接口,因此也可以去獲取類在運行時的注解定義信息。
public interface ITest {
}
public class ATest {
}
@Deprecated
public class Test<T> extends ATest implements ITest {
}
public class ClassDeclarationSpy {
public static void main(String[] args) {
Class c = Test.class;
//類的全限定名稱
System.out.println(c.getCanonicalName());
//獲取類的修飾符
System.out.println(Modifier.toString(c.getModifiers()));
//獲取泛型類型參數(shù)
TypeVariable[] typeVariables = c.getTypeParameters();
for (int i = 0; i < typeVariables.length; i++) {
System.out.println(typeVariables[i].getName());
}
//獲取實現(xiàn)的接口
Type[] intfs = c.getGenericInterfaces();
for (int i = 0; i < intfs.length; i++) {
System.out.println(intfs[i].toString());
}
//獲取繼承的類
List<Class> l = new ArrayList<Class>();
printAncestor(c, l); //遞歸的獲取
l.forEach(clazz -> System.out.println(clazz.getCanonicalName()));
//獲取運行時定義的注解
Annotation[] ann = c.getAnnotations();
for (Annotation a : ann) {
System.out.println(a.toString());
}
}
private static void printAncestor(Class<?> c, List<Class> l) {
Class<?> ancestor = c.getSuperclass();
if (ancestor != null) {
l.add(ancestor);
printAncestor(ancestor, l);
}
}
}
5,Discovering Class Members
在 Class 類中包括兩種類型的方法,用來訪問類的字段、方法和構造器。一種是枚舉這些成員信息,一種是獲取指定的成員,如下圖:

二,Members
反射API定義了一個 java.lang.reflect.Member 接口,這個接口由 java.lang.reflect.Field, java.lang.reflect.Method, 和 java.lang.reflect.Constructor 三個類實現(xiàn)。
1,F(xiàn)ields
Field 由類型和值組成。java.lang.reflect.Field 類提供了可以操作某個對象中指定 Field 的類型信息、值信息的方法。
Obtaining Field Types
一個 Field 可能是原始數(shù)據(jù)類型也可能是引用類型。下面的代碼向你展示了打印 Filed 的類型信息、泛型類型以及 Field 名稱。
public class TestField {
public static void main(String[] args) {
Class c = FieldSpy.class;
Field[] fields = c.getDeclaredFields();
for (Field field : fields) {
System.out.println("name: "+field.getName()+", type: "+field.getType()+", GenericType: "+field.getGenericType());
}
}
static class FieldSpy<T> {
public boolean[][] b = {{ false, false }, { true, true } };
public String name = "Alice";
public List<Integer> list;
public T val;
}
}
Retrieving and Parsing Field Modifiers
Field 修飾符可以包含如下幾類:
- Access modifiers: public, protected, and private
- Field-specific modifiers governing runtime behavior: transient and volatile
- Modifier restricting to one instance: static
- Modifier prohibiting value modification: final
- Annotations
通過 Field.getModifiers() 方法可以獲取到一個使用整數(shù)表示的該 Field 的修飾符集合。這個整數(shù)中的字節(jié)表示的修飾符定義在 java.lang.reflect.Modifier 類中,我們可以使用該類提供的方法來判斷 Field 的修飾符類型。
下面這個類向你展示了如何通過給定的修飾符檢索一個 Field,同時也展示了如何判斷一個 Field 是不是合成的,或者是一個枚舉的常量。
public class FieldModifierSpy {
public static void main(String[] args) {
Class c = FieldModifierDemo.class;
Field[] flds = c.getDeclaredFields();
for (Field field : flds) {
int modifiers = field.getModifiers();
if (Modifier.isPublic(modifiers)) {
System.out.println("Public FieldName: "+field.getName());
} else if (Modifier.isProtected(modifiers)) {
System.out.println("Protected FieldName: "+field.getName());
}
if (field.isSynthetic()) {
System.out.println("Field: "+field.getName()+" is Synthetic");
}
if (field.isEnumConstant()) {
System.out.println("Field: "+field.getName()+" is EnumConstant");
}
}
System.out.println("----------------------");
c = FieldModifierDemo.Spy.class;
flds = c.getDeclaredFields();
for (Field field : flds) {
int modifiers = field.getModifiers();
if (Modifier.isPublic(modifiers)) {
System.out.println("Public FieldName: "+field.getName());
} else if (Modifier.isProtected(modifiers)) {
System.out.println("Protected FieldName: "+field.getName());
} else if (Modifier.isPrivate(modifiers)) {
System.out.println("Private FieldName: "+field.getName());
}
if (field.isSynthetic()) {
System.out.println("Field: "+field.getName()+" is Synthetic");
}
if (field.isEnumConstant()) {
System.out.println("Field: "+field.getName()+" is EnumConstant");
}
}
System.out.println("----------------------");
c = FieldModifierDemo.Inner.class;
flds = c.getDeclaredFields();
// flds = c.getFields(); //這里將不包含合成字段
for (Field field : flds) {
int modifiers = field.getModifiers();
if (Modifier.isPublic(modifiers)) {
System.out.println("Public FieldName: "+field.getName());
} else if (Modifier.isProtected(modifiers)) {
System.out.println("Protected FieldName: "+field.getName());
} else if (Modifier.isPrivate(modifiers)) {
System.out.println("Private FieldName: "+field.getName());
} else if (Modifier.isFinal(modifiers)) {
System.out.println("Final FieldName: "+field.getName());
}
if (field.isSynthetic()) {
System.out.println("Field: "+field.getName()+" is Synthetic");
}
if (field.isEnumConstant()) {
System.out.println("Field: "+field.getName()+" is EnumConstant");
}
}
}
static class FieldModifierDemo {
protected volatile int share;
public int instance;
enum Spy { BLACK , WHITE }
class Inner {}
}
}
需要注意的是,合成字段在 getFields() 方法的返回值中并未包含,這是該方法和 getDeclaredFields() 方法的一點區(qū)別。同時,由于 Field 類實現(xiàn)了 java.lang.reflect.AnnotatedElement 接口,因此我們也可以方便的獲取到該字段上的所有運行時注解。
Getting and Setting Field Values
下面的代碼展示了如何設置 Field 的值。
public class SetFieldValTest {
enum Tweedle { DEE, DUM }
public static void main(String[] args) throws IllegalAccessException, NoSuchFieldException {
Book book = new Book();
String fmt = "%6S: %-12s = %s%n";
Class<?> c = book.getClass();
//對于要設置的字段類型是原始數(shù)據(jù)類型的包裝類型時,不能使用 setLong, setInt 等方法
//因為在反射中不會自動裝箱和拆箱,因此只能用 set 方法實現(xiàn)
Field chap = c.getDeclaredField("chapters");
chap.set(book, Long.valueOf(12));
out.format(fmt, "after", "chapters", chap.get(book));
Field age = c.getDeclaredField("age");
age.setInt(book, 10);
out.format(fmt, "after", "age", age.get(book));
Field chars = c.getDeclaredField("characters");
String[] newChars = { "Queen", "King" };
chars.set(book, newChars);
out.format(fmt, "after", "characters", Arrays.asList(book.characters));
Field t = c.getDeclaredField("twin");
t.set(book, Tweedle.DUM);
out.format(fmt, "after", "twin", t.get(book));
//對于用 private、final 修飾的字段,在設置值之前必須要設置其訪問屬性
//setAccessible 方法只有在 security context 中被允許時才能夠成功
Field flag = c.getDeclaredField("flag");
flag.setAccessible(true);
flag.set(book, true);
out.format(fmt, "after", "flag", flag.getBoolean(book));
flag.setAccessible(false);
}
static class Book {
public String[] characters = { "Alice", "White Rabbit" };
public Tweedle twin = Tweedle.DEE;
public int age = 5;
public Long chapters = 0L;
private boolean flag = false;
}
}
設置字段的值有可能會產(chǎn)生兩個異常:一個是修改使用 private、final 修飾的字段時訪問權限被拒絕,一個是使用 setLong、setInt 等方法時拋出設置失敗異常。關于解決方案和有可能的原因已經(jīng)在上述代碼中注釋描述,直接看代碼。
2,Methods
方法由返回值,參數(shù)以及可能拋出的異常構成。java.lang.reflect.Method 類提供了方法可以獲取方法參數(shù)的類型信息、方法的返回值信息,以及可以執(zhí)行指定對象的方法的方法。
Obtaining Method Type Information
以下代碼向你展示了如何獲取指定類中指定方法的參數(shù)信息、返回值信息、異常信息以及判斷該方法的參數(shù)是否是可變參數(shù),如下:
public class MethodSpy {
private static final String fmt = "%24s: %s%n";
public static void main(String[] args) {
try {
String className = "java.lang.Class";
String methodName = "cast";
Class<?> c = Class.forName(className);
Method[] allMethods = c.getDeclaredMethods();
for (Method m : allMethods) {
if (!m.getName().equals(methodName)) {
continue;
}
out.format("%s%n", m.toGenericString()); //獲取方法的泛型形式定義
out.format(fmt, "ReturnType", m.getReturnType());
out.format(fmt, "GenericReturnType", m.getGenericReturnType());
Class<?>[] pType = m.getParameterTypes();
Type[] gpType = m.getGenericParameterTypes();
for (int i = 0; i < pType.length; i++) {
out.format(fmt,"ParameterType", pType[i]);
out.format(fmt,"GenericParameterType", gpType[i]);
}
Class<?>[] xType = m.getExceptionTypes();
Type[] gxType = m.getGenericExceptionTypes();
for (int i = 0; i < xType.length; i++) {
out.format(fmt,"ExceptionType", xType[i]);
out.format(fmt,"GenericExceptionType", gxType[i]);
}
out.println("是否是可變參數(shù):"+m.isVarArgs());
out.println("------------------------------");
}
// production code should handle these exceptions more gracefully
} catch (ClassNotFoundException x) {
x.printStackTrace();
}
}
}
上面的代碼中,如果用下面的代碼簡單修改一下:
String className = "java.io.PrintStream";
String methodName = "format";
運行之后你會發(fā)現(xiàn),對于一個類中的多個重載方法,getDeclaredMethods 方法都會把它們返回回來。
Obtaining Names of Method Parameters
Method 類提供了 getParameters() 方法,用來獲取方法的參數(shù)名稱、類型等信息。但是在 .class 文件中并沒有存儲參數(shù)的名稱信息,這是因為許多使用和生成 .class 文件的工具,不太期望 .class 文件占用更大的靜態(tài)或動態(tài)空間,因為它們處理大的 .class 文件時,則會要求 jvm 占用更多的內(nèi)存,同時,方法參數(shù)名稱可能會暴露方法的一些安全信息,如果 passwrod 名稱等。
為了讓 .class 文件中存儲方法參數(shù)的名稱信息,則需要在編譯 java 源碼時給 javac 命令指定 -parameters 參數(shù)。
Retrieving and Parsing Method Modifiers
使用 getModifiers 方法可以獲取方法的修飾符信息,判斷方法具有哪些修飾符和 Field 中的操作是一樣的。
Invoking Methods
Method 類提供了一個 invoke() 方法用來通過反射的方式來執(zhí)行一個類中的某個方法,invoke() 方法需要兩個參數(shù),第一個參數(shù)是類的實例對象,如果要執(zhí)行的方法是 static 修飾的,那么第一個參數(shù)固定為 null,第二個參數(shù)是一個可變參數(shù),用來向要執(zhí)行的方法傳遞參數(shù)。
示例代碼如下:
public class MethodSpy {
private static final String fmt = "%24s: %s%n";
public static void main(String[] args) throws IllegalAccessException, InstantiationException, InvocationTargetException {
Class c = Deet.class;
Method[] allMethods = c.getDeclaredMethods();
Object t = c.newInstance();
for (Method m : allMethods) {
m.setAccessible(true);
Object o;
if (m.getParameters().length > 0) {
o = m.invoke(t, new Locale("zh-CN"));
} else {
o = m.invoke(t);
}
if (Modifier.isStatic(m.getModifiers())) {
out.println("invoke static method: "+m.getName());
o = m.invoke(null);
}
out.println(o);
}
}
static class Deet {
private boolean testBar() {
return true;
}
private int testFoo(Locale l) {
return 0;
}
private static int testStatic() {
return 1;
}
}
}
3,Constructors
反射API提供了 java.lang.reflect.Constructor 類用來操作構造方法,和 Method 類類似。
Finding Constructors
通過 Class.getDeclaredConstructors() 可以獲取到指定類的全部構造方法(包括 private 修飾的構造方法),但是 Class.getConstructors() 方法則只返回 public 修飾的構造方法。
Retrieving and Parsing Constructor Modifiers
直接參考普通方法的獲取方式即可。
Creating New Class Instances
有兩種用來創(chuàng)建一個類的實例的方式:
java.lang.reflect.Constructor.newInstance() //第一種,通過構造函數(shù)
Class.newInstance() //第二種,直接使用 Class 類
一般,推薦使用第一種,因為如下原因:
- Class.newInstance() 它不理會類的構造方法的參數(shù),只會執(zhí)行無參數(shù)的構造方法
- Class.newInstance() 它不理會構造函數(shù)的異常是 unchecked 或者是 checked,它直接將異常包裝成 InvocationTargetException 異常,然后拋出
- Class.newInstance() 不能執(zhí)行 private 修飾的構造方法
下面的代碼示例,使用第一種方式來創(chuàng)建一個類的實例,并且該代碼也展示了,如何獲取到指定的構造方法,如下:
public class RestoreAliases {
private static Map<String, String> defaultAliases = new HashMap<String, String>();
private static Set<String> defaultAliasesSet = new HashSet<String>();
static {
defaultAliases.put("Duke", "duke@i-love-java");
defaultAliases.put("Fang", "fang@evil-jealous-twin");
defaultAliasesSet.add("zh");
defaultAliasesSet.add("cn");
}
public static void main(String[] args) {
try {
//獲取具有指定參數(shù)個數(shù)和類型的構造方法
// Constructor ctor = EmailAliases.class.getDeclaredConstructor(HashMap.class);
Constructor ctor = EmailAliases.class.getDeclaredConstructor(HashSet.class);
ctor.setAccessible(true);
// EmailAliases email = (EmailAliases)ctor.newInstance(defaultAliases);
EmailAliases email = (EmailAliases)ctor.newInstance(defaultAliasesSet);
email.printKeys();
// production code should handle these exceptions more gracefully
} catch (InstantiationException x) {
x.printStackTrace();
} catch (IllegalAccessException x) {
x.printStackTrace();
} catch (InvocationTargetException x) {
x.printStackTrace();
} catch (NoSuchMethodException x) {
x.printStackTrace();
}
}
static class EmailAliases {
private Set<String> aliases;
private EmailAliases(HashMap<String, String> h) {
out.println("EmailAliases HashMap run");
aliases = h.keySet();
}
private EmailAliases(HashSet<String> h) {
out.println("EmailAliases HashSet run");
aliases = h;
}
public void printKeys() {
out.format("Mail keys:%n");
for (String k : aliases)
out.format(" %s%n", k);
}
}
}
三,實現(xiàn)動態(tài)代理
首先應該理解代理模式,代理模式分為靜態(tài)代理和動態(tài)代理。
JDK中提供了 Proxy 類來實現(xiàn)動態(tài)代理,示例代碼如下:
public interface MyDemoInterface {
int run(int time);
}
public class MyDemoClass implements MyDemoInterface {
@Override
public int run(int time) {
return time*10;
}
}
public class MyProxyHandler implements InvocationHandler {
private Object object;
public MyProxyHandler(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(object, args);
}
}
public class TestJdkProxy {
public static void main(String[] args) {
MyDemoInterface myDemoClass = new MyDemoClass();
MyProxyHandler handler = new MyProxyHandler(myDemoClass);
MyDemoInterface myDemoClassProxy = (MyDemoInterface) Proxy.newProxyInstance(
MyDemoInterface.class.getClassLoader(),
new Class<?>[] {MyDemoInterface.class},
handler
);
System.out.println(myDemoClassProxy.run(10));
}
}
JDK的動態(tài)代理要求要代理的類必須是實現(xiàn)了某個接口,不然無法代理。因此我們這個時候就需要使用 cglib 來實現(xiàn)動態(tài)代理,示例代碼如下:
public class DemoClass {
public int run(int time) {
return time*10;
}
}
public class MyCglibProxy implements MethodInterceptor {
private Object target;
public Object getInstance(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
return methodProxy.invokeSuper(o, args);
}
}
public class TestCglibProxy {
public static void main(String[] args) {
DemoClass demoClass = new DemoClass();
MyCglibProxy cglibProxy = new MyCglibProxy();
DemoClass demoClassProxy = (DemoClass) cglibProxy.getInstance(demoClass);
System.out.println(demoClassProxy.run(10));
}
}
四,Spring中AOP原理
在揭示SpringAOP的原理之前,我們需要先分析JDK的Proxy類的執(zhí)行流程,而后根據(jù)這個流程來反推Spring中關于AOP的兩種實現(xiàn)方式的大體原理,之后再通過查看Spring的源碼來進行證明。
首先我們把上面使用JDK實現(xiàn)動態(tài)代理的代碼中修改如下幾個地方:
public class MyProxyHandler implements InvocationHandler {
private Object object;
public MyProxyHandler(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.print(method.getName()+"==>");
for (Parameter parameter :method.getParameters()) {
System.out.println(parameter.getType().toGenericString());
}
System.out.println();
return method.invoke(object, args);
}
}
public class TestJdkProxy {
public static void main(String[] args) {
MyDemoInterface myDemoClass = new MyDemoClass();
MyProxyHandler handler = new MyProxyHandler(myDemoClass);
MyDemoInterface myDemoClassProxy = (MyDemoInterface) Proxy.newProxyInstance(
MyDemoInterface.class.getClassLoader(),
new Class<?>[] {MyDemoInterface.class},
handler
);
//打印代理類的類名
System.out.println(myDemoClassProxy.getClass().toGenericString());
System.out.println();
//打印代理類實現(xiàn)的接口信息
for (Type type : myDemoClassProxy.getClass().getGenericInterfaces()) {
System.out.println(type.getTypeName());
}
System.out.println();
//打印代理類中的方法名稱
for (Method method : myDemoClassProxy.getClass().getDeclaredMethods()) {
System.out.print(method.getName()+"==>");
for (Parameter parameter :method.getParameters()) {
System.out.println(parameter.getType().toGenericString());
}
System.out.println();
}
System.out.println("------------------");
System.out.println(myDemoClassProxy.run(10));
}
}
實際就是加了一些打印信息,利用這些打印信息,再結合 Proxy 類中的如下源碼(源碼解釋看注釋):
private static final Class<?>[] constructorParams =
{ InvocationHandler.class };
//獲取到的代理類構造函數(shù)
protected Proxy(InvocationHandler h) {
Objects.requireNonNull(h);
this.h = h;
}
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
*/
//創(chuàng)建出代理類,實際就是 Proxy 類本身
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
//獲取到代理中參數(shù)類型為 constructorParams 類型的構造函數(shù),實際就是獲取到了上面摘抄
//下來的構造函數(shù),
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
// 創(chuàng)建代理類的實例,實際就相當于調(diào)用了
// new Proxy(InvocationHandler h)
// 注意,這里的 InvocationHandler 就是我們外面?zhèn)鬟M來的,即我們上面代碼中的 MyProxyHandler 實例
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
根據(jù)對 JDK Proxy 類的源碼分析,以及 TestJdkProxy 類中的打印信息,最終分析推理且證明得到如下結論:
- Proxy.newProxyInstance 方法實際做的事情就是在內(nèi)部幫我們動態(tài)的生成了一個 Proxy 類,然后這個生成的類實現(xiàn)了我們準備被代理類 MyDemoClass 類的接口,即實現(xiàn)了 MyDemoInterface 接口,這也就是為什么 Proxy.newProxyInstance 返回的實例可以強制轉換成 MyDemoInterface 類型,根本原因是因為這個實例對應的類也實現(xiàn)了 MyDemoInterface 接口
- Proxy類中生成的代理類同時也實現(xiàn)了 MyDemoInterface 中的全部方法,同時參數(shù)信息也是保持一致的,這也就解釋了為什么 Proxy.newProxyInstance 返回的實例在后續(xù)階段可以直接調(diào)用 MyDemoInterface 中的方法
- Proxy類中生成的代理類中實現(xiàn)的 MyDemoInterface 中的方法的方法執(zhí)行流程為:直接調(diào)用 InvocationHandler 中的 invoke 方法同時傳遞進去必要的參數(shù)然后返回 invoke 方法的返回值,但是這里的返回值數(shù)據(jù)類型必須要和接口中的方法的返回值數(shù)據(jù)類型一致,不然會拋異常。
- 我們定義的 InvocationHandler 中的 invoke 方法成為了我們實際要執(zhí)行的被代理類的方法的入口,在該入口中,我們可以自主的選擇執(zhí)行被代理類方法的時機,因此該 invoke 方法可以被認為就是 AOP 中的JointPoint
清楚了 Proxy 類的流程和動態(tài)代理的本質之后,現(xiàn)在來看 Spring AOP 的實現(xiàn)原理。在開始之前,需要先澄清一些關于 AOP 的概念。首先 AOP 是一種編程模式,稱為面向切面編程。它與傳統(tǒng)面向對象編程的區(qū)別在于,使用 AOP 可以在系統(tǒng)中的某些位置動態(tài)的插入邏輯而不影響且不用修改原來的執(zhí)行邏輯和代碼。通常的應用包括日志打印,權限控制,事物控制等。
AOP 中包括如下幾個術語:
- JointPoint 連接點,即上面我們提到的 invoke 方法
- Advice 通知,定義在連接點的哪個位置執(zhí)行插入的邏輯,通常包括:前置,后置,環(huán)繞等
- Pointcut 切入點,在 AOP 中使用 AOP 表達式尋找到的一組連接點
- Aspect 切面,上述三個概念的組合稱為一個切面
清楚了概念之后,再來看原理。AOP 是一種規(guī)范,沒有固定的實現(xiàn),它的底層技術原理就是動態(tài)代理。在 Spring AOP 中提供了兩種方式的動態(tài)代理實現(xiàn):JDK 動態(tài)代理 和 cglib 動態(tài)代理。分別對應的源碼類為:JdkDynamicAopProxy 和 CglibAopProxy,它們的公共父類接口為:AopProxy。
我們通過閱讀 JdkDynamicAopProxy 的如下部分源碼可知,Spring 的 JdkDynamicAopProxy 實現(xiàn)實際就是 JDK Proxy 的一個簡單包裝,整體實現(xiàn)流程和上面我們分析的過程基本一致。關鍵源碼和注釋說明如下:
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
@Override
public Object getProxy(ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
}
// 得到代理類的接口信息,實際得到的就是 AopProxy 類,說明 Spring 中所有 Bean 的代理類實際
// 都實現(xiàn)了 AopProxy 類,雖然該類是空的
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
// 利用 JDK Proxy 創(chuàng)建代理類實例,同時指定 InvocationHandler 為它自己
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
}
理清了 JdkDynamicAopProxy 的流程和原理,那么如果你對 cglib 較為熟悉的話,相信 CglibAopProxy 也一樣可以看明白,因為這個類實際上就是 cglib 實現(xiàn)動態(tài)代理的一個簡單包裝。
掌握了原理之后再來學習 Spring AOP,會發(fā)現(xiàn),這個實際非常簡單。首先 JointPoint 的動態(tài)織入代理 Spring 通過底層的動態(tài)代理框架已經(jīng)幫我們實現(xiàn)了,我們要做的就是學習在 Spring 中可以使用什么樣的表達式來尋找這些連接點,即首先要學習如何定義一個 Pointcut,然后有了切入點之后再根據(jù)實際情況說明我們要執(zhí)行的通知,當然這也需要符合 Spring 聲明通知的規(guī)范,最后則是學習在 Spring 中如何把 Pointcut 和 Advice 組織到一起形成一個切面,這些都是固定的規(guī)范語法,查看文檔即可,而根據(jù)的底層原理我們已經(jīng)掌握了。
關于 Spring AOP 的語法規(guī)范可以查看這里