目录

  • 单例模式
  • 装饰器模式
  • 适配器模式
  • 工厂模式
  • 观察者模式
  • 代理模式
  • 命令模式

单例模式


装饰器模式


适配器模式


工厂模式


观察者模式


代理模式

使用代理对象来代替对真实对象的访问,这样就可以在不修改原目标对象的前提下,扩展目标对象的功能,提供额外的功能操作。
例子:下面将通过多种方式去增强 send 方法。

public interface SmsService {
    String send(String message);
}
public class SmsServiceImpl implements SmsService {
    public String send(String message) {
        System.out.println("Send message: " + message);
        return null;
    }
}

// Target Ouput
Before method send
Send message: java
After method send

1. 静态代理

// 代理类
public class SmsProxy implements SmsService {
    private final SmsService smsService;

    public SmsProxy(SmsService smsService) {
        this.smsService = smsService;
    }

    @Override
    public String send(String message) {
        System.out.println("Before method send()");
        smsService.send(message);
        System.out.println("After method send()");
        return null;
    }
}

// Usage
public class Main {
    public static void main(String[] args) {
        SmsService smsService = new SmsServiceImpl();
        SmsProxy smsProxy = new SmsProxy(smsService);
        smsProxy.send("java");
    }
}

2. JDK 动态代理

在 Java 动态代理机制中,InvocationHandler 接口用于自定义处理逻辑,Proxy 类的 newProxyInstance() 方法用于生成代理对象。

public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

public static Object newProxyInstance(ClassLoader loader, 
                                      Class<?>[] interfaces, 
                                      InvocationHandler h) 
                       throws IllegalArgumentException {
    ......
}

实现

// 定义一个动态代理类,实现 InvocationHandler,并重写 invoke 方法
public class MyInvocationHandler implements InvocationHandler {
    private final Object target; // 需要被代理的真实对象

    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) 
                    throws InvocationTargetException, IllegalAccessException {
        System.out.println("Before method " + method.getName());
        Object result = method.invoke(target, args);
        System.out.println("After method " + method.getName());
        return result;
    }
}

// 定义一个获取代理对象的工厂类
public class JdkProxyFactory {
    public static Object getProxy(Object target) {
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),  // 目标类的类加载器
                target.getClass().getInterfaces(),   // 代理需要实现的接口,可指定多个
                new MyInvocationHandler(target);      // 指定自定义 InvocationHandler
        )
    }
}

// Usage
SmsService smsService = (SmsService) JdkProxyFactory.getProxy(new SmsServiceImpl());
smsService.send("java");

3. CGLIB 动态代理

依赖

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

实现

// 定义一个方法拦截器,实现 MethodInterceptor,并重写 intercept 方法
public class MyMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) 
                    throws Throwable {
        System.out.println("Before method " + method.getName());
        Object object = methodProxy.invokeSuper(target, args);
        System.out.println("After method " + method.getName());
        return object;
    }
}

// 定义一个获取代理对象的工厂类
public class CglibProxyFactory {
    public static Object getProxy(Class<?> clazz) {
        Enhancer enhancer = new Enhancer();
        enhancer.setClassLoader(clazz.getClassLoader());
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(new MyMethodInterceptor());
        return enhancer.create();
    }
}

// Usage
AliSmsService aliSmsService = (AliSmsService) CglibProxyFactory.getProxy(AliSmsService.class);
aliSmsService.send("java");

4. 三种方式的对比

  • 静态代理中,接口一旦增加新方法,目标对象和代理对象都要进行修改;而动态代理不需要实现接口,可以直接代理实现类,并且不需要对每个目标类都创建一个代理类。
  • 静态代理在编译时就将接口、实现类、代理类这些都变成了一个个实际的 class 文件;而动态代理是在运行时动态生成类字节码,并加载到JVM中的。
  • JDK动态代理只能代理实现了接口的类或者直接代理接口,而CGLIB可以代理未实现任何接口的类。
  • JDK动态代理效率相较于CGLIB更加优秀。
  • CGLIB是通过生成一个被代理类的子类来拦截被代理类的方法调用,因此不能代理声明为 final 类型的类和方法。

命令模式


2024-11-30 技术学习·none