1 代理模式
代理模式是指,为其他对象提供一种代理控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户类和目标对象之间起到中介的作用。
换句话说,使用代理对象,是为了在不修改目标对象的基础上,增强主业务逻辑。
客户类真正的想要访问的对象是目标对象,但客户类真正可以访问的对象是代理对象。客户类对目标对象的访问是通过访问代理对象来实现的。当然,代理类与目标类要实现同一个接口。
1.1 静态代理
1.1.1 概念
静态代理是指,代理类在程序运行前就已经定义好,其与目标类的关系在程序运行前就已经确立。
实现示例:
/** 目标类 */ public class SomeServiceIMPL implements ISomeService { @Override public String doFirst() { System.out.println("执行doFrist.."); return "abcde"; } @Override public void doSecond() { System.out.println("执行doSecond.."); } } |
/** 代理类,实现对目标方法返回值的大写转换 */ public class ServiceProxy implements ISomeService { private ISomeService target; public ServiceProxy(){ target = new SomeServiceIMPL(); } @Override public String doFirst() { // TODO Auto-generated method stub String res = target.doFirst(); return res.toUpperCase(); } @Override public void doSecond() { target.doSecond(); } }
|
1.2 动态代理:
1.2.1 JDK的proxy方法实现
/** * 内部类使用外部类的变量必须是final的 * @param args */ public static void main(String[] args) { final ISomeService target = new SomeServiceIMPL(); /* * 使用JDK的Proxy动态代理,要求目标类必须实现接口 * 因为其底层的执行原理,与静态代理的相同 */ ISomeService iss = (ISomeService)Proxy.newProxyInstance( target.getClass().getClassLoader(), //目标类的类加载器 target.getClass().getInterfaces(), //目标类所实现的所有接口 new InvocationHandler(){//匿名内部类
/** * proxy:代理对象 * method:目标方法 * args:目标方法的参数列表 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //调用目标方法 Object res = method.invoke(target, args); if(res!=null)res = ((String)res).toUpperCase(); return res; } }); String res = iss.doFirst(); System.out.println(res); iss.doSecond(); } |
CGLIB动态代理
1.1 概念
使用JDK的Proxy实现代理,要求目标类与代理类实现相同接口,若目标类不存在接口,则无法使用该方法实现。
但对于无接口的类,要为其创建动态代理,就要使用CGLIB来实现,CGLIB代理的生成原理是生成目标类的子类,而子类是增强过的,这个子类对象就是代理对象。所以使用CGLIB生成动态代理,要求目标类必须能够被继承,即不能是final类。
CGLIB(Code Generation Library)是一个开源项目,是一个强大的、高性能的、高质量的代码生成类库,它可以在运行期扩展和增强java类。Hibernate用它来实现持久化对象的自己码的动态生成,Spring用来实现AOP编程。 |
CGLIB包的底层是通过使用一个小而块的字节码框架ASM,来转换字节码并生成新的类。
1.2 示例代码
/** * CGLIB主函数 */ import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy;
import edu.sdut.cglib.MyCglibFactory; import edu.sdut.cglib.SomeService; import edu.sdut.proxy.ISomeService; import edu.sdut.proxy.SomeServiceIMPL; import edu.sdut.toproxy.ServiceProxy;
public class Mytest { public static void main(String[] args) { System.out.println("qqq"); SomeService iss = new MyCglibFactory().myCglibCreator();
String res = iss.doFirst(); System.out.println(res); iss.doSecond(); } } /** * CGLIB工厂类实现代理 */ package edu.sdut.cglib;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy;
public class MyCglibFactory implements MethodInterceptor{ private SomeService target;
public MyCglibFactory() { super(); target = new SomeService(); // TODO Auto-generated constructor stub }
public SomeService myCglibCreator(){ //创建增强器对象 Enhancer enhancer = new Enhancer(); //指定目标类,即父类 enhancer.setSuperclass(SomeService.class); //设置回调接口对象 enhancer.setCallback(this); return (SomeService)enhancer.create(); }
@Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { // TODO Auto-generated method stub //调用目标方法 Object res = method.invoke(target, args); if(res!=null){ res = ((String)res).toUpperCase(); } return res; }
//回调方法
}
|
3.3.3 方法回调设计模式
在java中,就是类A调用类B中的某个方法b,然后类B又在某个时候反过来调用A中的某个方法a,对于A来说,这个a方法便叫做回调方法。Java的接口提供了一种很好的方式来实现回调。这个方法就是定义一个简单的接口,在接口之中定义一个我们希望回调的方法。这个接口称之为回调接口。
在前面的例子中,我们定义的AccountServiceCglibProxyFactory类就相当于前面所说的A类,而Enhancer类则是B类,A类中调用了Enhancer类的setCallback(this)方法。并将回调对象this作为实参传递给了Enhancer类,Enhancer类在后续执行过程中,会调用A类中的intercept()方法,这个intercept()方法就是回调方法。