暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

不得不会的23种Java设计模式(三)——代理模式

喝一杯咖啡吧 2020-04-22
265

代理模式

  • 定义

  • 目的

  • 静态代理

    • 特点

    • 案例

    • 缺点

  • 动态代理

    • 特点

    • 动态代理的方式

    • jdk实现方式

      • jdk动态代理生成对象的步骤

      • 案例

    • Cglib代理调用

      • 案例

    • Cglib和jdk动态代理对比

  • 代理模式与Spring

    • 代理模式在Spring源码中的应用


定义

  • 为其他对象提供一种代理,以控制对这个对象的访问。

  • 代理对象在客户端和目标对象之间起到中介作用。

  • 代理模式属于结构型设计模式,分为静态代理和动态代理

目的

  • 保护目标对象

  • 增强目标对象

静态代理

特点

  • 程序运行前代理类的.class文件就已经存在

  • 装饰者模式就是静态代理的一种体现。

案例

  • 被代理对象

package test23;

/**
* 人有很多行为,比如谈恋爱
*/

public interface Person {
void findLove();
}

package test23;

public class Son implements Person {
@Override
public void findLove() {
System.out.println("儿子要求:肤白貌美");
}
}

  • 代理对象

package test23;

/**
* 父亲要帮儿子相亲
*/

public class Father {
private Son son;

public Father(Son son) {
this.son = son;
}
public void findLove(){
System.out.println("父亲物色对象");
son.findLove();
System.out.println("双方同意交往");
}
}

  • 测试类

package test23;

public class Main {
public static void main(String[] args) {
Father father = new Father(new Son());
father.findLove();
}
}

  • 运行结果如下

缺点

  • 大量的代码重复。静态代理类和目标类实现了相同的接口,代理类通过目标类实现了相同的方法。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。

  • 静态代理对象只服务于一种类型的对象。如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。

动态代理

动态代理和静态代理的基本思路一致,只不过动态代理功能更加强大,随着业务的扩展适应性更强。

特点

字节码随用随创建,随用随加载。

动态代理的方式

  • 基于接口JDK代理
    提供者:JDK 官方的 Proxy 类。
    要求:被代理类最少实现一个接口

  • 基于子类CGLIB代理
    提供者:第三方的 CGLib,如果报 asmxxxx 异常,需要导入 asm.jar。
    要求:被代理类不能用 final 修饰的类(最终类)。

jdk实现方式

jdk动态代理生成对象的步骤

  • 1.获取被代理对象的引用,并且获取它的所有接口,反射获取

  • 2.jdk动态代理类重新生成一个新的类,同时新的类要实现被代理类实现的所有接口

  • 3.动态生成Java代码,新加的业务逻辑方法由一定的逻辑代码调用。

  • 4.编译新生成的Java代码.class文件

  • 5.重新加载到JVM中运行。

案例

package test24;

/**
* 人有很多行为,比如谈恋爱
*/

public interface Person {
void findLove();
}

  • 被代理对象(目标对象)

package test24;

public class Customer implements Person {
@Override
public void findLove() {
System.out.println("高富帅");
}
}

  • 代理对象

package test24;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JDKMeipo 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 {
before();
Object obj = method.invoke(this.target,args);
after();
return obj;
}

private void before() {
System.out.println("我是媒婆,我要给你找对象,现在已经确认你的需求");
System.out.println("开始物色");
}

private void after() {
System.out.println("如果合适的话,就准备办事");
}
}

  • 测试类

package test24;

public class Main {
public static void main(String[] args) {
try {
Person obj = (Person)new JDKMeipo().getInstance(new Customer());
obj.findLove();
}catch (Exception e){
e.printStackTrace();
}
}
}

  • 运行结果如下

Cglib代理调用

案例

  • maven项目引入以下依赖

<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.5</version>
</dependency>

package test25;

/**
* 人有很多行为,比如谈恋爱
*/

public interface Person {
void findLove();
}

  • 被代理对象(目标对象)

package test25;

public class Customer implements Person {
@Override
public void findLove() {
System.out.println("高富帅");
}
}

  • 代理对象

package test25;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CglibMeipo implements MethodInterceptor {
public Object getInstance(Class<?> clazz) throws Exception{
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 {
before();
Object obj = methodProxy.invokeSuper(o,objects);
after();
return obj;
}

private void before() {
System.out.println("我是媒婆,我要给你找对象,现在已经确认你的需求");
System.out.println("开始物色");
}

private void after() {
System.out.println("如果合适的话,就准备办事");
}
}

  • 测试类

package test25;

public class CglibTest {
public static void main(String[] args) {
try{
Customer obj = (Customer) new CglibMeipo().getInstance(Customer.class);
obj.findLove();
}catch (Exception e){
e.printStackTrace();
}
}
}

  • 运行结果如下

Cglib和jdk动态代理对比

  • jdk动态代理实现了被代理对象的接口,Cglib代理继承了被代理对象。

  • jdk动态代理和Cglib代理在运行期生成字节码,jdk动态代理直接写Class字节码,Cglib代理使用asm框架写Class字节码,Cglib代理实现更复杂,生成代理类比jdk动态代理效率低。

  • jdk的动态代理调用代理方法是通过反射机制调用的,Cglib代理是通过FastClass机制直接调用的,Cglib代理的执行效率更高。

代理模式与Spring

代理模式在Spring源码中的应用

ProxyFactoryBean核心方法getObject(),源码如下:

/**
* Return a proxy. Invoked when clients obtain beans from this factory bean.
* Create an instance of the AOP proxy to be returned by this factory.
* The instance will be cached for a singleton, and create on each call to
* {@code getObject()} for a proxy.
* @return a fresh AOP proxy reflecting the current state of this factory
*/

@Override
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();
}
}


文章转载自喝一杯咖啡吧,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论