`
jinnianshilongnian
  • 浏览: 21439894 次
  • 性别: Icon_minigender_1
博客专栏
5c8dac6a-21dc-3466-8abb-057664ab39c7
跟我学spring3
浏览量:2406127
D659df3e-4ad7-3b12-8b9a-1e94abd75ac3
Spring杂谈
浏览量:2998617
43989fe4-8b6b-3109-aaec-379d27dd4090
跟开涛学SpringMVC...
浏览量:5632141
1df97887-a9e1-3328-b6da-091f51f886a1
Servlet3.1规范翻...
浏览量:257785
4f347843-a078-36c1-977f-797c7fc123fc
springmvc杂谈
浏览量:1593493
22722232-95c1-34f2-b8e1-d059493d3d98
hibernate杂谈
浏览量:249079
45b32b6f-7468-3077-be40-00a5853c9a48
跟我学Shiro
浏览量:5848387
Group-logo
跟我学Nginx+Lua开...
浏览量:698421
5041f67a-12b2-30ba-814d-b55f466529d5
亿级流量网站架构核心技术
浏览量:780863
社区版块
存档分类
最新评论

从一而终只使用一种Spring编程风格

 
阅读更多

代码

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,基本能搞定,当然读一读源码对日常开发的调错还是很有帮助的。

 

27
6
分享到:
评论
23 楼 yunzhu 2013-07-15  
alvin198761 写道
写个helloword都要用spring了,岂不是又要几十kb?这个时代真神奇,框架组件离我越来越远了,什么都是自己实现,看看用框架的,真心伤不起

这个时代真是神奇,喷子越来越多了,也越来越浮躁了,不看看具体情况动不动就开喷,真心伤不起
22 楼 kyfxbl 2013-07-15  
alvin198761 写道
写个helloword都要用spring了,岂不是又要几十kb?这个时代真神奇,框架组件离我越来越远了,什么都是自己实现,看看用框架的,真心伤不起


作者本文是举例子,举例子不用hello world举,难道用个产品来举?

用框架的没什么伤不起的,我看你倒是有点伤不起
21 楼 alvin198761 2013-07-15  
写个helloword都要用spring了,岂不是又要几十kb?这个时代真神奇,框架组件离我越来越远了,什么都是自己实现,看看用框架的,真心伤不起
20 楼 jinnianshilongnian 2013-07-15  
kjmmlzq19851226 写道
要在一个项目里体现一种统一的编程风格,说起来是很简单滴,但是实际上我却从来没有遇到过

是的,但是注意总比不注意好 ,尤其AOP这块。
19 楼 jinnianshilongnian 2013-07-15  
white_crucifix 写道
有个更进一步的问题想请教下大家,就是我们的这个接口,即便在“给别人调用”的目的上,是不是也没有必要?

因为别人调用的时候,好像依然无法规避掉与实现类相关的代码,这样是不是不用接口效果也是一样?
求各位大神解惑啊~


1、比如 A需要B,那么A最终要调用B,即A和B之间有耦合,如果B是接口,那么耦合最低,也更容易更换

2、静态代码关系,如

A {
    Api api;
}

A依赖于接口Api,即想要的是我要个Api,给我即可,不管你咋实现的,即面向抽象编程(我也叫他面向使用编程);我需要这个Api做***事情,但是怎么做,随便;

3、如果把Api的实现给A呢? IoC/DI容器就是这个目的。

即使用时是面向接口的,,,,而实际实现通过容器注入的。
18 楼 kjmmlzq19851226 2013-07-15  
white_crucifix 写道
有个更进一步的问题想请教下大家,就是我们的这个接口,即便在“给别人调用”的目的上,是不是也没有必要?

因为别人调用的时候,好像依然无法规避掉与实现类相关的代码,这样是不是不用接口效果也是一样?
求各位大神解惑啊~

别人调用应该不会关心你是怎么去实现的吧,使用者只关心接口中的方法能做什么事情
17 楼 white_crucifix 2013-07-15  
有个更进一步的问题想请教下大家,就是我们的这个接口,即便在“给别人调用”的目的上,是不是也没有必要?

因为别人调用的时候,好像依然无法规避掉与实现类相关的代码,这样是不是不用接口效果也是一样?
求各位大神解惑啊~
16 楼 amoszhou 2013-07-15  
其实那天和我讨论接口与实现的事的时候,我就知道,如果这事发博客说一下,肯定会引来很多反对意见。
 
   大部分人不清楚接口是什么,为什么要用接口。 只是一味的为了接口而接口,或者说原因是:以前就这么做。
15 楼 kjmmlzq19851226 2013-07-15  
要在一个项目里体现一种统一的编程风格,说起来是很简单滴,但是实际上我却从来没有遇到过
14 楼 jinnianshilongnian 2013-07-15  
Cindy_Lee 写道
Dead_knight 写道

•给自己使用的无需定义接口;即一个模块内部的都是封装的,定义接口并不会得到很多好处,变过几次实现?? “优先面向接口编程,而非实现” 不是必须,是优先;

•给朋友(第三方)使用的定义接口;即要公开的功能,因为接口就是个契约,就是沟通用的;


这两点非常赞成。太多人不清楚接口的好处,在项目中,不管是service、dao层,都搞一个接口,实际上,开发者自己都不知道为啥要这样,仅仅是看别人也这样做。


如果你设计过较大的软件构架就不会说给自己用不需要接口了,如果是j2ee,那我没什么好说的,本身就比较简单,扩展性也有限,面向业务较多,接口的确用处不是特别大。

我限定了一个模块内的 (给自己使用的);

如果是多团队开发,肯定是先定义接口,再实现,对外只暴露接口(给朋友的)。

大部分企业应用定义接口意义不大,反正这是我的体会; 但是如果是提高给其他同事/团队使用,必须定义接口。

引用
如果你设计过较大的软件构架就不会说给自己用不需要接口了
哈哈,那也是为了扩展,是预留给别人扩展(当然也包括自己);即我说的(给朋友用的),理解方式不一样
13 楼 jinnianshilongnian 2013-07-15  
freezingsky 写道
没看出来具体想表达的内容,不知道是想说自己的编码风格,还是想批评什么,或者是建议什么。

还有如注入时: 有些人使用构造器注入,但大多数时候没这个必要(而且可能存在很多问题,比如name、index等), 所以建议首选setter注入。
12 楼 jinnianshilongnian 2013-07-15  
freezingsky 写道
没看出来具体想表达的内容,不知道是想说自己的编码风格,还是想批评什么,或者是建议什么。

倒不如直接使用CGLIB代理,不会有任何问题。但缺点也是有的:final类和方法不能代理。。
11 楼 jinnianshilongnian 2013-07-15  
freezingsky 写道
没看出来具体想表达的内容,不知道是想说自己的编码风格,还是想批评什么,或者是建议什么。

其实这里我最想表达的一个问题是:
有些人混用JDK动态代理 和 CGLIB代理,造成有的是JDK动态代理、有的是CGLIB代理,可能造成未知错误,尤其初学者 排查很困难
10 楼 jinnianshilongnian 2013-07-15  
xiaoxing598 写道
lucky16 写道
“我喜欢直接定义实现,如UserController--->UserService(实现),因为这是一个模块内部的操作,干嘛定义接口呢?可扩展?想想自己扩展过吗?”
哈哈,开涛ge,我现在也是差不多这样子弄的,现在的service层基本上可以不用写接口的,至于DAO,有时候还是有一点必要的。


在service层不写接口,其它模块使用的时候,直接注入这个service实现是吧!


这个根据需求去定,如果是API级别的,如系统消息、通知等尽量定义接口
9 楼 jinnianshilongnian 2013-07-15  
freezingsky 写道
没看出来具体想表达的内容,不知道是想说自己的编码风格,还是想批评什么,或者是建议什么。

1、先找了一个例子,说明混合注入接口和注入实现可能遇到的问题;
2、再总结:在使用spring时,统一一种编程风格:1、不要混合如注入接口和注入实现;2、aop的风格尽量采用一种:要么xml、要么@AspectJ,尽量不要混用,不要使用低层次API;
8 楼 freezingsky 2013-07-15  
没看出来具体想表达的内容,不知道是想说自己的编码风格,还是想批评什么,或者是建议什么。
7 楼 myangle89 2013-07-15  
xiaoxing598 写道
lucky16 写道
“我喜欢直接定义实现,如UserController--->UserService(实现),因为这是一个模块内部的操作,干嘛定义接口呢?可扩展?想想自己扩展过吗?”
哈哈,开涛ge,我现在也是差不多这样子弄的,现在的service层基本上可以不用写接口的,至于DAO,有时候还是有一点必要的。


在service层不写接口,其它模块使用的时候,直接注入这个service实现是吧!








的确,写接口是很多余!不过这只是小系统可以这么弄,稍微大点的系统必须有接口。另外,最好小系统就写接口,如果一个项目成功了,小项目有一天也会变成大项目!
6 楼 xiaoxing598 2013-07-15  
lucky16 写道
“我喜欢直接定义实现,如UserController--->UserService(实现),因为这是一个模块内部的操作,干嘛定义接口呢?可扩展?想想自己扩展过吗?”
哈哈,开涛ge,我现在也是差不多这样子弄的,现在的service层基本上可以不用写接口的,至于DAO,有时候还是有一点必要的。


在service层不写接口,其它模块使用的时候,直接注入这个service实现是吧!
5 楼 Dead_knight 2013-07-15  
Cindy_Lee 写道
Dead_knight 写道

•给自己使用的无需定义接口;即一个模块内部的都是封装的,定义接口并不会得到很多好处,变过几次实现?? “优先面向接口编程,而非实现” 不是必须,是优先;

•给朋友(第三方)使用的定义接口;即要公开的功能,因为接口就是个契约,就是沟通用的;


这两点非常赞成。太多人不清楚接口的好处,在项目中,不管是service、dao层,都搞一个接口,实际上,开发者自己都不知道为啥要这样,仅仅是看别人也这样做。


如果你设计过较大的软件构架就不会说给自己用不需要接口了,如果是j2ee,那我没什么好说的,本身就比较简单,扩展性也有限,面向业务较多,接口的确用处不是特别大。

嗯,我说的是刚接触j2ee的同学和之前用ssh框架的同学。如果涉及到模块化的协作式开发(经典的就像osgi那样),或者是开发框架(如工作流引擎、rbac安全方面)当然要考虑扩展性……
4 楼 lucky16 2013-07-15  
“我喜欢直接定义实现,如UserController--->UserService(实现),因为这是一个模块内部的操作,干嘛定义接口呢?可扩展?想想自己扩展过吗?”
哈哈,开涛ge,我现在也是差不多这样子弄的,现在的service层基本上可以不用写接口的,至于DAO,有时候还是有一点必要的。

相关推荐

    PyTorch的深度学习入门之PyTorch安装和配置

    深度神经网络是一种目前被广泛使用的工具,可以用于图像识别、分类,物体检测,机器翻译等等。深度学习(DeepLearning)是一种学习神经网络各种参数的方法。因此,我们将要介绍的深度学习,指的是构建神经网络结构,...

    PostMan接口调试工具.rar

    postman是一款功能强大的网页调试和模拟发送HTTP请求的Chrome插件,支持几乎所有类型的HTTP请求,操作简单且方便。

    Sql Server 2005中的架构用户登录和角色

    架构就是数据库对象的容器。数据库对象是饮料,架构就是杯子,谁拿杯子喝水呢?当然是用户,那么是不是一个用户只能用一个杯子,一个杯子是不是从一而终,只能给一个人用呢?

    C++中引用与指针的区别(详细介绍)

    指向不同类型的指针的区别在于指针类型可以知道编译器解释某个特定地址(指针指向的地址)中的内存内容及大小,而void*指针则只表示一个内存地址,编译器不能通过该指针所指向对象的类型和大小,因此想要通过void*...

    当前执行力建设中存在的问题.doc

    把简单重复上级文件和讲话精神看作是贯彻执行,只讲执行的形式,不讲执行的结果,根本达不到贯彻落实的目的。或者脱离本地、本单位的实际情况,教条式的执行。不带头做表率,不深入工作调查研究,做事草率马虎,热...

    c++中引用和指针的区别和联系

    引用“从一而终” 4. 引用没有 const,指针有 const,const 的指针不可变;5. 引用不能为空,指针可以为空;6. “sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身(所指向的变量...

    中美婚姻伦理差异研究

    中美两国不同的经济发展水平和文化传统使两国的婚姻伦理存在差异。...中美两国的离婚率和离婚原因反映出当婚姻出现问题时,中国家庭会本着"从一而终"的观念尽力挽回,而美国家庭则是用"顺其自然"的态度面对。

    北x咖啡屋创业计划书.doc

    我们的服务要求做到无微不至,任何情况下,都要诚恳地对待每一位顾客,服务周到细致,从一而终。 4.竞争对手和自身优势的维持 竞争对手的优势:成立的时间早,经验丰富;规模大;销售渠道广泛;有一大批老顾客。 ...

    最简单的深度学习算法——感知器的前世今生

    其实柳猫想要告诉大家,作为一个普通人,对各种信息越是了解的多,认识的越是浅薄,为了增强自己的不可替代性,必须增加自己专业的深度,从一而终。今天,想跟大家分享一下最早也是最简单的一个机器学习模型:感知器...

    qdan:个人网站

    作者简介:认真且鼓励,从一而终。 技术栈 JavaScript , Vue 2.6 , Bootstrap 4.6 , nodejs 15.x , elementUi 插件 轴距 网络请求 dayjs 日期格式化 ass 样式前置器 加密 散列加密 表示 http服务器框架 ...

    qt-vsaddin-msvc2017-2.6.0-rev.07.vsix.7z

    VS2017 导入QT开发

Global site tag (gtag.js) - Google Analytics