代理模式在开发中经常使用,用于维护扩展代码的功能很方便。
众所周知,spring的AOP底层用到了JDK的动态代理功能。
这里就用记事本写一个最简单的动态代理小程序。
需求:模拟保存和修改学生信息的功能,要求在方法中都加入起止日志和执行方法所用的时间。
先上效果截图:
使用普通业务处理效果如下:
加上动态代理后:
使用动态代理后,起止日志和方法耗时都加上去了,不管有多少个方法,该日志和耗时都会加上去,避免了写重复代码,放在生产环境中也可以很方便地拿掉不必要的功能。
源代码如下:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
class JdkProxyDemo
{
public static void main(String[] args)
{
/*
* 普通调用
*/
/*StudentService studentService=new StudentServiceImpl();
//添加学生张三
Student s1=new Student();
s1.setName("张三");
s1.setAge(18);
s1.setStudyNum("HBWH001");
studentService.saveStudent(s1);
//修改学生张三
s1.setAge(20);
studentService.modifyStudent(s1);*/
/*
* 代理调用
*/
//创建原始业务服务
StudentService studentService=new StudentServiceImpl();
//通过原始业务服务创建代理业务服务
StudentService proxyStudentService=(StudentService)new JdkProxy().getInstance(studentService);
Student s2=new Student();
s2.setName("李四");
s2.setAge(30);
s2.setStudyNum("HBSY008");
proxyStudentService.saveStudent(s2);
s2.setAge(25);
proxyStudentService.modifyStudent(s2);
}
}
interface StudentService
{
//模拟添加学生
void saveStudent(Student student);
//模拟修改学生
void modifyStudent(Student student);
}
class StudentServiceImpl implements StudentService
{
@Override
public void saveStudent(Student student){
System.out.println("保存学生【"+student.getName()+"】成功!");
System.out.println(student);
}
@Override
public void modifyStudent(Student student){
System.out.println("修改学生【"+student.getName()+"】成功!");
System.out.println(student);
}
}
/*
* 加入jdk动态代理
*/
class JdkProxy implements InvocationHandler
{
//改动态代理模拟代理StudentService的所有功能
private StudentService studentService;
//定义创建代理实例的方法
public Object getInstance(StudentService studentService){
System.out.println("创建动态代理实例");
this.studentService=studentService;
Class clazz=this.studentService.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
}
@Override
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
long start=System.currentTimeMillis();
System.out.println("执行开始");
Object invoke=method.invoke(this.studentService,args);
System.out.println("执行结束");
long end=System.currentTimeMillis();
System.out.println("执行该方法耗时:"+(end-start)+"毫秒");
return invoke;
}
}
class Person
{
private String name;
private Integer age;
public String getName(){
return this.name;
}
public void setName(String name){
this.name=name;
}
public Integer getAge(){
return this.age;
}
public void setAge(Integer age){
this.age=age;
}
}
class Student extends Person
{
private String studyNum;
public String getStudyNum(){
return this.studyNum;
}
public void setStudyNum(String studyNum){
this.studyNum=studyNum;
}
@Override
public String toString(){
return "姓名:"+super.getName()+"\t年龄:"+super.getAge()+"\t学号:"+studyNum;
}
}
完整代码,复制到记事本就可以编译运行。
总结:代理对象必须要实现InvocationHandler接口,并重写invoke方法,调用的时候一样要先创建自己写的普通业务接口,然后把自己写的接口丢给代理类,让其创建代理实例,通过该实例去间接调用接口服务。
API对InvocationHandler接口的解释很详细,好理解。