Java动态代理
为什么需要代理?
在不修改被代理类代码的条件下,可以对被代理类中的方法进行想要的处理,如添加日志,记录方法运行时间(testNG就有此功能,未修改我们的测试代码,却可以统计每个方法的运行时间)
从静态代理作为理解的入口:
何为静态代理?
可对应生活中的,hr统计测试岗位人员信息交给人事部主管。
首先创建一个Person接口,约束个人信息统计的方法:
public interface Person { void giveMessage(); void fun(); }
创建员工类:
package day03; public class Employee implements Person{ String name; Employee(String name){ this.name = name; } public void giveMessage(){ System.out.println("give Message"); } public void fun(){ System.out.println("other fun"); } }
创建员工代理类,相当于hr类:
public class EmployeePro implements Person{ Employee employee; EmployeePro(Employee employee){ this.employee = employee; } public void giveMessage(){ System.out.println("add logs"); System.out.println("give Message"); } public void fun(){ System.out.println("add logs"); System.out.println("other fun"); } }
测试下:
public class Test { public static void main(String[] args) { Employee employee = new Employee("Claire");//创建被代理人 EmployeePro employeePro = new EmployeePro(employee);//创建代理人对象 employeePro.giveMessage();//通过代理人来执行相应方法 } }
运行结果:
思考:
1.员工类中有2个方法,添加日志的代码就写了2遍,造成代码重复。
2.如果要统计开发岗位人员信息交给人事主管呢,又要怎么做?
需要再创建开发岗位的代理类,重写方法并添加日志。
以上缺点很明显:需要手动创建多个代理类,并且添加日志的功能重复N遍。
动态代理便很好的解决了该问题,下面是动态代理的实现方式:
创建一个类实现InvocationHandler:
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; //该类使用泛型,可以支持作为任何类的InvocationHandler,如果不理解,可以直接写成非泛型 public class MyInvocationHandler<T> implements InvocationHandler { T target;//需要关联的被代理对象,这里将代理与被代理关联起来 MyInvocationHandler(T target){ this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("add log");//在方法执行之前添加日志 Object result = method.invoke(target, args);//这里是通过反射调用target对象的method方法,参数是args return result; } }
测试下:
import java.lang.reflect.Proxy; public class ProxyTest { public static void main(String[] args) { //创建被代理人 Person employee = new Employee("claire"); //创建代理对象 Person employeePro = (Person) Proxy.newProxyInstance(ProxyTest.class.getClassLoader(), new Class<?>[]{Person.class}, new MyInvocationHandler<Person>(employee)); //代理对象执行相应方法 employeePro.giveMessage(); employeePro.fun(); } }
测试结果是:
我们还可以使用匿名类的方式来实现,代码就更简洁了,如下:
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class ProxyTestAnonymous { public static void main(String[] args) { Person employee = new Employee("claire"); Person emplPro = (Person) Proxy.newProxyInstance(ProxyTestAnonymous.class.getClassLoader(), new Class<?>[]{Person.class}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("add logs");//方法调用前添加日志 Object result = method.invoke(employee, args); return result; } }); emplPro.fun(); emplPro.giveMessage(); } }
以上我们就使用动态代理解决了上面提出的问题。
但是可以发现,Java自带动态代理,被代理类必须有对应的实现接口,这就比较受限了。