代理模式使用代理对象完成用户的请求,屏蔽用户对真实对象的访问。
代理模式的用途很多,比如因为安全原因,需要屏蔽客户端直接访问真实对象;或者在远程调用中,需要使用代理对象处理远程方法中的技术细节;或者为了提升系统,对真是对象进行封装,从而达到延迟加载的目的。
在系统启动时,将消耗资源最多的方法使用代理模式分离,就可以加快系统的启动速度,减少用户的等待时间。在用户真正在做查询是,再由代理类加载真实的类,完成用户请求。这就是使用代理模式达到延迟加载的目的。
1.静态代理实现:
主题接口:
1 public interface IDBQuery { 2 String request(); 3 }
真实主题:
1 public class DBQuery implements IDBQuery { 2 public DBQuery(){ 3 try { 4 Thread.sleep(10000); 5 } catch (Exception e) { 6 e.printStackTrace(); 7 } 8 } 9 public String request() { 10 return "string request"; 11 } 12 }
代理类:
1 public class IDBQueryProxy implements IDBQuery { 2 private DBQuery dbquery; 3 public String request() { 4 if(dbquery==null) 5 dbquery = new DBQuery(); 6 return dbquery.request(); 7 } 8 }
最后,主函数:
1 public class ProxyText { 2 public static void main(String[] args) { 3 IDBQuery dbquery = new IDBQueryProxy(); 4 System.out.println(dbquery.request()); 5 } 6 }
静态代理注意,代理类是真实类实现共同的接口,并且代理类引用真实类对象,将耗时操作放在代理类方法中实现。
动态代理:
动态代理即运行时,动态生成代理类。即:代理类的字节码在运行时生成并载入当前的classloader。与静态代理相比,动态代理不需要为真实注意封装一个形式上完全一样的封装类,假如主题接口很多,就要为每一个接口写一个代理方法是很烦人的,如果接口有变动,真实类和代理类都需要变化,这样不利于系统维护;其次,使用一些动态代理的生成方法甚至可以在运行是指定代理类的执行逻辑,从而大大提高的系统的灵活性。
主题接口:
1 public interface IDBQuery { 2 String request(); 3 }
jdk代理类:
1 public class JdbDbqueryHandler implements InvocationHandler{ 2 IDBQuery idbquery = null; 3 @Override 4 public Object invoke(Object proxy, Method method, Object[] args) 5 throws Throwable { 6 if(idbquery==null){ 7 idbquery = new DBQuery(); 8 } 9 return idbquery.request(); 10 } 11 public static IDBQuery createJdbProxy(){ 12 IDBQuery jdkProxy = (IDBQuery) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), 13 new Class[]{IDBQuery.class}, new JdbDbqueryHandler()); 14 System.out.println("JdbDbqueryHandler.createJdbProxy()"); 15 return jdkProxy; 16 } 17 18 }
主函数:
1 public class ProxyText { 2 public static void main(String[] args) { 3 IDBQuery idbQuery = JdbDbqueryHandler.createJdbProxy(); 4 System.out.println(idbQuery.request()); 5 } 6 }
另外,也可以使用CGLIB和javassist动态代理与jdk动态代理类似,但是jdk动态类的创建过程最快,因为这个内置实现的difineclass()方法被定义为native实现,故性能优于其他。在代理类的函数调用上,JDK的动态代理不如CGLIB和javassist动态代理,而javassist动态代理性能质量最差,甚至不如JDK的实现。在实际开发应用中,代理类的方法调用频率要远远高于代理类的实际生成频率,故动态代理的方法调用性能应该成为性能的关注点。JDK动态代理强制要求代理类和真是主题实现统一接口,CGLIB和javassist动态代理没有这样的要求。
在java中,动态代理的实现涉及到classloader的使用,以CGLIB为例,简要描述下动态类的加载过程。使用CGLIB生成动态代理,首先需要生成Enhancer类的实例,并制定用于处理代理业务的回调类。在enhancer.create()方法中,会使用DefaultGeneratorStrategy.Generate()方法生成代理类的字节码,并保存在byte数组中。接着调用reflectUtils.defineClass()方法,通过反射,调用ClassLoader.defineClass()方法,将字节码装载到classloader中,完成类的加载。最后,通过reflectUtils.newInstance()方法,通过反射生成动态类实例,并返回该实例。其他与该过程细节不同,但是生成逻辑相同。