代码
1、组件
现在很常见的就是不管如何先定义接口,如下所示:
package com.sishuok; public interface Interface { public void sayHello(); }
然后定义实现,真的有必要吗?思考下。
package com.sishuok; public class Impl implements Interface { @Override public void sayHello() { System.out.println("hello"); } }
Bean用于注入Impl,其实此处是错误的,因为定义了接口,应该注入接口的。
package com.sishuok; public class Bean { private Impl impl; public Bean() { } public Bean(final Impl impl) { this.impl = impl; } }
2、需要一个切面
怎么实现无所谓,就是为了生成代理对象,重现问题。
package com.sishuok; public class Aspect { public void before() { System.out.println("==before"); } }
3、 配置文件
<bean id="aspect" class="com.sishuok.Aspect"/> <aop:config> <aop:aspect ref="aspect"> <aop:before method="before" pointcut="execution(* com.sishuok.Impl.*(..))"/> </aop:aspect> </aop:config> <bean id="impl" class="com.sishuok.Impl"/> <bean id="b" class="com.sishuok.Bean"> <constructor-arg name="impl" ref="impl"/> </bean>
配置文件很简单。 一个AOP切面会横切Impl,即生成Impl代理,而Bean会注入Impl。
4、测试类
测试类很简单,只需要加载配置文件即可。
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations={"classpath:spring-config.xml"}) public class BeanIT { @Autowired private Bean bean; @Test public void test() { System.out.println("=hello test"); } }
整段代码很简单,应该能猜到是嘛问题。
抛出的异常
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'b' defined in class path resource [spring-config.xml]: Could not resolve matching constructor (hint: specify index/type/name arguments for simple parameters to avoid type ambiguities)
这个异常让人疑惑,因为它告诉你说可能是构造器的问题,所以可能把我们带坑里看不到真实问题。
主要原因是:
我们实际定义了两个构造器:
1、一个空参的:public Bean()
2、一个带Impl参数的:public Bean(final Impl impl)
如果把第一个构造器删除就会得到真实的异常:
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'b' defined in class path resource [spring-config.xml]: Unsatisfied dependency expressed through constructor argument with index 0 of type [com.sishuok.Impl]: Could not convert constructor argument value of type [$Proxy8] to required type [com.sishuok.Impl]: Failed to convert value of type '$Proxy8 implementing com.sishuok.Interface,org.springframework.aop.SpringProxy,org.springframework.aop.framework.Advised' to required type 'com.sishuok.Impl'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [$Proxy8 implementing com.sishuok.Interface,org.springframework.aop.SpringProxy,org.springframework.aop.framework.Advised] to required type [com.sishuok.Impl]: no matching editors or conversion strategy found
很明显是注入问题,见到$Proxy8 我们就能猜到是JDK动态代理(这个在《spring的二次代理原因及如何排查》说过),即它不能转换为实际的com.sishuok.Impl,问题很明显了.
此处还有另一个问题:关于按照构造器参数名注入时,具体参考《【第三章】 DI 之 3.1 DI的配置使用 ——跟我学spring3》中的构造器注入部分。
解决方案
1、要么构造器注入接口,即public Bean(final Interface impl)
2、要么使用CGLIB类代理
总结
1、知道自己使用的是啥编程风格:
- 先定义接口,然后实现;如UserController---->UserService(接口 其实现是UserServiceImpl),这种情况大多数人都使用接口注入即可
- 我喜欢直接定义实现,如UserController--->UserService(实现),因为这是一个模块内部的操作,干嘛定义接口呢?可扩展?想想自己扩展过吗?如果实在想要可扩展我一般是这样:UserApi UserApiImpl 可参考我的es脚手架
2、知道自己使用的啥代理:
- JDK动态代理 还是 CGLIB代理, 做到心中有数,尽量别混用。当然最保险的方式就是使用CGLIB代理。
明确自己的风格,从一而终,不要为了玩玩都用上,Spring已经很庞大且复杂了,,使用Spring出问题最多的就是AOP部分,遇到AOP问题。可参考《请不要再使用低级别的AOP API》
我喜欢
- 给自己使用的无需定义接口;即一个模块内部的都是封装的,定义接口并不会得到很多好处,变过几次实现?? “优先面向接口编程,而非实现” 不是必须,是优先;
- 给朋友(第三方)使用的定义接口;即要公开的功能,因为接口就是个契约,就是沟通用的;
- 优先使用setter注入,除非必要才使用构造器注入;
- 使用CGLIB代理,这样基本不会出现AOP代理注入不了或一些隐晦的问题;
- 优先使用Spring提供的XML标签简化功能定义,如<aop:config>、<task:executor>等,而不要使用低层次API;
- 尽量使用XML风格的事务,而不是注解风格;
- 按照配置的内容分多配置文件存放配置,不要一股脑的放在一起,就像不分包那样;
- 可配置部分(如db数据)还是放到XML中,不要什么都注解;
- 使用Spring profile 或 maven profile分环境测试(如开发环境、测试环境、正式机环境);
官方文档还是要看的,当然刚开始可能比较困难,但是坚持几次以后就轻松了,出问题先看文档,接着翻javadoc,基本能搞定,当然读一读源码对日常开发的调错还是很有帮助的。
相关推荐
中国古代婚姻制度其实是“一夫一妻多妾”制,在中国封建社会,妇女没有社会地位,夫为妻纲,妇女的一切只能服从和依赖其丈夫,从一而终。一个男人一般只有一个正妻,却有多个妾,同时男人娶几个女人都不受法律和道德...
首先,作者描述了自己的近况,表现为一种颓废的状态,连续旷课,沉溺于回忆过去。这可能源于一段未解决的感情问题,使作者陷入了深深的思考。这种行为在青少年时期并不罕见,特别是在面对情感困扰时,人们往往会选择...
另一方面,幽默的回答指出,爱情有时可能是一种冲动,一种两个人共同的追求。它可以是短暂的激情,也可以是长久的陪伴。爱情可能会让人感到兴奋,也可能变得平淡,但它始终是我们生活中不可或缺的一部分,超越了外在...
5. 招聘市场的未来:由于跳槽意愿增加,招聘市场可能会逐渐演变成一个高频次的市场,而不是父母辈那种从一而终的职业生涯。在使用频次、使用人数上呈现增长趋势。所以对招聘市场的未来是一个利好。 6. 招聘App的...
PostMan是一款广受欢迎的API(应用程序编程接口)测试和调试工具,尤其对于Web开发者和测试工程师来说,它是一个不可或缺的工具。它最初是一款Chrome浏览器扩展,但现在已经发展为一个独立的应用程序,支持Windows、...
Reference作为一个变量AA的别名,在它的整个生命周期内,它只能“从一而终”,始终是第一次初始化它的那个变量的别名,在这期间任何对它的操作,都等同于对变量AA的操作。 通过例子我们可以看到,Pointer可以随时...
在C++编程语言中,引用是一种特殊的类型,它不是变量,也不是指针,而是对已存在对象的另一种访问方式。引用一旦被初始化为某个对象,它就永久地绑定到那个对象上,不能改变引用的目标。这种特性使得引用在很多场合...
9. 标点符号运用:标点符号的正确使用是书面表达的规范之一,如逗号、句号、引号等的使用,对于句子结构的划分和语义的理解有直接影响。 综上所述,这些题目涉及了语文学习的基础知识,包括字音、字形、词语搭配、...
指向不同类型的指针的区别在于指针类型可以知道编译器解释某个特定地址(指针指向的地址)中的内存内容及大小,而void*指针则只表示一个内存地址,编译器不能通过该指针所指向对象的类型和大小,因此想要通过void*...
同时,文中涉及到词语的拼音、成语的正确书写及词义的应用,如“视频”、“出糗”、“募捐”等词的注音,“衰竭”的正确写法以及“从一而终”在语境中的不恰当使用。 3. 中国古典文学:《水浒传》是中国四大名著之...
C++ 引用和指针是两种不同的概念。虽然它们都可以用来表示内存地址,但是它们有着不同的特点和应用场景。 1. 相同点:引用和指针都是地址的概念,指针指向一块内存,它的内容是所指内存的地址;引用是某块内存的...
7. **集体无意识**:鲁四老爷代表的“干净”标准,实际上是一种集体无意识的表现,反映出封建社会对女性的刻板印象和不公待遇。祥林嫂被视为“不干净”,是因为她不符合传统道德规范,这种观念体现了旧社会的偏见和...
把简单重复上级文件和讲话精神看作是贯彻执行,只讲执行的形式,不讲执行的结果,根本达不到贯彻落实的目的。或者脱离本地、本单位的实际情况,教条式的执行。不带头做表率,不深入工作调查研究,做事草率马虎,热...
**贞操观的新解**:一些知识分子提出,贞操应当是一种双方共同承担的责任,而不是单方面强加给女性的道德约束。这种观点是对传统贞节观念的重大挑战。 **反对封建贞操观**:在《新青年》杂志上,围绕“贞操问题”...
虚荣型的大学生由于只追求一时的感情满足和快乐,缺乏明确的爱情基础和目标,极易破裂。 大学生的迷茫成年话题 根据相关调查,大学生中有高达87.9%的人在校期间谈过恋爱,其中沿海地区高达94.6%。但是,大学生恋爱...
在 C++ 编程语言中,指针和引用是两个常用的概念,但是初学者容易混淆它们二者之间的区别。在本文中,我们将从概念、函数参数传递和编译角度来阐述指针和引用的区别。 概念上,指针是一个变量,它存放的是变量的...
在舒婷的另一首诗《神女峰》中,她对神女峰这一传统女性贞洁象征进行了重新解读,提出对女性从一而终的封建观念的质疑。通过对神女峰的现代诠释,舒婷鼓励女性追求真实的感情和个体自由,而非被束缚于古老的道德规范...
C++中的引用和指针是两种重要的编程工具,它们在某些方面有相似之处,但在其他方面又有明显的区别。引用和指针都是地址的概念,都可以用来间接访问内存中的数据。然而,它们在使用和行为上存在本质的不同。 首先,...
另一方面,未来的职场可能将出现两种货币:机器币和人类币。机器币用于购买机器生产的产品和服务,而人类币则用于购买人类独有的资源和创造力。这种划分说明了为什么人类的创造力、服务意识和合作能力变得越来越重要...