Java静态代理与动态代理

代理模式

代理模式从字面理解可以和网络中的代理类比,之前的一篇博文中介绍了ngrok反向代理和nginx负载均衡。这整个链路都存在三个主体,客户端对象,代理对象,被代理对象。实际上java编程中代理模式也可以从这个链路过程去考虑。
cmd-markdown-logo

Java静态代理

Java静态代理的代码实现

1.创建一个明星类接口定义行为:

1
2
3
4
public interface IMovieStar {
public void movieShow(int money);
public void tvShow(int money);
}

2.实现这个明星类接口(被代理对象):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Star implements IMovieStar {
String starname;
public Star(String name){
this.starname=name;
}
@Override
public void movieShow(int money) {
System.out.println(starname+"出演了一部"+money+"的电影!");
}

@Override
public void tvShow(int money) {
System.out.println(starname+"出演了一部"+money+"的电视剧!");
}
}

3.静态代理类(实现明星类接口,并定义对象):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class StaticProxyHandler implements IMovieStar {
private IMovieStar star;
public StaticProxyHandler(IMovieStar star){
this.star=star;
}
@Override
public void movieShow(int money) {
System.out.println("before method");
star.movieShow(100000);
System.out.println("after method");
}

@Override
public void tvShow(int money) {
System.out.println("before method");
star.tvShow(200000);
System.out.println("after method");
}
}

4.客户端调用代理对象:

1
2
3
4
5
6
7
8
9
public class Main {
public static void main(String[] args){
Star huangbo=new Star("huangbo");
// ProxyHandler agenthandler=new ProxyHandler(huangbo);
StaticProxyHandler agenthandler=new StaticProxyHandler(huangbo);
agenthandler.movieShow(100000);
agenthandler.tvShow(200000);
}
}

5.输出结果:

1
2
3
4
5
6
before method
huangbo出演了一部100000的电影!
after method
before method
huangbo出演了一部200000的电视剧!
after method

总结

静态代理实际上是对代理对象的一个组合封装,通过代理对象去增强被代理对象的方法逻辑,在方法执行前增加了代理对象的逻辑,实现了代码的复用和解耦合。缺点是如果接口行为发生改变,如增加了接口,那么代理类和被代理类都需要改变(因为都实现了这个接口),同时如果需要对代理类的每个方法都添加统一的逻辑。如打日志通用的业务逻辑,通过静态代理的方式需要手动增加大量代码,不利于扩展。

Java动态代理

Java动态代理的代码实现

1.动态代理类(实现InvocationHandler接口):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class ProxyHandler implements InvocationHandler {
private Object mtarget;//被代理类对象,对应某个明星类
public ProxyHandler(Object target){
this.mtarget=target;
}
@Override
//关联的这个实现类的方法被调用时将被执行
/*InvocationHandler接口的方法,proxy表示代理,method表示原对象被调用的方法,args表示方法的参数*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodname=method.getName();
if(methodname.equals("movieShow") || methodname.equals("tvShow")){
if(args[0]instanceof Integer && (int)args[0]<3000000){
System.out.println(((int) args[0])+"?价格不达标不演");
}
}
Object result=method.invoke(mtarget,args);
return result;
}
//获取代理
//该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
//第一个参数指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器
//第二个参数要实现和目标对象一样的接口,所以只需要拿到目标对象的实现接口
//第三个参数表明这些被拦截的方法在被拦截时需要执行哪个InvocationHandler的invoke方法
//根据传入的目标返回一个代理对象
public Object getProxy(){
return Proxy.newProxyInstance(mtarget.getClass().getClassLoader(),mtarget.getClass().getInterfaces(),this);
}
}

2.客户端调用代理对象:

1
2
3
4
5
6
7
8
9
public class Main {
public static void main(String[] args){
Star huangbo=new Star("huangbo");
ProxyHandler agenthandler=new ProxyHandler(huangbo);
IMovieStar agent= (IMovieStar) agenthandler.getProxy();
agent.movieShow(100000);
agent.tvShow(200000);
}
}

3.输出结果:

1
2
3
4
100000?价格不达标不演
huangbo出演了一部100000的电影!
200000?价格不达标不演
huangbo出演了一部200000的电视剧!

总结

这里的动态代理更准确来说是JDK动态代理,它通过实现InvocationHandler接口的ProxyHandler类以发射的方式动态生成一个代理类(对应于我们上面的StaticProxyHandler)。代理类在调用每个方法时实际上执行的都是ProxyHandler类中的invoke方法,这样我们可以理解到这个代理类实际上是在生成实现类之后在每个方法中进行了反射调用。

Java静态代理与动态代理对比

Java静态代理是静态的,需要手动维护代理类,当接口变动时需要人为感知,同时修改被代理类和代理类,同时需要为每个接口的实现方法去手动添加一套逻辑。动态代理是动态的,接口变动时因为通过反射机制能够追溯到接口的变化情况去动态生成代理类,那么只需要手动修改被代理类即可,同时它会在每个实现方法中去自动添加一套逻辑。

总结

其实一开始想了解Spring AOP,最后发现AOP的面向切面编程也是基于JDK动态代理和CGLIB动态代理实现。之后会重点介绍这两种动态代理的区别已经在Spring AOP中的运用。


在此输入正文