Spring_AOP1
代理模式(Proxy)
Spring框架的核心是IOC和AOP。
IOC(Inversion Of Control)
很好理解,就是控制反转的意思,通俗的讲就是将控制权反转,打个比方,以前你是我的老板,你指挥(控制)我,现在我奋斗成了你的老板了,该由我来指挥(控制)你了,指挥(控制)权的倒转了。比如说以前我们没有应用Spring容器或其他的IOC容器,那么我们要手动在应用程序中指定好组件的具体实现类的对象,如:
private UserDao userDao =new UserJdbcDaoImpl();//程序自己指定
不然将会出现空指针异常,这个控制权是有应用程序自己控制的,而非外部控制,那么这就造成了耦合度比较高了,且灵活性不好,扩展性也不强,那么用了IOC容器,像Spring,就将这个控制权交给了Spring容器,在应用程序中不用指定组件的具体对象,而让Spring容器指定,如:
private UserDao userDao;//程序不用自己指定,只需定义一个接口
<bean id="userJdbcDao"class="com.spring.dao.impl.UserJdbcDaoImpl"/> <bean id="userBiz"class="com.spring.biz.impl.UserBizImpl"> <!—Spring容器负责注入一个实现类 --> <property name="userDao"ref="userHibernateDao"/> </bean>
Spring通过配置文件动态指定后进行装配(注入机制),有Spring来维护这些组件的生命周期,应用程序内部只需依赖抽象接口,降低了耦合性,这样就提高了灵活性和扩展性等,总之面向对象要求的扩展性,灵活性,复用性和可维护性都有改善,所以Spring单纯从降低程序之间的耦合性来讲也是很优秀的,当然Spring的强大功能远非这些,有待深入学习。关于IOC的一些运用,前面两篇文章都有入门的讲解。
AOP(AspectOrientedProgramming):
面向切面编程,这个可是一个好东西。比较一下OOP,通常OOP关注的是垂直的编程,即程序按照一定的流程从上往下走,多线程不过是多了几条往下走得流程,这种程序对垂直切切面的关注度很高,而对横向切面的关注度很难实现。OOP能很好的处理业务流程,却不能将系统 一些替丁的重复性的行为封装在一个模块中,比如说在很多业务中要记录日志,结果我们不得不在诸多业务流程种嵌入大量的日志记录代码,这种大量重复性的代码造成了系统难以维护等诸多不好的影响,那么AOP弥补了这一缺陷,它能够将这些重复性的代码封装在一个模块中,并转化成组件,动态加入到需要记录日志的业务流程中,好比在众多的业务流程中,横向切了一刀,在切开的口子塞入记录日志的代码,而不影响原来的业务流程,当然除了记录日志,还有比如权限设置,还有比如在操作数据库的前后开启事务和提交事务等等,用这种切面的思想,大大提高了程序的扩展性和灵活性。
啰嗦了一堆,其实结合生活,在大学宿舍中,很多同学在打牌,但学校又不允许打牌,定期检查,抓到的要处罚,所以爱玩牌的人不愿意被抓,又想玩,这是那帮家伙想了一个主意,雇一个小弟放哨,一旦老师来检查(多方向同时启动检查),就及时通知,那么在没有手机的时代,有好几个宿舍在不同的楼里,那么这个小弟就得一个一个楼的跑,这样显然来不及了,后来那帮家伙就每一栋楼雇一个小弟,这样就可以满足了,当然这样小弟多了,不好管理,也不好调度,而且佣金很高。那么这是手机这个东西被发明了,就一个小弟可以满足了,一旦检查开始,一条短信就可以搞定,而且需要通知的楼的数量可以任意的加了,无非就是多发几条短信。当然这要保证两方的通信要畅通的。
举了个不太恰当的例子,回到Spring中来,在说Spring的AOP前,先说一个设计模式中的代理模式,为什么要将这个,因为Spring的AOP就是通过代理实现的。这里的设计模式代码用Java实现的:
Proxy:
就以刚才那个打牌为例子,这里要做一些改变,显然不是每一间宿舍都有小弟通知的,开始那帮家伙在一件没有小弟的宿舍打牌,被抓几次后,有个同学建议换一件宿舍,这件宿舍有小弟通知,就是有一个小弟定向定点服务,相当于后面的宿舍是前面宿舍的一个代理,在这个代理中,多了一个小弟的放哨通知。
对上面一段话首先做面向对象分析:
1.提取对象和分析职责:
a.<Informer>显然要有通知者,即小弟,当然通知者还可以是别的,比如说来,
这样就一定会被抓了,呵呵,所以通知者是一个抽象类或接口,提供一个通知的方法。
b.<Room>还要有一个房间提供打牌的场所,这个房间也设计成抽象类多接口,
有一个提供条件的行为,在这里为了说明代理模式,就不增加打牌的人了,
只是在这个方法中加入一段代码,就是很多大学生在打牌。
2.分析类之间的交互。
这里很简单了,在代理中要加一个通知者,用了组合,在代理的方法中加入通知者通知 的方法,
被代理目标就是一间没有小弟的宿舍,当然需要代理和被代理目标同时实现
Room接口。
3.开始UML建模了,简单生成了一个关系图:
解释一下:虚线空心箭头表示实现接口,实现空心箭头表示继承,实现箭头表示关联关系,
很多书写的不一致这里表示ProxyDormitory中组合了Dormitory为了简化问题,
省略了name的getter方法,用构造方法设置,至于通知者对于代理来说不是必须的,
可以用一行代码代替。
具体代码:
IRoom接口:
public interface IRoom { /** * 提供一个场所 */ public void providePlace(); }
Dormitory类:
public class Dormitory implements IRoom { private String name; public Dormitory(Stringname) { this.name = name; } @Override public void providePlace() { //用一句话代表了 System.out.println(this.name +"宿舍中几个大学生打牌火热进行中..."); } }
显然上面的家伙打牌很危险,就换弄了个代理,加了个小弟。
通知者抽象类:
public abstract classInformer { public abstract void inform(); }
XiaoDi类,具体通知者:
public class XiaoDiextends Informer { @Override public void inform() { System.out.println("大家伙注意了,老师随时会检查,但请放心,只要我在,确保你们安全..."); } }
代理类:
public classProxyDormitory implements IRoom { private Dormitorydormitory; private IInformerinformer; public ProxyDormitory(String name) { dormitory =new Dormitory(name); informer =new XiaoDi(); } @Override public void providePlace() { //在代理中加入了小弟放哨通知的方法 informer.inform(); dormitory.providePlace(); } }
客户端调用:
public class Test { public static void main(String[]args) { IRoom iRoom = newProxyDormitory("319宿舍"); //此时打牌是安全的... iRoom.providePlace(); } }
输出:
大家伙注意了,老师随时会检查,但请放心,只要我在,确保你们安全...
319宿舍宿舍中几个大学生打牌火热进行中...
代理模式总结:
代理模式是对一个对象提供一种代理以控制对这个对象的访问。
代理模式中的代理和被代理目标实现相同的接口,在代理中加入了一些额外的业务逻辑,还有一种是生成被代理目标的子类,这种不推荐,因为继承有时候带来的问题比组合要大,能用组合的就尽量不用继承。
代理模式在实际运用:
1. 远程代理:即为一个对象在不同的地址空间提供局部的代表,这样可以隐藏一个对象存在于不同地址空间的事实,如在Tomcate中加入一个web应用,引用一个Web应用,此生在项目中会生成一个代理的文件夹。
2. 虚拟代理:根据需要创建开销很到的对象,通过它来存放实例化需要很长时间的真是对象,如在加载网页是,我们能很快看到所有的文字,但是图片却是一张一张的下载的,那些未打开的图片框,就是通过虚拟代理来代替了真是的图片,代理存储了真实图片的路径和尺寸。
3. 安全代理:用来控制对真是对象的访问时的权限。
4. 智能指引:当调用真是对象时,代理处理了另外一些事,上面的例子。就属于这种
代理模式的应用很广,在Spring中就是一个典型的例子,但Spring使用的是动态代理,就更灵活了,后面继续学习。