设计模式4-代理模式

代理模式 | 菜鸟教程

组成部分

subject: 抽象主题。定义了真实主题和代理主题共同的接口

realSubject: 真实主题。实现了抽象主题接口,是代理对象所代表的真实对象。客户端直接访问真实主题,但在某些情况下,可以通过代理主题来间接访问。

proxy: 代理。实现了抽象主题接口,并持有对真实主题的引用。代理主题通常在真实主题的基础上提供一些额外的功

代理模式的实现步骤:

  • 定义主题接口:创建一个接口,定义了RealSubject和Proxy共有的方法。
public interface Subject { void request(); }
  • 实现真实主题:创建一个实现了主题接口的类。
public class RealSubject implements Subject {
    @Override
    public void request() {
      System.out.println("RealSubject: Handling request."); 
    } 
}
  • 创建代理类:代理类实现与真实主题相同的接口,并包含对真实主题对象的引用。
public class Proxy implements Subject {
    private RealSubject realSubject;

    public Proxy() {
        this.realSubject = null;
    }

    public void setRealSubject(RealSubject realSubject) {
        this.realSubject = realSubject;
    }

    @Override
    public void request() {
        if (realSubject == null) {
            realSubject = new RealSubject();
        }
        realSubject.request();
        // 可以在此处添加额外的处理逻辑
    }
}
  • 使用代理:客户端代码通过代理类间接访问真实主题对象。
public class Client {
    public static void main(String[] args) {
        Proxy proxy = new Proxy();
        proxy.request(); // 代理对象处理请求
    }
}

能,例如延迟加载、权限控制、日志记录等。

案例:Spring框架的AOP

# JDK动态代理#

基于接口实现

package com.msq.pattern.proxy;

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

public class DynamicProxyExample {

  /**
   * 前置处理
   * RealSubject: 请求方法
   * 后置处理
   */
  public static void main(String[] args) {
    // 创建被代理对象
    RealSubject realSubject = new RealSubject();
    /**
     * 创建代理对象
     *  ClassLoader loader:  代理类的类加载器。
     *  Class<?>[] interfaces: 一个接口数组,指定代理类需要实现的接口。代理类将实现这些接口中定义的所有方法。
     *  InvocationHandler h: 一个调用处理器实例,用于拦截代理实例上的方法调用,并在调用真实主题对象的方法之前或之后执行自定义逻辑。
     */
    Subject proxySubject = (Subject) Proxy.newProxyInstance(RealSubject.class.getClassLoader(),
        new Class[]{Subject.class},
        new DynamicProxyHandler(realSubject));
    // 使用代理对象调用方法
    proxySubject.request();
  }

  static class DynamicProxyHandler implements InvocationHandler{
    // 被代理的对象
    private Object target;

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

    /**
     *
     * @param proxy 代理对象的引用
     * @param method 表示正在被调用的方法。
     *                  这个对象包含了方法的名称、返回类型、参数类型等信息,你可以通过这个Method对象来判断正在调用的具体是哪个方法
     * @param args 包含被调用方法的参数。
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      System.out.println("前置处理");
      Object result = method.invoke(target, args);
      if (method.getName().equals("write")){
        System.out.println(method.getDefaultValue());
      }
      System.out.println("后置处理");
      return result;
    }
  }
}

# CGlib动态代理#

类代理

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.InvocationHandler; // CGLIB 3.0 以后使用 InvocationHandler

// 定义一个目标类,不需要实现任何接口
class RealObject {
    public void performAction() {
        System.out.println("Real object performing action.");
    }
}

// 定义一个拦截器,实现 MethodInterceptor 接口。代理类
class MyMethodInterceptor implements MethodInterceptor {
    // 重写 intercept 方法,用于拦截目标方法的调用
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("前置处理.");
        // 调用目标方法
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("后置处理.");
        return result;
    }
    
    // 实现一个方法来处理方法调用的异常
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        try {
            return intercept(obj, method, args, proxy);
        } catch (Exception e) {
            // 异常处理逻辑
            throw e;
        }
    }
}

// client类
public class CglibProxyDemo {
    public static void main(String[] args) {
        // 创建 Enhancer 对象,它是 CGLIB 的核心类,用于增强类
        Enhancer enhancer = new Enhancer();
        
        // 设置 Enhancer 的父类,即目标类
        enhancer.setSuperclass(RealObject.class);
        
        // 设置回调,即拦截器
        enhancer.setCallback(new MyMethodInterceptor());
        
        // 创建代理对象,它扩展了目标类
        RealObject proxyInstance = (RealObject) enhancer.create();
        
        // 调用代理对象的方法,实际调用的是拦截器的 intercept 方法
        proxyInstance.performAction();
    }
}

pom依赖

<dependency>

        <groupId>cglib</groupId>

        <artifactId>cglib</artifactId>

        <version></version> 

</dependency>

# 扩展一:Spring Aop#

aop概念点
  • JoinPoint(连接点):表示程序执行过程中可以插入额外逻辑的点,通常是方法的调用。
  • Pointcut(切入点):是对JoinPoint的表达式选择,用于确定哪些JoinPoint需要被拦截。
  • Advice(通知):在特定的JoinPoint上执行的代码,可以是方法的前置、后置、异常处理或环绕通知。
  • Aspect(切面):一个或多个Advice的集合,以及它们应用的Pointcut。
  • Proxy(代理):Spring AOP会为目标对象创建代理,代理会拦截方法调用并应用Advice。
  • Target(目标对象):被代理的对象。
  • Weaving(织入):将Aspect应用到Target的过程。
代理的类型
  • JDK动态代理:如果目标对象实现了至少一个接口,Spring AOP会使用JDK的Proxy类来创建代理对象。

  • CGLIB代理:如果目标对象没有实现接口,Spring AOP会使用CGLIB库来为目标对象创建一个子类,并在子类中实现代理。

Spring AOP还支持其他类型的代理,如使用AspectJ的AOP实现,但这通常需要引入额外的依赖和配置。

Spring AOP的步骤:
  1. 定义切面,包括切入点和通知。
  2. 配置切面,指定目标对象和代理类型。
  3. 织入切面,将切面应用到目标对象。
@Component
@Aspect
// 定义一个切面
public class MyAspect {
  
  /*
  @Before("execution(* com.example.service.*.*(..)) && this(target)")
  public void beforeAdvice(JoinPoint joinPoint, Object target) {
    // 直接通过目标对象引用修改属性
    ((YourClass) target).yourProperty = newValue;
  }
   */

  // 连接点、切入点、通知
  @Before("execution(* com.example.service.*.*(..))")
  public void beforeAdvice(JoinPoint joinPoint) {
    // 执行前置通知
    // 获取目标对象
    Object target = joinPoint.getTarget();
    Field field = target.getClass().getDeclaredField("yourProperty");
    field.setAccessible(true);
    field.set(target, newValue);
  }

  // 连接点、切入点、通知
  @After("execution(* com.example.service.*.*(..))")
  public void afterAdvice() {
    // 执行后置通知
  }

  // 可以添加其他类型的Advice
}
spring声明式事务 @Transactional

在Spring Boot架构中,通过自动配置(auto-configuration)和条件注解(@Conditional)进一步简化了配置过程。以下是Spring Boot中声明式事务管理的关键步骤:

  1. 添加依赖: 确保你的Spring Boot项目中包含了Spring Boot的事务依赖,通常是spring-boot-starter-data-jpaspring-boot-starter-jdbc,这取决于你使用的是JPA还是JDBC。

  2. 配置数据源: 在application.propertiesapplication.yml中配置数据库连接信息,Spring Boot会自动配置数据源。

  3. 启用事务管理: Spring Boot自动配置机制会检测到@EnableTransactionManagement的存在,并自动启用基于注解的事务管理。你不需要显式地在配置类上添加这个注解,因为Spring Boot的自动配置已经包含了它。

  4. 使用@Transactional注解: 在服务层或数据访问层的类或方法上使用@Transactional注解来声明事务边界。Spring Boot会自动扫描这些注解,并使用AOP来创建代理,管理事务。

  5. 自动代理创建: Spring Boot使用@EnableAutoConfiguration@SpringBootApplication注解来激活自动配置。这包括了TransactionInterceptor用于拦截被@Transactional注解的方法;AnnotationAwareAspectJAutoProxyCreator,它会自动为使用@Transactional注解的Bean创建代理。这个自动代理创建器会扫描所有的beans,如果发现有@Transactional注解的beans,就会为它们创建代理。

  6. 事务属性的配置@Transactional注解本身包含了多个属性,如propagation(传播行为)、isolation(隔离级别)、timeout(超时时间)、readOnly(只读标志)等,这些属性定义了事务的具体行为。如果方法抛出异常,并且根据rollbackFor属性或其他规则需要回滚,事务将被回滚。

  7. 事务管理器的配置: 如果你使用的是JPA,Spring Boot会默认配置一个JpaTransactionManager。对于JDBC,会配置一个DataSourceTransactionManager。你可以通过配置文件或Java配置来自定义事务管理器。自定义事务配置: 也可以自定义事务配置,例如通过实现TransactionManagementConfigurer接口来提供自定义的PlatformTransactionManager

  8. 事务同步管理器: 使用TransactionSynchronizationManager来注册事务同步处理器,这些处理器可以在事务的不同阶段(如提交前后或回滚前后)执行额外的任务。

  9. Spring Boot的监控和管理: Spring Boot提供了多种监控和管理功能,例如健康检查、度量信息收集等,这些可以帮助你监控事务的状态和性能。

  • 20
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值