代理类本身不实现服务,而是通过调用被代理类中的方法来提供服务。 代理的作用,消除硬编码。
静态代理:将代理类的对象引用设为自己的成员变量。然后调用增强实现的委托方法。
public interface BuyHouse {
void buyHosue();
}
public class BuyHouseImpl implements BuyHouse {
@Override
public void buyHosue() {
System.out.println("我要买房");
}
}
public class BuyHouseProxy implements BuyHouse {
private BuyHouse buyHouse;
public BuyHouseProxy(final BuyHouse buyHouse) {
this.buyHouse = buyHouse;
}
@Override
public void buyHosue() {
System.out.println("买房前准备");
buyHouse.buyHosue();
System.out.println("买房后装修");
}
}
public class ProxyTest {
public static void main(String[] args) {
BuyHouse buyHouse = new BuyHouseImpl();
buyHouse.buyHosue();
BuyHouseProxy buyHouseProxy = new BuyHouseProxy(buyHouse);
buyHouseProxy.buyHosue();
}
}
JDK动态代理:
public class DynamicProxyHandler implements InvocationHandler {
private Object object;
public DynamicProxyHandler(final Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("买房前准备");
Object result = method.invoke(object, args);
System.out.println("买房后装修");
return result;
}
}
public class DynamicProxyTest {
public static void main(String[] args) {
BuyHouse buyHouse = new BuyHouseImpl();
BuyHouse proxyBuyHouse = (BuyHouse) Proxy.newProxyInstance(BuyHouse.class.getClassLoader(), new
Class[]{BuyHouse.class}, new DynamicProxyHandler(buyHouse));
proxyBuyHouse.buyHosue();
}
}
JDK动态代理,利用反射机制,调用invoke方法。此时动态代理不再像静态代理一样把代理类设置为自己的成员变量,然后调用带有增强实现的代理方法。而缺点是被代理的类必须实现接口(BuyHouse),有很强的局限性。动态代理返回一个代理对象Object,把它强转为BuyHouse接口的对象,然后该对象通过反射来调用带有增强实现的代理方法。invoke方法会根据传入的代理对象、方法名称以及参数决定调用代理的哪个方法。
CGLIB动态代理不要求代理类实现接口,简单的说,CGLIB会让生成的代理类继承被代理类,并在代理类中对代理方法进行强化处理(前置处理、后置处理等)。在CGLIB底层,其实是借助了ASM这个非常强大的Java字节码生成框架。如果委托类被final修饰,那么它不可被继承,即不可被代理;同样,如果委托类中存在final修饰的方法,那么该方法也不可被代理;原理就是当被代理类调用代理方法时拦截下来。
图1.1 被代理类
图1.2 实现MethodInterceptor接口生成方法拦截器
图1.3 生成代理类对象并打印在代理类对象调用方法之后的执行结果
CGLIB(Code Generation Library)是一个开源项目!
是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。
Enhancer类是CGLib中的一个字节码增强器,它可以方便的对你想要处理的类进行扩展
CGLIB创建的动态代理对象比JDK创建的动态代理对象的性能更高,但是CGLIB创建代理对象时所花费的时间却比JDK多得多。所以对于单例的对象,因为无需频繁创建对象,用CGLIB合适,反之使用JDK方式要更为合适一些。同时由于CGLib由于是采用动态创建子类的方法,对于final修饰的方法无法进行代理。
这些只是原理,但这样做会节省大量的代码。在实际的开发中通过AspecJ来基于XML和注解的方式设置通知和切面类。底层用的是ProxyFactoryBean来生成代理对象。
JDK动态代理本质上跟静态代理是一样的,只是JDK动态代理自动生成了代理类,省去了编写代理类的时间,另外,只需要传入一个被代理对象就可以自动识别实现的接口类,即不需要知道被代理类实现了什么接口。可以理解为自动编写了静态代理中的代理类的代码。
静态代理的缺点:
1、静态代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
2、静态代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。如上的代码是只为UserManager类的访问提供了代理,但是如果还要为其他类如Department类提供代理的话,就需要我们再次添加代理Department的代理类。
JDK动态代理本质上就是自动编写了静态代理的代理类方法,因此没有克服上面的缺点1,但是因为JDK动态代理只需要传入被代理对象即可,实现不同接口的对象对于JDK动态代理都是一样的,因为会自动实现接口。因此克服了上面的缺点2.
CGLIB和Java动态代理的区别
- Java动态代理只能够对接口进行代理,不能对普通的类进行代理(因为所有生成的代理类的父类为Proxy,Java类继承机制不允许多重继承);CGLIB能够代理普通类;
- Java动态代理使用Java原生的反射API进行操作,在生成类上比较高效;CGLIB使用ASM框架直接对字节码进行操作,在类的执行过程中比较高效
cglib proxy区别于jdk proxy的方式,一个是静态的代码调用,一个是动态的reflect。