代理模式

十年热恋 提交于 2020-04-05 16:46:07

代理模式(Proxy Pattern)是只为其他对象提供一种代理,以控制对这个对象的访问,属于结构行模式。

代理模式一般包含三种角色:

    抽象主题角色:抽象主题类的主要职责是声明真实主题与代理的共同接口方法,该类可以是接口也可以说抽象类。

    真实主题角色:该类被称为代理类,该类定义了代理所表示的真实对象,是负责执行系统真正逻辑业务对象。

    代理主角色:也被称为代理类;其内部持有(真实主题角色)的引用,因此具备完全的对(真实主题角色)的代理权。客户端调用代理对象方法,同时也调用了代理类方法,但是会在代理类对象前后增加一些处理代码。

优点:

1、代理模式能将代理对象与真实被调用目标对象分离。

2、在一定程度上降低了系统的耦合性,扩展性好。

3、可以起到保护目标对象的作用。

4、可以增强目标对象的功能。

缺点:

1、代理模式会造成系统设计中类的数量增加。

2、在客户端和目标对象中增加一个代理对象,会导致请求处理速度变慢。

3、增加了系统的复杂度。

应用场景:系统日志、事物代理、springAopd等。

静态代理:

    静态代理只能通过手动完成代理操作,如果被代理类增加了新的方法,代理类需同步增加,违背开闭原则。

    代码示例:  (静态代理只负责自己代理的;如下只能代理售票,如新增一个售卖其他的类无法代理)

//售票接口(抽象主题角色)
public interface ITicketSales {
    
    void sellTrainTicket();
}

//张三购票(真实主题角色)
public class ZhangsanBuyTickets implements ITicketSales {
    @Override
    public void sellTrainTicket() {
        System.out.println("张三购买火车票"); 
    }
}
//代理主角色
public class Proxy implements ITicketSales{
    
    private ITicketSales ticketSales;
    
    public Proxy(ITicketSales ticketSales) {
        this.ticketSales = ticketSales;
    }
    
    private void after(){
        System.out.println("黄牛代理售票收取手续费");
    }
    
    private void before(){
        System.out.println("取票");
    }
    
    @Override
    public void sellTrainTicket() {
        after();
        ticketSales.sellTrainTicket();
        before();
    }
}

动态代理:

动态代理采用在运行时动态生成代码的方式,取消了被代理类的扩展性,遵循开闭原则。

若动态代理要对目标类的增强逻辑进行扩展,结合策略模式,只需要新增策略类便可完成,无需修改代理类的代码

    JDK动态代理:

       代理示例:(JDK实现InvocationHandler 接口,JDK动态代理实现了被代理对象的接口,售卖其他的也可代理,动态代理更灵活,违背单一职责)

//JDK代理主角色
public class Cattle implements InvocationHandler {
    
    private Object target;
    
    public Object getInstance(Object target){
        this.target=target;
        Class<?> aClass = target.getClass();
        return Proxy.newProxyInstance(aClass.getClassLoader(),aClass.getInterfaces(),this);
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        after();
        Object invoke = method.invoke(this.target, args);
        before();
        return invoke;
        
    }
    
    private void after(){
        System.out.println("黄牛代理售票收取手续费");
    }
    
    private void before(){
        System.out.println("取票");
    }
}

    CGLIB动态代理:

        代理示例:(CGLIB实现MethodInterceptor 接口,CGLIB代理继承了被代理对象,售卖其他的也可代理,动态代理更灵活,违背单一职责)

public class Cattle implements MethodInterceptor {
    
    
    public Object getInstance(Class<?> clazz){
        Enhancer enhancer=new Enhancer();
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return enhancer.create();
    }
    
    
    private void after(){
        System.out.println("黄牛代理售票收取手续费");
    }
    
    private void before(){
        System.out.println("取票");
    }
    
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        after();
        Object invoke = methodProxy.invokeSuper(o, objects);
        before();
        return invoke;
    }
}

CGLIB和JDK动态代理区别:

    1、JDK动态代理实现了被代理对象的接口,CGLIB代理继承了被代理对象。

    2、JDK动态代理和CGLIB动态代理都在运行期间生成字节码,JDK动态代理直接写Class字节码,CGLIB代理使用ASM框架写Class字节码,CGLIB代理实现更复杂,生成代理类比JDK动态代理效率低。

    3、JDK动态代理调用代理方法是通过反射机制调用的,CGLIB代理是通过FastClass机制直接调用方法,CGLIB代理的执行效率更高。

 模拟JDK动态代理:

/**
 * @Title: MySimpleJavaFileObject
 * @Description: 为 JavaFileObject 中的大多数方法提供简单实现。应子类化此类并用作 
 *        JavaFileObject 实现的基础。子类可以重写此类任意方法的实现和规范,
 *        只要不违背 JavaFileObject 的常规协定。
 */

public class MySimpleJavaFileObject extends SimpleJavaFileObject {
    //源码
    private String source;
    //加载class流
    private ByteArrayOutputStream outputStream;
    /**
     * java源码抽象实现
     * @param className
     * @param source
     */
    public MySimpleJavaFileObject(String className, String source) {
        super(URI.create("string:///" + className.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
        this.source=source;
    }
    
    /**
     * 返回源码
     * @param ignoreEncodingErrors
     * @return
     * @throws IOException
     */
    @Override
    public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
        return this.source;
    }
    
    /**
     * class源码抽象实现
     * @param className
     * @param kind
     */
    public MySimpleJavaFileObject(String className, Kind kind) {
        super(URI.create("string:///" + className.replace('.', '/') + kind.extension), kind);
    }
    
    @Override
    public OutputStream openOutputStream() throws IOException {
        return outputStream=new ByteArrayOutputStream();
    }
    
    /**
     * 加载class需要当前class流
     * @return
     */
    public byte[] getClassBytes() {
        return outputStream.toByteArray();
    }
}



/**
 * @Title: MyJavaFileManager
 * @Description: 只要继承ForwardingJavaFileManager类就可以实现从内存中直接加载Java源文件,
 * 继承并重写URLClassLoader.findClass()就可以实现将编译结果直接以byte[]形式返回
 */

public class MyJavaFileManager extends ForwardingJavaFileManager<JavaFileManager> {
    private MySimpleJavaFileObject classFileObject;
    
    protected MyJavaFileManager(JavaFileManager fileManager) {
        super(fileManager);
    }
    
    @Override
    public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
        classFileObject = new MySimpleJavaFileObject(className, kind);
        return classFileObject;
    }
    
    @Override
    public ClassLoader getClassLoader(Location location) {
        return new ClassLoader() {
            @Override
            protected Class<?> findClass(String name) throws ClassNotFoundException {
                byte[] classBytes = classFileObject.getClassBytes();
                return super.defineClass(name, classBytes, 0, classBytes.length);
            }
        };
    }
}

/**
 * 模仿JDK InvocationHandler接口
 */
public interface ImitateInvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable;
}


/**
 * @Title: AchieveProxy
 * @Description: 模仿JDK代理
 */

public class AchieveProxy {
    
    public static final String ln = "\r\n";
    
    public static Object newProxyInstance(Class<?> [] interfaces, ImitateInvocationHandler h){
        String generateSrc = generateSrc(interfaces);
        String className=getFullClassName(generateSrc);
        //生成源代码的JavaFileObject
        SimpleJavaFileObject fileObject = new MySimpleJavaFileObject(className, generateSrc);
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        //被修改后的JavaFileManager
        JavaFileManager fileManager = new MyJavaFileManager(compiler.getStandardFileManager(null, null, null));
        //执行编译
        JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, null, null, Arrays.asList(fileObject));
        task.call();
        //获得ClassLoader,加载class文件
        ClassLoader classLoader = fileManager.getClassLoader(null);
        try {
            Class<?> clazz = classLoader.loadClass(className);
            Constructor c = clazz.getConstructor(ImitateInvocationHandler.class);
            return c.newInstance(h);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    
    
    private static String generateSrc(Class<?>[] interfaces){
        StringBuffer sb = new StringBuffer();
        sb.append(AchieveProxy.class.getPackage() + ";" + ln);
        sb.append("import " + interfaces[0].getName() + ";" + ln);
        sb.append("import java.lang.reflect.*;" + ln);
        sb.append("public class $Proxy0 implements " + interfaces[0].getName() + "{" + ln);
        sb.append("ImitateInvocationHandler h;" + ln);
        sb.append("public $Proxy0(ImitateInvocationHandler h) { " + ln);
        sb.append("this.h = h;");
        sb.append("}" + ln);
        for (Method m : interfaces[0].getMethods()){
            Class<?>[] params = m.getParameterTypes();
            
            StringBuffer paramNames = new StringBuffer();
            StringBuffer paramValues = new StringBuffer();
            StringBuffer paramClasses = new StringBuffer();
            
            for (int i = 0; i < params.length; i++) {
                Class clazz = params[i];
                String type = clazz.getName();
                String paramName = toLowerFirstCase(clazz.getSimpleName());
                paramNames.append(type + " " +  paramName);
                paramValues.append(paramName);
                paramClasses.append(clazz.getName() + ".class");
                if((i+1)!=params.length){
                    paramNames.append(",");
                    paramClasses.append(",");
                    paramValues.append(",");
                }
            }
            
            sb.append("public " + m.getReturnType().getName() + " " + m.getName() + "(" + paramNames.toString() + ") {" + ln);
            sb.append("try{" + ln);
            sb.append("Method m = " + interfaces[0].getName() + ".class.getMethod(\"" + m.getName() + "\",new Class[]{" + paramClasses.toString() + "});" + ln);
            sb.append((hasReturnValue(m.getReturnType()) ? "return " : "") + getCaseCode("this.h.invoke(this,m,new Object[]{" + paramValues + "})",m.getReturnType()) + ";" + ln);
            sb.append("}catch(Error _ex) { }");
            sb.append("catch(Throwable e){" + ln);
            sb.append("throw new UndeclaredThrowableException(e);" + ln);
            sb.append("}");
            sb.append(getReturnEmptyCode(m.getReturnType()));
            sb.append("}");
        }
        sb.append("}" + ln);
        return sb.toString();
    }
    
    
    private static Map<Class,Class> mappings = new HashMap<Class, Class>();
    static {
        mappings.put(int.class,Integer.class);
    }
    
    private static String getReturnEmptyCode(Class<?> returnClass){
        if(mappings.containsKey(returnClass)){
            return "return 0;";
        }else if(returnClass == void.class){
            return "";
        }else {
            return "return null;";
        }
    }
    
    private static String getCaseCode(String code,Class<?> returnClass){
        if(mappings.containsKey(returnClass)){
            return "((" + mappings.get(returnClass).getName() +  ")" + code + ")." + returnClass.getSimpleName() + "Value()";
        }
        return code;
    }
    
    private static boolean hasReturnValue(Class<?> clazz){
        return clazz != void.class;
    }
    
    private static String toLowerFirstCase(String src){
        char [] chars = src.toCharArray();
        chars[0] += 32;
        return String.valueOf(chars);
    }
    
    public static String getFullClassName(String sourceCode) {
        String className = "";
        Pattern pattern = Pattern.compile("package\\s+\\S+\\s*;");
        Matcher matcher = pattern.matcher(sourceCode);
        if (matcher.find()) {
            className = matcher.group().replaceFirst("package", "").replace(";", "").trim() + ".";
        }
        
        Pattern CLASS_PATTERN = Pattern.compile("class\\s+([$_a-zA-Z][$_a-zA-Z0-9]*)\\s*");
        matcher = CLASS_PATTERN.matcher(sourceCode);
        if(matcher.find()){
            //获取class类名
            className += matcher.group(1);
        } else{
            throw new IllegalArgumentException("No such class name in " + sourceCode);
        }
        return className;
    }
}



/**
 * @Title: Cattle
 * @Description: 黄牛代理售票
 */

public class Cattle implements ImitateInvocationHandler{
    
    private Object target;
    
    public Object getInstance(Object target){
        this.target=target;
        Class<?> aClass = target.getClass();
        return AchieveProxy.newProxyInstance(aClass.getInterfaces(),this);
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        after();
        Object invoke = method.invoke(this.target, args);
        before();
        return invoke;
        
    }
    
    private void after(){
        System.out.println("黄牛代理售票收取手续费");
    }
    
    private void before(){
        System.out.println("取票");
    }
}

//测试
    public static void main(String [] args){
    
        //模拟JDK动态代理
        com.learn.proxy.dynamic.imitatejdkproxy.client.Cattle ct=new com.learn.proxy.dynamic.imitatejdkproxy.client.Cattle();
        ITicketSales sales=(ITicketSales)ct.getInstance(new ZhangsanBuyTickets());
        sales.sellTrainTicket();
    
    
    
        //通过反编译工具可以查看源代码
        //try {
        //    byte [] bytes = ProxyGenerator.generateProxyClass("$Proxy0",
        //            new Class[]{ITicketSales.class});
        //    FileOutputStream os = new FileOutputStream("D://workspace//$Proxy0.class");
        //    os.write(bytes);
        //    os.close();
        //} catch (IOException e) {
        //    e.printStackTrace();
        //}
    }

输出:
黄牛代理售票收取手续费
张三购买火车票
取票

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!