Java 代理详解
Java的世界,框架永远如夜空繁星Java的框架,多如牛毛Spring ,hibernate,ibatis/myBatis, struts1/2, shiro, lucene等等,有时候会觉得这些框架都非常神奇,解决了很多开发的问题,让开发人员进行系统、服务开发时变得简单,框架内部变得神秘,其实无论什么框架,其实现无非是基于JDK来开发的,反射、代理、泛型、注解、以及各种设计模式是框架的灵魂。比如下图的代码
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
29
30
31
|
protected
Object createProxy(
Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
ProxyFactory proxyFactory =
new
ProxyFactory();
// Copy our properties (proxyTargetClass etc) inherited from ProxyConfig.
proxyFactory.copyFrom(
this
);
if
(!shouldProxyTargetClass(beanClass, beanName)) {
// Must allow for introductions; can't just set interfaces to
// the target's interfaces only.
Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass,
this
.proxyClassLoader);
for
(Class<?> targetInterface : targetInterfaces) {
proxyFactory.addInterface(targetInterface);
}
}
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
for
(Advisor advisor : advisors) {
proxyFactory.addAdvisor(advisor);
}
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(
this
.freezeProxy);
if
(advisorsPreFiltered()) {
proxyFactory.setPreFiltered(
true
);
}
return
proxyFactory.getProxy(
this
.proxyClassLoader);
}
|
用的正好是反射机制和代理模式,本文主要讲讲Java的代理模式的思想,以及他的分类和实现方式。
-
-
静态代理
静态代理是代理最简单的一种方式,实现为,一个实现类,一个接口,一个代理类,一个测试类,即可完成静态代 理,举个“栗子”,我们去银行存钱我们没有存钱的能力,需要漂亮的营业员小姐(可能是娘炮的先生
)帮我们把钱存到账户里面,我们实际是代理,真正的实现类是营业员小姐,我们在存钱时需要先给钱给营业员,而钱不会自己跑到营业员小姐那里,所以我们是代理类。代码如下
银行员工接口
-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
/**
*
*/
package
cn.inspries.test.proxy;
/* 银行美女
* ***********************************
* @author sandy
* @project cn.inspries.test.proxy
* @create_date 2014-8-14 下午9:07:32
* ***********************************
*/
public
class
BankBeauty
implements
MoneyManager{
@Override
public
void
putMoney(
double
money) {
System.out.println(
"您存了"
+ money +
"元人民币,请对我的服务进行评价!"
);
}
}
|
客户类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
/**
*
*/
package
cn.inspries.test.proxy;
/*
* ***********************************
* @author sandy
* @project cn.inspries.test.proxy
* @create_date 2014-8-14 下午9:06:50
* ***********************************
*/
public
class
Custom
implements
MoneyManager{
BankBeauty bankBeauty =
new
BankBeauty();
@Override
public
void
putMoney(
double
money) {
System.out.println(
"去到银行柜台!开始存钱"
);
bankBeauty.putMoney(money);
System.out.println(
"存好了"
);
}
}
|
静态代理接口代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
/**
*
*/
package
cn.inspries.test.proxy;
/*
* ***********************************
* @author sandy
* @project cn.inspries.test.proxy
* @create_date 2014-8-14 下午9:08:54
* ***********************************
*/
public
interface
MoneyManager {
void
putMoney(
double
money);
}
|
静态代理测试类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
/**
*
*/
package
cn.inspries.test.proxy;
/*
* ***********************************
* @author sandy
* @project cn.inspries.test.proxy
* @create_date 2014-8-14 下午9:26:41
* ***********************************
*/
public
class
Test {
public
static
void
main(String[] args) {
Custom custom =
new
Custom();
custom.putMoney(
200.0
);
}
}
|
输出结果:
去到银行柜台!开始存钱
您存了200.0元人民币,请对我的服务进行评价!
存好了
上面的例子很简单这里就不再多做解释了
-
动态代理
接口和实现类跟静态代理一样,代理类有所区别。代理类
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
29
30
31
32
33
34
35
36
37
38
39
40
|
/**
*
*/
package
cn.inspries.test.proxy.dynic;
import
java.lang.reflect.InvocationHandler;
import
java.lang.reflect.Method;
import
java.lang.reflect.Proxy;
/*
* ***********************************
* @author sandy
* @project cn.inspries.test.proxy
* @create_date 2014-8-14 下午9:06:50
* ***********************************
*/
public
class
DynicProxy
implements
InvocationHandler {
Object targetObject;
public
Object newProxyInstance(Object targetObject) {
this
.targetObject = targetObject;
return
Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(),
this
);
}
@Override
public
Object invoke(Object proxy, Method method, Object[] args)
throws
Throwable {
Object returnObject =
null
;
try
{
System.out.println(
"调用动态代理方法了!"
);
returnObject = method.invoke(targetObject, args);
}
catch
(Exception e) {
e.printStackTrace();
throw
e;
}
return
returnObject;
}
}
|
JDK动态代理测试类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
/**
*
*/
package
cn.inspries.test.proxy.dynic;
import
cn.inspries.test.proxy.BankBeauty;
import
cn.inspries.test.proxy.MoneyManager;
/*
* ***********************************
* @author sandy
* @project cn.inspries.test.proxy
* @create_date 2014-8-14 下午9:26:41
* ***********************************
*/
public
class
DynicTest {
public
static
void
main(String[] args) {
DynicProxy dynicProxy =
new
DynicProxy();
MoneyManager moneyManager = (MoneyManager) dynicProxy.newProxyInstance(
new
BankBeauty());
moneyManager.putMoney(
2000
);
}
}
|
输出
调用代理方法了!
您存了2000.0元人民币,请对我的服务进行评价!
-
-
-
Cglib动态代理,JDK动态代理的局限是,被代理的服务类必须实现接口,否则无法使用代理,将会报错,而Spring里面很多地方用的是类,他是通过Cglib来完成无接口的动态代理的。代码也比较简洁如下。
-
-
Cglib动态代理类
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
29
30
31
32
33
|
/**
*
*/
package
cn.inspries.test.proxy.dynic_cglib;
import
java.lang.reflect.Method;
import
org.springframework.cglib.proxy.Enhancer;
import
org.springframework.cglib.proxy.MethodInterceptor;
import
org.springframework.cglib.proxy.MethodProxy;
/*
* ***********************************
* @author sandy
* @project cn.inspries.test.proxy
* @create_date 2014-8-14 下午9:06:50
* ***********************************
*/
public
class
CglibDynicProxy
implements
MethodInterceptor {
Object targetObject;
public
Object newProxyInstance(Object targetObject) {
this
.targetObject = targetObject;
Enhancer enhancer =
new
Enhancer();
enhancer.setSuperclass(
this
.targetObject.getClass());
enhancer.setCallback(
this
);
// call back method
return
enhancer.create();
// create proxy instance
}
@Override
public
Object intercept(Object target, Method method, Object[] args,
MethodProxy proxy)
throws
Throwable {
System.out.println(
"使用cglib的MethodInterceptor.intercept方法调用代理了!"
);
Object result = proxy.invokeSuper(target, args);
System.out.println(
"方法走完了,哈哈"
);
return
result;
}
}
|
Cglib测试类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
/**
*
*/
package
cn.inspries.test.proxy.dynic_cglib;
import
cn.inspries.test.proxy.BankBeauty;
import
cn.inspries.test.proxy.MoneyManager;
/*
* ***********************************
* @author sandy
* @project cn.inspries.test.proxy
* @create_date 2014-8-14 下午9:26:41
* ***********************************
*/
public
class
DynicTest {
public
static
void
main(String[] args) {
CglibDynicProxy cglibDynicProxy =
new
CglibDynicProxy();
MoneyManager manager = (MoneyManager) cglibDynicProxy.newProxyInstance(
new
BankBeauty());
manager.putMoney(
1688
);
}
}
|
程序输出:
使用cglib的MethodInterceptor.intercept方法调用代理了!
您存了1688.0元人民币,请对我的服务进行评价!
方法走完了,哈哈
总结:动态代理和静态代理的区别
1、静态代理由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的class文件,代理类和委托类的关系在运行前就确定了。
2、静态代理只服务于一个接口。而动态代理可以服务于多个接口。
3、静态代理中如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
4、动态代理类的代码是在程序运行期间由JVM根据反射等机制动态的生成,不存在代理类的字节码文件。代理类和委托类的关系在程序运行时候确定。
5、动态代理与普通的代理相比较,最大的好处是接口中声明的所有方法都被转移到一个集中的方法中处理(invoke),这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。
而JDK动态代理方式和CGlib区别是,使用jdk代理是,代理类必须实现接口,而Cglib不论有没有接口都可以实现,所以cglib局限较小。