代理的意思:本来应该自己做的事情,因为没有某种原因不能直接做,只能请别人代理做。被请的人就是代理。比如春节买票回家,由于没有时间,只能找票务中介来买,这就是代理模式。
2、以下的例子将简单的模拟代理模式
比如一个信息管理系统,有的用户有浏览的权限,有的用户有修改和编辑的权限,有的用户除了以上的权限,还有删除的权限。
(1) 最普通的做法:
public class ViewAction {
public void doAction(){
String permission = "view";
if(permission.equals(Constants.PRIMISSION_VIEW)){
System.out.println("you could view information");
//........做view的动作
}
}
}
其他的动作和浏览的动作差不多。但这样做有两个缺点:(一)它把权限的控制的执行的动作放在一起,两者的功能混在一起,造成了维护和修改的不方便。不满足单一职责原则。(二)客户端调用的是具体的实现类,造成了扩展和运行时调用的困难,不满足依赖颠倒原则(最下面补充说明)。
(2)初步代理
因此对以上的类做了重新设计,通过代理实现。和买车票一样,代理类必须先检测权限,再执行动作。修改后的密码如下。
接口类(首先设计一个接口,满足依赖颠倒原来):
public interface IAction {
public void doAction();
}
动作执行类(相当于买车票的动作),动作的真正执行者:
public class ViewAction implements IAction {
public void doAction() {
System.out.println("you could view the information");
//............做view的动作
}
}
代理类(ViewActionProxy )除了做了客户要做的doAction()动作,还做了权限的判断。而ViewAction只做了核心动作,满足了单一原则。
public class ActionProxy implements IAction {
private ViewAction viewAction = new ViewAction();
public void doAction() {
if(Permission.getPermission(userid).equals
(Constants.PERMISSION_VIEW)){
viewAction.doAction();
}
}
}
总之,客户段通过调用这个代理类执行动作,这个代理类通过将权限判断和具体的动作执行分开。实现了单一原则。代理类又叫委派,即代理类并没有亲自执行,而是委派给另一个类来执行。ProxyViewAction并没有亲自执行,而是给ViewAction执行。
再看看ViewActionProxy,他不仅依赖于抽象接口IAction,而且依赖具体类ViewAction。这样对系统的扩展性很不好。因为我们还有add,delete,update动作等。(1)我们可以为每个动作都写一个代理类,可是每个代理类都做相同的动作:先进行权限的判断,再执行具体的动作。所以我们对ViewAction具体类再一次抽象,使得代理类(ViewActionProxy)只依赖于接口IAction,而不依赖具体类ViewAction。
(3)代理改进:将代理类脱离动作的具体执行类。
要实现这个功能,必须让代理类的具体实现去掉(private ViewAction viewAction = new ViewAction();),而让代理类的使用者,再使用的时候提供具体的实现类,这个就是依赖注入。
public class ViewActionProxy implements IAction {
private IAction action ;
public void setAction(IAction action) {
this.action = action;
}
public void doAction() {
if(Permission.getPermission(userid).
equals(Constants.PERMISSION_VIEW)){
action.doAction();
}
}
}
这样我们能就将所有使用了IAction接口的具体类都通过这个代理类来代理。比如AddAction、DeleteAction、 UpdateAction都可以用这个代理类来代理。
(4)动态代理:没有实现接口的类的某些方法也可以使用代理。分别添加增加add,delete,update方法,可是每个方法体内都做相同的动作:先进行权限的判断,再执行具体的动作。
如果我们没办法将所有的类抽象出一个统一的接口,我们可能有多个接口。但是按照上面的方法,每个代理都必须为每个接口写一个相应的代理类。没有统一的接口情况下,对一些零散的对象的某些动作使用代理模式。
动态代理的核心是InvocationHandler,要实现动态代理,就必须实现这个接口。这个接口的委派任务是在public Object invoke(Object proxy, Method method, Object[] args)方法中实现的。invoke的主要流程如下:
...........................//在调用核心动作前的作一些动作。
m.invoke(obj, args); //调用核心动作。
............................//在调用核心动作后的做一些动作。
我们看到动态代理是通过反射机制来调用核心功能m.invoke(obj, args); 这个反射机制使得我们不依赖任何一个具体的接口,而是依赖具体的类。
动态代理类的具体实现如下:
public class ActionProxy implements InvocationHandler {
private Object action;
public ActionProxy(Object action){
this.action = action;
}
public static Object getInstance(Object action){
return Proxy.newProxyInstance(
action.getClass().getClassLoader(),
action.getClass().getInterfaces(),
new ActionProxy(action));
//自动生成相应的代理类,且类里面有个变量是ActionProxy对象,当客户端调用这个代理类的方法时候,代理类会调用它的ActionProxy 变量的invoke方法,因此每次调用了代理类,就可以在方法执行前和方法执行后加入特定的执行内容(事务或日志等)。
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object result;
System.out.println("befor method:"+method.getName());
result = method.invoke(action, args);
System.out.println("after method:"+method.getName());
return result;
}
}
代理类必须实现InvocationHandler接口,getInstance()方法获得代理类的实例(实现了IAction接口)的对应代理,invoke方法实现了简单的代理方法。
客户段代码如下:
public class TrasasactionTest {
public static void main(String[] args) {
IAction action = (IAction) ActionProxy.getInstance(new ViewAction());
action.doAction();
}
}
由上得出,代理对于接口的依赖转移到客户端,代理不必再实现特定的接口。因此ActionProxy也可以用于任何的接口。
ITestService service = (ITestService ) ActionProxy.getInstance(new TestServiceImpl());
service .doTest();//只要TestServiceImpl实现ITest,就可以。
(5)在委派前的动作和委派后的动作在不同的代理类不用,可是代理类的生成和其他的内容都是相同的。所以我们可以用Template模式对代理类进行进一步的优化。
生成代理类的基类的代码如下:
public abstract class BaseProxy implements InvocationHandler {
private Object target;
public Object getTarget() {
return target;
}
public BaseProxy(Object target){
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object result;
doBefore();
result = method.invoke(target, args);
doAfter();
return result;
}
public abstract void doBefore();
public abstract void doAfter();
}
代理类基类的具体实现如下:
public class ProxyImpl extends BaseProxy {
public ProxyImpl(Object target) {
super(target);
}
//委派前的动作
public void doAfter() {
System.out.println("do before method");
}
//委派后的动作
public void doBefore() {
System.out.println("do after method");
}
}
因此,在OOP中,doBefore(),核心动作,doAfter()可能这3个动作在所有的类中都存在,但是doBefore,doAfter的内容都是相同的,而核心动作确实不相同的。因此把doBefore和doAfter提取出来,共同设计,这是AoP的基础。AoP的一个重要特点是找出哪些方法需要执行doBefore和doAfter,哪些不用。
2、以下的例子将简单的模拟代理模式
比如一个信息管理系统,有的用户有浏览的权限,有的用户有修改和编辑的权限,有的用户除了以上的权限,还有删除的权限。
(1) 最普通的做法:
public class ViewAction {
public void doAction(){
String permission = "view";
if(permission.equals(Constants.PRIMISSION_VIEW)){
System.out.println("you could view information");
//........做view的动作
}
}
}
其他的动作和浏览的动作差不多。但这样做有两个缺点:(一)它把权限的控制的执行的动作放在一起,两者的功能混在一起,造成了维护和修改的不方便。不满足单一职责原则。(二)客户端调用的是具体的实现类,造成了扩展和运行时调用的困难,不满足依赖颠倒原则(最下面补充说明)。
(2)初步代理
因此对以上的类做了重新设计,通过代理实现。和买车票一样,代理类必须先检测权限,再执行动作。修改后的密码如下。
接口类(首先设计一个接口,满足依赖颠倒原来):
public interface IAction {
public void doAction();
}
动作执行类(相当于买车票的动作),动作的真正执行者:
public class ViewAction implements IAction {
public void doAction() {
System.out.println("you could view the information");
//............做view的动作
}
}
代理类(ViewActionProxy )除了做了客户要做的doAction()动作,还做了权限的判断。而ViewAction只做了核心动作,满足了单一原则。
public class ActionProxy implements IAction {
private ViewAction viewAction = new ViewAction();
public void doAction() {
if(Permission.getPermission(userid).equals
(Constants.PERMISSION_VIEW)){
viewAction.doAction();
}
}
}
总之,客户段通过调用这个代理类执行动作,这个代理类通过将权限判断和具体的动作执行分开。实现了单一原则。代理类又叫委派,即代理类并没有亲自执行,而是委派给另一个类来执行。ProxyViewAction并没有亲自执行,而是给ViewAction执行。
再看看ViewActionProxy,他不仅依赖于抽象接口IAction,而且依赖具体类ViewAction。这样对系统的扩展性很不好。因为我们还有add,delete,update动作等。(1)我们可以为每个动作都写一个代理类,可是每个代理类都做相同的动作:先进行权限的判断,再执行具体的动作。所以我们对ViewAction具体类再一次抽象,使得代理类(ViewActionProxy)只依赖于接口IAction,而不依赖具体类ViewAction。
(3)代理改进:将代理类脱离动作的具体执行类。
要实现这个功能,必须让代理类的具体实现去掉(private ViewAction viewAction = new ViewAction();),而让代理类的使用者,再使用的时候提供具体的实现类,这个就是依赖注入。
public class ViewActionProxy implements IAction {
private IAction action ;
public void setAction(IAction action) {
this.action = action;
}
public void doAction() {
if(Permission.getPermission(userid).
equals(Constants.PERMISSION_VIEW)){
action.doAction();
}
}
}
这样我们能就将所有使用了IAction接口的具体类都通过这个代理类来代理。比如AddAction、DeleteAction、 UpdateAction都可以用这个代理类来代理。
(4)动态代理:没有实现接口的类的某些方法也可以使用代理。分别添加增加add,delete,update方法,可是每个方法体内都做相同的动作:先进行权限的判断,再执行具体的动作。
如果我们没办法将所有的类抽象出一个统一的接口,我们可能有多个接口。但是按照上面的方法,每个代理都必须为每个接口写一个相应的代理类。没有统一的接口情况下,对一些零散的对象的某些动作使用代理模式。
动态代理的核心是InvocationHandler,要实现动态代理,就必须实现这个接口。这个接口的委派任务是在public Object invoke(Object proxy, Method method, Object[] args)方法中实现的。invoke的主要流程如下:
...........................//在调用核心动作前的作一些动作。
m.invoke(obj, args); //调用核心动作。
............................//在调用核心动作后的做一些动作。
我们看到动态代理是通过反射机制来调用核心功能m.invoke(obj, args); 这个反射机制使得我们不依赖任何一个具体的接口,而是依赖具体的类。
动态代理类的具体实现如下:
public class ActionProxy implements InvocationHandler {
private Object action;
public ActionProxy(Object action){
this.action = action;
}
public static Object getInstance(Object action){
return Proxy.newProxyInstance(
action.getClass().getClassLoader(),
action.getClass().getInterfaces(),
new ActionProxy(action));
//自动生成相应的代理类,且类里面有个变量是ActionProxy对象,当客户端调用这个代理类的方法时候,代理类会调用它的ActionProxy 变量的invoke方法,因此每次调用了代理类,就可以在方法执行前和方法执行后加入特定的执行内容(事务或日志等)。
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object result;
System.out.println("befor method:"+method.getName());
result = method.invoke(action, args);
System.out.println("after method:"+method.getName());
return result;
}
}
代理类必须实现InvocationHandler接口,getInstance()方法获得代理类的实例(实现了IAction接口)的对应代理,invoke方法实现了简单的代理方法。
客户段代码如下:
public class TrasasactionTest {
public static void main(String[] args) {
IAction action = (IAction) ActionProxy.getInstance(new ViewAction());
action.doAction();
}
}
由上得出,代理对于接口的依赖转移到客户端,代理不必再实现特定的接口。因此ActionProxy也可以用于任何的接口。
ITestService service = (ITestService ) ActionProxy.getInstance(new TestServiceImpl());
service .doTest();//只要TestServiceImpl实现ITest,就可以。
(5)在委派前的动作和委派后的动作在不同的代理类不用,可是代理类的生成和其他的内容都是相同的。所以我们可以用Template模式对代理类进行进一步的优化。
生成代理类的基类的代码如下:
public abstract class BaseProxy implements InvocationHandler {
private Object target;
public Object getTarget() {
return target;
}
public BaseProxy(Object target){
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object result;
doBefore();
result = method.invoke(target, args);
doAfter();
return result;
}
public abstract void doBefore();
public abstract void doAfter();
}
代理类基类的具体实现如下:
public class ProxyImpl extends BaseProxy {
public ProxyImpl(Object target) {
super(target);
}
//委派前的动作
public void doAfter() {
System.out.println("do before method");
}
//委派后的动作
public void doBefore() {
System.out.println("do after method");
}
}
因此,在OOP中,doBefore(),核心动作,doAfter()可能这3个动作在所有的类中都存在,但是doBefore,doAfter的内容都是相同的,而核心动作确实不相同的。因此把doBefore和doAfter提取出来,共同设计,这是AoP的基础。AoP的一个重要特点是找出哪些方法需要执行doBefore和doAfter,哪些不用。