设计模式-类扩展-代理

不改老代码

日常 POJO 只增不改没问题的。字段命名不规范也不要改。

方法只有一处调用,或者是你开发的历史版本,改掉问题不大,多测测。

但其他情况,不要改老代码,考虑在代码上做扩展,原因是:

1. 看不惯想整合?改错了就是事故

2. 改东西要花时间的,开发要时间,测试要时间,验收也要时间

3. 底层框架里的类你改不了

类扩展的方式

Java8接口默认方法

Java API 都告诉你不要改老代码。他创建接口默认方法来实现老接口兼容。

public interface Collection<E> extends Iterable<E> {
    // 接口默认方法
    default Stream<E> stream() {
        return StreamSupport.stream(spliterator(), false);
    }
}

Collcetion是一个顶层接口,在Java8 加入一个 stream(),用于创建迭代流,所有实现类都可以用。

但假设没有接口默认方法,我们有个老代码,创建了一个 BizCollection implements Collection,代码升级的Java8就运行不起来了,必须实现 接口的 stream()。而且功能是 获取一个 Stream,这是通用的,不该在实现类里实现。也不能把接口改成抽象类,因为抽象类是一个类,单继承。

代理模式

看一个代理模式的典型实现,Spring的核心概念 AOP(Aspect Oriented Programming 面向切面编程)

Aspect Oriented Programming with Spring :: Spring Framework

以类为单位组织代码 是最常用的方式,但面向切面 提出,以切面 Aspect 为单位组织代码,实现对前者的扩展。
 

Spring AOP 使用方式

1. @Aspect 声明为切面类,可以重写 多个类的 方法

2. @Pointcut 声明 目标方法,即要重写哪个类的方法

3. @Around /@Before/@After 扩展方法执行时间。在目标方法执行之前之后都执行 / 先于目标方法实现 / 后于目标方法执行 

        @Before/@After,入参 JoinPoint jp,可以获取到目标方法声明和参数

        如果是 @Around,入参 ProceedingJoinPoint pjp,含方法 pjp.proceed(); 用于调用 目标方法,获取调用结果。来实现目标方法调用时间的灵活性,在目标方法调用前后都添加扩展代码

 AOP在 Spring frameWork中的应用

1. 事件(Events)

2. 事务(Transactions)

3. 缓存抽象(Caching Abstract)

4. 本地调度(Scheduling)

5. @RestControllerAdvice

看实现-AOP使用

spring-tx-5.3.22.jar

// org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction
/**
 * General delegate for around-advice-based subclasses, delegating to several other template
 * methods on this class. Able to handle {@link CallbackPreferringPlatformTransactionManager}
 * as well as regular {@link PlatformTransactionManager} implementations and
 * {@link ReactiveTransactionManager} implementations for reactive return types.
 * @param method the Method being invoked 目标方法
 * @param targetClass the target class that we're invoking the method on 目标类
 * @param invocation the callback to use for proceeding with the target invocation 目标调用
 * @return the return value of the method, if any 目标调用结果
 * @throws Throwable propagated from the target invocation
 */
@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
		final InvocationCallback invocation) throws Throwable {

    // 创建事务
    // Standard transaction demarcation with getTransaction and commit/rollback calls.
    TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr,         joinpointIdentification);
    Object retVal;
    try {
        // 反射获取目标对象结果
	    // This is an around advice: Invoke the next interceptor in the chain.
	    // This will normally result in a target object being invoked.
	    retVal = invocation.proceedWithInvocation();
    }
    catch (Throwable ex) {
	    // target invocation exception 异常抛出
	    completeTransactionAfterThrowing(txInfo, ex);
	    throw ex;
    }
    finally {
	    cleanupTransactionInfo(txInfo);
    }
    if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {
	    // Set rollback-only in case of Vavr failure matching our rollback rules...
	    TransactionStatus status = txInfo.getTransactionStatus();
	    if (status != null && txAttr != null) {
		    retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
	    }
    }
    // 提交事务
    commitTransactionAfterReturning(txInfo);
}

AOP实现

从使用上得知,AOP的功能是 扩展 目标方法,实现方式是 重写 目标方法。

什么方法才能重写呢

1. 语法支持 父子类 结构

2. 子类实现父类同名方法

3. 可通过父类访问 子类实现的 父类同名方法

Java 重写的实现方式 是 1. 父子类 重写方法  2. 接口实现类 实现目标方法

那AOP实现的是对一个普通类的目标方法重写,在不改变普通类下,用哪种方式重写呢,只有一个方法,给这个 普通类 创建一个 子类,使用反射。

看实现- AOP结构

spring-aop-5.3.22.jar

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
// 开启AOP 代理
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
}

/**
 * Registers an {@link org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator
 * AnnotationAwareAspectJAutoProxyCreator} against the current {@link BeanDefinitionRegistry}
 * as appropriate based on a given @{@link EnableAspectJAutoProxy} annotation.
 *
 * @author Chris Beams
 * @author Juergen Hoeller
 * @since 3.1
 * @see EnableAspectJAutoProxy
 */
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
// 
}

// doCreateBean 创建Bean -> initializeBean 初始化Bean -> 生成代理Bean
// wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);

// org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator
/**
 * Create a proxy with the configured interceptors if the bean is
 * identified as one to proxy by the subclass.
 * @see #getAdvicesAndAdvisorsForBean
 */
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
	if (bean != null) {
		Object cacheKey = getCacheKey(bean.getClass(), beanName);
		if (this.earlyProxyReferences.remove(cacheKey) != bean) {
            // 真正生成代理 
			return wrapIfNecessary(bean, beanName, cacheKey);
		}
	}
	return bean;
}


/**
 * Wrap the given bean if necessary, i.e. if it is eligible for being proxied.
 * @param bean the raw bean instance
 * @param beanName the name of the bean
 * @param cacheKey the cache key for metadata access
 * @return a proxy wrapping the bean, or the raw bean instance as-is
 */
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    // 这个Bean的方法是否被声明在 Ponitcut
    // Create proxy if we have advice.
	Object[] specificInterceptors = this.getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, (TargetSource)null);
	if (specificInterceptors != DO_NOT_PROXY) {
        // 标识 这个Bean 有切面
		this.advisedBeans.put(cacheKey, Boolean.TRUE);
        // 创建代理对象
		Object proxy = this.createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
		this.proxyTypes.put(cacheKey, proxy.getClass());
		return proxy;
	}
}

// 切面 匹配 from getAdvicesAndAdvisorsForBean
// org.springframework.aop.support.AopUtils#canApply(org.springframework.aop.Pointcut, java.lang.Class<?>, boolean)

/**
 * Can the given pointcut apply at all on the given class?
 * <p>This is an important test as it can be used to optimize
 * out a pointcut for a class.
 * @param pc the static or dynamic pointcut to check
 * @param targetClass the class to test
 * @param hasIntroductions whether the advisor chain
 * for this bean includes any introductions
 * @return whether the pointcut can apply on any method
 */
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
    // 类匹配
    if (!pc.getClassFilter().matches(targetClass)) {
	return false;
    }
	classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
	for (Class<?> clazz : classes) {
		Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
		for (Method method : methods) {
            // 方法匹配
			if (introductionAwareMethodMatcher != null ?
					introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
					methodMatcher.matches(method, targetClass)) {
				return true;
			}
		}
	}
	return false;
}


// org.springframework.aop.framework.ProxyCreatorSupport#createAopProxy
/**
 * Subclasses should call this to get a new AOP proxy. They should <b>not</b>
 * create an AOP proxy with {@code this} as an argument.
 */
protected final synchronized AopProxy createAopProxy() {
	if (!this.active) {
		activate();
	}
    // 代理工厂生成代理 this=ProxyCreatorSupport
	return getAopProxyFactory().createAopProxy(this);
}

// org.springframework.aop.framework.DefaultAopProxyFactory#createAopProxy
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    if (targetClass.isInterface() || Proxy.isProxyClass(targetClass) ||     ClassUtils.isLambdaClass(targetClass)) {
        // 代理类是 目标类实现 接口 的另一个实现类
	    return new JdkDynamicAopProxy(config);
    }
    // 代理类 是 目标类的 子类
    return new ObjenesisCglibAopProxy(config);
}

// org.springframework.aop.framework.JdkDynamicAopProxy#invoke 调用代理方法


/**
 * Implementation of {@code InvocationHandler.invoke}.
 * <p>Callers will see exactly the exception thrown by the target,
 * unless a hook method throws an exception.
 */
@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    TargetSource targetSource = this.advised.targetSource;
    target = targetSource.getTarget();
    Class<?> targetClass = (target != null ? target.getClass() : null);
    // Get the interception chain for this method. 获取方法拦截链
    List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
    // We need to create a method invocation...
    MethodInvocation invocation =
		new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
    // Proceed to the joinpoint through the interceptor chain.
    // 调用代理方法
    retVal = invocation.proceed();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值