Java 的静态代理和动态代理

一、代理概念:Proxy代理模式是一种结构型设计模式,其目的就是为其他对象提供一个代理以控制对该对象的访问。代理类和委托类有共同的父类或接口,这样在任何使用委托类对象的地方动可以用代理对象替代。代理类负责请求的预处理、过滤、将请求分派给委托类处理、以及委托类执行完后的后续处理。

二、静态代理

由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。

静态类的优点:业务类只需要关注业务逻辑本身,保证了业务类的重用性。这是代理的共同有点。

静态代理缺点:1代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。2代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。

package proxy;

/**
 * 代理接口,用于定义要执行的任务
 * @author 21409262
 *
 */
public interface Subject {

	/**
	 * 需要执行的任务
	 */
	public  void task(String taskName);
	
}

package proxy;

/**
 * 真正执行任务的类,实现了代理接口的方法
 * @author 21409262
 *
 */
public class RealSubject implements Subject {

	@Override
	public void task(String taskName) {
		// TODO Auto-generated method stub
		System.out.println("正在执行任务: "+taskName);
	}

}

package proxy;

/**
 * 静态代理类,实现了了代理接口
 * @author 21409262
 *
 */
public class StaticProxySubject implements Subject {

	/**
	 * 代理类持有一个委托类的对象引用
	 */
    private Subject delegate; 
    
    public StaticProxySubject(Subject delegate){
    	this.delegate = delegate;
    }
    
    /**
     * 代理类先执行预处理命令,再将请求执行的任务分派给委托类,委托类执行完任务后,再做后续处理
     */
	@Override
	public void task(String taskName) {
		// TODO Auto-generated method stub
		System.out.println("执行任务前,预处理...");
		//调用委托类执行任务
		delegate.task(taskName);
		
		System.out.println("执行任务后,后续处理...");
	}
}

三、动态代理:动态代理类的字节码在程序运行时有Java反射机制动态生成,无需成员编写他的源代码。

优点:动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展行,因为Java反射机制可以生成任意类型的动态代理类对象。java.lang.reflect 包中的Proxy类和InvocationHandler 接口提供了生成动态代理类的能力

缺点:JDK的动态代理依靠接口实现,如果有些累没有实现接口,则不能使用JDK代理,这就要使用CGLib的动态代理了。

 

package proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
 * Jdk 自带的动态代理
 */
public class JavaDynamicProxySubject implements InvocationHandler {
	//代理类持有个一委托类对象
	private Object target;	
	//绑定关系,也就是关联到哪个接口(与具体的实现类绑定)的哪些方法将被调用时,执行invoke方法。              
    public Object newProxyInstance(Object target){  
        this.target=target;  
        //该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例    
        //第一个参数指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器  
        //第二个参数要实现和委托类对象一样的接口,所以只需要拿到委托类对象的实现接口  
        //第三个参数表明这些被拦截的方法在被拦截时需要执行哪个InvocationHandler的invoke方法  
        //根据传入的委托类对象返回一个代理对象  
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),  
        		target.getClass().getInterfaces(),this);  
    }  
  
   //关联的这个实现类的方法被调用时将被执行  
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {		      
	        Object ret=null;  
	        try{  
	            /*原对象方法调用前处理日志信息*/ 
	        	System.out.println("执行任务之前的预处理..."); 
	        	
	            //调用目标方法  
	            ret=method.invoke(target, args); 
	            
	            /*原对象方法调用后处理日志信息*/  
	            System.out.println("执行任务之后的后续处理...");  
	        }catch(Exception e){  
	            System.out.println("执行任务时出现异常...");  
	            throw e;  
	        }  
	        return ret;  
	}
}

CGLIB代理:CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。

缺点:GLib创建的动态代理对象性能比JDK创建的动态代理对象的性能高不少,但是CGLib在创建代理对象时所花费的时间却比JDK多得多,所以对于单例的对象,因为无需频繁创建对象,用CGLib合适,反之,使用JDK方式要更为合适一些。同时,由于CGLib由于是采用动态创建子类的方法,对于final方法,无法进行代理。

</pre><pre name="code" class="html"><pre name="code" class="html"><span style="font-size:18px;">package proxy;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class CGLibDynamicProxySubject implements MethodInterceptor {

	private Object target;
	/** 
     * 创建代理对象 
     *  
     * @param target 
     * @return 
     */  
    public Object getInstance(Object target) {  
        this.target = target;  
        Enhancer enhancer = new Enhancer();  
        enhancer.setSuperclass(this.target.getClass());  
        // 回调方法  
        enhancer.setCallback(this);  
        // 创建代理对象  
        return enhancer.create();  
    }  
    
    /**
     * intercept()方法拦截所有目标类方法的调用,
     * obj表示目标类的实例,
     * method为目标类方法的反射对象,
     * args为方法的动态入参,
     * proxy为代理类实例。
     * proxy.invokeSuper(obj, args)通过代理类调用父类中的方法。
     */
	@Override
	public Object intercept(Object obj, Method method, Object[] arg,
			MethodProxy proxy) throws Throwable {
		// TODO Auto-generated method stub
		System.out.println("执行任务前,预处理...");  
        proxy.invokeSuper(obj, arg);  
        System.out.println("执行任务后,后续处理...");  
        return null;  
	}

}
</span>
<span style="font-size:18px;">
</span>
<span style="font-size:18px;">
</span>
package proxy;

public class TestProxy {

	public static void main(String[] args) {
		
		//定义委托类对象
		Subject subject = new RealSubject();
		
		//将委托类对象做为参数传入代理类,创建代理类对象
		/*	Subject proxy = new StaticProxySubject(subject);
		
		//代理类对象执行委托类对象的任务
		proxy.task("拯救A股");*/
		
		
		CGLibDynamicProxySubject cglib=new CGLibDynamicProxySubject();  
		//根据代理类获得具体的代理对象
		RealSubject subject1=(RealSubject)cglib.getInstance(subject);  
		subject1.task("拯救A股");
	}

}



 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值