1、Proxy代理模式

1、Proxy 代理模式

  • 代理(Proxy)提供了对目标对象另外的访问方式; 即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能.
  • 代理模式可分为静态代理和动态代理(JDK动态代理与CGLib动态代理)
1.1静态代理
  • 静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类。
1)定义接口
public interface Image { void display(); }

2)接口实现类,扩展了新功能
public class RealImage implements Image { private String fileName; public RealImage(String fileName){ this.fileName = fileName; loadFromDisk(fileName); } @Override public void display() { System.out.println("Displaying " + fileName); } private void loadFromDisk(String fileName){ System.out.println("Loading " + fileName); } }

3)接口代理类,可以调用实现类的功能。
public class ProxyImage implements Image{ private RealImage realImage; private String fileName; public ProxyImage(String fileName){ this.fileName = fileName; } @Override public void display() { if(realImage == null){ realImage = new RealImage(fileName); } realImage.display(); } }

4)Demo,通过代理类来调用实体类功能。
public class ProxyPatternDemo { public static void main(String[] args) { Image image = new ProxyImage("test_10mb.jpg"); // 图像将从磁盘加载 image.display(); System.out.println(""); // 图像不需要从磁盘加载 image.display(); } }

5)执行程序,输出结果:
Loading test_10mb.jpg Displaying test_10mb.jpgDisplaying test_10mb.jpg

1.2动态代理
  • 代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理
  • JDK代理:只对有实现接口的目标对象生成代理,通过反射机制实现aop的动态代理,使用的是委派机制,在动态生成的实现类里委托处理器去调用原始实现类的方法。
    -Cglib代理: 目标对象没有实现接口,也能生成代理,使用字节码处理框架asm,修改字节码生成子类,使用的是继承机制,代理类继承被代理类。
1.2.1 JDK 自带的动态代理
java.lang.reflect.Proxy:生成动态代理类和对象; java.lang.reflect.InvocationHandler(处理器接口):可以通过invoke方法实现对真实角色的代理访问。 每次通过 Proxy 生成的代理类对象都要指定对应的处理器对象。

JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:
static Object newProxyInstance(ClassLoader loader, Class[] interfaces,InvocationHandler h )

  • ClassLoader loader,:指定当前目标对象使用类加载器,获取加载器的方法是固定的
  • Class[] interfaces,:目标对象实现的接口的类型,使用泛型方式确认类型
  • InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入
1) 接口:Subject.java
public interface Subject { public int sellBooks(); public String speak(); }

2) 真实对象:RealSubject.java
public class RealSubject implements Subject{ @Override public int sellBooks() { System.out.println("卖书"); return 1 ; }@Override public String speak() { System.out.println("说话"); return "张三"; } }

3)处理器对象:MyInvocationHandler.java
public class MyInvocationHandler implements InvocationHandler { /** * 因为需要处理真实角色,所以要把真实角色传进来 */ Subject realSubject ; public MyInvocationHandler(Subject realSubject) { this.realSubject = realSubject; }/** * * @param proxy代理类 * @param method正在调用的方法 * @param args方法的参数 * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("调用代理类"); if(method.getName().equals("sellBooks")){ int invoke = (int)method.invoke(realSubject, args); System.out.println("调用的是卖书的方法"); return invoke ; }else { String string = (String) method.invoke(realSubject,args) ; System.out.println("调用的是说话的方法"); returnstring ; } } }

4)测试用例 :Client.java
public class Client { public static void main(String[] args) { //真实对象 Subject realSubject =new RealSubject(); MyInvocationHandler myInvocationHandler = new MyInvocationHandler(realSubject); //代理对象 Subject proxyClass = (Subject) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Subject.class}, myInvocationHandler); proxyClass.sellBooks(); proxyClass.speak(); } }

1.2.2 CGLib动态代理 【1、Proxy代理模式】CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。但因为采用的是继承,所以不能对final修饰的类进行代理。JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。
1) 目标对象类:UserDao.java
/** * 目标对象,没有实现任何接口 */ public class UserDao { public void save() { System.out.println("----已经保存数据!----"); } }

2) Cglib代理工厂:ProxyFactory.java
/** * Cglib子类代理工厂 * 对UserDao在内存中动态构建一个子类对象 */ public class ProxyFactory implements MethodInterceptor{ //维护目标对象 private Object target; public 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 obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("开始事务..."); //执行目标对象的方法 Object returnValue = https://www.it610.com/article/method.invoke(target, args); System.out.println("提交事务..."); return returnValue; } }

3) 测试类
/** * 测试类 */ public class App { @Test public void test(){ //目标对象 UserDao target = new UserDao(); //代理对象 UserDao proxy = (UserDao)new ProxyFactory(target).getProxyInstance(); //执行代理对象的方法 proxy.save(); } }

    推荐阅读