使用多种方式实现Ioc
使用构造注入
构造注入是一种高内聚的体现,特别是针对有些属性需要在对象在创建时候赋值,且后续不允许修改(不提供setter方法)
实例:
Greeting类:
配置applicationContext.xml文件:
测试:
结果:
注意点:
一个<constructor-arg>元素表示构造方法的一个参数,且使用时不区分顺序。当构造方法的参数出现混淆,无法区分时,可以通过<constructor-arg>元素的index属性指定该参数的位置索引,位置从0开始。<constructor-arg>元素还提供了type属性用来指定参数的类型,避免字符串和基本数据类型的混淆。
构造注入的时效性好,在对象实例化就得到所依赖的对象,便于在对象的初始化方法中使用依赖对象;但受限于方法重载的形式,使用灵活性不足。设值注入使用灵活,但时效性不足,并且大量的setter访问器增加了类的复杂性。Spring并不倾向于某种注入方式,用户应该根据实际情况进行合理的选择。
使用p命名空间注入
p命名空间的特点是使用属性而不是子元素的形式配置Bean的属性,从而简化了Bean的配置,使用传统的<property>子元素配置的代码
实例:
Greeting类:
xml文件:
测试:
结果:
注意:
需要注意的是,p名称空间没有标准的XML格式定义灵活,比如说,bean的属性名是以Ref结尾的,那么采用p名称空间定义就会导致冲突,而采用标准的XML格式定义则不会出现这种问题。这里我们提醒大家在项目中还是仔细权衡来决定到底采用那种方式,同时也可以在团队成员都理解不同的定义方式的基础上,在项目中根据需要同时选择三种定义方式。
P命名空间注入的方式也是使用setter方式注入,所有POJO类中必须存在对应的set方法
注入不同的数据类型
实例
编写Collection类:
编写xml文件:
测试:
结果:
代理
静态代理
静态代理需要代理对象和目标对象实现一样的接口;
优点:
可以在不修改目标对象的前提下扩展目标对象的功能;
缺点:
1. 冗余,由于代理对象要实现与目标对象一致的接口,会产生过多的代理类;
2. 不易维护,一旦接口增加的方法,目标对象与代理都要进行修改;
实例:
DoSomeService接口(抽象主题):
DoSomeServiceImpl实现类(真实主题):
Subject代理类:
测试:
结果:
JDK动态代理
JDK动态代理所用到的代理类在程序调用到代理类对象时才由JVM真正创建,JVM根据传进来的 业务实现类对象 以及 方法名 ,动态地创建了一个代理类的class文件并被字节码引擎执行,然后通过该代理类对象进行方法调用;
实例:
DoSomeService接口:
DoSomeServiceImpl实现类:
DoSomeHandler代理类:
测试:
结果:
结果:
1、因为利用JDKProxy生成的代理类实现了接口,所以目标类中所有的方法在代理类中都有。
2、生成的代理类的所有的方法都拦截了目标类的所有的方法。而拦截器中invoke方法的内容正好就是代理类的各个方法的组成体。
3、利用JDKProxy方式必须有接口的存在。
4、invoke方法中的三个参数可以访问目标类的被调用方法的API、被调用方法的参数、被调用方法的返回类型。
CGLIB动态代理
cglib是针对类来实现代理的,原理是对指定的业务类生成一个子类,并覆盖其中业务方法实现代理。因为采用的是继承,所以不能对final修饰的类进行代理。
实例:
DoSomeServiceImpl实现类:
不需要编写接口
CGLIBHandler代理类:
测试:
结果:
总结:
1、 CGlib是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。
2、 用CGlib生成代理类是目标类的子类。
3、 用CGlib生成 代理类不需要接口
4、 用CGLib生成的代理类重写了父类的各个方法。
5、 拦截器中的intercept方法内容正好就是代理类中的方法体