Java程序中的代理作用和应用场景及实现
Java程序中的代理作用和应用场景及实现
(The role of proxy, application scenarios, and implementation in Java programs.)
h1, h2, h3, h4 { color: rgba(17, 17, 17, 1); font-weight: 400 }
h1, h2, h3, h4, h5, p { margin-bottom: 16px; padding: 0 }
h1 { font-size: 28px }
h2 { font-size: 22px; margin: 20px 0 6px }
h3 { font-size: 21px }
h4 { font-size: 18px }
h5 { font-size: 16px }
a { color: rgba(0, 153, 255, 1); margin: 0; padding: 0; vertical-align: baseline }
a:link, a:visited { text-decoration: none }
a:hover { text-decoration: underline }
ul, ol { padding: 0; margin: 0 }
li { line-height: 24px; margin-left: 30px }
li ul, li ul { margin-left: 24px }
ul, ol { font-size: 14px; line-height: 20px; max-width: 98% }
p { font-size: 14px; line-height: 20px; max-width: 98%; margin-top: 3px }
pre { padding: 0 4px; max-width: 98%; white-space: pre; word-wrap: normal; overflow: auto; font-family: Consolas, Monaco, Andale Mono, monospace; line-height: 1.5; font-size: 13px; border: 1px solid rgba(221, 221, 221, 1); background-color: rgba(247, 247, 247, 1); border-radius: 3px }
code { font-family: Consolas, Monaco, Andale Mono, monospace; line-height: 1.5; font-size: 13px; border: 1px solid rgba(221, 221, 221, 1); background-color: rgba(247, 247, 247, 1); border-radius: 3px }
code pref { color: rgba(255, 0, 0, 1) }
pre code { border: 0 }
aside { display: block; float: right; width: 390px }
blockquote { border-left: 0.5em solid rgba(64, 170, 83, 1); padding: 0 2em; margin-left: 0; max-width: 98% }
blockquote cite { font-size: 14px; line-height: 20px; color: rgba(191, 191, 191, 1) }
blockquote cite:before { content: “— ” }
blockquote p { color: rgba(102, 102, 102, 1); max-width: 98% }
hr { height: 1px; border-top: 1px dashed rgba(0, 102, 204, 1); border-right: none; border-bottom: none; border-left: none }
button, input, select, textarea { font-size: 100%; margin: 0; vertical-align: baseline; *vertical-align: middle }
button, input { line-height: normal; *overflow: visible }
button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0 }
button, input[type=”button”], input[type=”reset”], input[type=”submit”] { cursor: pointer; -webkit-appearance: button }
input[type=”checkbox”], input[type=”radio”] { cursor: pointer }
input:not([type=”image”]), textarea { -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box }
input[type=”search”] { -webkit-appearance: textfield; -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box }
input[type=”search”]::-webkit-search-decoration { -webkit-appearance: none }
label, input, select, textarea { font-family: “Helvetica Neue”, Helvetica, Arial, sans-serif; font-size: 13px; font-weight: normal; line-height: normal; margin-bottom: 18px }
input[type=”checkbox”], input[type=”radio”] { cursor: pointer; margin-bottom: 0 }
input[type=”text”], input[type=”password”], textarea, select { display: inline-block; width: 210px; padding: 4px; font-size: 13px; font-weight: normal; line-height: 18px; height: 18px; color: rgba(128, 128, 128, 1); border: 1px solid rgba(204, 204, 204, 1); -webkit-border-radius: 3px; -moz-border-radius: 3px; border-radius: 3px }
select, input[type=”file”] { height: 27px; line-height: 27px }
textarea { height: auto }
:-moz-placeholder { color: rgba(191, 191, 191, 1) }
::-webkit-input-placeholder { color: rgba(191, 191, 191, 1) }
input[type=”text”], input[type=”password”], select, textarea { -webkit-transition: border linear 0.2s, box-shadow linear 0.2s; -moz-transition: border linear 0.2s, box-shadow linear 0.2s; transition: border 0.2s linear, box-shadow 0.2s linear; -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1); -moz-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1); box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1) }
input[type=”text”]:focus, input[type=”password”]:focus, textarea:focus { outline: none; border-color: rgba(82, 168, 236, 0.8); -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6); -moz-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6); box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6) }
button { display: inline-block; padding: 4px 14px; font-family: “Helvetica Neue”, Helvetica, Arial, sans-serif; font-size: 13px; line-height: 18px; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); box-shadow: inset 0 1px rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); background-color: rgba(0, 100, 205, 1); background-repeat: repeat-x; background-image: linear-gradient(0deg, rgba(4, 156, 219, 1), rgba(0, 100, 205, 1)); color: rgba(255, 255, 255, 1); text-shadow: 0 -1px rgba(0, 0, 0, 0.25); border-top: 1px solid rgba(0, 0, 0, 0.1); border-right: 1px solid rgba(0, 0, 0, 0.1); border-bottom: 1px solid rgba(0, 0, 0, 0.25); border-left: 1px solid rgba(0, 0, 0, 0.1); -webkit-transition: 0.1s linear all; -moz-transition: 0.1s linear all; transition: all 0.1s linear }
button:hover { color: rgba(255, 255, 255, 1); background-position: 0 -15px; text-decoration: none }
button:active { -webkit-box-shadow: inset 0 3px 7px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); -moz-box-shadow: inset 0 3px 7px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); box-shadow: inset 0 3px 7px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05) }
button::-moz-focus-inner { padding: 0; border: 0 }
table { border-spacing: 0; border: 1px solid rgba(204, 204, 204, 1) }
td, th { border: 1px solid rgba(204, 204, 204, 1); padding: 5px }
pre .literal, pre .comment, pre .template_comment, pre .diff .header, pre .javadoc { color: rgba(0, 128, 0, 1) }
pre .keyword, pre .css .rule .keyword, pre .winutils, pre .javascript .title, pre .nginx .title, pre .subst, pre .request, pre .status { color: rgba(0, 0, 255, 1); font-weight: bold }
pre .number, pre .hexcolor, pre .python .decorator, pre .ruby .constant { color: rgba(0, 0, 255, 1) }
pre .string, pre .tag .value, pre .phpdoc, pre .tex .formula { color: rgba(221, 17, 68, 1) }
pre .title, pre .id { color: rgba(153, 0, 0, 1); font-weight: bold }
pre .javascript .title, pre .lisp .title, pre .clojure .title, pre .subst { font-weight: normal }
pre .class .title, pre .haskell .type, pre .vhdl .literal, pre .tex .command { color: rgba(68, 85, 136, 1); font-weight: bold }
pre .tag, pre .tag .title, pre .rules .property, pre .django .tag .keyword { color: rgba(0, 0, 128, 1); font-weight: normal }
pre .attribute, pre .variable, pre .lisp .body { color: rgba(0, 128, 128, 1) }
pre .regexp { color: rgba(0, 153, 38, 1) }
pre .class { color: rgba(68, 85, 136, 1); font-weight: bold }
pre .symbol, pre .ruby .symbol .string, pre .lisp .keyword, pre .tex .special, pre .prompt { color: rgba(153, 0, 115, 1) }
pre .built_in, pre .lisp .title, pre .clojure .built_in { color: rgba(0, 134, 179, 1) }
pre .preprocessor, pre .pi, pre .doctype, pre .shebang, pre .cdata { color: rgba(153, 153, 153, 1); font-weight: bold }
pre .deletion { background: rgba(255, 221, 221, 1) }
pre .addition { background: rgba(221, 255, 221, 1) }
pre .diff .change { background: rgba(0, 134, 179, 1) }
pre .chunk { color: rgba(170, 170, 170, 1) }
pre .markdown .header { color: rgba(136, 0, 0, 1); font-weight: bold }
pre .markdown .blockquote { color: rgba(136, 136, 136, 1) }
pre .markdown .link_label { color: rgba(136, 136, 255, 1) }
pre .markdown .strong { font-weight: bold }
pre .markdown .emphasis { font-style: italic }
pref { color: rgba(255, 0, 0, 1) }
The role of proxy, application scenarios, and implementation in Java programs.
代理是程序设计和开发时频繁使用的技术,可以提高程序灵活性和可扩展性。
1 代理作用
- 在不修改原代码的基础上,扩展和增强实现;
- 代码解耦,在代理中通过参数可以判断真实类,做出不同的响应或调用,灵活方便;
- 隐藏部分实现过程和细节。
2 应用场景
ICar接口类,有一个drive方法,传入speed(速度)行驶,现在已经有Mitsubishi(三菱车)和Bwm(宝马车)实现了接口,随着业务发展,可能有更多的车加入;程序稳健运行一段时间后,针对不同的车发现了不同的问题需要处理,比如Mitsubishi车drive前必须检查和保证冷却液充足;Bwm车drive前必须检查和保证机油充足;由于改写ICar接口可能影响到其它的车;如果有很多的车要处理,更改实现类可能需要花费大量时间,此时可以通过代理类扩展和增强。
在代理类(CarProxy)中通过carCheck方法实现对不同汽车检查,检查通过后drive汽车,从而实现对原有代码增强。
3 代理的实现
3.1 静态代理
静态代理的思想:代理类和其它汽车类一样实现接口类,在重写方法里实现扩展或增强,并调要汽车类实现。
//宝马汽车实现类
class Bwm implements ICar {
public void drive(int speed) {
System.out.println("Bwm drive with speed:"+speed);
}
//三菱汽车实现类
class Mitsubishi implements ICar {
public void drive(int speed) {
System.out.println("Mitsubishi drive with speed:"+speed);
}
//汽车代理类
public class CarProxy implements ICar {
private ICar carTarget;
public CarProxy(ICar carTarget){
this.carTarget=carTarget;
}
// 车辆检查
private boolean carCheck() {
if(this.carTarget instanceof Mitsubishi){ //判断汽车类型
System.out.println("Mitsubishi 检查冷却液完成。");
return true;
}else if(this.carTarget instanceof Bwm){
System.out.println("Bwm 检查机油完成。");
return true;
}else{
System.out.println("该车不需要检查。");
return true;
}
}
/** 驾驶
* @param speed 速度
*/
public void drive(int speed) {
if(carCheck()){ //检查通过后drive
carTarget.drive(speed); //调用汽车类
}
}
}
在CarProxy代理类中,增加了对不同汽车的检查方法(carCheck),当汽车检查通过后调用(drive)方法驾驶汽车。这样代理类便实现了对三菱和宝马车的扩展,调用时传入ICar实现类对象;
new CarProxy(new Mitsubishi()).drive(80); //drive 三菱车
new CarProxy(new Bwm()).drive(100); //drive 宝马车
运行输出;
Mitsubishi 检查冷却液完成。
Mitsubishi drive with speed:80
Bwm 检查机油完成。
Bwm drive with speed:100
3.1.1 缺点
接口类变化会影响实现类和代理类;比如方法修改返回值、参数类型、增加方法,实现类和代理类都需要修改。
3.2 动态代理
Java 提供(java.lang.reflect.InvocationHandler)代理实例接口,代理类实现该接口关联调用处理程序的方法(invoke),当在代理实例调用方法时,方法调用被编码(encoded)并分配给其调用处理程序的方法。
public class DynamicProxy implements InvocationHandler{
Object carTarget;
//车辆检查
private boolean carCheck() {
if(this.carTarget instanceof Mitsubishi){
System.out.println("Mitsubishi 检查冷却液完成。");
return true;
}else if(this.carTarget instanceof Bwm){
System.out.println("Bwm 检查机油完成。");
return true;
}else{
System.out.println("该车不需要检查。");
return true;
}
}
//创建代理类实例
Object crateProxyInstance(Object carTarget) {
this.carTarget = carTarget;
return Proxy.newProxyInstance(carTarget.getClass().getClassLoader(),
carTarget.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if(carCheck()){//汽车检查通过后drive
return method.invoke(carTarget,args);
}
return null;
}
}
除了crateProxyInstance 和 invoke 方法外,对三菱和宝马车的扩展(车辆检查)与静态类相同。调用时同样传入ICar实现类对象。
((ICar)new DynamicProxy().crateProxyInstance(new Mitsubishi())).drive(80);//drive 三菱车
((ICar)new DynamicProxy().crateProxyInstance(new Bwm())).drive(80);//drive 宝马车
3.3 cglib 代理
除了java自身的代理类,还有第三方代理类,比如(cglib),通过实现方法拦截器(MethodInterceptor),在拦截方法中(intercept)调用处理程序的方法。
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class CarInterceptor implements MethodInterceptor{
Object carTarget;
//车辆检查
private boolean carCheck() {
if(this.carTarget instanceof Mitsubishi){
System.out.println("Mitsubishi 检查冷却液完成。");
return true;
}else if(this.carTarget instanceof Bwm){
System.out.println("Bwm 检查机油完成。");
return true;
}else{
System.out.println("该车不需要检查。");
return true;
}
}
//拦截方法
@Override
public Object intercept(Object carTarget, Method method, Object[] args, MethodProxy proxy) throws Throwable {
this.carTarget=carTarget;
if(carCheck()){//汽车检查通过后drive
Object obj=proxy.invokeSuper(carTarget, args);
return obj;
}
return null;
}
}
最后调用时,首先实例化(Enhancer)对象启用方法拦截器,设置方法拦截类和Superclass;
Enhancer en=new Enhancer();
en.setCallback(new CarInterceptor());//设置拦截类
//drive 三菱车
en.setSuperclass(new Mitsubishi().getClass());//设置superClass
((ICar) en.create()).drive(80);
//drive 宝马车
en.setSuperclass(new Bwm().getClass());
((ICar) en.create()).drive(100);
4 总结
静态代理更像是开发层面的实现,每次修改接口类或实现类都可能需要修改代理类,随着程序扩展,代码量和维护量增加;Java 提供的(java.lang.reflect.InvocationHandler)代理实例接口和第三方代理类(cglib)大大弥补了静态类的不足,也应用在许多开源框架上,比如Spring。
参考文献
- https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/InvocationHandler.html – Java Interface InvocationHandler
- https://github.com/cglib/cglib/wiki – cglib