一起学SF框架系列7.4-spring-AOP-AOP代理创建

2023-11-11

AOP的BeanDefinition加载后,Spring提供了自动代理机制,让容器自动根据目标bean生成AOP代理bean,本文讲述具体如何实现。

基本机制

Spring的启动过程中,在bean实例化前后、初始化前后均提供了外部介入处理机制(详见“一起学SF框架系列5.3-spring-Beans-bean与Spring容器的交互方式”)。AOP正是通过其中BeanPostProcessor类来自动完成这项工作。下面是自动代理创建类关系图:
在这里插入图片描述
从上图可看出,自动创建代理类均继承于AbstractAutoProxyCreator,而AbstractAutoProxyCreator实现了接口SmartInstantiationAwareBeanPostProcessor,该接口继承于BeanPostProcessor。

启动入口

在bean创建过程中,自动代理创建类可以有四个介入时机:
1、实例化前(SmartInstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation)
2、实例化后(SmartInstantiationAwareBeanPostProcessor.postProcessAfterInstantiation)
3、初始化前(BeanPostProcessor.postProcessBeforeInitialization)
4、初始化后(BeanPostProcessor.postProcessAfterInitialization)
实际代码跟踪,AbstractAutoProxyCreator实现了如下两个接口:
实例化前:postProcessBeforeInstantiation
初始化后:postProcessAfterInitialization
跟踪启动过程,真正入口在AbstractAutoProxyCreator.postProcessAfterInitialization。这更符合实际:因为应用开发时,目标对象bean是不需要知道AOP任何内容的,有AOP能正常工作,无AOP也能正常工作,因此目标对象bean在初始化后再转换成代理对象就更恰当。

源码跟踪

AbstractAutoProxyCreator.postProcessAfterInitialization(@Nullable Object bean, String beanName)

创建bean的代理对象。

	@Override
	public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
		if (bean != null) {
			// 为给定的bean类和bean名称构建一个缓存键
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			if (this.earlyProxyReferences.remove(cacheKey) != bean) {
				// bean没有对应的代理,则创建代理
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		return bean;
	}

AbstractAutoProxyCreator.wrapIfNecessary(Object bean, String beanName, Object cacheKey)

	protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
		// 如果目标bean已存在(在bean实例化前创建的),直接返回
		if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
			return bean;
		}
		// 目标bean不是advisedBean,直接返回
		if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
			return bean;
		}
		// 如果是基础类(是不能被代理的)或者 应该跳过的bean(“跳过”意思是给定的bean不应被此后处理器进行自动代理),直接返回
		if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
			this.advisedBeans.put(cacheKey, Boolean.FALSE);
			return bean;
		}

		/* 创建代理类 */
		// 获取所有适用于当前Bean的Advisors 注1
		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
		/* 存在适用于当前Bean的Advisors */
		if (specificInterceptors != DO_NOT_PROXY) {
			// 标记该bean是被代理类的bean
			this.advisedBeans.put(cacheKey, Boolean.TRUE);
			// 创建代理bean  注2
			Object proxy = createProxy(
					bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
			// 标记bean的代理类型
			this.proxyTypes.put(cacheKey, proxy.getClass());
			return proxy;
		}

		// 标记该bean非被代理类的bean
		this.advisedBeans.put(cacheKey, Boolean.FALSE);
		return bean;
	}

注1/ 注2方法过程均比较复杂,见下面专门章节。

获取所有适用于当前Bean的Advisors

AbstractAdvisorAutoProxyCreator.getAdvicesAndAdvisorsForBean( Class<?> beanClass, String beanName, @Nullable TargetSource targetSource)

	// 过渡类
	@Override
	@Nullable
	protected Object[] getAdvicesAndAdvisorsForBean(
			Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {

		List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
		if (advisors.isEmpty()) {
			return DO_NOT_PROXY;
		}
		return advisors.toArray();
	}

	// 查找适用于bean所有符合条件的Advisors
	protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
		// 查找BeanFactory所有Advisors
		List<Advisor> candidateAdvisors = findCandidateAdvisors();
		// 找适用于bean的Advisors
		List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
		extendAdvisors(eligibleAdvisors);
		if (!eligibleAdvisors.isEmpty()) {
			// 排序
			eligibleAdvisors = sortAdvisors(eligibleAdvisors);
		}
		return eligibleAdvisors;
	}
	// 查找BeanFactory所有Advisors
	protected List<Advisor> findCandidateAdvisors() {
		Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available");
		// 返回所有Advisors
		return this.advisorRetrievalHelper.findAdvisorBeans();
	}

	// 找适用于bean的Advisors
	protected List<Advisor> findAdvisorsThatCanApply(
			List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {
		// 设置代理创建容器当前处理的beanName
		ProxyCreationContext.setCurrentProxiedBeanName(beanName);
		try {
			// 适配查找
			return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
		}
		finally {
			// 清理代理创建容器当前处理的beanName
			ProxyCreationContext.setCurrentProxiedBeanName(null);
		}
	}

BeanFactoryAdvisorRetrievalHelper.findAdvisorBeans()

// 查找BeanFactory所有Advisors

	public List<Advisor> findAdvisorBeans() {
		// 获取所有已缓存的AdvisorBeanNames
		String[] advisorNames = this.cachedAdvisorBeanNames;
		/* 缓存没有 */
		if (advisorNames == null) {
			// 在beanFactory中查找所有Advisor类
			advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
					this.beanFactory, Advisor.class, true, false);
			// 结果放到缓存
			this.cachedAdvisorBeanNames = advisorNames;
		}
		// 不存在Advisor类
		if (advisorNames.length == 0) {
			return new ArrayList<>();
		}

		List<Advisor> advisors = new ArrayList<>();
		/* 逐一判断每个advisor是否适合当前bean */
		for (String name : advisorNames) {
			if (isEligibleBean(name)) {  // isEligibleBean返回的总是true
				if (this.beanFactory.isCurrentlyInCreation(name)) {
					// 当前bean正被创建中,就跳过
					if (logger.isTraceEnabled()) {
						logger.trace("Skipping currently created advisor '" + name + "'");
					}
				}
				else {
					try {
						// 获取每个advisor的bean实例(当成普通bean获取实例),加入到集合中
						advisors.add(this.beanFactory.getBean(name, Advisor.class));
					}
					catch (BeanCreationException ex) {
						Throwable rootCause = ex.getMostSpecificCause();
						if (rootCause instanceof BeanCurrentlyInCreationException) {
							BeanCreationException bce = (BeanCreationException) rootCause;
							String bceBeanName = bce.getBeanName();
							if (bceBeanName != null && this.beanFactory.isCurrentlyInCreation(bceBeanName)) {
							// 如果异常是因为bean正在创建中引起的,则continue处理下一个advisor
								if (logger.isTraceEnabled()) {
									logger.trace("Skipping advisor '" + name +
											"' with dependency on currently created bean: " + ex.getMessage());
								}
								continue;
							}
						}
						throw ex;
					}
				}
			}
		}
		return advisors;
	}

注意:advisor类都是prototype类型,因此每次获取都需要创建

AopUtils.findAdvisorsThatCanApply(List candidateAdvisors, Class<?> clazz)

从Advisors侯选择中选择适合beanClass的Advisors

	public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
		// 后选择为空
		if (candidateAdvisors.isEmpty()) {
			return candidateAdvisors;
		}

		// 选择结果集合
		List<Advisor> eligibleAdvisors = new ArrayList<>();

		/* 处理引介增强 declare-parents*/
		for (Advisor candidate : candidateAdvisors) {
			if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
				eligibleAdvisors.add(candidate);
			}
		}
		
		boolean hasIntroductions = !eligibleAdvisors.isEmpty();
		for (Advisor candidate : candidateAdvisors) {
			if (candidate instanceof IntroductionAdvisor) {
				// 上一步已处理
				continue;
			}
			if (canApply(candidate, clazz, hasIntroductions)) {
				eligibleAdvisors.add(candidate);
			}
		}
		return eligibleAdvisors;
	}

	// advisor是否适合class --过渡类
	public static boolean canApply(Advisor advisor, Class<?> targetClass) {
		return canApply(advisor, targetClass, false);
	}
	// advisor是否适合class
	public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
		if (advisor instanceof IntroductionAdvisor) {
			// 引介增强器判断是否合适(同切点判断方式不一样)
			return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
		}
		else if (advisor instanceof PointcutAdvisor pca) {
			// 切点判断是否合适
			return canApply(pca.getPointcut(), targetClass, hasIntroductions);
		}
		else {
			// 没有切点的话,假定是合适的 (why?)
			return true;
		}
	}
	// 用切点判断advisor是否适合class
	public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
		Assert.notNull(pc, "Pointcut must not be null");
		// 判断切点表达式是否匹配目标类
		if (!pc.getClassFilter().matches(targetClass)) {
			return false;
		}

		// 判断方法是否匹配
		MethodMatcher methodMatcher = pc.getMethodMatcher();
		if (methodMatcher == MethodMatcher.TRUE) {
			// No need to iterate the methods if we're matching any method anyway...
			return true;
		}

		// 判断是否引介增强匹配 注1
		IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
		if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
			introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
		}

		/* 对目标类和所有实现的接口类的方法进行匹配判断 */
		Set<Class<?>> classes = new LinkedHashSet<>();
		// 如果目标类不是代理类,加入待处理类
		if (!Proxy.isProxyClass(targetClass)) {
			classes.add(ClassUtils.getUserClass(targetClass));
		}
		// 获取目标类实现的所有接口(包括由祖先类实现的)
		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;
	}

注1:切点匹配主要是通过表达式同类或方法进行匹配判断,不再进一步跟踪。相关类主要是AspectJExpressionPointcut,相关类图如下:
在这里插入图片描述

创建代理bean

AbstractAutoProxyCreator.createProxyClass(Class<?> beanClass, @Nullable String beanName,@Nullable Object[] specificInterceptors, TargetSource targetSource)

创建代理bean

	//过渡类
	private Class<?> createProxyClass(Class<?> beanClass, @Nullable String beanName,@Nullable Object[] specificInterceptors, TargetSource targetSource) {
		return (Class<?>) buildProxy(beanClass, beanName, specificInterceptors, targetSource, true);
	}

	//创建代理bean
	private Object buildProxy(Class<?> beanClass, @Nullable String beanName,			@Nullable Object[] specificInterceptors, TargetSource targetSource, boolean classOnly) {
		// 在ConfigurableListableBeanFactory中设置目标类为原始类
		if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
			AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
		}
		
		// 创建代理工厂
		ProxyFactory proxyFactory = new ProxyFactory();
		// 获取当前类的ProxyConfig属性 (两者都祖先类都有ProxyConfig)
		proxyFactory.copyFrom(this);
		// 判断当前代理工厂(来自上一步的copy属性)是基于TargetClass代理还是基于接口代理
		if (proxyFactory.isProxyTargetClass()) {
			// 代理工厂是基于TargetClass代理
			if (Proxy.isProxyClass(beanClass) || ClassUtils.isLambdaClass(beanClass)) {
				// bean是处理类或lambdas类
				for (Class<?> ifc : beanClass.getInterfaces()) {
					// 把接口方法加入到代理工厂
					proxyFactory.addInterface(ifc);
				}
			}
		}
		else {
			// 代理工厂是基于接口代理
			// 确定bean是目标类而不是接口
			if (shouldProxyTargetClass(beanClass, beanName)) {
				proxyFactory.setProxyTargetClass(true);
			}
			else {
				// bean的接口加到代理工厂
				evaluateProxyInterfaces(beanClass, proxyFactory);
			}
		}

		// 封装所有Advisors
		Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
		// 把Advisors加到代理工行
		proxyFactory.addAdvisors(advisors);
		proxyFactory.setTargetSource(targetSource);
		customizeProxyFactory(proxyFactory);

		proxyFactory.setFrozen(this.freezeProxy);
		if (advisorsPreFiltered()) {
			proxyFactory.setPreFiltered(true);
		}

		// 如果bean类不是用重写类类加载器加载,就使用原始类加载器
		ClassLoader classLoader = getProxyClassLoader();
		if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader()) {
			classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
		}
		// 创建代理类返回
		return (classOnly ? proxyFactory.getProxyClass(classLoader) : proxyFactory.getProxy(classLoader));
	}

ProxyFactory.getProxyClass(@Nullable ClassLoader classLoader)

	public Object getProxyClass(@Nullable ClassLoader classLoader) {
		// createAopProxy()创建AOP代理器,再由代理器生成代理类(getProxyClass(classLoader))
		return createAopProxy().getProxyClass(classLoader);
	}

	// createAopProxy()-创建AOP代理器的实现过程
	protected final synchronized AopProxy createAopProxy() {
		if (!this.active) {
			activate();
		}
		return getAopProxyFactory().createAopProxy(this);
	}
	// createAopProxy(this)-创建AOP代理器的实现过程 注1
	@Override
	public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
		// optimize 默认false,为true表示可启用CGLIB动态代理器
		if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
			Class<?> targetClass = config.getTargetClass();
			if (targetClass == null) {
				throw new AopConfigException("TargetSource cannot determine target class: " +
						"Either an interface or a target is required for proxy creation.");
			}
			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass) || ClassUtils.isLambdaClass(targetClass)) {
				// 接口类、代理目标类或Lambda类用JDK的动态代理器
				return new JdkDynamicAopProxy(config);
			}
			// 用CGLIB动态代理器
			return new ObjenesisCglibAopProxy(config);
		}
		else {
			// 接口类、代理目标类或Lambda类用JDK的动态代理器
			return new JdkDynamicAopProxy(config);
		}
	}

注1:JDK的动态代理器或CGLIB动态代理器的类图关系:
在这里插入图片描述

JDK动态代理器-JdkDynamicAopProxy生成代理类

	@Override
	public Object getProxy(@Nullable ClassLoader classLoader) {
		if (logger.isTraceEnabled()) {
			logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
		}
		return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
	}

	/* 下面方法来自:java.lang.reflect.Proxy (均是jdk本身的,同spring无关)*/
	/** 新生成指定接口的代理实例,该实例将方法调用分派给指定的调用处理程序
	@param loader 定义代理类的类加载器
	@param interfaces 要实现的代理类的接口列表
	@param h InvocationHandler是方法调用处理器,就是在invoke方法时进行代理封装
	*/
    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h) {
		// h 不能为空                                          
        Objects.requireNonNull(h);

		// 获取调用本方法的方法的调用类
        @SuppressWarnings("removal")
        final Class<?> caller = System.getSecurityManager() == null
                                    ? null
                                    : Reflection.getCallerClass();

        //查找或生成指定的代理类的构造器
        Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);

		// 构建代理类新实例并返回
        return newProxyInstance(caller, cons, h);
    }
    //查找或生成指定的代理类的构造器
    private static Constructor<?> getProxyConstructor(Class<?> caller,
                                                      ClassLoader loader,
                                                      Class<?>... interfaces)
    {
        // optimization for single interface
        // 只有一个接口需做代理
        if (interfaces.length == 1) {
            Class<?> intf = interfaces[0];
            if (caller != null) {
            	// 检查是否允许被代理
                checkProxyAccess(caller, loader, intf);
            }
            return proxyCache.sub(intf).computeIfAbsent(
                loader,
                (ld, clv) -> new ProxyBuilder(ld, clv.key()).build()
            );
        } else {
        // 多个接口需做代理
            //克隆拿到接口
            final Class<?>[] intfsArray = interfaces.clone();
            if (caller != null) {
                checkProxyAccess(caller, loader, intfsArray);
            }
            final List<Class<?>> intfs = Arrays.asList(intfsArray);
            return proxyCache.sub(intfs).computeIfAbsent(
                loader,
                (ld, clv) -> new ProxyBuilder(ld, clv.key()).build()
            );
        }
    }   
    // 生成新实例 
    private static Object newProxyInstance(Class<?> caller, 
                                           Constructor<?> cons,
                                           InvocationHandler h) {
        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (caller != null) {
                checkNewProxyPermission(caller, cons.getDeclaringClass());
            }
			// 用构建器生成新实例
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException | InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        }
    }

CGLIB代理器-CglibAopProxy生成代理类

	// 过渡类
	@Override
	public Object getProxy(@Nullable ClassLoader classLoader) {
		return buildProxy(classLoader, false);
	}
	
	private Object buildProxy(@Nullable ClassLoader classLoader, boolean classOnly) {
		if (logger.isTraceEnabled()) {
			logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource());
		}

		try {
			// 获取目标类
			Class<?> rootClass = this.advised.getTargetClass();
			Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");
			// 上级类默认为自己本身
			Class<?> proxySuperClass = rootClass;
			// 类是CGLIB类(beanName字含有“$$“,注意同java嵌套类区别(含有"$");针对引入增强
			if (rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {
				// 取上级类
				proxySuperClass = rootClass.getSuperclass();
				// 获取所有接口加入到advisor
				Class<?>[] additionalInterfaces = rootClass.getInterfaces();
				for (Class<?> additionalInterface : additionalInterfaces) {
					this.advised.addInterface(additionalInterface);
				}
			}

			// Validate the class, writing log messages as necessary.
			validateClassIfNecessary(proxySuperClass, classLoader);

			// Configure CGLIB Enhancer...
			// Enhancer是CGLIB封装的增强类,支持CGLIB功能处理
			Enhancer enhancer = createEnhancer();
			if (classLoader != null) {
				enhancer.setClassLoader(classLoader);
				if (classLoader instanceof SmartClassLoader &&
						((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
					enhancer.setUseCache(false);
				}
			}
			// 设置增强类的上级类为代理目标类,增强类就代表了代理目标类
			enhancer.setSuperclass(proxySuperClass);
			//设置增强类的需封装的代理目标类接口
			enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
			enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
			enhancer.setAttemptLoad(true);
			enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));

			//获取代理的回调方法集合
			Callback[] callbacks = getCallbacks(rootClass);
			Class<?>[] types = new Class<?>[callbacks.length];
			for (int x = 0; x < types.length; x++) {
				types[x] = callbacks[x].getClass();
			}
			// fixedInterceptorMap only populated at this point, after getCallbacks call above
			enhancer.setCallbackFilter(new ProxyCallbackFilter(
					this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
			enhancer.setCallbackTypes(types);

			// Generate the proxy class and create a proxy instance.
			return (classOnly ? createProxyClass(enhancer) : createProxyClassAndInstance(enhancer, callbacks));
		}
		catch (CodeGenerationException | IllegalArgumentException ex) {
			throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +
					": Common causes of this problem include using a final class or a non-visible class",
					ex);
		}
		catch (Throwable ex) {
			// TargetSource.getTarget() failed
			throw new AopConfigException("Unexpected AOP exception", ex);
		}
	}
	// 用enhancer创建目标类代理实例
	protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
		enhancer.setInterceptDuringConstruction(false);
		enhancer.setCallbacks(callbacks);
		return (this.constructorArgs != null && this.constructorArgTypes != null ?
				enhancer.create(this.constructorArgTypes, this.constructorArgs) :
				enhancer.create());
	}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

一起学SF框架系列7.4-spring-AOP-AOP代理创建 的相关文章

  • 如何在 Openfire 中使用 smack

    你好 我计划开发一个可以连接到 gtalk facebook 等的聊天客户端 我决定将 smack API 与 openfire 一起使用 但我需要很少的指导来了解如何将它与 openfire 服务器一起使用 openfire 是否提供了基
  • FileNotFoundException - Struts2 文件上传

    Strange FileNotFoundException使用Struts2上传文件时 这是 JSP 的一部分
  • Spring数据中的本机查询连接

    我有课 Entity public class User Id Long id String name ManyToMany List
  • Android蓝牙java.io.IOException:bt套接字已关闭,读取返回:-1

    我正在尝试编写一个代码 仅连接到运行 Android 5 0 KitKat 的设备上的 目前 唯一配对的设备 无论我尝试了多少方法 我仍然会收到此错误 这是我尝试过的最后一个代码 它似乎完成了我看到人们报告为成功的所有事情 有人能指出我做错
  • Java:从集合中获取第一项

    如果我有一个集合 例如Collection
  • 如何使用正则表达式验证 1-99 范围?

    我需要验证一些用户输入 以确保输入的数字在 1 99 范围内 含 这些必须是整数 Integer 值 允许前面加 0 但可选 有效值 1 01 10 99 09 无效值 0 007 100 10 5 010 到目前为止 我已经制定了以下正则
  • 如何通过注解用try-catch包装方法?

    如果应该在方法调用中忽略异常 则可以编写以下内容 public void addEntryIfPresent String key Dto dto try Map
  • 添加到列表时有没有办法避免循环?

    我想知道这样的代码 List
  • 如何删除日期对象的亚秒部分

    当 SQL 数据类型为时间戳时 java util Date 存储为 2010 09 03 15 33 22 246 如何在存储记录之前将亚秒设置为零 例如 在本例中为 246 最简单的方法是这样的 long time date getTi
  • JAVA中遍历JSON数据

    我是 JSON 新手 我使用 HTTPUrlConnections 并在 JAVA 程序中获得一些响应 响应数据将类似于 data id 1 userId 1 name ABC modified 2014 12 04 created 201
  • IntelliJ 组织导入

    IntelliJ 是否具有类似于 Eclipse 中的组织导入功能 我拥有的是一个 Java 文件 其中多个类缺少导入 例子 package com test public class Foo public Map map public J
  • 如何从 Ant 启动聚合 jetty-server JAR?

    背景 免责声明 I have veryJava 经验很少 我们之前在 Ant 构建期间使用了 Jetty 6 的包装版本来处理按需静态内容 JS CSS 图像 HTML 因此我们可以使用 PhantomJS 针对 HTTP 托管环境运行单元
  • 无需登录即可直接从 Alfresco 访问文件/内容

    我的场景是这样的 我有一个使用 ALFRESCO CMS 来显示文件或图像的 Web 应用程序 我正在做的是在 Java servlet 中使用用户名和密码登录 alfresco 并且我可以获得该登录的票证 但我无法使用该票证直接从浏览器访
  • 禁用 Android 菜单组

    我尝试使用以下代码禁用菜单组 但它不起作用 菜单项仍然启用 你能告诉我出了什么问题吗 资源 菜单 menu xml menu menu
  • Hadoop NoSuchMethodError apache.commons.cli

    我在用着hadoop 2 7 2我用 IntelliJ 做了一个 MapReduce 工作 在我的工作中 我正在使用apache commons cli 1 3 1我把库放在罐子里 当我在 Hadoop 集群上使用 MapReduceJob
  • 替换文件中的字符串

    我正在寻找一种方法来替换文件中的字符串而不将整个文件读入内存 通常我会使用 Reader 和 Writer 即如下所示 public static void replace String oldstring String newstring
  • 记录类名、方法名和行号的性能影响

    我正在我的 java 应用程序中实现日志记录 以便我可以调试应用程序投入生产后可能出现的潜在问题 考虑到在这种情况下 人们不会奢侈地使用 IDE 开发工具 以调试模式运行事物或单步执行完整代码 因此在每条消息中记录类名 方法名和行号将非常有
  • 检查应用程序是否在 Android Market 上可用

    给定 Android 应用程序 ID 包名称 如何以编程方式检查该应用程序是否在 Android Market 上可用 例如 com rovio angrybirds 可用 而 com random app ibuilt 不可用 我计划从
  • 将对象从手机共享到 Android Wear

    我创建了一个应用程序 在此应用程序中 您拥有包含 2 个字符串 姓名和年龄 和一个位图 头像 的对象 所有内容都保存到 sqlite 数据库中 现在我希望可以在我的智能手表上访问这些对象 所以我想实现的是你可以去启动 启动应用程序并向左和向
  • 基于 Spring Boot 的测试中的上下文层次结构

    我的 Spring Boot 应用程序是这样启动的 new SpringApplicationBuilder sources ParentCtxConfig class child ChildFirstCtxConfig class sib

随机推荐

  • Ubuntu20.04系统使用笔记

    笔者安装的ubuntu版本是20 04 双系统安装 参考教程 link 用于深度学习 总共给ubuntu的空间为200GB 交换区分l8GB 16GB内存 EFI系统分区1GB 剩余181GB全给根目录 操作记录 使用命令sudo ubun
  • python + selenium

    selenium是一个模拟浏览器的类库 经常用来做自动化测试 python 可以直接使用安装目录下的Scripts pip工具安装 以windows7 python3 4为例 运行cmd cd C Python34 Scripts pip
  • 【LTspice】005 伯德图绘制

    目录 1 伯德图介绍 2 LTspice 截止频率验证 3 LTspice中如何添加光标 4 LTspice中如何将 幅频 和 相频曲线分开 1 伯德图介绍 Bode图由对数幅频特性和对数相频特性两张图组成 伯德图 百度百科 1 对数幅频特
  • 学习vue之node的安装

    关于node 简单的说 Node js 就是运行在服务端的 JavaScript Node js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境 Node js 使用了一个事件驱动 非阻塞式 I O 的模型 使其轻量
  • Java 学习路线一条龙版

    Java 学习路线一条龙版 Java 学习路线一条龙版 by 程序员鱼皮 学习路线来源于 程序员鱼皮 大家可以去b站看看他的视频 视频导读 https www bilibili com video BV1Qf4y1K7ff 大纲 路线特点
  • wireshark抓包数据提取TCP/UDP/RTP负载数据方法

    1 背景 在视频抓包分析过程中 有时候需要从TCP UDP RTP中直接提取payload数据 比如较老的摄像机 有一些直接通过TCP UDP传输视频裸流 或者PS打包的视频流 通过提取TCP和UDP的负载数据就可以直接组成裸流或者PS流文
  • [Unity] AnimatorStates中的write defaults详解

    AnimatorState中有一个参数writeDefaultValues 在Inspector中显示的则是Write Defaults 官方文档对这个参数的解释是 Whether or not the AnimatorStates wri
  • WPF快速搭建MVVM框架

    WPFDemo项目结构 需更改App xaml上的属性StartupUri Views MainWindow xaml Views MainWindow xaml
  • Few-shot learning(少样本学习,入门篇)

    本文介绍一篇来自 https www analyticsvidhya com 关于少样本学习的的博客 原文地址 文章目录 1 少样本学习 1 1 为什么要有少样本学习 什么是少样本学习 1 2 元学习和传统有监督学习的区别是什么 1 3 一
  • 几个特殊TCP报文及TCP

    TCP Window Full 接收方接收缓冲区满了后 导致发送方的发送缓冲区装满待确认数据 此时发送方会发送一个TCP Window Full消息 TCP ZeroWindow 接收方应用没有及时recv消息 导致接收缓冲满 即滑动窗口为
  • Java开发学习----AOP通知获取数据(参数、返回值、异常)

    前面的博客我们写AOP仅仅是在原始方法前后追加一些操作 接下来我们要说说AOP中数据相关的内容 我们将从 获取参数 获取返回值 和 获取异常 三个方面来研究切入点的相关信息 前面我们介绍通知类型的时候总共讲了五种 那么对于这五种类型都会有参
  • Farbic Java SDK 1.4安装方法

    Hyperledger Fabric Java SDK是开发基于Hyperledger Fabric区块链的Java应用之必备开发包 本文将介绍如何在Maven Gradle和Eclipse中安装使用Hyperledger Fabric J
  • 【计算机二级】Python类

    1 程序设计语言的发展经历了从机器语言 汇编语言 到高级语言的发展历程 2 程序设计语言是计算机能够理解和识别用户操作意图的一种交互体系 它按照特定规则组织计算机指令 使计算机能够自动进行各种运算处理 按照程序设计语言规则组织起来的一组计算
  • Apache Flink:特性、概念、组件栈、架构及原理分析

    Table of Contents 1 摘要 2 基本特性 3 流处理特性 4 API支持 5 Libraries支持 6 整合支持 7 基本概念 7 1 Stream Transformation Operator 7 2 Paralle
  • 蓝桥杯2015年第六届真题-广场舞

    说在前面 其他博客中的代码应该保证不了健壮性 我这个 应该 可以 题目 题目链接 题解 数学 计算几何 提示 这题默认好像是顺时针或逆时针输入坐标 也就是说先后输入的两个点一定是多边形的一条边 前置知识 PNPoly算法 何为PNPoly算
  • SHT3x-DIS驱动及应用详解(附源码和手册)

    文章目录 一 电路组成 二 通讯指令说明 一 单次获取数据指令 二 周期获取数据指令 1 配置模式 2 读取数据 三 加快响应时间指令 四 停止周期读取数据指令 五 复位 1 IIC接口复位 2 软复位 重新初始化 3 一般呼叫复位指令 4
  • 小米路由器4A,(R4A千兆版)刷openwrt系统(Linux的一种)

    下载系统 第一步下载自己路由器型号的系统固件 https openwrt org toh views toh fwdownload 直接在页面搜索自己路由器品牌找到区域再找到自己的型号对应的 然后选第一个链接是稳定版系统固件 下载后改名为o
  • 日语疑问句

    你好 的用法很简单就是主题主语提示词 难的是他与 的区别是什么 在这儿我列举一下 我们应该怎样区分什么时候用主格助词 什么时候用提示助词 呢 其实只要记住 的重点在于其前面部分 而 的重点在于其后面部分 为了更好掌握主格助词 和提示助词 的
  • 挂载分区提示can't read superblock on /dev/sda1

    环境为linux 某分区数据无法读取 1 fstab及磁盘列表正常 2 手动挂在时系统提示can t read superblock on dev xxx 故障现象如上图 解决办法 root下使用fsck 进行修复指定分区 fsck dev
  • 一起学SF框架系列7.4-spring-AOP-AOP代理创建

    AOP的BeanDefinition加载后 Spring提供了自动代理机制 让容器自动根据目标bean生成AOP代理bean 本文讲述具体如何实现 基本机制 Spring的启动过程中 在bean实例化前后 初始化前后均提供了外部介入处理机制