Java代理1 代理和动态代理的基础与使用
前言
预计分两篇写,第一篇是基础和一般用法,第二篇详细写下动态代理。本篇基础主要来自《Think in java》阅读笔记。
代理
代理是基本的设计模式之一。 是为了提供额外的或不同的操作,而插入的用来代替“实际”对象的对象。 将额外的操作从“实际”对象中分离到不同的地方,特别是希望能够容易的做出修改,从没有使用额外操作转为使用这些操作,或者反过来时,代理就显得很有用。 设计模式的关键就是封装修改-因此需要修改事务以证明这种模式的正确性。如,当希望跟踪Dog中的方法调用,或度量这些调用的开销,这些代码肯定不希望将其合并到应用中的代码,此时代理可以很容易的添加或移除他们。 通俗说,代理就是对象需要一些附加或不同的操作时,使用第三方对象操作原对象以及处理这些操作,从而达到不直接修改原对象的目的的一种设计模式。 静态代理是在编译时就将接口、实现类、代理类全部写出来,但若是需要很多代理,就需要每一次都这样写一遍,这就可能导致时间与资源的浪费。 此时可以使用动态代理来代替静态代理。
静态代理的实现
接口类 Animal
public interface Animal {
void doBark();
void somethingElse(String arg);
}
2
3
4
具体实现类 Dog
public class Dog implements Animal {
public void doBark() {
PrintUtill.println("doBark wow");
}
public void somethingElse(String arg) {
PrintUtill.println("somethingElse " + arg);
}
}
2
3
4
5
6
7
8
9
代理类 SimpleProxy
public class SimpleProxy implements Animal {
private Animal proxied;
public SimpleProxy(Animal proxied) {
this.proxied = proxied;
}
public void doBark() {
PrintUtill.println("SimpleProxy doSomething");
proxied.doBark();
}
public void somethingElse(String arg) {
PrintUtill.println("SimpleProxy somethingElse " + arg);
proxied.somethingElse(arg);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
测试代理类 SimpleProxyDemo
/**
* consumer 接收的Animal,所以它无法知道正则获得的到底是Dog还是SimpleProxy,因为这两者都实现了Animal。
*
* 但是SimpleProxy已经被插入到consumer与Dog之间,因此它会执行操作,然后调用Dog上相同的方法。
*/
public class SimpleProxyDemo {
public static void consumer(Animal iface){
iface.doBark();
iface.somethingElse("WindCoder.com");
}
public static void main(String[] args) {
consumer(new Dog());
consumer(new SimpleProxy(new Dog()));
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
测试结果:
doBark wow
somethingElse WindCoder.com
SimpleProxy doSomething
doBark wow
SimpleProxy somethingElse WindCoder.com
somethingElse WindCoder.com
2
3
4
5
6
动态代理
动态代理可以动态地创建代理并动态地处理对所代理方法的调用。 在动态代理上所做的所有调用都会被重定向到单一的调用处理器上。 该调用处理器的工作是揭示调用的类型并确定相应的对策。
JDK动态代理实现
**InvocationHandler接口的实现类DynamicProxyHandler **
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class DynamicProxyHandler implements InvocationHandler {
private Object proxied;
public DynamicProxyHandler(Object proxied) {
this.proxied = proxied;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("*** proxy: " + proxy.getClass() + " , method: " + method + " , args: " + args);
if (args != null){
for (Object arg: args){
PrintUtill.println(" " + arg);
}
}
return method.invoke(proxied, args);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
动态代理测试类SimpleDynamicProxy
public class SimpleDynamicProxy {
public static void consumer(Animal iface){
iface.doBark();
iface.somethingElse("windCoder.com DynamicProxy");
}
public static void main(String[] args) {
Dog dog = new Dog();
consumer(dog);
// Insert a proxy and call again
Animal prox = (Animal) Proxy.newProxyInstance(
Animal.class.getClassLoader(),
new Class[]{Animal.class},
new DynamicProxyHandler(dog)
);
consumer(prox);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
测试结果:
doBark wow
somethingElse windCoder.com DynamicProxy
*** proxy: class com.sun.proxy.$Proxy0 , method: public abstract void Others.base.SimpleProxy.Animal.doBark() , args: null
doBark wow
*** proxy: class com.sun.proxy.$Proxy0 , method: public abstract void Others.base.SimpleProxy.Animal.somethingElse(java.lang.String) , args: [Ljava.lang.Object;@4b67cf4d
windCoder.com DynamicProxy
somethingElse windCoder.com DynamicProxy
2
3
4
5
6
7
关键代码解析
动态代理测试类SimpleDynamicProxy.java中
Animal prox = (Animal) Proxy.newProxyInstance(
Animal.class.getClassLoader(),
new Class[]{Animal.class},
new DynamicProxyHandler(dog)
);
2
3
4
5
通过调用静态方法Proxy.newProxyInstance()可以创建动态代理,这个方法需要得到:
-
- 一个类加载器(你通常可以从已经被加载的对象获取其类加载器,然后传递给它,如此处的Animal.class.getClassLoader())
-
- 一个你希望该代理类实现的接口列表(不是类或抽象类),如 new Class[]{Animal.class}
-
- InvocationHandler接口的一个实现,如DynamicProxyHandler
动态代理可以将所有调用重定向到调用处理器。 通常会向调用处理器的构造器传递一个“实际”对象的引用,从而使得调用处理器在执行其中介任务时,可以将请求转发。 InvocationHandler接口的实现类DynamicProxyHandler.java中:
method.invoke(proxied, args);
亦既:
public Object invoke(Object obj, Object... args)
invoke()方法中传递进来了代理对象,以防使用时需要区分请求的来源,但在许多情况下并不关心这一点。 在invoke()内部,在代理调用方法时,对接口的调用将被重定向为对代理的调用,因此要格外当心。 通常,执行被代理的操作,然后使用Method.invoke()将请求转发给被代对象,并传入必需的参数。 初看起来可能有些受限,就像只能执行泛化操作一样。 但可以通过传递其他的参数,来过滤某些方法的调用。
CGLIB动态代理(CGLibDemo)
JDK动态代理: 只能代理实现了接口的类,没有实现接口的类不能实现JDK动态代理。 Cglib代理: 针对类来实现代理,对指定目标产生一个子类 通过方法拦截技术拦截所有父类方法的调用。 在实现内部,CGLIB库使用了ASM这一个轻量但高性能的字节码操作框架来转化字节码,产生新类。
CGLIB实现方式
1.首先定义业务类,无需实现接口(当然,实现接口也可以,不影响的)
public class DogCGlib {
public void doBark() {
PrintUtill.println("doBark wow CGlib");
}
public void somethingElse(String arg) {
PrintUtill.println("somethingElse CGlib" + arg);
}
}
2
3
4
5
6
7
8
9
2.实现 MethodInterceptor方法代理接口,创建代理类
public class CGlibProxy implements MethodInterceptor {
public Object getInstance(Class target){
//创建加强器,用来创建动态代理类
Enhancer enhancer = new Enhancer();
//为加强器指定要代理的业务类(即:为下面生成的代理类指定父类)
enhancer.setSuperclass(target);
//设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦
enhancer.setCallback(this);
// 创建动态代理类对象并返回
return enhancer.create();
}
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("windcoder.com日志开始...");
//代理类调用父类的方法
proxy.invokeSuper(obj, args);
System.out.println("windcoder.com日志结束...");
return null;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
3.创建业务类和代理类对象,然后通过 代理类对象.getInstance(业务类对象) 返回一个动态代理类对象(它是业务类的子类,可以用业务类引用指向它)。最后通过动态代理类对象进行方法调用。
public class CGLIBDemo {
public static void main(String[] args) {
// 设置输出目录,方便之后查看CGLIB生成的class---非必选项
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "./tmp/");
// 正式执行
CGlibProxy proxy = new CGlibProxy();
DogCGlib dog = (DogCGlib)proxy.getInstance(DogCGlib.class);
dog.doBark();
PrintUtill.printlnRule();
dog.somethingElse(" 你好");
}
}
2
3
4
5
6
7
8
9
10
11
12
参考资料
《Java编程思想 第4版》 CGLIB动态代理介绍
除特别注明外,本站所有文章均为 windcoder 原创,转载请注明出处来自: javadailiyudongtaidailiyi

[…] 详细代码实例请见:Java代理1 代理和动态代理的基础与使用 中的静态代理部分。 […]