让我们考虑以下bean:
@Service
@Scope(value = "prototype", proxyMode = ScopedProxyMode.INTERFACES)
public class MyBeanB implements MyBeanBInterface {
private static final AtomicLong COUNTER = new AtomicLong(0);
private Long index;
public MyBeanB() {
index = COUNTER.getAndIncrement();
System.out.println("constructor invocation:" + index);
}
@Transactional
@Override
public long getCounter() {
return index;
}
}
并考虑两种不同的用法:
@Service
public class MyBeanA {
@Autowired
private MyBeanB myBeanB;
....
}
***************************
APPLICATION FAILED TO START
***************************
Description:
The bean 'myBeanB' could not be injected as a 'my.pack.MyBeanB' because it is a JDK dynamic proxy that implements:
my.pack.MyBeanBInterface
Action:
Consider injecting the bean as one of its interfaces or forcing the use of CGLib-based proxies by setting proxyTargetClass=true on @EnableAsync and/or @EnableCaching.
我希望看到它,因为我要求spring为beanmybeanb
创建JDK动态代理,而该代理不是mybeanb的子类型。我们可以这样轻松修复:
@Service
public class MyBeanA {
@Autowired
private MyBeanBInterface myBeanB;
....
}
MyBeanB beanB = context.getBean(MyBeanB.class);
System.out.println(beanB.getCounter());
对我来说,令人惊讶的是,它可以在排除任何运行时异常的情况下工作,但我希望在这种情况下看到nosuchBeanDefinitionException
,因为int case 1应用程序无法启动
感谢来自comments的guy--我检查了BeanB
的类,它是My.Pack.MyBeanB$$EnhancerBySpringCglib$$B1346261
,所以Spring使用CGLIB创建代理,但它与bean定义相矛盾(@scope(value=“prototype”,proxyMode=scopedProxyMode.interfaces
)并且看起来像一个bug。)
你能解释一下为什么它适用于情况2而不适用情况1吗?
正如我在对另一个问题的评论中向您解释的,Spring AOP可以根据情况同时使用CGLIB和JDK代理。默认值是实现接口的类的JDK代理,但您也可以对它们强制使用CGLIB。对于不实现接口的类,只保留CGLIB,因为JDK代理只能基于接口创建动态代理。
因此,看看您的案例1,您明确地说您想要接口代理,即JDK代理:
@Scope(value = "prototype", proxyMode = ScopedProxyMode.INTERFACES)
但是mybeana
不实现任何接口。因此,您将得到在本例中看到的错误消息。
这里没什么惊喜。
如果您想避免出现情况1中的错误消息,也许您应该使用scopedproxymode.target_class
。
更新:对不起,我被你们类似的、不伦不类名myBeana
和myBeanb
激怒了。下次使用更具描述性的、类似于干净代码的类名是有意义的,理想情况下是描述场景中类的角色的类名,如MyService
、MyInterface
、MyScopedBean
。
不管怎样,我又读了你的问题和错误信息。错误消息表示,根据您的注释,正在生成一个基于接口的代理,但您正在尝试将其注入到类类型中。您可以通过如下声明来修复此问题:
@Autowired
private MyBeanBInterface myBeanB;
在case/usage 2中,您再次显式声明了一个类,而不是bean的接口类型。因此,正如我所说的,Spring试图通过唯一可能的方式来满足您的需求,即为类创建一个CGLIB代理。您可以通过声明一个接口类型来修复这个问题,您将得到预期的JDK代理:
MyBeanBInterface myBeanBInterface = appContext.getBean(MyBeanBInterface.class);
System.out.println(myBeanBInterface.getCounter());
System.out.println(myBeanBInterface.getClass());
更新2:根据您的评论,我认为您仍然不理解的是OOP的基本事实:如果您有
基
和类子扩展基
或base
和类sub实现base
您可以声明Base=new Sub()
,但当然不能声明Sub=new Base()
,因为Sub
也是Base
,但并非每个Base
都是Sub
。例如,如果您也有othersubextendsbase
,当尝试将base
对象赋给sub
变量时,它可能是othersub
实例。这就是为什么dot甚至不使用Sub s=(Sub)MyBaseObject
进行编译的原因。
到目前为止还不错。现在再看一遍您的代码:
在用法1中,您有@autowired private MyBeanB MyBeanB;
但配置了MyBeanB
来生成JDK代理,即将创建一个新的代理类,父类proxy
直接实现mybeanbinterface
。即。您有两个不同的类,它们都直接实现相同的接口。由于我上面解释的原因,这些类互不兼容。关于接口,我们有类层次结构
MyBeanBInterface
MyBeanB
MyBeanB_JDKProxy
因此,您不能将mybeanb_jdkproxy
注入mybeanb
字段,因为代理对象不是mybeanb
的实例。你还不明白吗?问题坐在电脑前,没有神秘的春虫。您将其配置为失败。
这就是为什么我告诉您将代码更改为@autowired private MyBeanBInterface mybeanb;
的原因,因为这样当然可以工作,因为代理实现了接口,一切都很好。我还告诉您,如果您使用proxymode=scopedproxymode.target_class
进行作用域声明,您也可以保留@autowired private MyBeanB;
。
在用法2中,问题是相同的:您说的是getBean(ClassB.Class)
,也就是说,您显式地指示Spring为该类创建一个代理。但是对于一个类,您不能创建一个JDK代理,只能创建一个CGLIB代理,这正是Spring所做的。同样,我给出了解决方案,指导您改用getBean(MyBeanBInterface.class)
。然后您将获得所需的JDK代理。
Spring对两者都足够聪明
问题内容: JDK Proxy类仅在工厂方法newProxyInstance()中接受接口。 是否有可用的解决方法或替代实施?如果我必须将方法提取到接口以使其能够与代理一起使用,则用例是有限的。我想包装它们以在运行时应用基于注释的动作。 问题答案: 您可以像这样使用cglib: 例如,这使您可以使用默认的实现方法来构建抽象类。但是您可以将增强器更改为所需的增强器。
问题内容: 使用动态代理的用例是什么? 它们与字节码生成和反射有何关系? 有什么推荐的读物吗? 问题答案: 我强烈推荐此资源。 首先,您必须了解什么是代理模式用例。请记住,代理的主要目的是控制对目标对象的访问,而不是增强目标对象的功能。访问控制包括同步,身份验证,远程访问(RPC),惰性实例化(休眠,Mybatis),AOP(事务)。 与静态代理相反,动态代理生成在运行时需要Java反射的字节码。
当我运行(Windows 7命令行)时: C:\rest-app\src\main\java\com\mycompany\app\test>java org.testng.testng testng.xml Suite1运行的测试总数:0,失败:0,跳过:0 ================================================== 此时我的testng.xml文件如下所
问题内容: 如果是代理设计模式,那么JDK的动态代理和第三方动态代码生成API(例如CGLib)有什么区别? 使用这两种方法之间的区别是什么?何时应该优先选择另一种方法? 问题答案: JDK动态代理只能按接口进行代理(因此,您的目标类需要实现一个接口,然后该接口也可以由代理类实现)。 CGLIB(和javassist)可以通过子类化创建代理。在这种情况下,代理将成为目标类的子类。无需接口。 因此,
在代理设计模式的情况下,jdk的动态代理和第三方的动态代码生成API(如cglib)有什么区别? 使用这两种方法之间有什么区别?什么时候应该选择一种方法而不是另一种方法?
我知道在Java中,静态方法和实例方法一样是继承的,不同的是,当它们被重新声明时,父实现是隐藏的,而不是重写的。好吧,这有道理。但是,Java教程指出 接口中的静态方法从不继承。 然而,