zoukankan      html  css  js  c++  java
  • 代理模式、动态代理与AOP

    先来说说代理模式(静态代理):
    其为23种设计模式之一,属于结构型模式,其主要思想是通过一个代理对象来代替真实对象来响应client的调用或请求。静态代理要求代理类与真实类实现一个共同的接口,这样代理对象才能在“型”上代替真实对象。类图如下:


    一个通过代理模式来代理并增强真实对象的简单示例:

    //定义代理类与真实类的公共接口
    public interface Subject {
    	public void operation();
    }
    
    //真实主题,即被代理对象的类
    public class RealSubject implements Subject {
    	public void operation() {
    		System.out.println("执行业务处理... ...");
    	}
    }
    
    //使用代理对象来代替真实对象,并对真实对象中的处理方法进行增强
    public class ProxySubject implements Subject {
    	private Subject real;                        //持有真实对象
    	
    	public ProxySubject(Subject subject){        //constructor,注入真实的被代理的对象
    		this.real = subject;
    	}
    	
    	public void operation() {                    //对被代理的对象方法进行增强
    		beforeOperation();
    		real.operation();                    //真实主题中的方法
    		afterOperation();
    	}
    	
    	private void beforeOperation(){                 //增强处理
    		System.out.println("业务预先处理...");
    	}
    	private void afterOperation(){                  //增强处理
    		System.out.println("业务善后处理...");
    	}
    
    }
    
    //测试main函数
    public class Main {
    	public static void main(String[] args) {
    		Subject real = new RealSubject();
    		Subject proxy = new ProxySubject(real);
    		proxy.operation();                       //在实际使用时,操作代理对象来代替真实对象
    	}
    }

    代理模式应用非常广泛,在各种框架中也能找到其应用。例如在DAO(data access object)模式中:
    DAO接口:声明要对DB进行哪些操作
    DAOImpl类:实现DAO接口,定义对DB的具体操作
    DAOProxy类:代理DAOImpl,并额外进行DB的打开、关闭操作
    DAOFactory:创建代理对象的工厂

    然而静态代理并不是很灵活,如上例,就只能代理和增强实现了Subject接口的真实类,不能代理任意类型的真实类。

    下面来看动态代理
    动态代理即可以在运行时生成代理,其更加灵活,Java提供了Proxy类和InvocationHandler接口两个工具来实现动态代理功能。动态代理的流程如下图:


    使用java api中静态方法:Proxy.newProxyInstance()来生成对象:
    Proxy.newProxyInstance(classloader, interface, handler)  => proxy对象
    classloader:类加载器,用于将动态生成的代理类的字节码加载到JVM中,一般就使用被代理类的加载器
    interface:被代理类实现的接口,故此,当被代理中存在非接口的方法时,应该就无法被动态增强
    handler:InvocationHandler接口的实现类,在handler.invoke()中实现对被代理类接口方法进行增强

    动态代理示例:

    //真实类的接口型
    public interface Subject {
    	public void operation();                    //定义了真实类的操作
    }
    
    //真实类:
    public class RealSubject implements Subject {      //实现接口,完成业务处理
    	public void operation() {
    		System.out.println("执行业务处理... ...");
    	}
    }
    
    //增强工具:注意此工具没有实现Subject接口,完全是一个独立的工具方法单元,一般被称为拦截器
    public class EnhanceTool {
    	public void beforeOperation(){                  //增强处理
    		System.out.println("业务预先处理...");
    	}
    	public void afterOperation(){                   //增强处理
    		System.out.println("业务善后处理...");
    	}
    }
    
    //***动态代理的核心之处***
    public class MyInvocationHandler implements InvocationHandler {
    	private Object real;
    	public void setReal(Object real){              //持有被代理的对象
    		this.real = real;
    	}
                                          //InvocationHandler接口中的方法,在此方法中对被代理类接口方法增强
    	public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
    		EnhanceTool tool = new EnhanceTool();
    		tool.beforeOperation();                       //增强工具方法
    		Object result = method.invoke(real, args);    //通过反射调用真实对象中的方法
    		tool.afterOperation();                        //增强工具方法
    		return result;
    	}
    }
    
    //生成代理对象的工厂:
    public class MyProxyFactory {
    	public static Object getProxy(Object real) throws Exception{
    		MyInvocationHandler handler = new MyInvocationHandler();
    		handler.setReal(real);
    
                    //Proxy中的静态方法:输入参数(类加载器,被代理类的接口,InvocationHandler的实现类),返回代理对象
    		return Proxy.newProxyInstance(real.getClass().getClassLoader(), real.getClass().getInterfaces(), handler);
    	}
    }
    
    //测试main:
    	public static void main(String[] args) throws Exception {
    		Subject real = new RealSubject();
    		Subject proxy = (Subject) MyProxyFactory.getProxy(real);
    		proxy.operation();
    	}

    通过上面的示例能够看到,动态代理更加的灵活,其增强方法的类完全是一个独立于代理过程的单元,只是单纯的被调用,能随时被替换。且像生成代理对象或调用真实对象方法的具体操作,都由Java API来提供。能够非常好的实现解耦。

    接下来是AOP(aspect orient program),下面的示例使用动态代理来模拟spring中的AOP。根据xml文件中的配置信息来确定动态代理的增强流程和方法。(在时间切面上插入增强方法,来增加整体程序的功能)。

    //AOP接口,即动态代理中被代理类实现的接口
    public interface AOP {
    	public void saveMoney();
    	public void getMoney();
    }
    
    //被代理类,实现接口中的方法
    public class AOPImpl implements AOP {
    	public void saveMoney() {	
    		System.out.println("正在存钱...");
    	}
    	public void getMoney() {
    		System.out.println("正在取钱...");
    	}
    }
    //动态代理中的handler:
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.util.Map;
    
    public class AOPHandler implements InvocationHandler {
    	private Object obj; 
    	private boolean flag;  
    	
    	public AOPHandler(Object obj){
    		this.obj = obj;
    	}
    	
    	public void setFlag(Map<String, String> config) {     //根据解析xml得到的map,设置标志位,进而影响invoke中的执行流程
    		if (config == null) {
    			flag = false;
    		} else {
    			if (config.containsKey("transaction") && "true".equalsIgnoreCase(config.get("transaction"))) {
    				flag = true;
    			} else {
    				flag = false;
    			}
    		}
    	}
    	//关键之处,接口方法,当通过代理对象调用真实类中的方法时,实际调用的是下面的invoke方法
    	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if (flag) {                                //根据配置信息来决定是否执行
                doBefore();  
            }  
            Object result = method.invoke(obj, args);  //根据反射调用被代理类中的方法
            if (flag) {                                //根据配置信息来决定是否执行
                doAfter();  
            }  
            return result;  
    	}
    	
        private void doBefore() {                           //增强处理,或叫时间切面长插入的方法
            System.out.println("Transaction start...");  
        }  
      
        private void doAfter() {  
            System.out.println("Transaction commit...");  
        } 
        
    }
    //通过DOM( Document Object Model)来解析xml配置文件
    //将xml配置文件中的配置信息,解析为<key, value>对的形式放入map中
    import java.io.InputStream;
    import java.util.HashMap;
    import java.util.Map;
    import javax.xml.parsers.DocumentBuilder;
    import javax.xml.parsers.DocumentBuilderFactory;
    import org.w3c.dom.Document;
    import org.w3c.dom.Element;
    import org.w3c.dom.Node;
    import org.w3c.dom.NodeList;
    
    public class XMLUtil {
    	public static Map<String, String> parseXML() throws Exception {
    		Map<String, String> result = new HashMap<String, String>();
    		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    		DocumentBuilder db = dbf.newDocumentBuilder();
    
    		InputStream in = XMLUtil.class.getClassLoader().getResourceAsStream("config.xml");
    		Document document = db.parse(in);
    		Element root = document.getDocumentElement();
    		NodeList xmlNodes = root.getChildNodes();                        //将整个xml文件转换为NodeList形式
    		for (int i = 0; i < xmlNodes.getLength(); i++) {                  //遍历NodeList取得配置信息
    			Node config = xmlNodes.item(i);                           //Node封装了配置的节点名与值的信息
    			if (null != config && config.getNodeType() == Node.ELEMENT_NODE) {
    				String nodeName = config.getNodeName();
    				if ("transaction".equals(nodeName)) {                     //Node.getNodeName()
    					String textContent = config.getTextContent();     //Node.getTextContext()
    					result.put("transaction", textContent);           //将配置信息放入map中
    				}
    			}
    		}
    		return result;
    	}
    }
    //调用解析方法,将xml文件的解析结果放入map中
    import java.util.Map;
    
    public class JVMCache {
        private static Map<String, String> config;         //储存解析xml得到的配置信息
    	
        public synchronized static Map<String, String> getConfig() throws Exception {  
            if (config == null) {  
                config = XMLUtil.parseXML();  
            }  
            return config;  
        } 	
    }
    /src/config.xml配置文件
    <?xml version="1.0" encoding="UTF-8"?>
    <config>
    	<transaction>true</transaction>        //此处为改为fals,则handler.invoke()中的两个增强方法就不会执行
    </config>
    //测试主函数:
    public class Client {
    	public static void main(String[] args) throws Exception {
    		AOPImpl impl = new AOPImpl(); 
    		AOPHandler handler = new AOPHandler(impl);      //在handler中,持有一个被代理对象
    		handler.setFlag(JVMCache.getConfig());          //将xml配置文件解析出来的map传入handler
    		//获取代理对象
    		AOP aop = (AOP) Proxy.newProxyInstance(AOPImpl.class.getClassLoader(), new Class<?>[] {AOP.class}, handler); 
    		
    		aop.saveMoney();
    		aop.getMoney();
    	}
    }
    
  • 相关阅读:
    卡特兰数
    hdu 1023 Train Problem II
    hdu 1022 Train Problem
    hdu 1021 Fibonacci Again 找规律
    java大数模板
    gcd
    object dection资源
    Rich feature hierarchies for accurate object detection and semantic segmentation(RCNN)
    softmax sigmoid
    凸优化
  • 原文地址:https://www.cnblogs.com/dosmile/p/6444416.html
Copyright © 2011-2022 走看看