Java设计模式之静态代理模式与动态代理模式的区别(附源码)

Java设计模式之静态代理模式与动态代理模式的区别(附源码)

先看定义,代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。
在大部分场景中,都是在原有的业务逻辑代码前后设置前后置通知,在前后添加统一处理逻辑,利用代理来处理统一操作,当然也可以在通知中添加条件是否跳过业务逻辑代码的执行。
实际的应用场景有:日志、aop、事务处理、异常处理等功能,需要从业务代码逻辑中抽离出来的部分一般用代理类去操作。
静态代理:
举一个交班费的例子

public interface Person {
	// 上交班费
	void giveMoney();
}
public class Student implements Person {
	private String name;

	public Student(String name) {
		this.name = name;
	}

	@Override
	public void giveMoney() {
		System.out.println(name + "上交班费50元");
	}
}
public class StudentsProxy implements Person {
	// 被代理的学生
	Student stu;

	public StudentsProxy(Person stu) {
		// 只代理学生对象
		if (stu.getClass() == Student.class) {
			this.stu = (Student) stu;
		}
	}
	// 代理上交班费,调用被代理学生的上交班费行为
	public void giveMoney() {
		System.out.println("准备交班费了!");//设置前置通知
		stu.giveMoney();
		System.out.println("班费交好了");//后置通知
	}
}

测试

public class StaticProxyTest {
	public static void main(String[] args) {
		// 被代理的学生张三,他的班费上交有代理对象monitor(班长)完成
		Person zhangsan = new Student("张三");
		// 生成代理对象,并将张三传给代理对象
		Person monitor = new StudentsProxy(zhangsan);
		// 班长代理上交班费
		monitor.giveMoney();
	}
}

动态代理
代理类实现逻辑不一样

public class StuInvocationHandler<T> implements InvocationHandler {
	// invocationHandler持有的被代理对象
	T target;

	public StuInvocationHandler(T target) {
		this.target = target;
	}
	/**
	     * proxy:代表动态代理对象
	     * method:代表正在执行的方法
	     * args:代表调用目标方法时传入的实参
	     */
	    @Override
	    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
	        System.out.println("代理执行" +method.getName() + "方法");
	        //代理过程中插入监测方法,计算该方法耗时
	        MonitorUtil.start();
	        Object result = method.invoke(target, args);
	        MonitorUtil.finish(method.getName());
	        return result;
	    }
}

添加监控类(通知类)

public class MonitorUtil {
    private static ThreadLocal<Long> tl = new ThreadLocal<>();
    
    public static void start() {
        tl.set(System.currentTimeMillis());
    }
    
    //结束时打印耗时
    public static void finish(String methodName) {
        long finishTime = System.currentTimeMillis();
        System.out.println(methodName + "方法耗时" + (finishTime - tl.get()) + "ms");
    }
}

测试

public class ProxyTest {
	public static void main(String[] args) {
        
        //创建一个实例对象,这个对象是被代理的对象
        Person zhangsan = new Student("张三");
        
        //创建一个与代理对象相关联的InvocationHandler
        InvocationHandler stuHandler = new StuInvocationHandler<Person>(zhangsan);
        
        //创建一个代理对象stuProxy来代理zhangsan,代理对象的每个执行方法都会替换执行Invocation中的invoke方法
		Person stuProxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(),
				new Class<?>[] { Person.class }, stuHandler);

       //代理执行上交班费的方法
        stuProxy.giveMoney();
    }
}

学生类和person类不变
代码库:https://gitee.com/SlienceDemo/java-ee.git
java-ee /proxy 模块

可以看出代理类StuInvocationHandler通过实现InvocationHandler接口,重写invoke方法,利用反射将对象实例化。

总结
静态代理要在代理类中写死代理的对象,即便它可以抽象成接口,使得实现此接口的类都能使用代理,但是对于已有的业务逻辑再去为了这些功能去实现接口并不友好。
而动态代理通过泛型和反射来导入要代理的类,不用指定具体的类或接口,更具有扩展性,但也会因为调用时通过反射来实例化对象而影响性能。
总的来说,静态代理适用于已经开发好的程序,确定需要代理的类来扩展通知功能,并且以后不再扩展新的类或者接口。
动态代理适用于未开发完成的项目,后期可能其他类也需要此代理。建议用动态代理,毕竟需求变更这事儿就更天气一样。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值