今天在写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进阶的文章,感谢关注