为什么要自定义动态代理类?
动态代理可以给目标类方法增加额外功能(功能增强)而不改变原目标类的代码,如添加日志
如果改变目标类方法很有可能使 业务方法 与 非业务方法(增加日志功能) 冗杂在一起,不符合开闭原则,从而使代码耦合度提高,并且不利于维护(详细可以看下面的例子)
JDK动态代理
Jdk动态代理要求目标对象必须实现接口
jdk动态代理实现步骤
-
创建目标类,SomeServiceImpl目标类,给它的doSome,doOther增加输出时间,事务。
-
创建InvocationHandler接口的实现类,在这个类实现给目标方法增加功能。
-
使用jdk中类Proxy,创建代理对象,实现创建对象的能力。
首先写一个需要实现动态代理功能的功能接口(如下有两个方法)
public interface SomeService {
void doThis();
void doThat();
}
写一个接口实现类(这是业务方法)
public class DoSomeService implements SomeService{
public void doThis(){
System.out.println("I am doThis");
}
public void doThat(){
System.out.println("I am doThat");
}
}
写一个日志工具类(这是非业务方法)
public class ServiceTool {
private ServiceTool(){}
public static void doLog(){
System.out.println("现在的时间是:"+ new Date());
}
public static void doCommit(){
System.out.println("事物已提交...");
}
}
如果没有动态代理,代码很有可能这样
public class DoSomeService implements SomeService{
public void doThis(){
ServiceTool.doLog(); //非业务代码
System.out.println("I am doThis");
ServiceTool.doCommit(); //非业务代码
}
public void doThat(){
ServiceTool.doLog(); //非业务代码
System.out.println("I am doThat");
ServiceTool.doCommit(); //非业务代码
}
}
缺点
虽然对于工具类的方法已经进行了很好的封装,但是对于业务代码和非业务代码还是冗杂在一起,如果后期需求需要修改,那么 doThis 和 doThat 方法又需要整个重新修改,很明显这是不符合开闭原则的,并且如果同样需要增加日志功能的模块不止两个,而是两百个那么这样进行修改的方法必然就是一种时间资源的浪费和代码的重复。因此我们需要使用动态代理。
创建一个动态代理类的构建器(可能不准确,但是可以这么理解)
public class MyInvocationHandler implements InvocationHandler {
public Object target; //声明一个target,代表着目标类(需要代理的类,如DoSomeService)
public MyInvocationHandler(Object target){ //当构造这个动态代理类时,指明目标类
this.target = target;
}
//重写invoke()方法,当对象执行方法时就会先执行invoke()方法
//因此我们在invoke()方法中进行额外功能的增加
@Override
public Object invoke(Object proxy, Method method,Object[] args){
Object result = null; //用来存储目标类执行后的返回值
try {
ServiceTool.doLog(); //额外功能(日志)
result = method.invoke(target,args); //目标类方法真正被执行
//注意这里的invoke()方法不是上方重写的那个invoke()方法
ServiceTool.doCommit(); //额外功能(提交)
} catch (Exception e) {
e.printStackTrace();
}
return result; //返回目标类方法执行的返回值
}
}
动态代理类的使用方法
public static void main(String[] args){
//target 代表需要被代理的目标类
SomeService target = new DoSomeService();
//构建动态代理类
MyInvocationHandler myInvocationHandler = new MyInvocationHandler(target);
//获取动态代理对象(这个才是真正能被使用的)
//需要的三个参数分别是:目标类的类构建器,目标类的实现接口,刚刚构建的动态代理类
SomeService someService =
(SomeService) Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
myInvocationHandler);
someService.doThat();
}
执行结果