今天的主角是:java.lang.reflect.Proxy
什么是代理
当我们要去找一个目标A的时候,目标说你不能直接找我,先找B,要找我做什么事跟B说好,B会跟我来弹。
上面的情景中B就是代理。说白了就是个转话的,但又不仅仅是传说的。它还有讨价还价、“擦屁股”等的作用,反正目标只管他能做的专一的一件事,其他的前前后后的可以让代理做。听完这个大家是不是想到一个很熟悉的词,没错“拦截”,回到根源它们做的事他么的就是一样的,但是伟大的人类为了彰显语言的美妙,起欢起各种各样的名字。毕竟,*硬币和*纸币都可以叫*,这么区分下也是没错的。
上面是我的理解,要正式点的语言表达的话,我也在网上查了查,如下:
Proxy可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy这个词的原意是代理,用在这里表示由它来"代理"某些操作,可以译为"代理器"
什么是动态代理
首先要给大家再明确一下我这个系列是干什么的,就是给大家简单介绍反射包中的一些类及其常用用法的。从前面几个类Class 、Field 、Method 等我们可以得出一个规律:其实这些类都是类中某一部分信息的抽象,比如类、字段、方法、构造函数等。
那么今天这个
Proxy类是哪部分信息的抽象呢,它就是我们代理的抽象(说实在我不清楚这个代理算不算一个类中的信息,按理说每个类都可以有一个或无数个代理类,管他呢,反正代理模式是我们Java中很重要的一个模式就行了)。
所谓动态代理,就是在写代码的时候不确定是哪个代理,要在运行时才确定出来。这个Proxy就是动态代理类。就是在不确定是哪个代理类的时候统 一用它来表示,在代码中写它就没错了,它是所有代理类的抽象。
话不多说,我们先来看一下代理可以怎么用吧。
首先我们来一个不用代理的场景:我某个接口实现了十个类,每个类的执行方法前后都要加上日志打印出执行什么方法,开始和结束。
类(我只写了一个,请想像有类似的十个)
首先是接口
package
com.hlmtest.java.reflect.proxy;
/**
* 目标类的接口
*
@author
mottohlm
*
*/
public
interface
TargetInterface {
/**
* 做好事
*
@param
good
*
@return
*/
public
String doSomethingGood(String
good
);
/**
* 做坏事
*
@param
bad
*
@return
*/
public
String doSomethingBad(String
bad
);
}
其中一个实现类
package
com.hlmtest.java.reflect.proxy.impl;
import
org.apache.log4j.Logger;
import
com.hlmtest.java.reflect.proxy.TargetInterface;
/**
* 目标类,实现了目标接口
*
@author
mottohlm
*
*/
public
class
Target
implements
TargetInterface {
/**
* 用于记录日志
*/
Logger
log
= Logger.
getLogger
(Target.
class
);
@Override
public
String doSomethingGood(String
good
) {
log
.info(
"日志开始:doSomethingGood方法开始执行啦!"
);
StringBuilder
things
=
new
StringBuilder (
"做好事第一步:出门多留心眼;"
);
things
.append(
"做好事第二步:发现好事--"
+
good
+
";"
);
things
.append(
"做好事第三步:不留名,深藏功与名!"
);
alert(
things
.toString());
log
.info(
"日志结束:doSomethingGood方法结束执行啦!"
);
return
null
;
}
@Override
public
String doSomethingBad(String
bad
) {
log
.info(
"日志开始:doSomethingBad方法开始执行啦!"
);
StringBuilder
things
=
new
StringBuilder (
"无语;"
);
things
.append(
"做坏事第二步:发现坏事--"
+
bad
+
";"
);
things
.append(
"做坏事第三步:无语"
);
alert(
things
.toString());
log
.info(
"日志结束:doSomethingBad方法结束执行啦!"
);
return
null
;
}
public
void
alert(String
str
){
System.
out
.println(
str
);
}
}
调用类
package
com.hlmtest.java.reflect.proxy;
import
com.hlmtest.java.reflect.proxy.impl.Target;
public
class
TestTarger {
public
static
void
main(String[]
args
)
throws
IllegalArgumentException, ClassNotFoundException {
do1
();
}
/**
* 直接在方法中写死日志记录方法
*/
public
static
void
do1
(){
Target
i
=
new
Target();
i
.doSomethingGood(
"扶老奶奶过马路"
);
System.
out
.println(
"<------------->"
);
i
.doSomethingBad(
"作为老奶奶到街上碰瓷扶老奶奶过马路的人 "
);
}
}
执行结果
接下来我写一个代理类,用动态代理来代理所有实现
TargetInterface 接口的类
package
com.hlmtest.java.reflect.proxy;
import
java.lang.reflect.InvocationHandler;
import
java.lang.reflect.Method;
import
java.lang.reflect.Proxy;
import
org.apache.log4j.Logger;
public
class
TargetProxy {
/**
* 用于记录日志
*/
Logger
log
= Logger.
getLogger
(TargetProxy.
class
);
public
TargetInterface getProxy(TargetInterface
target
)
throws
IllegalArgumentException, ClassNotFoundException{
//使用代码类
Proxy
创建className的对象,并且为其每个方法设置日志记录
return
(TargetInterface)Proxy.
newProxyInstance
(TargetProxy.
class
.getClassLoader(),
target
.getClass().getInterfaces(),
new
InvocationHandler(){
@Override
public
Object invoke(Object
proxy
,
Method
method
, Object[]
args
)
throws
Throwable {
log
.info(
"日志开始:"
+
method
.getName()+
"方法开始执行啦!"
);
Object
obj
=
method
.invoke(
target
,
args
);
log
.info(
"日志结束:"
+
method
.getName()+
"方法结束执行啦!"
);
return
obj
;
}
});
}
}
再改一个实现类,将写在类方法中的日志记录注掉
package
com.hlmtest.java.reflect.proxy.impl;
import
org.apache.log4j.Logger
;
import
com.hlmtest.java.reflect.proxy.TargetInterface;
/**
* 目标类,实现了目标接口
*
@author
mottohlm
*
*/
public
class
Target
implements
TargetInterface {
/**
* 用于记录日志
*/
//Logger log = Logger.getLogger(Target.class);
@Override
public
String doSomethingGood(String
good
) {
//log.info("日志开始:doSomethingGood方法开始执行啦!");
StringBuilder
things
=
new
StringBuilder (
"做好事第一步:出门多留心眼;"
);
things
.append(
"做好事第二步:发现好事--"
+
good
+
";"
);
things
.append(
"做好事第三步:不留名,深藏功与名!"
);
alert(
things
.toString());
//log.info("日志结束:doSomethingGood方法结束执行啦!");
return
null
;
}
@Override
public
String doSomethingBad(String
bad
) {
//log.info("日志开始:doSomethingBad方法开始执行啦!");
StringBuilder
things
=
new
StringBuilder (
"无语;"
);
things
.append(
"做坏事第二步:发现坏事--"
+
bad
+
";"
);
things
.append(
"做坏事第三步:无语"
);
alert(
things
.toString());
//log.info("日志结束:doSomethingBad方法结束执行啦!");
return
null
;
}
public
void
alert(String
str
){
System.
out
.println(
str
);
}
}
调用处新写方法
package
com.hlmtest.java.reflect.proxy;
import
com.hlmtest.java.reflect.proxy.impl.Target;
public
class
TestTarger {
public
static
void
main(String[]
args
)
throws
IllegalArgumentException, ClassNotFoundException {
do2
();
}
/**
* 直接在方法中写死日志记录方法
*/
public
static
void
do1(){
Target
i
=
new
Target();
i
.doSomethingGood(
"扶老奶奶过马路"
);
System.
out
.println(
"<------------->"
);
i
.doSomethingBad(
"作为老奶奶到街上碰瓷扶老奶奶过马路的人 "
);
}
/**
* 使用代理类来作日志记录方法
*
@throws
ClassNotFoundException
*
@throws
IllegalArgumentException
*/
public
static
void
do2()
throws
IllegalArgumentException, ClassNotFoundException{
//先得到代理对象
TargetProxy
proxy
=
new
TargetProxy();
//取得将要被代码的对象
Target
t
=
new
Target();
//取得代理
TargetInterface
obj
=
proxy
.getProxy(
t
);
obj
.doSomethingGood(
"扶老奶奶过马路"
);
System.
out
.println(
"<------------->"
);
obj
.doSomethingBad(
"作为老奶奶到街上碰瓷扶老奶奶过马路的人 "
);
}
}
执行结果
这样,我就不用在每个类中的方法处写日志记录啦。只要在这个接口的代理类中写一次就好了。这也就是动态代理的一个好处:解决多处重复逻辑代码。
在这里,大家应该也注意到一个问题,要想用动态代理,必需要实现一个接口,并且这个代理只能代理一个接口。这显然是不能满足要求的,请关注Java 中 Proxy 动态代理类 探秘(二),你将豁然开朗。
到此也就差不多了,大家想要了解更多的使用方法的话当然还是文档啦。
Field
的更多API可以查询:
其他
其他反射相关博客:
201800603