一. 静态代理
通过继承被代理类的父类或者接口,然后在创建代理类时传入一个被代理类对象,然后在使用代理类中方法时调用被代理类中相应的方法达到代理效果。
缺点: 因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护.
二. 动态代理
利用 java.lang.reflect.Proxy 类中
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 方法实现代理
interface Interface1 {
void method1();
void method2();
}
interface Interface2 {
void method3();
}
class Test implements Interface1, Interface2 {
@Override
public void method1() {
System.out.println("Test.method1");
}
@Override
public void method2() {
System.out.println("Test.method2");
}
@Override
public void method3() {
System.out.println("Test.method3");
}
}
Test test = new Test();
//只能用接口进行强转,不能用(Test)
//所以代理对象只能保留一个接口中的方法
Interface1 proxy=(Interface1) Proxy.newProxyInstance(
test.getClass().getClassLoader(),
new Class[] {Interface1.class, Interface2.class},
// test.getClass().getInterfaces(), //也可以直接用这种写法
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
//只调用method1方法时进行处理
if("method1".equals(methodName)) {//调用接口中所有方法之前都会调用invoke方法,可以自己根据某一个方法名进行处理
System.out.println("开始调用 Test method1 方法");
test.method1();
System.out.println("调用 Test method1 方法结束");
} else {//其他方法不进行处理
return method.invoke(test, args); //返回值
}
return null;
}
});
proxy.method1();
proxy.method2();
// proxy.method3(); //错误,interface1 接口中没有method3
缺点:被代理类必须要实现一个接口,如果实现了多个接口,只能对一个接口的方法进行保留(个人理解,如有错误欢迎指出)
三. Cglib动态代理
上面的静态代理和动态代理模式都是要求目标对象是实现一个接口的目标对象,
Cglib代理,也叫作子类代理,是基于asm框架,实现了无反射机制进行代理,利用空间来换取了时间,代理效率高于jdk ,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.
它有如下特点:
- JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,如果想代理没有实现接口的类,就可以使用Cglib实现.
- Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP和synaop,为他们提供方法的interception(拦截)
-
Cglib包的底层是通过使用一个小而块的字节码处理框架ASM来转换字节码并生成新的类.不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉.
注意
- 需要引入cglib的jar文件,但是Spring的核心包中已经包括了Cglib功能,所以直接引入spring-core-3.2.5.jar即可.(否则要导入两个jar 包,cglib-3.2.7.jar asm-6.2.1.jar)
- 引入功能包后,就可以在内存中动态构建子类
- 代理的类不能为final,否则报错
-
目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.
class Test {
public void method1() {
System.out.println("Test.method1");
}
public void method2() {
System.out.println("Test.method2");
}
public void method3() {
System.out.println("Test.method3");
}
}
public static void cglibProxy(String[] args) {
Test test=new Test();
//创建代理对象
Enhancer enhancer=new Enhancer();//cglib中的工具类
enhancer.setSuperclass(test.getClass());//设置父类
enhancer.setCallback(new MethodInterceptor() {//设置代理的函数,与InvocationHandler中的invoke类似
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
String methodName = method.getName();
Object result=null;
if("method1".equals(methodName)) {//对method1进行代理
System.out.println("cglib method1 代理");
test.method1();
} else if ("method2".equals(methodName)) {//对method2进行代理
System.out.println("cglib method2 代理");
test.method2();
} else { //其他方法直接调用其本身,不进行任何操作
result=method.invoke(test, args);
}
return result;
}
});
Test proxy = (Test) enhancer.create();
proxy.method1();
proxy.method2();
proxy.method3();
}
使用CGLIB即使代理类没有实现任何接口也可以实现动态代理功能。CGLIB具有简单易用,它的运行速度要远远快于JDK的
CGLIB的核心类:
net.sf.cglib.proxy.Enhancer – 主要的增强类
net.sf.cglib.proxy.MethodInterceptor – 主要的方法拦截类,它是Callback接口的子接口,需要用户实现
net.sf.cglib.proxy.MethodProxy – JDK的java.lang.reflect.Method类的代理类,可以方便的实现对源对象方法的调用