代理机制的理解与应用
1.概述:
在Java的框架Spring中,有两个很重要的的概念:IOC和AOP,其中IOC(Inversion of Control ,控制反转)是其实现面向切面编程的重要执行手段。其中有一个比较重要的实现方式就是代理机制。代理机制,就是将一个已经封装好的类(很有可能已经成为二进制字节码,无法更改源代码),用一个新的类(代理类)来代替他执行。这样做的最大优点就是可以在原始类的某个方法执行时,在执行前后(所谓的切面)增加一些业务。代理机制主要分为静态代理和动态代理,下面我们就来看一下这两个代理。
2.静态代理(代理模式):
静态代理严格意义上是一种设计模式,为了让大家更好的理解代理模式的原理,先介绍一下静态代理(也叫代理模式)。
第一步:建立接口
package com.my.about_staticProxy;
public interface Interface {
public void doSomthing();
}
第二步:建立原始类
package com.my.about_staticProxy;
public class UserClass implements Interface{
@Override
public void doSomthing() {
System.out.println("做了一些事情");
}
}
第三步:建立代理类
package com.my.about_staticProxy;
public class UserProxy implements Interface{
private Interface user = new UserClass();
@Override
public void doSomthing() {
System.out.println("执行前做的事");
user.doSomthing();
System.out.println("执行后做的事");
}
}
测试类:
package com.my.about_staticProxy;
public class Test {
public static void main(String arg[]) {
UserProxy userproxy = new UserProxy();
userproxy.doSomthing();
}
}
测试结果:
静态代理主要是依赖接口类来创建代理对象,因为其已经拥有原始类的对象,所以被称为静态代理(代理模式)。静态代理的源码比较简单,在这里就不再赘述,不过这里的代码对于动态代理的理解具有一定的意义,希望初学者能够充分的理解。
3.动态代理:
Spring中实现的IOC容器,主要就是基于动态代理实现,所以动态代理是Java高级编程中非常重要的的内容,其主要的实现方式有两种最常用的方式:一是通过反射机制,创建基于原始类接口的代理对象;二是通过CGLib,通过字节码直接生成原始类的子类。两种实现各有优劣,下面我们结合源代码具体分析。
(1)JDKProxy:
代理的第一种实现方式是常规的通过反射机制形成代理的方式,以下为源代码:
原始类同上;
JDKProxy:
package com.my.about_staticProxy.JDKProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import com.my.about_staticProxy.Interface;
public class JDKProxy{
public JDKProxy() {
// TODO Auto-generated constructor stub
}
@SuppressWarnings("unchecked")
//使用泛型创建获得代理方法,可以在使用时无需强转
public <T> T getJDKProxy(Object object) {
Class<?> klass = object.getClass();
return (T)Proxy.newProxyInstance(
klass.getClassLoader(),
klass.getInterfaces(),
//通过InvocationHandler() 方法来具体实现代理
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object result = null;
System.out.println("在调用方法" + method.getName() + "前执行");
result = method.invoke(object, args);//反射执行被代理的方法
System.out.println("在调用方法" + method.getName() + "前执行");
return result;
}
});
}
}
测试类:
package com.my.about_staticProxy.JDKProxy;
import com.my.about_staticProxy.Interface;
import com.my.about_staticProxy.UserClass;
public class Test {
public static void main(String[] args) {
Interface userProxy = new JDKProxy().getJDKProxy(new UserClass());
userProxy.doSomthing();
}
}
测试结果:
java.lang.reflect.Proxy包中的代理是基于接口实现的,通过反射机制,动态的生成代理类,实现代理方法。其最大的缺点在于,原始类必须拥有接口,并且原始类中非接口的方法无法被代理。
(2)CGLProxy:
CGLIB是动态字节码生成库,通过系统运行期间动态地为目标对象生成相应的扩展子类。在使用时必须导入相应的cglib工具包。代码实现如下:
原始类同上
CGLProxy:
package com.my.about_staticProxy.JDKProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import com.my.about_staticProxy.Interface;
public class JDKProxy{
public JDKProxy() {
// TODO Auto-generated constructor stub
}
@SuppressWarnings("unchecked")
public <T> T getCGLProxy(Object object) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(object.getClass());
MethodInterceptor methodInterceptor = new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
Object result = null;
System.out.println("在调用方法" + method.getName() + "前执行");
result = method.invoke(object, args);
System.out.println("在调用方法" + method.getName() + "前执行");
return result;
}
};
enhancer.setCallback(methodInterceptor);
return (T) enhancer.create();
}
}
测试类:
package com.my.about_staticProxy.JDKProxy;
import com.my.about_staticProxy.Interface;
import com.my.about_staticProxy.UserClass;
public class Test {
public static void main(String[] args) {
UserClass userProxy = new JDKProxy().getCGLProxy(new UserClass());
userProxy.doSomthing();
}
}
测试结果:
CGLIB主要是由Enhancer最终为我们生成所需要的代理。其主要的缺点是不能对final方法进行覆写。
总结:
代理机制是Spring的基础之一,本篇博客只介绍其中的两种操作,各有有优劣,读者可以根据自己的需要选用适合的代理。这里只介绍代理机制的最基础内容,如果想看后续的操作可以继续关注博主的博客的后续博客。(令:感谢教主的教导)。