1.概念

1.1静态代理

静态代理是指代理类的代码在编译时就已经确定,也就是说,代理类的实现是由程序员手动编写的。静态代理的优点在于实现简单,易于理解和维护。但是,由于代理类的代码在编译时就确定,因此无法在运行时动态地改变代理类的行为。

在Java中,静态代理的实现方式通常是通过接口实现的。首先定义一个接口,然后编写一个实现了该接口的类,最后编写一个代理类,该代理类调用实现了接口的类的相应方法。这样,就可以通过代理类来调用实现了接口的类的方法,而不需要修改原始代码。

1.2动态代理

动态代理是指代理类的代码在运行时动态生成,也就是说,代理类的实现是由运行时环境自动生成的。动态代理的优点在于可以在运行时动态地改变代理类的行为,提高了代码的灵活性和可维护性。

在Java中,动态代理的实现方式通常是通过Java反射机制和字节码操作库(如CGLIB、Javassist等)实现的。具体来说,通过反射机制获取目标对象的类信息,然后使用字节码操作库动态生成一个实现了相同接口的代理类,该代理类在调用方法时会自动调用目标对象的方法。

2.静态代理和动态代理的优缺点和应用场景 

2.1静态代理

优点:

  1. 实现简单:静态代理的代码在编译时就已经确定,因此实现起来比较简单。
  2. 易于理解和维护:由于代理类的代码在编译时就确定,因此易于阅读和理解,也方便维护。

缺点:

  1. 无法动态地改变代理类的行为:由于代理类的代码在编译时就确定,因此无法在运行时动态地改变代理类的行为。
  2. 扩展性较差:如果要添加新的功能或者修改原有的功能,需要修改和重新编译代理类的代码,比较麻烦。

应用场景:

静态代理适用于一些功能相对固定、不需要动态改变代理类的行为的情况。例如,在某些框架中,代理类用于拦截方法调用,实现AOP(面向切面编程)等功能。

2.2动态代理 

优点:

  1. 动态性:动态代理可以在运行时动态地生成代理类的代码,因此可以在不修改原始代码的情况下改变代理类的行为。
  2. 灵活性:动态代理可以根据需要添加新的功能或者修改原有的功能,提高了代码的灵活性和可维护性。
  3. 扩展性:由于动态代理可以在运行时动态地生成代理类的代码,因此可以方便地扩展代理类的功能。

缺点:

  1. 实现复杂:动态代理的实现涉及到Java反射机制和字节码操作库,因此实现起来比较复杂。
  2. 性能开销较大:由于动态代理涉及到反射和字节码操作,因此性能开销较大,可能会影响程序的性能。

应用场景:

动态代理适用于需要动态改变代理类的行为、扩展代理类的功能的情况。例如,在某些框架中,动态代理用于实现远程调用、事务管理、日志记录等功能。同时,在一些需要动态生成和修改代理类的场景中,也可以使用动态代理,例如在测试框架中模拟对象的行为等。

3.两者区别 

静态代理和动态代理的主要区别在于以下几个方面:

  1. 代理类的创建时期:静态代理的代理类在编译时期就已经确定,而动态代理的代理类是在运行时动态地生成的。
  2. 实现方式:静态代理通常由程序员手动编写代理类,而动态代理的实现方式通常是通过Java反射机制和字节码操作库(如CGLIB、Javassist等)实现的。
  3. 扩展性:静态代理的扩展性较差,如果要添加新的功能或者修改原有的功能,需要修改和重新编译代理类的代码。而动态代理的扩展性较好,可以根据需要添加新的功能或者修改原有的功能,提高了代码的灵活性和可维护性。
  4. 性能开销:静态代理的性能开销较小,因为代理类的代码在编译时就确定,没有额外的反射和字节码操作开销。而动态代理的性能开销较大,因为涉及到反射和字节码操作,可能会影响程序的性能。
  5. 适用场景:静态代理适用于一些功能相对固定、不需要动态改变代理类的行为的情况,例如在某些框架中用于拦截方法调用、实现AOP等功能。而动态代理适用于需要动态改变代理类的行为、扩展代理类的功能的情况,例如在某些框架中用于实现远程调用、事务管理、日志记录等功能。

综上所述,静态代理和动态代理的主要区别在于代理类的创建时期、实现方式、扩展性、性能开销和适用场景等方面。根据实际需求选择合适的代理方式可以更好地提高代码的灵活性和可维护性。

4.代码案例

4.1静态代理

1.抽象角色

package com.by.proxy.StaticProxy;

public interface Star {
	/**
	 * 面谈
	 */
	void confer();
	/**
	 * 签合同
	 */
	void signContract();
	/**
	 * 订票
	 */
	void bookTicket();
	/**
	 * 唱歌
	 */
	void sing();
	/**
	 * 收钱
	 */
	void collectMoney();
}

2.真正角色

package com.by.proxy.StaticProxy;

public class RealStar implements Star {

	public void bookTicket() {

	}

	public void collectMoney() {

	}

	public void confer() {

	}

	public void signContract() {

	}

	public void sing() {
		System.out.println("RealStar(周杰伦本人).sing()");
	}
}

3.代理角色

package com.by.proxy.StaticProxy;

public class ProxyStar implements Star {
	
	private Star star;
	
	public ProxyStar(Star star) {
		super();
		this.star = star;
	}

	public void bookTicket() {
		System.out.println("ProxyStar.bookTicket()");
	}

	public void collectMoney() {
		System.out.println("ProxyStar.collectMoney()");
	}

	public void confer() {
		System.out.println("ProxyStar.confer()");
	}

	public void signContract() {
		System.out.println("ProxyStar.signContract()");
	}

	public void sing() {
		star.sing();
	}
}

4.测试

package com.by.proxy.StaticProxy;

public class Client {
	public static void main(String[] args) {
		Star proxy = new ProxyStar(new RealStar());
		
		proxy.confer();
		proxy.signContract();
		proxy.bookTicket();
		proxy.sing();
		
		proxy.collectMoney();
		
	}
}

4.2动态代理

1.抽象角色

public interface Star {
    /**
     * 唱歌
     */
    void sing();
}

2.真正角色

package com.by.JdkProxy;

//真实角色(周杰伦)
public class RealStar implements Star {
    //优点:此时代码不再重复

    public void sing() {
        System.out.println("周杰伦:快使用双截棍,哼哼哈嘿....");

    }
}

3.代理角色

package com.by.JdkProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

//代理类工厂
public class ProxyFactory {

    //优点:此时可以代理任意类型的对象
    //真实角色(周杰伦)
    private Object realObj;

    public ProxyFactory(Object realObj) {
        this.realObj = realObj;
    }

    //获得代理对象
    public Object getProxyObject(){

        /**
         * Proxy:作用创建代理对象
         *      ClassLoader loader:类加载器
         *      Class<?>[] interfaces:真实角色实现的接口,根据接口生成代理类
         *      InvocationHandler h:增强的逻辑,即如何代理(宋吉吉要做的事)
         */
        return Proxy.newProxyInstance(
                realObj.getClass().getClassLoader(),
                realObj.getClass().getInterfaces(),
                new InvocationHandler() {
                    /**
                     *
                     * @param proxy:代理类,一般不用
                     * @param method:要调用的方法
                     * @param args:调用方法时的参数
                     * @return
                     * @throws Throwable
                     */
                    public Object invoke(Object proxy, Method method, Object[] args)
                        											throws Throwable {

                        System.out.println("真正的方法执行前!");
                        System.out.println("面谈,签合同,预付款,订机票");

                        Object result = method.invoke(realObj, args);

                        System.out.println("真正的方法执行后!");
                        System.out.println("收尾款");

                        return result;
                    }
                }
        );
    }
}

4.测试

public class Client {
    public static void main(String[] args) {
        //获得代理对象
        Star proxyObject = (Star) new ProxyFactory(new RealStar()).getProxyObject();
        System.out.println(proxyObject.getClass());//class com.sun.proxy.$Proxy0
        proxyObject.sing();
    }
}

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐