那么OOP(面向对象)相对于AOP有些什么缺点呢?面向对象设计最根本的魅力在于它能够将 真实的世界领域中的实体及各自的行为建模为抽象的对象。以面向对象方式设计的系统产生了很多有效的业务对象,比如Person,Account,Order以及Event.面向对象设计的缺点在于,这样的业务对象会因为混合的属性和与对象最初意图不一致的操作而变的混乱!
我们可以举个例子,假设我们现实中的笔记本电脑,现在有一款IBM T60型号笔记本,那么我们如果要设计这样一个笔记本类,当然就需要有 硬件和软件,但IBM T60型笔记本 蓝块的它提供一个服务 三年质保!那么现在如果以OO的方式,就不大好办,这服务是不是要写到笔记本类中去呢,但假设现在有这样一款水货的T60,它没有这样一个服务,那么难不成它就不属于笔记本这样一个类吗?所以说这样的一个服务并不是笔记本类它必有的!这样就引出了AOP。面向切面的技术!
面向切面性,首先什么是切面,我们小时候应该削过铅笔,我们一刀削下去 不就有一个切面吗,我们学习的几何中也有经常要计算一个切面的面积,就是一个面,其实也就相当于初识Spring讲到的网游,我们的角色本来是只有血这样的属性,但它本身并没有加血这样一种行为,我们设计一扇魔法门,角色经过的时候就会加血!那么我们在设计这个战士的时候只设计一个普通的OO就行了,那么门就是一切面,当角色经过的时候就有了该服务!如果不经过,那么我们的角色依然是完整的!这就是面向切面性!
现在我们再看看JAVA代理:Spring AOP的实现是基于JAVA代理模式的。
代理模式在设计模式中的定义是:为其他对象提供一种代理以控制对这个对象的访问。说白了就是在一些情况下客户不想或者不能直接引用一个对象,而代理对象可以在客户和目标对象之间之间起到中介作用,去掉客户不能看到的内容和服务或者增添客户需要的额外服务。
我们还是来做一个例子,建立一个普通的JAVA PROJECT,
我们选择Create separate source and output folders带一个专门存入源码的src文件夹!然后选中项目添加Spring,
添加核心库及AOP,
我们将 配置文件放在src目录下的fengyan.efly包中!
我们先使用普通的OO模式在fengyan.efly包下创建一个普通的Computer.java类
Computer.java
那么我们现在来改用接口做式式:
package fengyan.efly;
public interface Ipc {
public void buy();
}
然后改动Computer类。使其继承该接口,并去掉其sendMouse属性public interface Ipc {
public void buy();
}
package fengyan.efly;
public class Computer implements Ipc{
private String pcName = "IBM";
private int pcPrice = 19000 ;
//购买是否送鼠标
// private boolean sendMouse = false;
public String getPcName() {
return pcName;
}
public void setPcName(String pcName) {
this.pcName = pcName;
}
public int getPcPrice() {
return pcPrice;
}
public void setPcPrice(int pcPrice) {
this.pcPrice = pcPrice;
}
/*public boolean isSendMouse() {
return sendMouse;
}
public void setSendMouse(boolean sendMouse) {
this.sendMouse = sendMouse;
}
*/
public void buy()
{
System.out.println("获取:"+pcName+"电脑一台");
/* if(sendMouse)
{
System.out.println("另外赠送鼠标一只!");
}
*/
}
}
public class Computer implements Ipc{
private String pcName = "IBM";
private int pcPrice = 19000 ;
//购买是否送鼠标
// private boolean sendMouse = false;
public String getPcName() {
return pcName;
}
public void setPcName(String pcName) {
this.pcName = pcName;
}
public int getPcPrice() {
return pcPrice;
}
public void setPcPrice(int pcPrice) {
this.pcPrice = pcPrice;
}
/*public boolean isSendMouse() {
return sendMouse;
}
public void setSendMouse(boolean sendMouse) {
this.sendMouse = sendMouse;
}
*/
public void buy()
{
System.out.println("获取:"+pcName+"电脑一台");
/* if(sendMouse)
{
System.out.println("另外赠送鼠标一只!");
}
*/
}
}
那送鼠标在哪里呢,我们来写一个代理类,让它来 实现,如下:
package fengyan.efly;
/**
* 代理类
* @author fengyan
* date : 2006-12-31 21:12
*
*/
public class Substitute {
private Ipc ipc ;
public Ipc getIpc() {
return ipc;
}
public void setIpc(Ipc ipc) {
this.ipc = ipc;
}
//所有通过该代理购买的用户均可得到小鼠标一只
public void buy()
{
ipc.buy();//调用接口的方法!
System.out.println("送鼠标");
}
}
运行测试代码则通过使用代理来做事情:/**
* 代理类
* @author fengyan
* date : 2006-12-31 21:12
*
*/
public class Substitute {
private Ipc ipc ;
public Ipc getIpc() {
return ipc;
}
public void setIpc(Ipc ipc) {
this.ipc = ipc;
}
//所有通过该代理购买的用户均可得到小鼠标一只
public void buy()
{
ipc.buy();//调用接口的方法!
System.out.println("送鼠标");
}
}
package fengyan.efly;
public class TestMain {
public static void main(String[] args) {
//声明一代理类对象,让代理来做事情
Substitute sb = new Substitute();
sb.setIpc(new Computer());//参数Ipc接口类型
sb.buy();//通过代理买
}
}
运行结果如下:public class TestMain {
public static void main(String[] args) {
//声明一代理类对象,让代理来做事情
Substitute sb = new Substitute();
sb.setIpc(new Computer());//参数Ipc接口类型
sb.buy();//通过代理买
}
}
获取:IBM电脑一台
送鼠标
通过上面的小例子可以看到,原先的Computer类我们没有破坏,但我们 通过代理来做事可以得到比以前更多的东西了!这也是一种AOP的思想,当然这里面的实现我们并没有通过Spring来实现!
那么这样其实有一个问题,就是我们的代理类Substitute.java需要我们自己去书写,如果能有一种动态的代理类岂不更好,否则我们每写珍上类都需要我们手动去写代理类,我们能不能写一个通用的代理来呢,答案是有,JAVA给我们提供了这样一种机制.
JAVA提供了InvocationHandler接口可以实现动态代理功能!
知道上面知识后,我们将 我们的代理类改改,让其实现该接口,
package fengyan.efly;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 代理类
* @author fengyan
* date : 2006-12-31 21:12
*
*/
public class Substitute implements InvocationHandler{
/*
private Ipc ipc ;
public Ipc getIpc() {
return ipc;
}
public void setIpc(Ipc ipc) {
this.ipc = ipc;
}
//所有通过该代理购买的用户均可得到小鼠标一只
public void buy()
{
ipc.buy();//调用接口的方法!
System.out.println("送鼠标");
}
*/
//它并不知道它与哪个类产生关联。所以我们要写一个方法
private Object delegate;//代理对象
public Object bind(Object delegate)
{//需要返回一个代理的对象
this.delegate = delegate;
// 我们用静态的对象 新的代理实例
return Proxy.newProxyInstance(delegate.getClass().getClassLoader(), delegate.getClass().getInterfaces(), this);
//参数1: 代理对象的类
//参数2:接口
//参数3:与谁绑定,因为我们已经继承了这个动态的代理类接口,所以直接this即可
}
//实现InvocationHandler接口的 invoke
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 新的内容。就是我们要实现送鼠标这样一种操作
System.out.println("送鼠标");
Object result = method.invoke(delegate, args);
return result;
}
/*在代理实例上处理方法调用并返回结果,在与方法关联的代理实例上调用方法时,将在
* 调用处理程序上调用此方法。
* proxy :在其上调用方法的代理实例,也就是我们要给哪个类做代理
method:对应用于在代理实例上调用的接口方法的Method实例。Method对象的声
明类将是在其中声明方法的接口,该接口可以是代理类赖以继承方法的代
理接口的超类接口。也就是我们要代理类针对哪个方法做代理!
args: 包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用
参数,则为NULL。基本类型的参数被包装在适当基本包装器类(
如java.lang.Integer或java.lang.Boolean)的实例中。
和main方法中的String[] args差不多
*
*/
}
/**
* 这种情况与产区别在于。前面的示例仅仅是针对卖IBM的电脑送鼠标,而我们的这个例子则不同,
* 可以根据传遘的任何对象,如联想,HP等等
* */
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 代理类
* @author fengyan
* date : 2006-12-31 21:12
*
*/
public class Substitute implements InvocationHandler{
/*
private Ipc ipc ;
public Ipc getIpc() {
return ipc;
}
public void setIpc(Ipc ipc) {
this.ipc = ipc;
}
//所有通过该代理购买的用户均可得到小鼠标一只
public void buy()
{
ipc.buy();//调用接口的方法!
System.out.println("送鼠标");
}
*/
//它并不知道它与哪个类产生关联。所以我们要写一个方法
private Object delegate;//代理对象
public Object bind(Object delegate)
{//需要返回一个代理的对象
this.delegate = delegate;
// 我们用静态的对象 新的代理实例
return Proxy.newProxyInstance(delegate.getClass().getClassLoader(), delegate.getClass().getInterfaces(), this);
//参数1: 代理对象的类
//参数2:接口
//参数3:与谁绑定,因为我们已经继承了这个动态的代理类接口,所以直接this即可
}
//实现InvocationHandler接口的 invoke
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 新的内容。就是我们要实现送鼠标这样一种操作
System.out.println("送鼠标");
Object result = method.invoke(delegate, args);
return result;
}
/*在代理实例上处理方法调用并返回结果,在与方法关联的代理实例上调用方法时,将在
* 调用处理程序上调用此方法。
* proxy :在其上调用方法的代理实例,也就是我们要给哪个类做代理
method:对应用于在代理实例上调用的接口方法的Method实例。Method对象的声
明类将是在其中声明方法的接口,该接口可以是代理类赖以继承方法的代
理接口的超类接口。也就是我们要代理类针对哪个方法做代理!
args: 包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用
参数,则为NULL。基本类型的参数被包装在适当基本包装器类(
如java.lang.Integer或java.lang.Boolean)的实例中。
和main方法中的String[] args差不多
*
*/
}
/**
* 这种情况与产区别在于。前面的示例仅仅是针对卖IBM的电脑送鼠标,而我们的这个例子则不同,
* 可以根据传遘的任何对象,如联想,HP等等
* */
测试代码则如下:
package fengyan.efly;
public class TestMain {
public static void main(String[] args) {
//声明一代理类对象,让代理来做事情
Substitute sb = new Substitute();
/*
sb.setIpc(new Computer());//参数Ipc接口类型
sb.buy();//通过代理买
*/
Ipc ipc = (Ipc)sb.bind(new Computer());
ipc.buy();
//该接口在调用buy方法时先调用代理类中的incoke方法!
}
}
public class TestMain {
public static void main(String[] args) {
//声明一代理类对象,让代理来做事情
Substitute sb = new Substitute();
/*
sb.setIpc(new Computer());//参数Ipc接口类型
sb.buy();//通过代理买
*/
Ipc ipc = (Ipc)sb.bind(new Computer());
ipc.buy();
//该接口在调用buy方法时先调用代理类中的incoke方法!
}
}
运行结果如下:
送鼠标
获取:IBM电脑一台