静态代理
- 用父亲帮儿子找对象来举例:
public interface Person {
public void findLove();
}
public class Son implements Person {
@Override
public void findLove() {
System.out.println("儿子要找一个对象");
}
}
public class Father implements Person {
private Son son;
public Father (Son son){
this.son = son ;
}
@Override
public void findLove() {
System.out.println("开始物色");
son.findLove();
System.out.println("找到");
}
}
public class StaticProxyTest {
public static void main(String[] args) {
Father father = new Father(new Son());
father.findLove();
}
}
输出:
开始物色
儿子要找一个对象
找到
代 理对象持有被代理对象的引用,客户端调用代理对象方法,同时也调用被代理对象的方 法,但是在代理对象前后增加一些处理。这就是一个简单的静态代理。
动态代理:
public class Girl implements Person{
@Override
public void findLove() {
System.out.println("女兒要找一个对象");
}
}
public class JDKProxy implements InvocationHandler{
private Object target;
public Object getInstance(Object target){
this.target = target;
Class<?> clazz = target.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开始物色");
Object invoke = method.invoke(this.target, args);
System.out.println("找到");
return invoke;
}
}
public class JDKProxyTest {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
// Object instance = new JDKProxy().getInstance(new Girl());
// Method method = instance.getClass().getMethod("findLove", null);
// method.invoke(instance);
//或者:
Person instance =(Person) new JDKProxy().getInstance(new Girl());
instance.findLove();
}
}
CGLib方式
public class CGLibProxy implements MethodInterceptor {
public Object getInstance(Class<?> clazz){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("开始物色");
Object object = methodProxy.invokeSuper(o, objects);
System.out.println("找到");
return object;
}
}
public class CglibTest {
public static void main(String[] args) {
Girl instance = (Girl)new CGLibProxy().getInstance(Girl.class);
instance.findLove();
}
}
JDK动态代理的实现原理
- 1、拿到被代理类的引用,并且获取它的所有的接口(反射获 取)。
- 2、JDK Proxy类重新生成一个新的类,实现了被代理类所有 接口的方法。
- 3、动态生成Java代码,把增强逻辑加入到新生成代码中。
- 4、编译生成新的Java代码的class文件。
- 5、再重新加载到 JVM 中运行。
CGLib 和 JDK 动态代理对比
- 1.JDK 动态代理是实现了被代理对象的接口,CGLib 是继承了被代理对象。
- 2.JDK 和 CGLib 都是在运行期生成字节码,JDK 是直接写 Class 字节码,CGLib 使用 ASM 框架写 Class 字节码,Cglib 代理实现更复杂,生成代理类比 JDK 效率低。
- 3.JDK 调用代理方法,是通过反射机制调用,CGLib 是通过 FastClass 机制直接调用方法, CGLib 执行效率更高。
代理模式与 Spring
ProxyFactoryBean 核心的方法就是 getObject()方法
public Object getObject() throws BeansException { initializeAdvisorChain(); if (isSingleton()) { return getSingletonInstance(); }else { if (this.targetName == null) { logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " + "Enable prototype proxies by setting the 'targetName' property."); }return newPrototypeInstance(); } }
在 getObject()方法中,主要调用 getSingletonInstance()和 newPrototypeInstance(); 在 Spring 的配置中,如果不做任何设置,那么 Spring 代理生成的 Bean 都是单例对象。 如果修改 scope 则每次创建一个新的原型对象。
Spring 中的代理选择原则
- 1、当 Bean 有实现接口时,Spring 就会用 JDK 的动态代理
- 2、当 Bean 没有实现接口时,Spring 选择 CGLib。
- 3、Spring 可以通过配置强制使用 CGLib,只需在 Spring 的配置文件中加入如下代码:
<aop:aspectj-autoproxy proxy-target-class=“true”/>
代理模式的优缺点
使用代理模式具有以下几个优点:
- 1、代理模式能将代理对象与真实被调用的目标对象分离。
- 2、一定程度上降低了系统的耦合度,扩展性好。
- 3、可以起到保护目标对象的作用。
- 4、可以对目标对象的功能增强。
缺点
- 1、代理模式会造成系统设计中类的数量增加。
- 2、在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢。
- 3、增加了系统的复杂度。