设计模式:Proxy--代理四种具体实现

代码在github上面可以参考

定义:动态代理是指客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。

一、代理的原理:

使用一个代理将对象包装起来, 然后用该代理对象取代原始对象. 任何对原始对象的调用都要通过代理. 代理对象决定是否以及何时将方法调用转到原始对象上

二、实现代理

静态代理,jdk动态代理,cglib动态代理,通过编译期提供的API动态创建代理类

静态代理:

        由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口,被代理类,代理类等确定下来。在程序运行之前,代理类的.class文件就已经生成。

缺点:每一个真实类都需要一个创建新的代理类。


//静态代理:

/**
 * 接口
 */
interface ClothProduct {
    void productCloth();
}

/**
 * 接口实现类
 */
class NikeClothFactory implements  ClothProduct{
    @Override
    public void productCloth() {
        System.out.println("Nike工厂生产衣服 ");
    }
}

//代理类
class ProxyFactory implements ClothProduct{
    NikeClothFactory nikeClothFactory;

    //代理类执行中转化为nikeClothFactory执行
    public ProxyFactory(NikeClothFactory nikeClothFactory) {
        System.out.println("代理类执行");
        this.nikeClothFactory = nikeClothFactory;
    }

    @Override
    public void productCloth() {
        nikeClothFactory.productCloth();
    }
}

public class TestClothProduct {
    public static void main(String[] args) {
        NikeClothFactory nikeClothFactory = new NikeClothFactory();
        ProxyFactory proxyFactory = new ProxyFactory(nikeClothFactory);
        proxyFactory.productCloth();
    }
}

代理模式最主要的就是有一个公共接口(ClothProduct),一个具体的类(NikeClothFactory),一个代理类(ProxyFactory),代理类持有具体类的实例,代为执行具体类实例方法。上面说到,代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。这里的间接性就是指不直接调用实际对象的方法,那么我们在代理过程中就可以加上一些其他用途。

动态代理(jdk和cglib)

2 jdk代理:

缺点:被代理类必须是实现了一个接口

代理类在程序运行时创建的代理方式被成为动态代理。 我们上面静态代理的例子中,代理类(studentProxy)是自己定义好的,在程序运行之前就已经编译完成。然而动态代理,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。 比如说,想要在每个代理的方法前都加上一个处理方法:

1 在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。

package com.atguigu.reflect.instancetestProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

//接口
interface Subject {
    void action();
}

//被代理类
class  RealSubject implements  Subject{
    @Override
    public void action() {
        System.out.println("我是被代理类,记得执行我");
    }
}

class MyInvocationHandler implements InvocationHandler {

    Object obj;//实现了接口的被代理类的声明

    //1 给被代理类实例化,2 返回一个代理类对象
    public Object banding (Object obj) {
        this.obj= obj;
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),this);
    }

    //当通过代理类对象发起对被重写方法的调用时,实际转化为对如下方法的调用
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return method.invoke(obj,args);
    }
}

public class TestProxy {
    public static void main(String[] args) {
        //被代理类对象
        RealSubject realSubject = new RealSubject();
        //创建一个实现InvocationHandler接口的对象
        MyInvocationHandler myInvocationHandler = new MyInvocationHandler();
        //调用banding方法动态返回一个同样实现real所在类实现的接口Subject的代理对象
        Subject banding = (Subject) myInvocationHandler.banding(realSubject);//此时banding就是代理对象
        banding.action();

        //再举一个例
        NikeClothFactory nikeClothFactory = new NikeClothFactory();
        ClothProduct banding1 = (ClothProduct) myInvocationHandler.banding(nikeClothFactory);
        banding1.productCloth();

    }
}

结果:
我是被代理类,记得执行我
Nike工厂生产衣服 

示例


public class ProxyTest {

    public static void main(String[] args) {
        NikeFactory nikeFactory = new NikeFactory();
        ProxyFactory proxyFactory = new ProxyFactory(nikeFactory);
        Object proxyInstance =  Proxy.newProxyInstance(nikeFactory.getClass().getClassLoader(), nikeFactory.getClass().getInterfaces(), proxyFactory);
        System.out.println(proxyFactory.getClass());
        ClouseFactory clouseFactory = (ClouseFactory) proxyInstance;
        clouseFactory.produceClouse();
    }
}

interface ClouseFactory {

    void produceClouse();
}

class NikeFactory implements ClouseFactory {
    @Override
    public void produceClouse() {
        System.out.println("NIKE 生产衣服");
    }
}

class ProxyFactory implements InvocationHandler {

    private ClouseFactory clouseFactory;

    public ProxyFactory(ClouseFactory clouseFactory) {
        this.clouseFactory = clouseFactory;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return method.invoke(clouseFactory,args);
    }
}

3 动态代理(CGLIB)一般而言,动态代理分为两种,一种是JDK反射机制提供的代理,另一种是CGLIB代理。在JDK代理,必须提供接口,而CGLIB则不需要提供接口,

缺点:这个类不能有final修饰使其不可被继承


class LandLoad {
    public void rent() {
        System.out.println("中介,有房子租");
    }
}

class MyMethodInterceptor implements MethodInterceptor{
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("cglib 被代理的类不需要提供接口");
        Object o1 = methodProxy.invokeSuper(o, objects);
        return o1;
    }
}
public class TestCglibProxy {
    public static void main(String[] args) {
        MyMethodInterceptor myMethodInterceptor = new MyMethodInterceptor();
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(LandLoad.class);

        enhancer.setCallback(myMethodInterceptor);
        LandLoad landLoad = (LandLoad) enhancer.create();
        landLoad.rent();
    }

}

另为一种用法


interface UserManager {
    public void addUser(String id, String password);
    public void delUser(String id);
}

class UserManagerImpl implements UserManager {

    @Override
    public void addUser(String id, String password) {
        System.out.println("调用了UserManagerImpl.addUser()方法!");
    }

    @Override
    public void delUser(String id) {
        System.out.println("调用了UserManagerImpl.delUser()方法!");
    }
}

class CGLibProxy implements MethodInterceptor {
    // CGlib需要代理的目标对象
    private Object targetObject;

    public Object createProxyObject(Object obj) {
        this.targetObject = obj;
        //返回代理对象
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(obj.getClass());
        enhancer.setCallback(this);
        Object proxyObj = enhancer.create();
        return proxyObj;
    }

    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        Object obj = null;
        // 过滤方法
        if ("addUser".equals(method.getName())) {
            // 检查权限
            checkPopedom();
        }
        obj = method.invoke(targetObject, args);
        return obj;


    }
    private void checkPopedom() {
        System.out.println("检查权限:checkPopedom()!");
    }
}

public class TestCglib2 {
    public static void main(String[] args) {
        UserManagerImpl u = new UserManagerImpl();
        CGLibProxy cgLibProxy = new CGLibProxy();
        UserManager proxyObject = (UserManager) cgLibProxy.createProxyObject(u);
        proxyObject.addUser("1","123456");
        proxyObject.delUser("1");
    }

}
Object o1 = methodProxy.invokeSuper(o, objects);
obj = method.invoke(targetObject, args);

            两种方法调用都可以

CGLIB的核心类:
       net.sf.cglib.proxy.Enhancer – 主要的增强类
       net.sf.cglib.proxy.MethodInterceptor – 主要的方法拦截类,它是Callback接口的子接口,需要用户实现
       net.sf.cglib.proxy.MethodProxy – JDK的java.lang.reflect.Method类的代理类,可以方便的实现对源对象方法的调用,如使用:
      Object o = methodProxy.invokeSuper(proxy, args);//虽然第一个参数是被代理对象,也不会出现死循环的问题。

     net.sf.cglib.proxy.MethodInterceptor接口是最通用的回调(callback)类型,它经常被基于代理的AOP用来实现拦截(intercept)方法的调用。这个接口只定义了一个方法
public Object intercept(Object object, java.lang.reflect.Method method,
Object[] args, MethodProxy proxy) throws Throwable;

      第一个参数是代理对像,第二和第三个参数分别是拦截的方法和方法的参数。第四个参数代理方法

(被代理方法method用inoke方法obj = method.invoke(targetObject, args),用被代理方法调用,参数是被代理类和参数

(代理方法methodProxyinvokeSuper方法,Object o1 = methodProxy.invokeSuper(o, objects);参数是代理类和参数

原来的方法可能通过使用java.lang.reflect.Method对象的一般反射调用,或者使用 net.sf.cglib.proxy.MethodProxy对象调用。net.sf.cglib.proxy.MethodProxy通常被首选使用,因为它更快。

Object o1 = methodProxy.invokeSuper(o, objects);
obj = method.invoke(targetObject, args);

public class CglibTeacherDaoProxy implements MethodInterceptor {
    private TeacherDao teacherDao;

    public CglibTeacherDaoProxy(TeacherDao teacherDao) {
        this.teacherDao = teacherDao;
    }

    public TeacherDao getInstance() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(teacherDao.getClass());
        enhancer.setCallback(this);
        return (TeacherDao) enhancer.create();
    }


    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("cglib代理开始");
        //Object invoke = method.invoke(teacherDao, objects);
        Object invoke = methodProxy.invoke(teacherDao, objects);
        //Object invoke = methodProxy.invokeSuper(o, objects);
        System.out.println("cglib代理提交");
        return invoke;
    }
}

invoke方法参数都是目标对象,也就是被代理队形,只有invokeSuper用代理对象

jdk和cglib代理区别:

JDK动态代理:利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
CGlib动态代理:利用ASM(开源的Java字节码编辑库,操作字节码)开源包,将代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

区别:JDK代理只能对实现接口的类生成代理;CGlib是针对类实现代理,对指定的类生成一个子类,并覆盖其中的方法,这种通过继承类的实现方式,不能代理final修饰的类。


1.JDK代理使用的是反射机制实现aop的动态代理,CGLIB代理使用字节码处理框架asm,通过修改字节码生成子类。所以jdk动态代理的方式创建代理对象效率较高,执行效率较低,cglib创建效率较低,执行效率高;

2.JDK动态代理机制是委托机制,具体说动态实现接口类,在动态生成的实现类里面委托hanlder去调用原始实现类方法,CGLIB则使用的继承机制,具体说被代理类和代理类是继承关系,所以代理类是可以赋值给被代理类的,如果被代理类有接口,那么代理类也可以赋值给接口。
 

***********************************************************

4  通过编译期提供的API动态创建代理类

假设我们确实需要给一个既是final,又未实现任何接口的ProductOwner类创建动态代码。除了InvocationHandler和CGLIB外,我们还有最后一招:

我直接把一个代理类的源代码用字符串拼出来,然后基于这个字符串调用JDK的Compiler(编译期)API,动态的创建一个新的.java文件,然后动态编译这个.java文件,这样也能得到一个新的代理类。

package com.atguigu.reflect.instancetestProxy;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;



public class ProductOwnerSourceCodeProxy {
    public static void main(String[] arg) throws Exception {
        Class<?> c = getProxyClass();
        Constructor<?>[] constructor = c.getConstructors();
        Object POProxy = constructor[0].newInstance("Ross");
        Method defineBackLog = c.getDeclaredMethod("defineBackLog");
        defineBackLog.invoke(POProxy);
    }

    private static String getSourceCode() {
        String src = ""//"package com.atguigu.reflect.instancetestProxy;\n\n"
                + "public final class ProductOwnerSCProxy {\n"
                + "\tprivate String name;\n\n"
                + "\tpublic ProductOwnerSCProxy(String name){\n"
                + "\t\tthis.name = name;\n" + "\t}\n\n"
                + "\t\tpublic void defineBackLog(){\n"
                + "\t\tSystem.out.println(\"PO writes some document before defining BackLog\");"
                + "\t\tSystem.out.println(\"PO: \" + name + \" defines Backlog.\");}}\n";
        return src;
    }

    private static String createJavaFile(String sourceCode) {
        String fileName = "G:\\ideaIU-2018.1.4.win\\IdeaProjects\\TestReflectDemoApp\\src\\main\\java\\com\\atguigu\\reflect\\instancetestProxy\\ProductOwnerSCProxy.java";
        File javaFile = new File(fileName);
        Writer writer;
        try {
            writer = new FileWriter(javaFile);
            writer.write(sourceCode);
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return fileName;
    }

    private static void compile(String fileName) {
        try {
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager sjfm = compiler.getStandardFileManager(null, null, null);
            Iterable<? extends JavaFileObject> iter = sjfm.getJavaFileObjects(fileName);
            JavaCompiler.CompilationTask ct = compiler.getTask(null, sjfm, null, null, null, iter);
            ct.call();
            sjfm.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static Class<?> loadClass() {
        URL[] urls;
        String path = "file:G:\\ideaIU-2018.1.4.win\\IdeaProjects\\TestReflectDemoApp\\src\\main\\java\\com\\atguigu\\reflect\\instancetestProxy\\";
        Class<?> c = null;
        try {
            urls = new URL[] { (new URL(path)) };
            URLClassLoader ul = new URLClassLoader(urls);
            c = ul.loadClass("ProductOwnerSCProxy");
        } catch (MalformedURLException | ClassNotFoundException e) {
            e.printStackTrace();
        }
        return c;
    }

    private static Class<?> getProxyClass() {
        String sourceCode = getSourceCode();
        String javaFile = createJavaFile(sourceCode);
        compile(javaFile);
        return loadClass();
    }
}

**************************************************************************************************************************

Proxy :专门完成代理的操作类,是所有动态代理类的父类。通过此类为一个或多个接口动态地生成实现类。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
设计模式是一种被广泛应用于软件开发中的最佳实践方法,用于解决常见的设计问题。在C#中,有23种经典的设计模式,它们可以分为三个主要的分类:创建型模式(Creational Patterns)、结构型模式(Structural Patterns)和行为型模式(Behavioral Patterns)。下面是这些模式的简要介绍: 1. 创建型模式: - 工厂方法模式(Factory Method Pattern) - 抽象工厂模式(Abstract Factory Pattern) - 建造者模式(Builder Pattern) - 原型模式(Prototype Pattern) - 单例模式(Singleton Pattern) 2. 结构型模式: - 适配器模式(Adapter Pattern) - 桥接模式(Bridge Pattern) - 组合模式(Composite Pattern) - 装饰器模式(Decorator Pattern) - 外观模式(Facade Pattern) - 享元模式(Flyweight Pattern) - 代理模式(Proxy Pattern) 3. 行为型模式: - 责任链模式(Chain of Responsibility Pattern) - 命令模式(Command Pattern) - 解释器模式(Interpreter Pattern) - 迭代器模式(Iterator Pattern) - 中介者模式(Mediator Pattern) - 备忘录模式(Memento Pattern) - 观察者模式(Observer Pattern) - 状态模式(State Pattern) - 策略模式(Strategy Pattern) - 模板方法模式(Template Method Pattern) - 访问者模式(Visitor Pattern) 这些设计模式提供了一种通用的思路和结构,可以帮助开发人员更好地组织和设计他们的代码。具体实现细节和用法可以根据具体的需求进行深入学习和研究。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值