代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能. 这里使用到编程中的一个思想:不要随意去修改别人已经写好的代码或者方法,如果需改修改,可以通过代理的方式来扩展该方法;
静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类.
代码如下:
定义接口
public interface A1_IUserDao { void save(); }实现类
public class A2_UserDao implements A1_IUserDao { @Override public void save() { System.out.println("保存数据"); } }代理类
/** * 静态代理 */ public class A3_UserDaoProxy implements A1_IUserDao { //接收目标对象 private A1_IUserDao target; public A3_UserDaoProxy(A1_IUserDao target){ this.target = target; } @Override public void save() { System.out.println("准备保存数据"); target.save();//执行目标对象的方法 System.out.println("数据保存完毕"); } }调用类
public class A4_Client { public static void main(String[] args) { A1_IUserDao userDao = new A2_UserDao(); A3_UserDaoProxy userDaoProxy = new A3_UserDaoProxy(userDao); userDaoProxy.save(); } }运行结果
准备保存数据 保存数据 数据保存完毕静态代理---优点:可以做到在不修改目标对象的功能前提下,对目标功能扩展.静态代理---缺点: 因为代理对象要与目标对象实现一样的接口,所以会有很多代理类,类太多;同事一旦接口增加方法目标对象与代理对象都需要维护;
如何解决静态代理中的缺点呢?答案是可以使用动态代理方式;
动态代理有以下特点:
代理对象,不需要实现接口(很棒);代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象;动态代理也叫做:JDK代理,接口代理;JDK中生成代理对象的API
代理类所在包:java.lang.reflect.ProxyJDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是 static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )三个参数分别说明:
ClassLoader : 指定当前目标对象使用类加载器,获取加载器的方法是固定的;Class<?>[] : 目标对象实现的接口的类型,使用泛型方式确认类型;InvocationHandler:事件处理执行目标对象的方法时会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入;代码示例:
代理类:
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class B1_ProxyFactory { //维护目标对象 private Object target; public B1_ProxyFactory (Object target){ this.target = target; } public Object getProxyInstance(){ return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("[准备执行目标方法]"); Object result = method.invoke(target,args); System.out.println("[准备执行目标结束]"); return result; } }); } }运行代理:
public class B2_Client { public static void main(String[] args) { A1_IUserDao userDao = new A2_UserDao(); userDao.save(); System.out.println("=============="); A1_IUserDao userDaoProxy = (A1_IUserDao) new B1_ProxyFactory(userDao).getProxyInstance(); userDaoProxy.save(); } }代理运行结果:
保存数据 ============== [准备执行目标方法] 保存数据 [准备执行目标结束]总结:代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理(缺点也很明显);
上面的静态代理和动态代理模式都是要求目标对象是实现一个接口的目标对象,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用以目标对象子类的方式类实现代理,这种方法就叫做:Cglib代理;
Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.
JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,如果想代理没有实现接口的类,就可以使用Cglib实现.Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用;Cglib包的底层是通过使用一个小而快的字节码处理框架ASM来转换字节码并生成新的类.不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉.很多框架都集成的Cglib工具包,如果单独使用引用以下Maven依赖即可;
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.2.12</version> </dependency>示例代码:
一个普通对象
public class D1_UserDao { public void save(){ System.out.println("保存数据-入库"); } }Cglib代理类
import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class D2_Cglib_ProxyFactory implements MethodInterceptor { //目标对象 private Object target; public D2_Cglib_ProxyFactory(Object target){ this.target = target; } //给目标对象创建一个代理对象 public Object getProxyInstance(){ //1.工具类 Enhancer en = new Enhancer(); //2.设置父类 en.setSuperclass(target.getClass()); //3.设置回调函数 en.setCallback(this); //4.创建子类(代理对象) return en.create(); } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("准备保存数据"); //执行目标对象的方法 Object result = method.invoke(target, objects); System.out.println("数据保存完成"); return result; } }代理类运行Client
public class D3_Client { public static void main(String[] args) { //目标对象 D1_UserDao dao = new D1_UserDao(); //代理对象 D1_UserDao proxy = (D1_UserDao) new D2_Cglib_ProxyFactory(dao).getProxyInstance(); //执行代理对象方法 proxy.save(); } }运行结果
准备保存数据 保存数据-入库 数据保存完成
三种代理在使用的时候根据具体场景选择更合适的即可;