Java设计模式之JDK动态代理

1、代理的概念
为某个对象提供一个代理,以控制对这个对象的访问。 代理类和委托类有共同的父类或父接口,这样在任何使用委托类对象的地方都可以
用代理对象替代。代理类负责请求的预处理、过滤、将请求分派给委托类处理、以及委托类执行完请求后的后续处理。
     
          功能:负责请求的预处理、过滤、执行完请求后续处理,使得委托类专注于业务处理.SpringAOP就是采用的这种模式

A1、根据代理类的生成时间不同,分为静态代理和动态代理.此处所讲为JDK代理,主要运用java的反射机制
(1)所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。
    Begin-------->生成步骤:   
       a首先业务接口 SubJect
       b然后委托类实现:RealSubject
       c再者:建立代理类,拥有一个委托类对象,然后重写方法,其中重写方法调用委托类的方法,并且可以做一些前置和后续处理
       d最后:建立代理对象的访问方式,一般是通过静态:static ProxyClass getInstance();
       End---------->
      
(2)动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成,所以不存在代理类的字节码文件。
         代理类和委托类的关系是在程序运行时确定。
    Begin-------->生成步骤:
    a. 实现InvocationHandler接口创建自己的调用处理器--->主要是对委托类的一些方法进行处理封装,其内部通常包含指向委托类
                 实例的引用,用于真正执行分派转发过来的方法调用 
   InvocationHandler handler = new InvocationHandlerImpl(..);  
    b. 给Proxy类提供ClassLoader和代理接口类型数组创建动态代理类
   Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... });  
    c. 以调用处理器类型为参数,利用反射机制得到动态代理类的构造函数
         Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class });  
    d. 以调用处理器对象为参数,利用动态代理类的构造函数创建动态代理类对象
         Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler }); 
        其中:Proxy类的静态方法newProxyInstance对上面具体步骤的后三步做了封装,简化了动态代理对象的获取过程。
    a.InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发 
   InvocationHandler handler = new InvocationHandlerImpl(..);  
    b.通过 Proxy 直接创建动态代理类实例 
   Interface proxy = (Interface)Proxy.newProxyInstance( classLoader,new Class[] { Interface.class },  handler );
    End---------->

(3)、优缺点比较:
  X1:静态代理类优缺点
  Advantage:业务类只需要关注业务逻辑本身,保证了业务类的重用性。这是代理的共有优点。
  Disadvantage:
  a.代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。
  b.如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
  X2:动态代理类优缺点
  Advantage:最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。这样,
           在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。此处的外围业务类似类似Spring AOP
  Disadvantage:无法对Class进行动态代理,因为所有代理类有共同的父类:Proxy,由于不能进行多继承。
    
A2、另外一种常用代理为cglib代理,基于类的动态代理,cglib动态代理底层是借助asm来实现的,而asm在生成类之后的相关执行过程中比较高效
  (可以通过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)
(1)
   Begin------------->生成步骤:
   a.需要实现MethodInterceptor接口,实现intercept方法。该代理中在委托类方法前后加入了自定义的切面逻辑,委托类方法的执行语句为:
     proxy.invokeSuper(object, args);
   b.获取增强的目标类的工厂Factory,其中增强的方法类对象是有Enhancer来实现的
     RealSubject realSubject = (RealSubject) enhancer.create(); 
   End------------->
  
(2) 
  cglib代理和jdk代理的区别:
  jdk动态代理的应用前提,必须是目标类基于统一的接口。如果没有上述前提,jdk动态代理不能应用。由此可以看出,jdk动态代理有
  一定的局限性,cglib这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势。
  相对来说,动态代理的实现更简单一些。
总结:
代理类和委托类都要去实现一个接口。以接口为桥梁实现代理。
代理类中设置接口为句柄属性,并提供setter方法。并构造一个带参(接口句柄)的构造器。覆盖接口中的方法中编写代理逻辑(之前或之后)。
测试---> 接口 接口句柄  =   new 代理类(new 委托类());

 
接口类
/**
 * 代理接口。处理给定名字的任务。
 */
public interface Subject {
	/**
	 * 执行给定名字的任务。
	 * @param taskName
	 *            任务名
	 */
	public void dealTask(String taskName);

	public void test2(String s);
}

委托类
/**
 * 真正执行任务的类,实现了代理接口。 也叫委托类
 */
public class RealSubject implements Subject {

	/**
	 * 执行给定名字的任务。这里打印出任务名,并休眠500ms模拟任务执行了很长时间
	 * @param taskName
	 */
	@Override
	public void dealTask(String taskName) {
		System.out.println("正在执行任务:" + taskName);
		try {
			Thread.sleep(500);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	public void test2(String s) {
		System.out.println("123" + s);
	}
}

动态代理处理类
/**
 * 动态代理类对应的调用处理程序类
 */
public class SubjectInvocationHandler implements InvocationHandler {

	// 代理类持有一个委托类的对象引用
	private Object delegate;

	public SubjectInvocationHandler(Object delegate) {
		this.delegate = delegate;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		long stime = System.currentTimeMillis();
		// 利用反射机制将请求分派给委托类处理。Method的invoke返回Object对象作为方法执行结果。
		// 因为示例程序没有返回值,所以这里忽略了返回值处理 ,这里method类似于一个对所有方法的描述..
		Object object = method.invoke(delegate, args);
		long ftime = System.currentTimeMillis();
		System.out.println("执行任务耗时" + (ftime - stime) + "毫秒");
		return object;
	}
}

动态工厂及测试
/**
 * 生成动态代理对象的工厂.
 */
public class DynProxyFactory {
	// 客户类调用此工厂方法获得代理对象。
	// 对客户类来说,其并不知道返回的是代理类对象还是委托类对象。
	public static Subject getInstance() {
		Subject delegate = new RealSubject();
		// InvocationHandler相当于对某个指定对象所有方法包括构造器的一个封装,通过InvocationHandler可以指定调用哪个方法...
		InvocationHandler handler = new SubjectInvocationHandler(delegate);
		Subject proxy = null;
		// proxy = Proxy.newProxyInstance(loader, interfaces, h);
		proxy = (Subject) Proxy
				.newProxyInstance(delegate.getClass().getClassLoader(),
						delegate.getClass().getInterfaces(), handler);
		return proxy;
	}

	public static void main(String[] args) {
		// DynProxyFactory.getInstance().dealTask("DBQueryTask");
		DynProxyFactory.getInstance().test2("china");
		// Class cls = Proxy.getProxyClass(new
		// RealSubject().getClass().getClassLoader(), Subject.class);
		// System.out.println(cls.getName());
		// System.out.println(Proxy.isProxyClass(cls));
	}
}

测试结果
123china
执行任务耗时1毫秒

猜你喜欢

转载自mr-white-b.iteye.com/blog/2281964
今日推荐