Spring之Joinpoint类详解

2023-11-02

说明

Joinpoint是AOP的连接点。一个连接点代表一个被代理的方法。我们从源码角度看连接点有哪些属性和功能。

源码

/*
 * Copyright 2002-2016 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.aopalliance.intercept;

import java.lang.reflect.AccessibleObject;

/**
 * This interface represents a generic runtime joinpoint (in the AOP
 * terminology).
 *这个接口就是AOP中的连接点。
 * <p>A runtime joinpoint is an <i>event</i> that occurs on a static
 * joinpoint (i.e. a location in a the program). For instance, an
 * invocation is the runtime joinpoint on a method (static joinpoint).
 * The static part of a given joinpoint can be generically retrieved
 * using the {@link #getStaticPart()} method.
 *静态连接点就是被代理的方法本身,可以通过getStaticPart方法调用。动态连接点就是对
 *静态方法之外的增强方法
 * <p>In the context of an interception framework, a runtime joinpoint
 * is then the reification of an access to an accessible object (a
 * method, a constructor, a field), i.e. the static part of the
 * joinpoint. It is passed to the interceptors that are installed on
 * the static joinpoint.
 *动态连接点从静态连接点的拦截器上获取静态部分,并进行相应的加强,从而形成动态连接点
 * @author Rod Johnson
 * @see Interceptor
 */
public interface Joinpoint {

	/**
	 * Proceed to the next interceptor in the chain.
	 * 转到链的下一个拦截器上。详细请看子类的实现
	 * <p>The implementation and the semantics of this method depends
	 * on the actual joinpoint type (see the children interfaces).
	 * @return see the children interfaces' proceed definition
	 * @throws Throwable if the joinpoint throws an exception
	 */
	Object proceed() throws Throwable;

	/**
	 * Return the object that holds the current joinpoint's static part.
	 * 返回持有当前连接点静态部分的对象,例如,调用的目标对象
	 * <p>For instance, the target object for an invocation.
	 * @return the object (can be null if the accessible object is static)
	 */
	Object getThis();

	/**
	 * Return the static part of this joinpoint.
	 * <p>The static part is an accessible object on which a chain of
	 * interceptors are installed.
	 * 返回连接点的静态部分。静态部分是一个拥有连接器链的对象。意思就是这个静态方法都要被谁拦截,可以通过getStaticPart返回。
	 */
	AccessibleObject getStaticPart();

}

从上面源码来看,我们可以知道,Joinpoint分为动态和静态,且有一个拦截器链作用于Joinpoint。那么具体静态和动态是什么,拦截器链又是什么,我们看其子类实现。

ReflectiveMethodInvocation类源码

Spring提供的Joinpoint实现类只有ReflectiveMethodInvocation类。当然这个类实现了Joinpoint的扩展来,要比Jointpoint功能强大,我们来看这个类源码。

/*
 * Copyright 2002-2019 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.aop.framework;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

import org.springframework.aop.ProxyMethodInvocation;
import org.springframework.aop.support.AopUtils;
import org.springframework.core.BridgeMethodResolver;
import org.springframework.lang.Nullable;

/**
 * Spring's implementation of the AOP Alliance
 * {@link org.aopalliance.intercept.MethodInvocation} interface,
 * implementing the extended
 * {@link org.springframework.aop.ProxyMethodInvocation} interface.
 *
 * <p>Invokes the target object using reflection. Subclasses can override the
 * {@link #invokeJoinpoint()} method to change this behavior, so this is also
 * a useful base class for more specialized MethodInvocation implementations.
 *
 * <p>It is possible to clone an invocation, to invoke {@link #proceed()}
 * repeatedly (once per clone), using the {@link #invocableClone()} method.
 * It is also possible to attach custom attributes to the invocation,
 * using the {@link #setUserAttribute} / {@link #getUserAttribute} methods.
 *
 * <p><b>NOTE:</b> This class is considered internal and should not be
 * directly accessed. The sole reason for it being public is compatibility
 * with existing framework integrations (e.g. Pitchfork). For any other
 * purposes, use the {@link ProxyMethodInvocation} interface instead.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @author Adrian Colyer
 * @see #invokeJoinpoint
 * @see #proceed
 * @see #invocableClone
 * @see #setUserAttribute
 * @see #getUserAttribute
 */
public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Cloneable {

	protected final Object proxy;

	@Nullable
	protected final Object target;

	protected final Method method;

	protected Object[] arguments;

	@Nullable
	private final Class<?> targetClass;

	/**
	 * Lazily initialized map of user-specific attributes for this invocation.
	 */
	@Nullable
	private Map<String, Object> userAttributes;

	/**
	 * List of MethodInterceptor and InterceptorAndDynamicMethodMatcher
	 * that need dynamic checks.
	 */
	protected final List<?> interceptorsAndDynamicMethodMatchers;

	/**
	 * Index from 0 of the current interceptor we're invoking.
	 * -1 until we invoke: then the current interceptor.
	 */
	private int currentInterceptorIndex = -1;


	/**
	 * Construct a new ReflectiveMethodInvocation with the given arguments.
	 * @param proxy the proxy object that the invocation was made on
	 * @param target the target object to invoke
	 * @param method the method to invoke
	 * @param arguments the arguments to invoke the method with
	 * @param targetClass the target class, for MethodMatcher invocations
	 * @param interceptorsAndDynamicMethodMatchers interceptors that should be applied,
	 * along with any InterceptorAndDynamicMethodMatchers that need evaluation at runtime.
	 * MethodMatchers included in this struct must already have been found to have matched
	 * as far as was possibly statically. Passing an array might be about 10% faster,
	 * but would complicate the code. And it would work only for static pointcuts.
	 */
	protected ReflectiveMethodInvocation(
			Object proxy, @Nullable Object target, Method method, @Nullable Object[] arguments,
			@Nullable Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers) {

		this.proxy = proxy;
		this.target = target;
		this.targetClass = targetClass;
		this.method = BridgeMethodResolver.findBridgedMethod(method);
		this.arguments = AopProxyUtils.adaptArgumentsIfNecessary(method, arguments);
		this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;
	}


	@Override
	public final Object getProxy() {
		return this.proxy;
	}

	@Override
	@Nullable
	public final Object getThis() {
		return this.target;
	}

	@Override
	public final AccessibleObject getStaticPart() {
		return this.method;
	}

	/**
	 * Return the method invoked on the proxied interface.
	 * May or may not correspond with a method invoked on an underlying
	 * implementation of that interface.
	 */
	@Override
	public final Method getMethod() {
		return this.method;
	}

	@Override
	public final Object[] getArguments() {
		return this.arguments;
	}

	@Override
	public void setArguments(Object... arguments) {
		this.arguments = arguments;
	}


	@Override
	@Nullable
	public Object proceed() throws Throwable {
		// We start with an index of -1 and increment early.
		if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
			return invokeJoinpoint();
		}

		Object interceptorOrInterceptionAdvice =
				this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
		if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
			// Evaluate dynamic method matcher here: static part will already have
			// been evaluated and found to match.
			InterceptorAndDynamicMethodMatcher dm =
					(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
			Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
			if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
				return dm.interceptor.invoke(this);
			}
			else {
				// Dynamic matching failed.
				// Skip this interceptor and invoke the next in the chain.
				return proceed();
			}
		}
		else {
			// It's an interceptor, so we just invoke it: The pointcut will have
			// been evaluated statically before this object was constructed.
			return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
		}
	}

	/**
	 * Invoke the joinpoint using reflection.
	 * Subclasses can override this to use custom invocation.
	 * @return the return value of the joinpoint
	 * @throws Throwable if invoking the joinpoint resulted in an exception
	 */
	@Nullable
	protected Object invokeJoinpoint() throws Throwable {
		return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);
	}


	/**
	 * This implementation returns a shallow copy of this invocation object,
	 * including an independent copy of the original arguments array.
	 * <p>We want a shallow copy in this case: We want to use the same interceptor
	 * chain and other object references, but we want an independent value for the
	 * current interceptor index.
	 * @see java.lang.Object#clone()
	 */
	@Override
	public MethodInvocation invocableClone() {
		Object[] cloneArguments = this.arguments;
		if (this.arguments.length > 0) {
			// Build an independent copy of the arguments array.
			cloneArguments = new Object[this.arguments.length];
			System.arraycopy(this.arguments, 0, cloneArguments, 0, this.arguments.length);
		}
		return invocableClone(cloneArguments);
	}

	/**
	 * This implementation returns a shallow copy of this invocation object,
	 * using the given arguments array for the clone.
	 * <p>We want a shallow copy in this case: We want to use the same interceptor
	 * chain and other object references, but we want an independent value for the
	 * current interceptor index.
	 * @see java.lang.Object#clone()
	 */
	@Override
	public MethodInvocation invocableClone(Object... arguments) {
		// Force initialization of the user attributes Map,
		// for having a shared Map reference in the clone.
		if (this.userAttributes == null) {
			this.userAttributes = new HashMap<>();
		}

		// Create the MethodInvocation clone.
		try {
			ReflectiveMethodInvocation clone = (ReflectiveMethodInvocation) clone();
			clone.arguments = arguments;
			return clone;
		}
		catch (CloneNotSupportedException ex) {
			throw new IllegalStateException(
					"Should be able to clone object of type [" + getClass() + "]: " + ex);
		}
	}


	@Override
	public void setUserAttribute(String key, @Nullable Object value) {
		if (value != null) {
			if (this.userAttributes == null) {
				this.userAttributes = new HashMap<>();
			}
			this.userAttributes.put(key, value);
		}
		else {
			if (this.userAttributes != null) {
				this.userAttributes.remove(key);
			}
		}
	}

	@Override
	@Nullable
	public Object getUserAttribute(String key) {
		return (this.userAttributes != null ? this.userAttributes.get(key) : null);
	}

	/**
	 * Return user attributes associated with this invocation.
	 * This method provides an invocation-bound alternative to a ThreadLocal.
	 * <p>This map is initialized lazily and is not used in the AOP framework itself.
	 * @return any user attributes associated with this invocation
	 * (never {@code null})
	 */
	public Map<String, Object> getUserAttributes() {
		if (this.userAttributes == null) {
			this.userAttributes = new HashMap<>();
		}
		return this.userAttributes;
	}


	@Override
	public String toString() {
		// Don't do toString on target, it may be proxied.
		StringBuilder sb = new StringBuilder("ReflectiveMethodInvocation: ");
		sb.append(this.method).append("; ");
		if (this.target == null) {
			sb.append("target is null");
		}
		else {
			sb.append("target is of class [").append(this.target.getClass().getName()).append(']');
		}
		return sb.toString();
	}

}

我们看其几个重要的属性:

   protected final Object proxy; //代理对象

	@Nullable
	protected final Object target; //目标对象

	protected final Method method; //调用的方法

	protected Object[] arguments; //调用方法的参数

	@Nullable
	private final Class<?> targetClass; //目标类的class对象

    //需要动态核对的MethodInterceptor和InterceptorAndDynamicMethodMatcher列表
    protected final List<?> interceptorsAndDynamicMethodMatchers;

    private int currentInterceptorIndex = -1; //拦截器脚本

我们分析其核心方法,proceed()方法代码:

public Object proceed() throws Throwable {
		// We start with an index of -1 and increment early.
		if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
			return invokeJoinpoint();
		}

		Object interceptorOrInterceptionAdvice =
				this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
		if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
			// Evaluate dynamic method matcher here: static part will already have
			// been evaluated and found to match.
			InterceptorAndDynamicMethodMatcher dm =
					(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
			Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
			if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
				return dm.interceptor.invoke(this);
			}
			else {
				// Dynamic matching failed.
				// Skip this interceptor and invoke the next in the chain.
				return proceed();
			}
		}
		else {
			// It's an interceptor, so we just invoke it: The pointcut will have
			// been evaluated statically before this object was constructed.
			return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
		}
	}

首先,分析这段:
在这里插入图片描述
当拦截器的角标达到和interceptorsAndDynamicMethodMatchers列表的最后一个元素时,执行invokeJoinpoint()方法。
我们首先要搞清楚,interceptorsAndDynamicMethodMatchers的作用是什么。上面提到,interceptorsAndDynamicMethodMatchers里存放的是MethodInterceptor和InterceptorAndDynamicMethodMatcher对象。要清楚interceptorsAndDynamicMethodMatchers的作用,必须知道MethodInterceptor和InterceptorAndDynamicMethodMatcher对象是干什么的。
我们看MethodInterceptor的源码:

public interface MethodInterceptor extends Interceptor {

	/**
	 * Implement this method to perform extra treatments before and
	 * after the invocation. Polite implementations would certainly
	 * like to invoke {@link Joinpoint#proceed()}.
	 * @param invocation the method invocation joinpoint
	 * @return the result of the call to {@link Joinpoint#proceed()};
	 * might be intercepted by the interceptor
	 * @throws Throwable if the interceptors or the target object
	 * throws an exception
	 */
	Object invoke(MethodInvocation invocation) throws Throwable;

}

该接口只有一个invoke方法,该方法的参数MethodInvocation接口就是Joinpoint接口的一个扩展接口。在Joinpoint的proceed方法中调用此方法,通过MethodInvocation对象可以获取到目标类的原方法,然后还能进行一些其他的操作。所以,invoke方法就是原方法的增强,也就是代理方法的实现。
我们看InterceptorAndDynamicMethodMatcher对象的源码:

class InterceptorAndDynamicMethodMatcher {

	final MethodInterceptor interceptor;

	final MethodMatcher methodMatcher;

	public InterceptorAndDynamicMethodMatcher(MethodInterceptor interceptor, MethodMatcher methodMatcher) {
		this.interceptor = interceptor;
		this.methodMatcher = methodMatcher;
	}

}

由源码可知,InterceptorAndDynamicMethodMatcher对象将MethodInterceptor 和MethodMatcher 整合到了一起。MethodMatcher 类我们在之前讲过,具体请查阅《Spring AOP之PointCut详解》
综上所述,Joinpoint的interceptorsAndDynamicMethodMatchers属性里存的是MethodInterceptor对象和MethodMatcher对象。通过MethodMatcher对象判断调用方法是否需要代理,如果需要代理,则放入MethodIntecptor对象。所以,interceptorsAndDynamicMethodMatchers属性里存的是需要被代理加强的方法。

我们继续看proceed方法,搞清楚interceptorsAndDynamicMethodMatchers属性后,当过滤器的角标到interceptorsAndDynamicMethodMatchers集合的最后一位时,会执行invokeJoinpoint()方法。我们看这个方法源码:

protected Object invokeJoinpoint() throws Throwable {
		return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);
	}

可以看到,该方法是利用反射调用目标方法执行了。即一个方法如果没有被增强,interceptorsAndDynamicMethodMatchers集合就没有值,角标就是-1。这时就直接执行目标方法,不做任何处理。如此看来,只要一个类的某个方法被加强了,那么这个类所有方法都会执行proceed()方法,然后通过MethodMatcher方法,判断是否要进行加强。

我们继续往下看proceed()方法的源码:
在这里插入图片描述
如果interceptorsAndDynamicMethodMatchers有值,则说明要进行拦截操作了。如果获取的元素是InterceptorAndDynamicMethodMatcher对象,则进入if分支,否则,进入else分支。我们先看if分支的逻辑。
if分支的核心代码为:
在这里插入图片描述
这里我们一定要有一个概念,连接点针对的是方法,所以,这里用MethodMatcher比较该连接点的方法是否匹配,如果匹配,则调用MethodIntecptor的invoke方法,进行增强。如果不匹配,则说明该InterceptorAndDynamicMethodMatcher里的MethodMatcher不是该连接点方法的,则递归调用proceed方法,比较interceptorsAndDynamicMethodMatchers属性中的下一个对象。
(问题:interceptorsAndDynamicMethodMatchers属性中存的是这个类相关的MethodMatcher还是所有类的MethodMatcher?需要验证)

下面我们看else分支,即interceptorsAndDynamicMethodMatchers属性里取出的元素不是InterceptorAndDynamicMethodMatcher对象。
在这里插入图片描述
可以看到其直接强转成了MethodInterceptor对象,调用了invoke方法。
(问题:interceptorsAndDynamicMethodMatchers属性里为什么会存两种类型的对象呢? )

至此,proceed()方法解析完毕。我们来总结一下。
首先要明确的一点就是一个连接点代表着一个对象里的一个方法。一个对象里的多个方法,就是多个连接点。每个连接点对象中,都存着一个拦截器链,proceed方法就是遍历拦截器链,如果和连接点所代表的方法一致,则执行MethodInterceptor的invoke方法,进行方法的代理,如果拦截器和代理方法不匹配,则进入下一个拦截器。直到都不匹配,则执行原始方法。

下面,我们结合实际应用,来梳理一下上面的源码流程。我们先定义一个切面:

@Aspect
@Component
public class SendMessageAspect{

    public SendMessageAspect(){
        System.out.println("测试入口");
    }

    @Autowired
    ICimKeywordService keywordService;


    @Pointcut("execution(public * com.farsunset.cim.component.handler.SendMessageHandler.process(..))")
   // @Pointcut("execution(public * com.farsunset.cim.config.CIMConfig.process(..))")
    public void execute() {

    }


    @Around("execute()")
    public void around(ProceedingJoinPoint joinPoint)throws Throwable {
        SendMessageHandler sendMessageHandler = SpringUtils.getBean("sendMessageHandler");
        sendMessageHandler.toString();
        // 获取目标方法的名称
        String methodName = joinPoint.getSignature().getName();
        // 获取方法传入参数
        Object[] params = joinPoint.getArgs();
        SentBody body=(SentBody)params[1];
        String content=body.get("content");
        String format=body.get("format");
        if("text".equals(format)&& StringUtils.isNotEmpty(content)){
            //将关键字替换成*
          List<CimKeyword> keywords= keywordService.selectCimKeywordList(null);
          if(keywords!=null&&keywords.size()>0){
              for (CimKeyword keyword:
                      keywords) {
                  if(content.contains(keyword.getKeyword())){
                      content=content.replaceAll(keyword.getKeyword(),"**");
                  }
              }
              body.put("content",content);
              params[1]=body;
          }
        }

        // 执行源方法
        joinPoint.proceed(params);

    }

    @Before("execute()")
    public void before()throws Throwable {
        System.out.println("执行了");
        // 获取目标方法的名称


    }
}

这里我们定义了两个加强,Around和Before。实际开发中不会同时定义这两个。这里我们只是用于研究测试用。
我们从Spring容器中先获得SendMessageAspect对象,可以看到,其是一个代理对象:
在这里插入图片描述
然后我们调用该对象的toString方法,可以看到,代码会进入proceed方法。这也说明,代理对象的所有方法,都会形成一个连接点对象。如下:
在这里插入图片描述
可以看到,此时的连接点中的method属性,就是toString()。然后执行proceed方法,因为并没有对toString方法进行加强,所以最终执行的是目标对象的toString方法。
我们再看连接点的interceptorsAndDynamicMethodMatchers属性,看其拦截器都是什么:
在这里插入图片描述
可以看到,拦截器中就一个默认的ExposeInvocationInterceptor拦截器。这个拦截器我们单独讲解,所有的代理对象第一个拦截器都是这个默认拦截器。所以,toString方法没有任何其他拦截器,不进行加强。

我们再看调用SendMessageHandler对象的process方法,连接点对象的内部情况:
在这里插入图片描述
该连接点是要加强的方法,我们看其拦截器有哪些:
在这里插入图片描述
可以看到,除了第一个默认的拦截器,process连接点的拦截器还有AspectJAroundAdvice拦截器和MethodBeforeAdviceInterceptor。这两个对象都是MethodInterceptor的子类。当调用proceed方法的时候,会执行这两个类的invoke方法。这两个类的invoke方法就分别对应着@Around和@Before所对应的方法。
由此可知,我们定义的加强方法,最终会封装成相应的MethodInterceptor对象,在连接点的proceed中被调用。而在连接点的拦截器中,已经封装好了连接点所代理方法MethodInterceptor。连接点直接用拦截器进行方法的调用即可。

总结

综上所述,连接点是Spring AOP中的最小单元。连接点里存放了代理对象的目标类,目标方法,方法拦截器。进行代理的时候,调用拦截器MethodInterceptor的加强方法,执行代理方法。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring之Joinpoint类详解 的相关文章

  • 如何生成源代码来创建我正在调试的对象?

    我的典型场景 我处理的遗留代码有一个错误 只有生产中的客户端才会遇到 我附加了一个调试器并找出如何重现该问题their系统给定their输入 但是 我还不知道为什么会发生错误 现在我想在本地系统上编写一个自动化测试来尝试重现然后修复错误 最
  • Spring JSON序列化、Gson反序列化

    我目前在某些内部对象的反序列化方面遇到问题 在春天 我在使用输出之前初始化所有对象 ResponseBody 例如 这是一个响应 id 1 location id 1 extra location data id 2 location 1
  • 如何在线程和小程序中使用双缓冲

    我有一个关于何时调用绘制和更新方法的问题 我有游戏小程序 我想在其中使用双缓冲 但我无法使用它 问题是 在我的游戏中 有一个球在 run 方法内移动 我想知道如何使用双缓冲来交换屏幕外图像和当前图像 请有人帮忙 当同时存在 update 和
  • Java中单击和双击的区别

    我搜索论坛并看到以下代码 public void mouseClicked MouseEvent e if e getClickCount 2 System out println and it s a double click wasDo
  • JP QL - 一对多关系中的过滤结果

    我在尝试构建 JPQL 查询时陷入困境 并希望比我拥有更多 JPA 经验的人能够提供帮助 考虑以下两个实体 class Author String name OneToMany mappedBy author Set
  • 在java中查找OSX的版本

    我需要测试 java 中 osx 的版本是否 Try System getProperty os name and or System getProperty os version 它返回字符串 HERE https docs oracle
  • [重复]

    这个问题在这里已经有答案了 有什么区别List
  • Struts ActionForm 属性应该是什么类型?

    我使用 Struts 1 2 4 继承了这个巨大的遗留 Java Web 应用程序 我有一个关于 ActionForms 的具体问题 其中一些仅具有字符串属性 即使对于数字 其中一些使用看似合适的类型 整数 日期 字符串等 这里的最佳实践是
  • 由于 maven-surefire-plugin,Maven 构建失败

    我这里有类似的问题eclipse 中缺少 maven surefire plugin https stackoverflow com questions 23588957 maven surefire plugin missing in e
  • Spring Security 的 AJAX 请求给出 403 Forbidden

    我有一个基于spring boot spring security thymeleaf的网站 在某些情况下我也使用ajax 问题 我在 Spring Security 中使用表单登录安全性 在浏览器中 登录后我可以使用rest API GE
  • 如何将 java ArrayList 转换为等效的 double[] [重复]

    这个问题在这里已经有答案了 可能的重复 如何在 Java 中从 List 转换为 double https stackoverflow com questions 6018267 how to cast from listdouble to
  • 如何在 jax-ws 客户端中隐藏(可能)由 jax-ws 库引起的警告

    我正在使用 netbeans 在我的应用程序中生成 Web 服务客户端 我的程序使用 jax ws 库来设置调用 Web 服务的超时 出现问题是因为每当我启动这个程序时它都会生成很多这样的警告消息 2010 年 12 月 13 日下午 4
  • 调整 Java 类以提高 CPU 缓存友好性

    在设计java类时 对于实现CPU缓存友好性有哪些建议 到目前为止我学到的是应该尽可能多地使用 POD 即 int 而不是整数 这样 在分配包含对象时 数据将被连续分配 例如 class Local private int data0 pr
  • 从 API Explorer 调用 API 方法时不允许使用范围

    我在 Google App Engine 中有一个奇怪的行为 我正在使用 Eclipse 和 Java 进行开发 特别是使用 Google Cloud Endpoints 我使用以下设置创建了一个示例 API 实际上 我正在使用许多其他示波
  • 无法实例化类对象的类型 (Java)

    这是我收到错误的代码 在 new 之后的第二个 Killer 处 String classes new String 5 kills 0 Brian Moser kills 1 James Doakes kills 2 Lila Tourn
  • 添加元数据到快速路线

    有什么方法可以将元数据添加到 Express 的路线中吗 例如 app get some route function req res some meta data 我正在寻找一种针对我的节点应用程序的 AOP 方法 因此我想通过身份验证和
  • 为什么 writeObject 抛出 java.io.NotSerializedException 以及如何修复它?

    我有这个异常 我不明白为什么会抛出它 或者我应该如何处理它 try os writeObject element catch IOException e e printStackTrace Where element is a Transf
  • 部署到 Glassfish 4.1 时 URL 模式无效

    如果用户已经通过身份验证 我有一个网络过滤器可以从登录和索引页面重定向 最初我有一个无效的 URL 模式 我修复了无效模式并尝试重新部署以接收以下内容 java lang IllegalArgumentException Invalid U
  • JPA中如何连接多个数据库?

    我有一个 Spring Boot 应用程序 当前使用 JPA 连接到单个数据库 application properties 文件中的连接详细信息 spring datasource url jdbc oracle thin localho
  • DOM 中不再存在缓存元素

    就像在类似的问题中一样 我使用appium java 尝试选择元素 在移动应用程序中 我要转到页面 之后有许多元素 android widget ImageView 0 我需要选择 6 个 例如 这样的元素并执行其他步骤 Byt 只能选择一

随机推荐

  • 2020年校赛网络赛

    51假期那段时间因为水了一段时间的数模校赛 加上专业课的坑越欠越多 因而已经很久很久没有补过题目了 从近到远 先把西电校赛的坑填起来 再把之前的CF牛客Atcoder补起来 qaq C 发现交换律后就显然了 D 双指针 好像第一天晚上还有个
  • 蓝桥杯2022真题:统计子矩阵、字符统计、排列字母、顺子日期、特殊时间、三角回文数、2022、星期计算

    目录 1 统计子矩阵 2 字符统计 3 排列字母 4 顺子日期 5 特殊时间 6 三角回文数 7 2022 8 星期计算 1 统计子矩阵 import os import sys 请在此输入您的代码 n m k map int input
  • chrome扩展结构

    每个打开的页面都运行在web页面环境中 一个正常的web页面环境 会先初始化css 然后建立DOM树 接着加载图片和frame等子资源 然后顺序执行所有js l chrome扩展本身运行在一个进程中 称之为background环境 back
  • 【软件测试学习笔记】白盒测试

    文章目录 一 白盒测试概述 二 分类 1 静态测试 2 动态测试 三 白盒测试原则 四 白盒测试类别 五 不同阶段不同侧重点 1 单元测试 2 集成测试 3 系统测试 4 验收测试 六 测试覆盖率 1 功能点覆盖 2 结构覆盖率 七 逻辑覆
  • LVGL在linux环境搭建篇

    LVGL环境搭建 1 环境准备 1 下载源码 https github com lvgl lvgl https github com lvgl lvgl 2 新建lvgl 文件夹 把src 和lvgl h 和lv conf template
  • 疯狂的采药(完全背包例题详解)

    题目 每种草药可以无限制地采摘 每种草药对应采药时间 草药价值 求在一定的采药时间下 采出的药最大总价值是多少 输入格式 输入第一行有两个整数 分别代表总共能够用来采药的时间 t 和代表山洞里的草药的数目 m 第 2 到第 m 1 行 每行
  • Entity Framework Core系列教程-18-断开模式下删除数据

    Entity Framework Core 断开模式下删除数据 EF Core API会为EntityState为Deleted的实体建立并执行数据库中的DELETE语句 在EF Core中已连接和已断开连接的场景中删除实体没有什么区别 E
  • 【数据结构】二叉树接口的实现及OJ题

    需要云服务器等云产品来学习Linux的同学可以移步 gt 腾讯云 lt gt 阿里云 lt gt 华为云 lt 官网 轻量型云服务器低至112元 年 新用户首次下单享超低折扣 目录 一 二叉树的接口 1 二叉树的结构体 2 手动造一颗二叉树
  • 防止Sql注入拦截

    这两天在做一个sql注入拦截 期间遇到了不少问题 最大的问题是在 拦截sql注入后利用response 重定向到错误页面 始终无法实现跳转 发现原因是 ajax 异步请求时并不会对response重定向做处理 当然包括response ge
  • vite 配置路径别名@和动态引入assets资源

    vite 配置路径别名 vite config js配置 import fileURLToPath URL from node url 如果是ts 则需下载 types node以来 import defineConfig from vit
  • 解决socket.error: [Errno 98] Address already in use问题

    一 基本设置 如果python中socket 绑定的地址正在使用 往往会出现错误 在linux下 则会显示 socket error Errno 98 Address already in use 在windows下 则会显示 socket
  • linux访问有域名的ftp,Linux安装了ftp服务怎么用域名访?

    ftp directory怎么配置根 请自行准备好华为交换机和电脑 并且让你的电脑和交换机连接上 不管是telnet还是terminal都是可以的 首先需要在 Quidway 下启动ftp服务 Quidway ftp server enab
  • GPU比较(1285Lv4&1245v5)

    1285Lv4 Intel Iris Pro Graphics P6300 Iris Graphics 6200 P6300 EU 48 核心代号 GT3e 1245v5 HD Graphics P530 EU 48 核心代号 GT3e
  • 网络安全面试题

    IT面试 前言 首先 从底层的环境 计算机基础 即网络 系统方面开始 网络从交换 路由的基本认知到排错 系统从命令查看方面 其次 最后 通过编程语言 如python 提供自动化运维的方法 提高办公 简历方面 专业技能模块可以写成 1 熟悉网
  • sql-labs闯关26~31

    sql labs闯关26 31 友善爱国平等诚信民主富强爱国友善自由友善爱国平等诚信民主爱国爱国爱国 复习笔记1 第29 31关先跳过 回头再做 内容 sql labs第26关 GET请求 基于错误 过滤空格和注释 sql labs第26a
  • 报错解决:PermissionError

    在linux环境中安装jupyter notebook的时候遇到的错误 记录一下 PermissionError Errno 13 Permission denied run user 1002 jupyter 解决办法 chmod 777
  • 一文读懂深度学习中的矩阵微积分

    点击上方 小白学视觉 选择加 星标 或 置顶 重磅干货 第一时间送达 本文转自 视学算法 想要真正了解深度神经网络是如何训练的 免不了从矩阵微积分说起 虽然网络上已经有不少关于多元微积分和线性代数的在线资料 但它们通常都被视作两门独立的课程
  • 双向LSTM 对航空乘客预测

    前言 1 LSTM 航空乘客预测 单步预测和多步预测 简单运用LSTM 模型进行预测分析 2 加入注意力机制的LSTM 对航空乘客预测采用了目前市面上比较流行的注意力机制 将两者进行结合预测 3 多层 LSTM 对航空乘客预测 简单运用多层
  • cmd 用命令连接oracle数据库

    这里所用的数据库在tnsnames ora里的配置 mesdb155 DESCRIPTION ADDRESS PROTOCOL TCP HOST IP地址 PORT 端口号 CONNECT DATA SERVER XXX SERVICE N
  • Spring之Joinpoint类详解

    说明 Joinpoint是AOP的连接点 一个连接点代表一个被代理的方法 我们从源码角度看连接点有哪些属性和功能 源码 Copyright 2002 2016 the original author or authors Licensed