Spring静态代理和动态代理代码详解

本节要点:

Java静态代理
Jdk动态代理

1 面向对象设计思想遇到的问题

在传统OOP编程里以对象为核心,并通过对象之间的协作来形成一个完整的软件功能,由于对象可以继承,因此我们可以把具有相同功能或相同特征的属性抽象到一个层次分明的类结构体系中。随着软件规范的不断扩大,专业化分工越来越系列,以及OOP应用实践的不断增多,随之也暴露了一些OOP无法很好解决的问题。

现在假设系统中有三段完全相似的代码,这些代码通常会采用“复制”、“粘贴”方式来完成,通过这种方式开发出来的软件如图所示:

Spring静态代理和动态代理代码详解_第1张图片

可能读者已经发现了这种做法的不足之处,如果有一天,蓝色背景的代码需要修改,那是不是要同时修改三个地方?如果不仅仅是这三个地方包含这段代码,而是100个,甚至是1000个地方,那会是什么后果?

记录日志在代码中无处不在---先来看一个例子:

为了跟踪应用程序的运行过程,很多方法都需要记录日志信息。我们一般这样写:

//log4j的使用见文章“log4j介绍”
import org.apache.log4j.Logger;
public class Person {
	private Logger logger = Logger.getLogger(Person.class);
	public void sleep(){
		logger.info(“开始执行时间:“ + new Date());
		System.out.println("睡觉中");
		logger.info(“执行结束时间:” + new Date());
	}
	public void eating(){
		logger.info("开始执行时间:“ + new Date()");
		System.out.println("正在吃饭中");
		logger.info("“执行结束时间:” + new Date()");
	}
}

提问:弊端在哪里?

l混淆了业务方法本身的职责

l维护工作量巨大

2解决方案1

静态代理:
   1、需要知道核心类(被代理类)是哪一个类,并且有什么方法。 
   2、非核心的代码需要重复写多次,显得代码的结构臃肿,形成代码冗余。
   3、非核心类(代理类)需要实现核心类(被代理类)实现的接口,也就是他们需要实现共同的接口,但是以核心类实现的接口(被代理类)为准。

l目地是将业务代码与日志代码完全分离,实现松散耦合.

l代理对象与被代理对象必须实现同一接口,在代理对象中实现与日志记录的相关服务,并在需要的时候呼叫被代理对象,而被代理对象只保留业务代码.

静态代理的实现

1)定义接口:

public interface IPerson {
	public abstract void sleep();
	public abstract void eating();
}

2) 被代理类

public class Person implements IPerson {
	public void sleep(){
		System.out.println("睡觉中");
	}
	public void eating(){
		System.out.println("正在吃饭中");
	}
}

3) 代理类

import org.apache.log4j.Logger;
public class PersonProxy implements IPerson {
	private IPerson person;
	private Logger logger = Logger.getLogger(PersonProxy.class);
	public PersonProxy(IPerson person) {
		this.person = person;
	}
	public void eating() {
		logger.info("开始执行时间:“ + new Date()");
		person.eating();
		logger.info("“执行结束时间:” + new Date()");
	}
	public void sleep() {
		logger.info("开始执行时间:“ + new Date()");
		person.sleep();
		logger.info("“执行结束时间:” + new Date()");
	}
}

4) 测试类

package com.aptech.aop2;
public class PersonTest {
	public static void main(String[] args) {
		IPerson proxy = new PersonProxy(new Person());
		proxy.eating();
		proxy.sleep();
	}
}

静态代理的弊端:

一个代理接口只能服务于一种类型的对象.对于稍大点的项目根本无法胜任.

3 解决方案2-动态代理

InvocationHandler:每一个动态代理类都必须实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的invoke方法来进行调用。

在JDK1.3之后加入了可协助开发的动态代理功能.不必为特定对象与方法编写特定的代理对象,使用动态代理,可以使得一个处理者(Handler)服务于各个对象.
一个处理者的类设计必须实现java.lang.reflect.InvocationHandler接口.
通过InvocationHandler接口实现的动态代理只能代理接口的实现类.

动态代理实现

1) 处理者(Handler)

public class DynaProxyHandler implements InvocationHandler {
	private Logger logger = Logger.getLogger(DynaProxyHandler.class);
	private Object target;
	//被代理对象
	public void setTarget(Object target) {
		this.target = target;
	}
	public Object invoke(Object proxy, Method method, Object[] args)
	              throws Throwable {
		logger.info("执行开始时间:" + new Date());
		Object result = method.invoke(target, args);
		logger.info("执行结束时间:" + new Date());
		return result;
		//返回method执行结果
	}
}

2) 生产代理对象的工厂

import java.lang.reflect.Proxy;
public class DynaProxyFactory {
	//obj为被代理对象
	public static Object getProxy(Object obj){
		DynaProxyHandler handler = new DynaProxyHandler();
		handler.setTarget(obj);
		return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler);
	}
}

3) 测试类

public class PersonTest {
	public static void main(String[] args) {
		IPerson person = (IPerson) DynaProxyFactory.getProxy(new Person());
		//返回代理类,代理类是JVM在内存中动态创建的,该类实现传入的接口数组的全部接口(的全部方法).
		person.eating();
		person.sleep();
	}
}

总结

以上就是本文关于Spring静态代理和动态代理代码详解的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站:

Spring常用配置及解析类说明

SpringMVC拦截器实现单点登录

Java编程实现springMVC简单登录实例

如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!

你可能感兴趣的:(Spring静态代理和动态代理代码详解)