Dynamic Proxy与Proxy Pattern

Dynamic Proxy,动态代理,你就可以想成是代购。你要法国的商品,你自己买不到,但是可以通过国内的代购公司买到。生产法国商品的原公司,就像是原对象(original object),而国内的代购公司就像是代理商(proxy),我们通过代理商(proxy)就能够拿到法国商品(调用original object的method),并且还可以对原来的法国商品进行“强化”,比如快递服务,售后服务等等(对original object的method进行前置和后置增强)。


JDK动态代理,需要有一个Handler,这个Handler,必须实现InvocationHandler接口,同时我们要实现接口中的invoke方法。因为代理产生后,我们就要拿到原公司的产品(调用original object的method),找谁调用,就找InvocationHandler的实现类。


一个JDK动态代理的案例:

package proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JdkProxyDemo {
	
    interface If {
        void originalMethod(String s);
    }
    
    static class Original implements If {
        public void originalMethod(String s) {
            System.out.println(s);
        }
    }
    
    static class Handler implements InvocationHandler {
        private final If original;
        public Handler(If original) {
            this.original = original;
        }
        public Object invoke(Object proxy, Method method, Object[] args)
                throws IllegalAccessException, IllegalArgumentException,
                InvocationTargetException {
            System.out.println("BEFORE");
            method.invoke(original, args);
            System.out.println("AFTER");
            return null;
        }
    }
    
    
    public static void main(String[] args){
        Original original = new Original();
        Handler handler = new Handler(original);
        If f = (If) Proxy.newProxyInstance(If.class.getClassLoader(),
                new Class[] { If.class },
                handler);
        f.originalMethod("Hello");
    }
}

Handler中包含了真实对象,在调用真实对象的方法的同时,进行一些增强:

  			System.out.println("BEFORE");
            method.invoke(original, args);
            System.out.println("AFTER");

注意产生代理的代码If f = (If) Proxy.newProxyInstance(If.class.getClassLoader(), new Class[] { If.class }, handler);

我们用Proxy的静态方法来产生代理,这是类似于工厂模式的产生代理的方式,产生的代理叫做$Proxy0, $Proxy1等等。

至于传入的参数,我们需要传入一个类加载器,一个Class数组,这个Class数组中,我们要放接口的Class对象,这些接口,就是原对象实现的接口,这决定了我们调用原对象的方法有多少、是什麽。


至于Proxy Pattern,你可以想象成给班主任教课本费。课本费当然不是直接交给班主任的,而是先交给班长,再由班长转交给班主任。这里班长有两个作用,第一个,他收集课本费,第二个,他可以检查你交的对不对。你交的不对(比如交少了),拿回去重新交过。

现在举一个办公室网络权限的问题。并不是所有的员工都能上所有的网站的,这是根据的你的身份来决定的。

首先,我们要有一个接口,定义一些方法:

package proxy_pattern;

public interface OfficeInternetAccess {

	public void grantInternetAccess();  
}

然后是一个实现类,这个类里面规定的员工可以有足够的权限:

package proxy_pattern;


public class RealInternetAccess implements OfficeInternetAccess {  
    private String employeeName;  
    public RealInternetAccess(String empName) {  
        this.employeeName = empName;  
    }  
    @Override  
    public void grantInternetAccess() {  
        System.out.println("Internet Access granted for employee: "+ employeeName);  
    }  
}  

然后就是代理类,他的功能就相当于是班长:

package proxy_pattern;

public class ProxyInternetAccess implements OfficeInternetAccess {
	private String employeeName;
	private RealInternetAccess realaccess;

	public ProxyInternetAccess(String employeeName) {
		this.employeeName = employeeName;
	}

	@Override
	public void grantInternetAccess() {
		if (getRole(employeeName) > 4) {
			realaccess = new RealInternetAccess(employeeName);
			realaccess.grantInternetAccess();
		} else {
			System.out.println("No Internet access granted. Your job level is below 5");
		}
	}

	public int getRole(String emplName) {
		// Check role from the database based on Name and designation
		// return job level or job designation.
		return 9;
	}
}

注意,他也实现了接口。不过这个代理类中实现的方法就更严格了,他要检查你的职位高低,低于某个阶段就没有足够的权限。


当我们要加载一些大的配置对象时,比如JDBC或者SessionFactory,这时,我们就应该加载一次就行了,并不是每次连接都要加载,所以我们要找一个代理来完成。

接口:

package proxy_pattern;

public interface ExpensiveObject {

	void process();
}

实际实现对象:

package proxy_pattern;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class ExpensiveObjectImpl implements ExpensiveObject {

	private static Logger LOG = LoggerFactory.getLogger(ExpensiveObjectImpl.class);

	public ExpensiveObjectImpl() {
		heavyInitialConfiguration();
	}

	@Override
	public void process() {
		LOG.info("processing complete.");

	}

	private void heavyInitialConfiguration() {
		LOG.info("Loading initial configuration...");
	}

}

代理对象:

package proxy_pattern;

public class ExpensiveObjectProxy implements ExpensiveObject {

	private static ExpensiveObject object;
	@Override
	public void process() {
		if(object == null) {
			object = new ExpensiveObjectImpl();
	
		}
		
		object.process();

	}

}

测试类:

package test;

import proxy_pattern.ExpensiveObject;
import proxy_pattern.ExpensiveObjectProxy;

public class ProxyPatternTest {

	public static void main(String[] args) {
		ExpensiveObject expensiveObject = new ExpensiveObjectProxy();
		expensiveObject.process();
		expensiveObject.process();
	}
}

这不仅使得配置文件只加载一次,而且隐藏了内部的具体操作,我们只需调用代理对象的process方法就行了,具体是怎么加载的我们不用关心。

另外,这个代理类中的方法process实在是太像单例模式了。对,就是几乎一样,他们的不同在于目的。单例模式的目的是一个类只有一个对象,而我们代理模式的目的是简化加载过程或者做一些安全检查之类的。

发布了25 篇原创文章 · 获赞 26 · 访问量 1145

猜你喜欢

转载自blog.csdn.net/weixin_43810802/article/details/103299032