精华内容
下载资源
问答
  • spring使用@Autowired和@Value实现了属性的注入,本文重点分析两种注入方式的源码,然后说明@Lazy注解在属性注入时的作用,将上篇文章中的遗留问题解答。 一、整体流程 对@Value和@Autowired注解的处理...

    目录

    前言

    一、整体流程

    二、findAutowiringMetadata

    三、inject

     1.整体流程

     2.@Value注解解析过程

     3.@Autowired注解解析过程 

     四、@Lazy注解的作用

    总结


    前言

            spring使用@Autowired和@Value实现了属性的注入,本文重点分析两种注入方式的源码,然后说明@Lazy注解在属性注入时的作用,将上篇文章中的遗留问题解答。


    一、整体流程

           对@Value和@Autowired注解的处理主要是在AutowiredAnnotationBeanPostProcessor的postProcessPropertyValues方法,首先是查找@Value和@Autowired注解相关的属性,然后给相关的属性赋值,如果属性中引用的外部bean没有创建,还要进行bean的创建,这也是循环依赖产生的地方。

    	public PropertyValues postProcessPropertyValues(
    			PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
    		//自动注入属性包含@Value@Autowired注解,this.autowiredAnnotationTypes.add(Autowired.class);
    		//		this.autowiredAnnotationTypes.add(Value.class);
    		InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
    		try {
    			//完成属性注入
    			metadata.inject(bean, beanName, pvs);
    		}
    		catch (BeanCreationException ex) {
    			throw ex;
    		}
    		catch (Throwable ex) {
    			throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
    		}
    		return pvs;
    	}

    二、findAutowiringMetadata

            这个方法主要用于查找对象中标注了@Value或者@Autowired注解的属性和方法,本文主要针对属性进行分析,关于方法的处理不会涉及。使用了spring自行封装的ReflectionUtils工具类,遍历对象的所有属性,找出具有相关注解的属性。

    	private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, PropertyValues pvs) {
    	    //含有@Value和@Autowired注解的属性
    		metadata = buildAutowiringMetadata(clazz);
    	    this.injectionMetadataCache.put(cacheKey, metadata);
    		return metadata;
    	}
    	private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
    		LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<InjectionMetadata.InjectedElement>();
    		Class<?> targetClass = clazz;
    
    		do {
    			final LinkedList<InjectionMetadata.InjectedElement> currElements =
    					new LinkedList<InjectionMetadata.InjectedElement>();
    
    			ReflectionUtils.doWithLocalFields(targetClass, new ReflectionUtils.FieldCallback() {
    				@Override
    				public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
    					//遍历所有属性判断是否含有@Value或者@Autowired注解
    					AnnotationAttributes ann = findAutowiredAnnotation(field);
    					if (ann != null) {
    						if (Modifier.isStatic(field.getModifiers())) {
    							if (logger.isWarnEnabled()) {
    								logger.warn("Autowired annotation is not supported on static fields: " + field);
    							}
    							return;
    						}
    						boolean required = determineRequiredStatus(ann);
    						currElements.add(new AutowiredFieldElement(field, required));
    					}
    				}
    			});
    		return new InjectionMetadata(clazz, elements);
    	}

    三、inject

     1.整体流程

            在找到需要注入的属性后,会在环境中查找对应的值,通过反射给创建的对象属性进行赋值。

    		protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
    			Field field = (Field) this.member;
    			Object value;
    			if (this.cached) {
    				value = resolvedCachedArgument(beanName, this.cachedFieldValue);
    			}
    			else {
    				DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
    				desc.setContainingClass(bean.getClass());
    				Set<String> autowiredBeanNames = new LinkedHashSet<String>(1);
    				TypeConverter typeConverter = beanFactory.getTypeConverter();
    				try {
    					//处理依赖的bean对象 @Value@autowried,返回对应的值
    					value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
    				}
    				catch (BeansException ex) {
    					throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
    				}
    			}
    			if (value != null) {
    				//反射调用设置属性的值
    				ReflectionUtils.makeAccessible(field);
    				field.set(bean, value);
    			}
    		}
    	}

           如果属性上有@Lazy注解,则直接生成相应的代理对象,将代理对象赋值给相应属性。没有会在环境中进行查找。

    	public Object resolveDependency(DependencyDescriptor descriptor, String requestingBeanName,
    			//判断属性上是否有@Lazy注解,有注解生成代理对象,调用时通过DynamicAdvisedInterceptor拦截器解析
    			Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
    					descriptor, requestingBeanName);
    			if (result == null) {
    				//在此处解析@value和@Autowaired注解,,,,,,
    				result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
    			}
    			return result;
    	}
    

            在环境中查找分为@Value和@Autowired两种处理方式,@Value用于注入字面量,@Autowired用于注入依赖的对象属性,下面分别进行分析。

    	public Object doResolveDependency(DependencyDescriptor descriptor, String beanName,
    			Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {
    
    		InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
    		try {
    			Class<?> type = descriptor.getDependencyType();
    			//获取注解中的value值,${name} QualifierAnnotationAutowireCandidateResolver
    			Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
    			if (value != null) {
    				//可用于解析 @Value("${name}")类似属性注入
    				if (value instanceof String) {
    					String strVal = resolveEmbeddedValue((String) value); //从propertysource中读取注入的真正属性值,,
    					BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null);
    					value = evaluateBeanDefinitionString(strVal, bd);
    				}
    				TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
    				return (descriptor.getField() != null ?
    						converter.convertIfNecessary(value, type, descriptor.getField()) :
    						converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
    			}
    
    			Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
    			if (multipleBeans != null) {
    				return multipleBeans;
    			}
    			//获取自动注入的候选 @Autowaird
    			Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
    			if (matchingBeans.isEmpty()) {
    				if (isRequired(descriptor)) {
    					raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
    				}
    				return null;
    			}		
    			return (instanceCandidate instanceof Class ?
    					descriptor.resolveCandidate(autowiredBeanName, type, this) : instanceCandidate);
    		}
    		finally {
    			ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
    		}
    	}

     2.@Value注解解析过程

            在@Value解析过程中,@Value("${name:xiaoming}")为例,首先以注解中的全部字符 作为key(name:xiaoming)进行查找,结果为null时,查看key中是否存在冒号,有则将key以冒号分割,以冒号前的字符(name)作为key进行查找,结果如果还是null,就以冒号后的字符(xiaoming)作为默认值返回。

    protected String parseStringValue(
    			String value, PlaceholderResolver placeholderResolver, Set<String> visitedPlaceholders) {
    
    		StringBuilder result = new StringBuilder(value);
    
    		int startIndex = value.indexOf(this.placeholderPrefix);
    		while (startIndex != -1) {
    			int endIndex = findPlaceholderEndIndex(result, startIndex);
    			if (endIndex != -1) {
    				String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex);
    				String originalPlaceholder = placeholder;
    				if (!visitedPlaceholders.add(originalPlaceholder)) {
    					throw new IllegalArgumentException(
    							"Circular placeholder reference '" + originalPlaceholder + "' in property definitions");
    				}
    				// Recursive invocation, parsing placeholders contained in the placeholder key.
    				placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
    				// Now obtain the value for the fully resolved key...
    				String propVal = placeholderResolver.resolvePlaceholder(placeholder);
    				//@Value设置默认值逻辑 @Value("${name:xiaoming}"),以“:”分割进行查找,结果null值以默认值返回
    				if (propVal == null && this.valueSeparator != null) {
    					int separatorIndex = placeholder.indexOf(this.valueSeparator);
    					if (separatorIndex != -1) {
    						String actualPlaceholder = placeholder.substring(0, separatorIndex);
    						String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length());
    						propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder);
    						if (propVal == null) {
    							propVal = defaultValue;
    						}
    					}
    				}
    		}
    
    		return result.toString();
    	}

            查找的过程就是在预先加载的propertySource中根据key进行查找,propertySources中存储着容器中的所有属性,包括系统属性,属性文件以及apollo中的属性等。

    	protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
    		if (this.propertySources != null) {
    			//在所有的propertySources中进行寻找,找到之后退出
    			for (PropertySource<?> propertySource : this.propertySources) {
    				if (logger.isTraceEnabled()) {
    					logger.trace("Searching for key '" + key + "' in PropertySource '" +
    							propertySource.getName() + "'");
    				}
    				Object value = propertySource.getProperty(key);
    				if (value != null) {
    					if (resolveNestedPlaceholders && value instanceof String) {
    						value = resolveNestedPlaceholders((String) value);
    					}
    					logKeyFound(key, propertySource, value);
    					return convertValueIfNecessary(value, targetValueType);
    				}
    			}
    		}
    		return null;
    	}

     3.@Autowired注解解析过程 

            对@Autowired注解属性的查找就是在容器中根据类型查找对应的bean,查找到多个会根据其他属性进行排序,此处不做过多介绍。

    	protected Map<String, Object> findAutowireCandidates(
    			String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {
    
    		String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
    				this, requiredType, true, descriptor.isEager());
    	}
    	public static String[] beanNamesForTypeIncludingAncestors(
    			ListableBeanFactory lbf, Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {
    
    		Assert.notNull(lbf, "ListableBeanFactory must not be null");
    		//根据类型获取
    		String[] result = lbf.getBeanNamesForType(type, includeNonSingletons, allowEagerInit);
    		if (lbf instanceof HierarchicalBeanFactory) {
    			HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf;
    			if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
    				String[] parentResult = beanNamesForTypeIncludingAncestors(
    						(ListableBeanFactory) hbf.getParentBeanFactory(), type, includeNonSingletons, allowEagerInit);
    				result = mergeNamesWithParent(result, parentResult, hbf);
    			}
    		}
    		return result;
    	}

     四、@Lazy注解的作用

           前面介绍了两种属性的获取过程,在有@Lazy注解的情况下,不会直接注入相关属性,会构建代理对象注入。

    			Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
    					descriptor, requestingBeanName);
    	public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, String beanName) {
    		return (isLazy(descriptor) ? buildLazyResolutionProxy(descriptor, beanName) : null);
    	}

            构建的代理对象重写了TargetSource的getTarget方法,在调用代理对象的方法时,会调用到getTarget方法,获取真正的属性值,此时才会进行属性的加载。@Lazy注解延迟了属性的加载时间,在调用方法时才会加载,在面对循环依赖和初始化顺序导致的问题时,这个注解有奇效。上篇文章中提到,在有异步任务存在的情况下,循环依赖会有问题,这时我们把相关注入属性加上@Lazy注解,在容器初始化完成后调用相关方法,能够保证对象中的属性都是代理完成后的,从而异步任务能够生效。。

    	protected Object buildLazyResolutionProxy(final DependencyDescriptor descriptor, final String beanName) {
    		BeanFactory beanFactory = getBeanFactory();
    		Assert.state(beanFactory instanceof DefaultListableBeanFactory,
    				"BeanFactory needs to be a DefaultListableBeanFactory");
    		final DefaultListableBeanFactory dlbf = (DefaultListableBeanFactory) beanFactory;
    
    		TargetSource ts = new TargetSource() {
    			@Override
    			public Class<?> getTargetClass() {
    				return descriptor.getDependencyType();
    			}
    			@Override
    			public boolean isStatic() {
    				return false;
    			}
    			@Override
    			public Object getTarget() {
    				Set<String> autowiredBeanNames = (beanName != null ? new LinkedHashSet<String>(1) : null);
    				//获取自动注入的对象,这波操作实现了延迟加载,在调用代理对象方法的时候才会加载,在解决循环依赖和bean加载顺序导致的问题时有奇效
    				Object target = dlbf.doResolveDependency(descriptor, beanName, autowiredBeanNames, null);
    				return target;
    			}
    			@Override
    			public void releaseTarget(Object target) {
    			}
    		};

    总结

           本文主要讲解了spring中属性注入的相关源码,主要时@Value,@Autowired和@Lazy注解的相关作用,针对上篇文章中遗留的循环依赖和异步任务的额相关问题进行了补充分析,如有不当之处,欢迎大家一起讨论。

    展开全文
  • 类似的实现有Selenium的webdriver支持的各种driver,它们都是调用了浏览器的原始接口。...目前这些driver提供支持很多的浏览器操作,注入源码应该也提供了吧!具体的还没有试过,但是也算是一种可...

    1、通过各浏览器提供的接口调用

    IE的COM接口,FF的插件、Chrome的API接口等;类似的实现有Selenium的webdriver支持的各种driver,

    它们都是调用了浏览器的原始接口。

    2、通过已有的第三方程序来间接调用浏览器:

    比如上面的所提到的webdriver所支持的各种driver;目前这些driver提供支持很多的浏览器操作,注入源码应该也提供了吧!

    具体的还没有试过,但是也算是一种可能。

    3、利用嵌入式浏览器引擎提供的接口:

    CEF是webkit的嵌入式引擎,可以直接程序调用它来实现一个自定义的浏览器,国内大部分浏览器应该就是这样出来的吧;

    所以注入点html源码应该是没有什么问题。

    4、利用类浏览器程序来实现:

    PhantomJS是一款基于webkit的无GUI浏览器引擎,可以做很多浏览器都支持的事情,更方便的是它有提供非常好的编程接口,

    注入HTML就是其中一个。

    5、利用JS注入到浏览器页面:

    直接通过JS获取病改变HTML内容;或者新建一个iframe后注入HTML到iframe中,其实效果就等同于注入到一个新开的浏览器页面。

    6、通过自动化工具直接注入:

    类似selenium的自动化工具,其实都可以直接获取到document对象,所以也就可以直接操作dom从而修改页面内容,达到注入新html的目的。

    因为前3种的研究和开发成本都较高,所以可以考虑选择4、5种方法;这里记录下4、5种方法的实现方式:

    PhantomJS的注入样例:

    var page = require('webpage').create();

    page.viewportSize = { width: 400, height : 400 };

    page.content = '

    '; //要注入的HTML代码

    page.render('2.png'); //截屏

    phantom.exit();

    JS利用iframe注入的样例:

    var iframe = '';

    $("body").append(iframe); //添加iframe

    var ifs = $('#FirebugUI').contents().get(); //获取iframe的document对象

    var ifs2 = document.getElementById('FirebugUI').contentWindow.document; //获取iframe的document对象

    var ifs3 = window.frames["FirebugUI"].document;

    ifs.head.innerHTML = "

    test JS iframe";

    ifs.body.innerHTML = "

    success
    ";
    展开全文
  • Spring中依赖注入的方式 分为两种:手动注入、自动注入 手动注入 在XML中定义Bean时,就是手动注入,手动注入分为两种:set方法注入、构造方法注入 下面这种是通过set方法进行注入 <bean name="userService" ...

    Spring中依赖注入的方式

    分为两种:手动注入、自动注入

    手动注入

    在XML中定义Bean时,就是手动注入,手动注入分为两种:set方法注入、构造方法注入

    下面这种是通过set方法进行注入

    <bean name="userService" class="com.example.spring.UserService">
    	<property name="orderService" ref="orderService"/>
    </bean>
    
    

    下面这种是通过构造方法进行注入

    <bean name="userService" class="com.example.spring.UserService">
    	<constructor‐arg index="0" ref="orderService"/>
    </bean>
    

    自动注入

    自动注入也分为两种:XML的autowire自动注入、@Autowired注解的自动注入

    XML的autowire自动注入

    在XML中,我们可以在定义一个Bean时去指定这个Bean的自动注入模式:

    autowire的参数有:byType、byName、constructor、default、no

    <bean name="userService" class="com.example.spring.UserService" autowire="byType"/>
    

    这样写Spring会自动给UserService中所有的属性自动赋值,不需要在这个属性上加@Autowired注解,但需要这个属性有对应的set方法。

    在创建Bean的过程中填充属性时Spring会去解析当前类,把当前类的所有方法都解析出来,Spring会去解析每个方法得到对应的PropertyDescriptor对象。

    PropertyDescriptor中的属性:

    • name:这个name并不是方法名字,而是方法名字进行处理后的名字,如setXXX,那么name=xXX
    • readMethodRef:表示get方法的Method对象的引用
    • readMethodName:表示get方法的名字
    • writeMethodRef:表示set方法的Method对象的引用
    • writeMethodName:表示set方法的名字
    • propertyTypeRef:如果有get方法那么对应的就是返回值类型,如果是set方法那么对应的就是set方法中唯一参数的类型。

    autowire的byType和byName情况:

    Spring通过ByName自动填充属性时流程:

    1. 找到所有set方法所对应的XXX部分的名字
    2. 根据XXX部分的名字去获取bean

    Spring通过ByType自动填充属性时流程:

    1. 获取到set方法中的唯一参数的参数类型,并且根据该类型去容器中获取bean
    2. 如果找到多个会报错

    @Autowired注解的自动注入

    @Autowired注解相当于XML中的autowire属性的注解方式的替代。@Autowired注解提供了与autowire相同的功能,但是拥有更细粒度的控 制和更广泛的适用性。

    @Autowired无法区分byType和byName,@Autowired是先byType,如果找到多个则ByName。

    @Autowired注解可以写在属性上,构造方法上,set方法上:

    • 写在属性上:先根据属性类型去找Bean,如果找到多个再根据属性名确定一个
    • 构造方法上:先根据方法参数类型去找Bean,如果找到多个再根据参数名确定一个
    • set方法上:先根据方法参数类型去找Bean,如果找到多个再根据参数名确定一个

    寻找注入点

    在创建一个Bean的过程中,Spring会利用AutowiredAnnotationBeanPostProcessor的postProcessMergedBeanDefinition()找出注入点并缓存,找注入点的流程为:

    1. 遍历当前类的所有属性字段Field
    2. 查看字段上是否存在@Autowired、@Value、@Inject中的其中任意一个,存在则认为该字段是一个注入点
    3. 如果字段是static的,则不进行注入
    4. 获取@Autowired中的required属性的值
    5. 将字段信息构造成一个AutowiredFieldElement对象,作为一个注入点对象添加到currElements集合中
    6. 遍历当前类的所有方法Method
    7. 判断当前Method是否是桥接方法,如果是找到原方法
    8. 查看方法上是否存在@Autowired、@Value、@Inject中其中任意一个,存在则认为该方法是一个注入点
    9. 如果方法是static的,则不进行注入
    10. 获取@Autowired中的required属性的值
    11. 将方法信息构造成一个AutowiredMethodElement对象,作为一个注入点对象添加到currElements集合中
    12. 遍历完当前类的字段和方法后,将遍历父类的,直到没有父类
    13. 最后将currElements集合封装成一个InjectionMetadata对象,作为当前Bean对象的注入点集合对象,并缓存

    注入点进行注入

    Spring在AutowiredAnnotationBeanPostProcessor的postProcessProperties方法中会遍历所找到的注入点依次进行注入

    字段注入

    1. 遍历所有的AutowiredFieldElement对象
    2. 将对应的字段封装为DependencyDescriptor对象
    3. 调用BeanFactory的resolveDependency()方法,传入DependencyDescriptor对象,进行依赖查找,找到当前字段所匹配的Bean对象
    4. 将DependencyDescriptor对象和所找到的结果对象beanName封装成一个ShortcutDependencyDescriptor对象作为缓存,如果当前Bean是原型Bean,那么下次再来创建Bean时,就可以直接拿缓存的结果对象beanName去BeanFactory中去拿bean对象了,不用再次进行查找
    5. 利用反射将结果对象赋值给字段

    Set方法注入

    1. 遍历所有的AutowiredMethodElement对象
    2. 遍历将对应方法的参数,将每个参数封装成MethodParameter对象
    3. MethodParameter对象封装为DependencyDescriptor对象
    4. 调用BeanFactory的resolveDependency()方法,传入DependencyDescriptor对象,进行依赖查找,找到当前方法参数所匹配的Bean对象
    5. 将DependencyDescriptor对象和所找到的结果对象beanName封装成一个ShortcutDependencyDescriptor对象作为缓存,比如如果当前Bean是原型Bean,那么下次 再来创建该Bean时,就可以直接拿缓存的结果对象beanName去BeanFactory中去那bean对象了,不用再次进行查找了
    6. 利用反射将找到的所有结果对象传给当前方法,并执行

    关于依赖注入中泛型注入的实现

    在Java反射中,有一个Type接口,表示类型,具体分类为:

    1. raw types:普通Class
    2. parameterized types:对应ParameterizedType接口,泛型类型
    3. array types:对应GenericArrayType,泛型数组
    4. type variables:对应TypeVariable接口,表示类型变量,也就是所定义的泛型,比如T、K
    5. primitive types:基本类型,int、boolean
    public class TypeTest {
        private int i;
        private Integer it;
        private int[] iarray;
        private List list;
        private List<String> slist;
        private List<T> tlist;
        private T t;
        private T[] tarray;
    
        public TypeTest() {
        }
    
        public static void main(String[] args) throws NoSuchFieldException {
            test(TypeTest.class.getDeclaredField("i"));
            System.out.println("=======");
            test(TypeTest.class.getDeclaredField("it"));
            System.out.println("=======");
            test(TypeTest.class.getDeclaredField("iarray"));
            System.out.println("=======");
            test(TypeTest.class.getDeclaredField("list"));
            System.out.println("=======");
            test(TypeTest.class.getDeclaredField("slist"));
            System.out.println("=======");
            test(TypeTest.class.getDeclaredField("tlist"));
            System.out.println("=======");
            test(TypeTest.class.getDeclaredField("t"));
            System.out.println("=======");
            test(TypeTest.class.getDeclaredField("tarray"));
        }
        public static void test(Field field) {
            if (field.getType().isPrimitive()) {
                System.out.println(field.getName() + "是基本数据类型");
            } else {
                System.out.println(field.getName() + "不是基本数据类型");
            }
            if (field.getGenericType() instanceof ParameterizedType) {
                System.out.println(field.getName() + "是泛型类型");
            } else {
                System.out.println(field.getName() + "不是泛型类型");
            }
            if (field.getType().isArray()) {
                System.out.println(field.getName() + "是普通数组");
            } else {
                System.out.println(field.getName() + "不是普通数组");
            }
            if (field.getGenericType() instanceof GenericArrayType) {
                System.out.println(field.getName() + "是泛型数组");
            } else {
                System.out.println(field.getName() + "不是泛型数组");
            }
            if (field.getGenericType() instanceof TypeVariable) {
                System.out.println(field.getName() + "是泛型变量");
            } else {
                System.out.println(field.getName() + "不是泛型变量");
            }
        }
    }
    

    打印结果:

    i是基本数据类型
    i不是泛型类型
    i不是普通数组
    i不是泛型数组
    i不是泛型变量
    =======
    it不是基本数据类型
    it不是泛型类型
    it不是普通数组
    it不是泛型数组
    it不是泛型变量
    =======
    iarray不是基本数据类型
    iarray不是泛型类型
    iarray是普通数组
    iarray不是泛型数组
    iarray不是泛型变量
    =======
    list不是基本数据类型
    list不是泛型类型
    list不是普通数组
    list不是泛型数组
    list不是泛型变量
    =======
    slist不是基本数据类型
    slist是泛型类型
    slist不是普通数组
    slist不是泛型数组
    slist不是泛型变量
    =======
    tlist不是基本数据类型
    tlist是泛型类型
    tlist不是普通数组
    tlist不是泛型数组
    tlist不是泛型变量
    =======
    t不是基本数据类型
    t不是泛型类型
    t不是普通数组
    t不是泛型数组
    t不是泛型变量
    =======
    tarray不是基本数据类型
    tarray不是泛型类型
    tarray是普通数组
    tarray不是泛型数组
    tarray不是泛型变量
    

    @Qualifier的使用

    定义两个注解

    @Target({ElementType.TYPE, ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    @Qualifier("random")
    public @interface Random {
    }
    
    @Target({ElementType.TYPE, ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    @Qualifier("roundRobin")
    public @interface RoundRobin {
    }
    
    

    定义一个接口和两个实现类,表示负载均衡

    public interface LoadBalance {
    	String select();
    }
    
    @Component
    @Random
    public class RandomStrategy implements LoadBalance {
        @Override
        public String select() {
            return null;
        }
    }
    
    @Component
    @RoundRobin
    public class RoundRobinStrategy implements LoadBalance {
        @Override
        public String select() {
            return null;
        }
    }
    

    使用:

    @Component
    public class UserService {
        @Autowired
        @RoundRobin
        private LoadBalance loadBalance;
        
        public void test() {
        	System.out.println(loadBalance);
        }
    }
    

    @Resource注解底层工作流程

    在这里插入图片描述

    展开全文
  • 建议使用以下措施防范SQL注入漏洞:对于开发========使用以下建议编写不受SQL注入***影响的web应用。参数化查询:SQL注入源于***者控制查询数据以修改查询逻辑,因此防范SQL注入***的最佳方式就是将查询的逻辑与其...

    解决办法防护建议包括部署分层安全措施(包括在接受用户输入时使用参数化的查询)、确保应用程序仅使用预期的数据、加固数据库服务器防止不恰当的访问数据。

    建议使用以下措施防范SQL注入漏洞:

    对于开发

    ========

    使用以下建议编写不受SQL注入***影响的web应用。

    参数化查询:SQL注入源于***者控制查询数据以修改查询逻辑,因此防范SQL注入***的最佳方式就是将查询的逻辑与其数据分隔,这可以防止执行从用户输入所注入的命令。这种方式的缺陷是可能对性能产生影响(但影响很小),且必须以这种方式构建站点上的每个查询才能完全有效。只要无意中绕过了一个查询,就足以导致应用受SQL注入的影响。以下代码显示的是可以进行SQL注入的SQL语句示例。

    sSql = "SELECT LocationName FROM Locations "; sSql = sSql + " WHERE LocationID = " + Request["LocationID"]; oCmd.CommandText = sSql;

    下面的例子使用了参数化的查询,不受SQL注入***的影响。

    sSql = "SELECT * FROM Locations ";

    sSql = sSql + " WHERE LocationID = @LocationID"; oCmd.CommandText = sSql; oCmd.Parameters.Add("@LocationID", Request["LocationID"]);

    应用程序没有包含用户输入向服务器发送SQL语句,而是使用-@LocationID-参数替代该输入,这样用户输入就无法成为SQL执行的命令。这种方式可以有效的拒绝***者所注入的任何输入,尽管仍会生成错误,但仅为数据类型转换错误,而不是***可以利用的错误。

    以下代码示例显示从HTTP查询字符串中获得产品ID并使用到SQL查询中。请注意传送给SqlCommand的包含有SELECT的字符串仅仅是个静态字符串,不是从输入中截取的。此外还请注意使用SqlParameter对象传送输入参数的方式,该对象的名称(@pid)匹配SQL查询中所使用的名称。

    C#示例:

    string connString = WebConfigurationManager.ConnectionStrings["myConn"].ConnectionString;

    using (SqlConnection conn = new SqlConnection(connString))

    {

    conn.Open();

    SqlCommand cmd = new SqlCommand("SELECT Count(*) FROM Products WHERE ProdID=@pid", conn);

    SqlParameter prm = new SqlParameter("@pid", SqlDbType.VarChar, 50);

    prm.Value = Request.QueryString["pid"];

    cmd.Parameters.Add(prm);

    int recCount = (int)cmd.ExecuteScalar();

    }

    VB.NET示例:

    Dim connString As String = WebConfigurationManager.ConnectionStrings("myConn").ConnectionString

    Using conn As New SqlConnection(connString) conn.Open()

    Dim cmd As SqlCommand = New SqlCommand("SELECT Count(*) FROM Products WHERE ProdID=@pid", conn)

    Dim prm As SqlParameter = New SqlParameter("@pid", SqlDbType.VarChar, 50)

    prm.Value = Request.QueryString("pid")

    cmd.Parameters.Add(prm)

    Dim recCount As Integer = cmd.ExecuteScalar()

    End Using

    验证输入:可通过正确验证用户输入的类型和格式防范大多数SQL注入***,最佳方式是通过白名单,定义方法为对于相关的字段只接受特定的帐号号码或帐号类型,或对于其他仅接受英文字母表的整数或字母。很多开发人员都试图使用黑名单字符或转义的方式验证输入。总体上讲,这种方式通过在恶意数据前添加转义字符来拒绝已知的恶意数据,如单引号,这样之后的项就可以用作文字值。这种方式没有白名单有效,因为不可能事先知道所有形式的恶意数据。

    对于安全操作

    ============

    使用以下建议帮助防范对web应用的SQL注入***。

    限制应用程序权限:限制用户凭据,仅使用应用运行所必需权限的。任何成功的SQL注入***都会运行在用户凭据的环境中,尽管限制权限无法完全防范SQL注入***,但可以大大增加其难度。

    强系统管理员口令策略:通常***者需要管理员帐号的功能才能使用特定的SQL命令,如果系统管理员口令较弱的话就比较容易暴力猜测,增加成功SQL注入***的可能性。另一个选项就是根本不使用系统管理员口令,而是为特定目的创建特定的帐号。

    一致的错误消息方案:确保在出现数据库错误时向用户提供尽可能少的信息。不要泄漏整个错误消息,要同时在web和应用服务器上处理错误消息。当web服务器遇到处理错误时,应使用通用的web页面响应,或将用户重新定向到标准的位置。绝不要泄漏调试信息或其他可能对***者有用的细节。

    有关如何在IIS中关闭详细错误消息的说明请见:

    http://www.microsoft.com/windows2000/en/server/iis/default.asp?url= /windows2000/en/server/iis/htm/core/iierrcst.htm

    使用以下句法在Apache服务器上取缔错误消息:

    Syntax: ErrorDocument <3-digit-code>

    Example: ErrorDocument 500 /webserver_errors/server_error500.txt

    WebSphere之类的应用服务器通常默认安装启用了错误消息或调试设置。有关如何取缔这些错误消息的信息,请参考应用服务器文档。

    存储过程:如果不使用的话,请***master..Xp_cmdshell、xp_startmail、xp_sendmail、

    sp_makewebtask之类的SQL存储过程。

    SQL注入漏洞根本上还是取决于web应用程序的代码。尽管不是修复,但可以通过向IDS中添加结合了正则表达式的规则作为紧急措施检测SQL注入***。尽管这无法修复所有可能的SQL注入漏洞,但便于实施,并且要求***者必须要改进其方法才能实现成功的***。可如下使用正则表达式。

    ***SQL元字符的正则表达式:

    /(\%27)|(\')|(\-\-)|(\%23)|(#)/ix

    可如下将上述正则表达式添加到Snort规则:

    alert tcp $EXTERNAL_NET any -> $HTTP_SERVERS $HTTP_PORTS (msg:"SQL Injection- Paranoid";flow:to_server,established;uricontent:".pl";pcre:"/(\%27)|(\')|(\-\-)|(%23)|(#)/i"; classtype:Web-application-attack; sid:9099; rev:5;)

    传统SQL注入***的正则表达式:

    /\w*((\%27)|(\'))((\%6F)|o|(\%4F))((\%72)|r|(\%52))/ix

    ***有UNION关键字的SQL注入***的正则表达式:

    /((\%27)|(\'))union/ix

    (\%27)|(\')

    可为其他的SQL查询(如select、insert、update、delete、drop等)编写类似的正则表达式。

    在MS SQL服务器上检测SQL注入***的正则表达式:

    /exec(\s|\+)+(s|x)p\w+/ix

    对于质量保证

    ============

    解决SQL注入缺陷最终要求基于代码的修复,“对于开发”和“对于安全操作”部分所述的步骤提供了修复这些漏洞所必要的信息。以下步骤概述了如何对应用程序手动测试SQL注入。

    如何对应用程序手动测试SQL注入:

    1. 在浏览器中打开希望测试SQL注入漏洞的web应用。

    2. 将鼠标光标悬停在Web站点的链接上并注意底部的状态栏,可以看到链接所指向的URL。找到其中带有参数的URL,如http://www.site.com/articleid.asp?id=42

    注释:如果没有在状态栏中看到任何URL,请点击链接然后查看地址栏,直到找到带有参数的URL。

    3. 找到带有参数的URL后,点击链接进入网页,在地址栏中可以看到状态栏中的URL。

    4. 有两种测试SQL注入脚本的方法,请使用全部两种方式依次测试每个参数值。

    方法1. 在地址栏中点击光标,高亮显示参数值,如高亮显示name=value中的value并用单引号(')替换,这时应类似于name='。

    方法2. 在地址栏中点击光标,在value中间输入单引号('),这时应类似于name=val'ue。

    5. 点击GO键将请求发送到Web服务器。

    6. 分析Web服务器响应中的错误消息,大多数数据库错误消息都类似于以下示例:

    Example error 1:

    Microsoft OLE DB Provider for SQL Server error '80040e14'

    Unclosed quotation mark before the character string '51 ORDER BY some_name'. /some_directory/some_file.asp, line 5

    Example error 2:

    ODBC Error Code = S1000 (General error)

    [Oracle][ODBC][Ora]ORA-00933: SQL command not properly ended

    Example error 3:

    Error: 1353 SQLSTATE: HY000 (ER_VIEW_WRONG_LIST)

    Message: View's SELECT and view's field list have different column counts

    7. 有时错误消息并不明显,隐藏在页面源码中。如果要查看这些消息,必须查看页面的HTML源码并搜索错误。如果要在Internet Explorer中实现这个操作,点击“查看”菜单,然后选择“源码”选项,这可以打开记事本显示页面的HTML源码。在记事本中,打开“编辑”菜单并选择“查找”。这时会出现一个对话框询问“查找内容”。输入Microsoft OLE DB或[ODBC]然后点击“查找下一个”。

    8. 如果6或7步成功,则Web站点存在SQL注入漏洞。

    展开全文
  • 易语言APC内存注入主要流程1、打开进程进程句柄 = OpenProcess (2035711, 假, 进程ID)2、读入DLL文件及获取主要汇编指令DLL文件 = 读入文件 (DLL路径)DLL文件长度 = 取字节集长度 (DLL文件)主要汇编指令长度 = ...
  • 如果对流程还有一些疑惑或者对数据库查询不清楚请先阅读此文章SQL注入基础笔记 正常打开 输入1,然后点击按钮,此时回显 ID: 1 First name: admin Surname: admin 判读是否存在注入 输入1‘,这个时候页面报错了,...
  • 001 | 搭上SpringBoot自动注入源码分析专车点击上方“java进阶架构师”,选择右上角“置顶公众号”20大进阶架构专题每日送达本系列为SpringBoot深度源码专车系列,第一篇发车!专车介绍该趟专车是开往Spring Boot...
  • 文章目录DVWACommand Injection 命令注入- 前言一、low级别二、medium级别三、high级别四、impossible级别五、漏洞利用- 获取目标shell DVWA Command Injection 命令注入 - 前言 命令拼接符:命令注入漏洞的利用,...
  • Spring 源码解读:IOC 原理之依赖注入,Bean 的创建过程,循环依赖解决尝试获取 getBean获取 Bean name尝试获取单例对象,处理循环依赖假如成功获取到对象检查后返回 Bean 对象假如没获取到对象,双亲向上寻找确实没...
  • 命令注入命令注入就是通过利用无验证变量构造特殊语句对...):命令注入 (Command Injection)Eval注入(Eval Injection)客户端脚本攻击(Script Injection)跨网站脚本攻击(Cross Site Scripting,XSS)SQL注入攻击(SQ...
  • 基于时间的延时注入: 1 ' or sleep(10) and ''=' 发现存在延时,此时首先测试flag的长度: ' and select((select length(flag) from flag)=32) or '1 7.基于Mongodb的注入: 利用正则爆破admin账户的密码: 当匹配每...
  • 上篇文章分析了Spring中的自动注入(byName,byType)和@Autowired注解的工作原理以及源码,@Autowired注解依赖注入其中注入注入,无论是属性注入还是方法注入都有一个相同的方法 org.springframework.beans.factory...
  • 该篇在上一篇的基础上进行深入,从源码了解依赖注入(控制反转)的过程,这个也是个比较重要的东西
  • 这个类主要作用的就是流程...这个Bean被注解@ConditionalOnMissingBean修饰,意思是只有在容器中不存在SpringProcessEngineConfiguration的情况下这个Bean才会被注册,因此如果要自定义自己的引擎配置,重新定义一个...
  • 1.2受影响的系统版本 UCMS  漏洞编号 N/A 二、漏洞细节 漏洞源码位置: ucms\ucms\sadmin\fileedit.php 34行 如图,该文件对传进来的路径与内容没有进行任何过滤与验证,从而造成了漏洞的产生。 三、漏洞利用\复现...
  • Spring中依赖注入底层原理与源码分析 前言:Spring框架是一套非常成熟的框架,同时也被大多数开发者所喜欢。在Spring中我们通常用到@Autowired注解,也就是我们常说的依赖注入。那么本篇文章带大家一起分析@...
  • 一、读写分离和防止sql注入的必要性(foreword)1、 读写分离:一句话定义:读写分离,基本的原理是让主数据库处理事务性增、改、删操作(INSERT、UPDATE、DELETE),而从数据库处理SELECT查询操作。数据库复制被用来把...
  • 初始化的过程,主要完成的工作是在容器中建立 BeanDefinition 数据映射,并没有看到容器对Bean依赖关系进行注入。 假设当前IoC容器已经载入用户定义的Bean信息,依赖注入主要发生在两个阶段 正常情况下,由用户第一...
  • 通过 SQL 注入攻击,掌握网站的工作机制,认识到 SQL 注入攻击的防范措施,加强对 Web 攻击的防范。一、实验环境(1) 下载并安装WAMP,文件夹下的wampserver2.2d32位(2) 运行python漏洞利用脚本的环境,需要安装...
  • 今天看一个项目代码,文件不多,不过每个文件中都N多注入,一个一个看实在太累,索性花了点时间,弄了个正则表达式,搜索出来,然后再将安全的筛选出去。省了不少时间的说。1.查找select、update、delete语句(...
  • Spring的自动注入,我想任何一个Java的程序员都会无比的自信的说:这个我知道;但是很遗憾的说,笔者理解的自动注入可能与你们理解的自动注入有很大的出入。那我们需要搞清楚Spring的自动注入是什么?自动注入也可以...
  • ### 简要描述:RT### 详细说明:```注入链接:/virtual-office/job.php注入参数:job漏洞代码:(第52行开始)if (!empty($_POST['job']) && $_POST['save']) {$vals = $_POST['job'];pb_submit_check('job');...
  • 一)针对asp+access网站进行SQL注入利用 1)asp+access网站技术介绍 2)web漏洞扫描 3)漏洞分析 4)SQL注入点利用 ——————————————————————————————————————————————...
  • 客户端配置属性注入 SpringBoot 源码分析1.1 Apollo 组件的注入1.2 Apollo 配置属性的更新 前言 在上一篇文章 Apollo 客户端集成 SpringBoot 的源码分析(1)- 启动时配置获取 中,笔者分析了 Apollo 客户端拉取远端...
  • 以low级别为例,发现一个可以输入内容的文本框时,首先测试一下是不是存在sql注入。Low Level分别在文本框中输入1和1’进行测试。输入1时,回显正常,如下:输入1’时,提示错误。此外,我们可以看到,我们在文本框...
  • 目录前言函数介绍OpenProcess函数语法...远程线程注入是指一个进程在另一个进程中创建线程的技术。 函数介绍 OpenProcess函数 打开一个已存在的进程对象,并返回进程的句柄。 语法 HANDLE OpenProcess( [in] DWORD d
  • 这篇文章主要介绍了DVWA平台中的高危漏洞之一:SQL注入漏洞的测试以及DVWA平台关于SQL注入的PHP源码分析。 在进行攻击之前,首先要了解SQL注入攻击的原理,在Web程序运行过程中,数据库是不可缺少的一部分,当我们...
  • sql注入详解

    千次阅读 2021-03-03 17:38:47
    注入可能存在的位置 四. 漏洞危害 五. sql注入防范 六. sql注入分类 七. 常见的注入手法 联合查询 报错注入 布尔盲注 延时注入 HTTP头注入 宽字节注入 堆叠查询 base64注入 一. 前言 结构化查询语言...
  • 源码分析@Autowired和@Resource的区别

    千次阅读 2021-03-08 15:12:34
    @Resource和@Autowired都是做bean的注入时使用,其实@Resource并不是Spring的注解,它的包是javax.annotation.Resource,需要导入,但是Spring支持该注解的注入。 spring不但支持自己定义的@Autowired注解,还支持几...
  • 本帖最后由 Aedoo 于 2018-3-21 10:06 编辑0x00 前言继上一篇Union注入之后的另一个宽字节注入。(Union注入传送门:https://bbs.ichunqiu.com/thread-30212-1-1.html)本文中提到的漏洞网上已公开并且已经修复。文章...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 69,263
精华内容 27,705
关键字:

存在注入网站源码