林炳文Evankaka原创作品。转自https://blog.csdn.net/Evankaka/article/details/45195383
一、JAVA动态代理
1.1 代理模式 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。
按照代理的创建时期,代理类可以分为两种。
静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
动态代理:在程序运行时,运用反射机制动态创建而成。
我们知道,通过使用代理,可以在被代理的类的方法的前后添加一些处理方法,这样就达到了类似AOP的效果。而JDK中提供的动态代理,就是实现AOP的绝好底层技术。
1.2 JDK动态代理
JDK动态代理主要涉及到java.lang.reflect包中的两个类:Proxy和InvocationHandler。InvocationHandler是一个接口,通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编制在一起。
Proxy利用InvocationHandler动态创建一个符合某一接口的实例,生成目标类的代理对象。
1.3 CGLib动态代理
还有一个叫CGLib的动态代理,CGLib全称为Code Generation Library,是一个强大的高性能,高质量的代码生成类库,可以在运行期扩展Java类与实现Java接口,CGLib封装了asm,可以再运行期动态生成新的class。和JDK动态代理相比较:JDK创建代理有一个限制,就是只能为接口创建代理实例,而对于没有通过接口定义业务方法的类,则可以通过CGLib创建动态代理
(以下代码来自Spring.3.x企业应用开发实战,其中有些地方笔者做了些增加)
二、JDK动态代理
1、接口ForumService
-
package aop;
-
-
public
interface ForumService {
-
public void removeTopic(int topic);
-
public void removeForum(int forumId);
-
-
}
2、ForumServiceImpl实现
-
package aop;
-
public
class ForumServiceImpl implements ForumService{
-
public void removeTopic(int topic){
-
-
System.out.println(
"模拟删除记录"+topic);
-
try{
-
Thread.currentThread().sleep(
20);
-
-
}
catch(Exception e){
-
throw
new RuntimeException(e);
-
}
-
-
}
-
public void removeForum(int forumId){
-
-
System.out.println(
"模拟删除记录"+forumId);
-
try{
-
Thread.currentThread().sleep(
20);
-
-
}
catch(Exception e){
-
throw
new RuntimeException(e);
-
}
-
-
}
-
}
这里我们可以来看看上面的方法:
先测试下:
-
ForumServiceImpl forumServiceImpl=
new ForumServiceImpl();
-
forumServiceImpl.removeForum(
190);
-
forumServiceImpl.removeTopic(
123);
![](https://i-blog.csdnimg.cn/blog_migrate/c266d0a3576e1eba8c8b68571925e0a3.jpeg)
接下来我们要实现在删除记录之前和之后增加一些方法
3、要在ForumServiceImpl.java中插入的增强方法
-
package aop;
-
-
public
class PerformanceMonitor {
-
private
static ThreadLocal<MethodPerformance> performanceRecord=
new ThreadLocal<MethodPerformance>();
-
-
public static void begin(String method){
-
System.out.println(
"begin monitor..");
-
MethodPerformance mp=
new MethodPerformance(method);
-
performanceRecord.set(mp);
-
}
-
public static void end(){
-
System.out.println(
"end monitor...");
-
MethodPerformance mp=performanceRecord.get();
-
mp.printPerformance();
-
}
-
}
MethodPerformance.java是真正加入的方法
-
package aop;
-
-
public
class MethodPerformance {
-
private
long begin;
-
private
long end;
-
private String serviceMethod;
-
public MethodPerformance(String serviceMethod){
-
this.serviceMethod=serviceMethod;
-
this.begin=System.currentTimeMillis();
-
}
-
public void printPerformance(){
-
end=System.currentTimeMillis();
-
long elapse=end-begin;
-
System.out.println(serviceMethod+
"花费"+elapse+
"毫秒");
-
}
-
}
4、JDK动态代理PerformanceHandler.java
-
package aop;
-
-
import java.lang.reflect.InvocationHandler;
-
import java.lang.reflect.Method;
-
-
public
class PerformanceHandler implements InvocationHandler {
-
private Object target;
-
-
public PerformanceHandler(Object object) {
-
this.target = object;
-
}
-
-
@Override
-
public Object invoke(Object arg0, Method arg1, Object[] arg2)
-
throws Throwable {
-
PerformanceMonitor.begin(target.getClass().getName() +
"."
-
+ arg1.getName());
-
Object obj = arg1.invoke(target, arg2);
-
PerformanceMonitor.end();
-
return obj;
-
-
}
-
-
}
5、使用
-
ForumServiceImpl target=
new ForumServiceImpl();
-
PerformanceHandler handler=
new PerformanceHandler(target);
-
ForumService proxy=(ForumService)Proxy.newProxyInstance(target.getClass().getClassLoader(),
-
target.getClass().getInterfaces(), handler);
-
proxy.removeForum(
23);
-
proxy.removeTopic(
678);
-
System.out.println(
"end monitor...");
结果:
三、CGlib动态代理
1、CglibProxy.java
-
package aop;
-
-
import java.lang.reflect.Method;
-
-
import org.springframework.cglib.proxy.Enhancer;
-
import org.springframework.cglib.proxy.MethodInterceptor;
-
import org.springframework.cglib.proxy.MethodProxy;
-
-
public
class CglibProxy implements MethodInterceptor{
-
private Enhancer enhancer=
new Enhancer();
-
public Object getProxy(Class clazz){
-
enhancer.setSuperclass(clazz);
-
enhancer.setCallback(
this);
-
return enhancer.create();
-
}
-
@Override
-
public Object intercept(Object arg0, Method arg1, Object[] arg2,
-
MethodProxy arg3)
throws Throwable {
-
PerformanceMonitor.begin(arg0.getClass().getName() +
"."
-
+ arg1.getName());
-
Object obj = arg3.invoke(arg0, arg2);
-
PerformanceMonitor.end();
-
return obj;
-
}
-
-
-
}
2、测试
-
CglibProxy proxy2=
new CglibProxy();
-
ForumServiceImpl forumServiceImpl=(ForumServiceImpl)proxy2.getProxy(ForumServiceImpl.class);
-
forumServiceImpl.removeForum(
456);
-
forumServiceImpl.removeTopic(
987);
-
System.out.println(
"end monitor...");
3、结果:
四、JDK动态代理和CGLib的比较
CGLib所创建的动态代理对象的性能比JDK所创建的代理对象性能高不少,大概10倍,但CGLib在创建代理对象时所花费的时间却比JDK动态代理多大概8倍,所以对于singleton的代理对象或者具有实例池的代理,因为无需频繁的创建新的实例,所以比较适合CGLib动态代理技术,反之则适用于JDK动态代理技术。另外,由于CGLib采用动态创建子类的方式生成代理对象,所以不能对目标类中的final,private等方法进行处理。所以,大家需要根据实际的情况选择使用什么样的代理了。同样的,Spring的AOP编程中相关的ProxyFactory代理工厂内部就是使用JDK动态代理或CGLib动态代理的,通过动态代理,将增强(advice)应用到目标类中。