• 代理模式、动态代理与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();
    	}
    }
    
  • 相关阅读:
    转:CSS设置HTML元素的高度与宽度的各种情况总结
    Java、mysql、html、css、js 注释&大小写
    Dom4j与sax 简单对比
    转:Java properties | FileNotFoundException: properties (系统找不到指定的文件。)
    转:SAX解析的characters方法被多次调用
    转:HashMap实现原理分析(面试问题:两个hashcode相同 的对象怎么存入hashmap的)
    转:Scanner中nextLine()方法和next()方法的区别
    转:JDBC中关于PreparedStatement.setObject的一些细节说明
    转:Eclipse 各种小图标的含义
    转:Mysql float类型where 语句判断相等问题
  • 原文地址:https://www.cnblogs.com/dosmile/p/6444416.html
Copyright © 2020-2023  润新知