今天在寫Spring
的引介代理的時候,報了一個錯:java
Exception in thread "main" org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'inter1' is expected to be of type 'com.dengchengchao.springtest.intertest.Inter1Impl' but was actually of type 'com.sun.proxy.$Proxy14'
大概的意思是類型轉換錯誤。spring
源代碼以下:微信
ApplicationContext ctx = new AnnotationConfigApplicationContext(Conf.class); Inter1 inter1 = ctx.getBean("inter1", Inter1Impl.class); inter1.say1(); Inter2 inter2=(Inter2) inter1; inter2.say2();
後來google
了一下發現把代理方式改爲CGLIB
就行。ide
咱們都知道JDK
只能代理接口,對於非接口的類的代理,應該使用CGLIB
。ui
由於CGLIB
是經過繼承代理類實現,而JDK
是經過實現接口實現。this
可是我這裏Inter1
分明就是一個接口。後來仔細檢查了代碼,發現其實使用Java
代理也行,只要改以下一行代碼便可:google
Inter1 inter1 = ctx.getBean("inter1", Inter1.class);
也就是說,須要轉換成類型應該是Inter1.class
而不能是具體的類Inter1Impl
。.net
爲何Java
代理只支持接口代理,這裏咱們來深扒一下:代理
首先定義一個接口:code
public interface People { void eat(); }
而後定義一個實現類:
public class Student implements People{ @Override public void eat() { System.out.println("用手吃"); } }
接着定義一個代理類:
public class StudentInvokeHandler implements InvocationHandler { private Object target; public StudentInvokeHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("飯前洗手"); Object retVal = method.invoke(target, args); System.out.println("飯後吃水果"); return retVal; } }
接下來,經過代理來調用Student
public static void main(String[] args) { //初始化Student Student student = new Student(); //初始化Student代理類 StudentInvokeHandler studentInvokeHandler = new StudentInvokeHandler(student); //經過代理獲取代理獨享 People studentProxy = (People) Proxy.newProxyInstance(StudentInvokeHandler.class.getClassLoader(), new Class[]{People.class}, studentInvokeHandler); //經過代理對象調用eat方法 studentProxy.eat(); }
能夠看見,Java
的代理很是簡單,可是底層是如何實現的呢?
參照細說JDK動態代理的實現原理,咱們在main
中設置一下JVM
屬性
public static void main(String[] args) { //將生成的代理類文件保存 System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); Student student = new Student(); StudentInvokeHandler studentInvokeHandler = new StudentInvokeHandler(student); People studentProxy = (People) Proxy.newProxyInstance(StudentInvokeHandler.class.getClassLoader(), new Class[]{People.class}, studentInvokeHandler); studentProxy.eat(); }
運行以後,能夠在項目根目錄中找到com/sun/proxy/$Proxy0.class
文件,這個文件即是代理Student
生成的對象的.class
文件:
public final class $Proxy0 extends Proxy implements People { private static Method m1; private static Method m3; private static Method m2; private static Method m0; public $Proxy0(InvocationHandler var1) throws { super(var1); } public final boolean equals(Object var1) throws { try { return (Boolean)super.h.invoke(this, m1, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final void eat() throws { try { super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final String toString() throws { try { return (String)super.h.invoke(this, m2, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final int hashCode() throws { try { return (Integer)super.h.invoke(this, m0, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m3 = Class.forName("com.dengchengchao.springtest.proxy.People").getMethod("eat"); m2 = Class.forName("java.lang.Object").getMethod("toString"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }
經過以上文件咱們能夠發現:
生成的代理類繼承了Proxy
,實現了People
接口
這也就是爲何JDK
代理只能代理接口,不能代理具體的類,由於Java
不能多繼承,所以只能實現接口
因爲實現的是接口,所以對於生成的代理對象proxy
proxy instanceof People //true proxy instanceof Student //false
這即是開始咱們所遇到的問題的根源所在,proxy
僅僅是實現了People
接口,卻不是繼承自Student
類,所以沒法將proxy
對象轉換爲Student
類型,因此才報的錯。
明白了這個問題,之後使用底層爲JDK
代理的類,就不會再出錯了。
若是以爲寫得不錯,歡迎掃描下面二維碼關注微信公衆號:逸遊Java ,天天不定時發佈一些有關Java進階的文章,感謝關注