Spring注解开发之Spring常用注解

主要的注解使用:

本文用的Spring源码是4.3.16

@Configuration

此注解的作用是告诉Spring,添加该注解的类是配置类。


@ComponentScan

相信大家看到这个组合单词就知道是有什么作用了,扫描用的。直接看看源码吧

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {

	@AliasFor("basePackages")
	String[] value() default {};

	@AliasFor("value")
	String[] basePackages() default {};

	Class<?>[] basePackageClasses() default {};

	Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;

	Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;

	ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;

	String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;

	boolean useDefaultFilters() default true;

	Filter[] includeFilters() default {};

	Filter[] excludeFilters() default {};

	boolean lazyInit() default false;

	@Retention(RetentionPolicy.RUNTIME)
	@Target({})
	@interface Filter {

		FilterType type() default FilterType.ANNOTATION;

		@AliasFor("classes")
		Class<?>[] value() default {};

		@AliasFor("value")
		Class<?>[] classes() default {};

		String[] pattern() default {};

	}

}
由于不想篇幅过长,我把注释都去掉了,如果需要完整的Spring源码可以评论留下邮箱!

这边我主要讲一下我常用到的注释,其他注释读者可以自行学习。

value  指定要扫描的包。

==================================================简单的分隔符吧。下面一样

includeFilters 指定扫描的时候只需要包含哪些组件;

excludeFilters 指定扫描的时候按照什么规则排除那些组件;

它们两个都是一个Filter[],在上面的代码中也有,Filter中有个FilterType,它是一个枚举类,有5种类型。

FilterType.ANNOTATION:按照注解
FilterType.ASSIGNABLE_TYPE:按照给定的类型;
FilterType.ASPECTJ:使用ASPECTJ表达式
FilterType.REGEX:使用正则指定
FilterType.CUSTOM:使用自定义规则

来个示例吧:

@ComponentScan(value="com.csdn.dh",includeFilters = {
						@Filter(type=FilterType.ANNOTATION,classes={Controller.class}),
						@Filter(type=FilterType.ASSIGNABLE_TYPE,classes={UserService.class}),
						@Filter(type=FilterType.CUSTOM,classes={MyTypeFilter.class})
				}

Controller.class是包含@Controller的注解的类,还有是UserService的类,然后最后一个是自定义的规则类,自定义规则类需要实现TypeFilter接口,实现match()方法;

boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException;

match 方法上的两个参数分别是:①metadataReader:读取到的当前正在扫描的类的信息

②metadataReaderFactory:可以获取到其他任何类信息的

留意到的读者会发现方法的返回值是boolean类型的,说明返回true就包含该组件,反之排除。我们来个尝试吧。看一下下面的代码和结果吧!

@Configuration
@ComponentScans({
	@ComponentScan(value = "com.csdn.dh",
			excludeFilters = {@Filter(type=FilterType.CUSTOM,classes={MyTypeFilter.class})}
	)
})
public class MainConfig {
}
public class MyTypeFilter implements TypeFilter {
	/**
	 * metadataReader:读取到的当前正在扫描的类的信息
	 * metadataReaderFactory:可以获取到其他任何类信息的
	 */
	@Override
	public boolean match(MetadataReader metadataReader,
			MetadataReaderFactory metadataReaderFactory) throws IOException {
		//获取当前类注解的信息
		AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
		//获取当前正在扫描的类的类信息
		ClassMetadata classMetadata = metadataReader.getClassMetadata();
		//获取当前类资源(类的路径)
		Resource resource = metadataReader.getResource();
		
		//className是全类名  com.csdn.dh.pojo.User
		String className = classMetadata.getClassName();
		System.out.println(className);
		if (className.contains("Pojo")) 
			return true;
		return false;
	}
}
public class TestMain {
	public static void main(String[] args) {
		ApplicationContext ac = new AnnotationConfigApplicationContext(MainConfig.class);
		String[] beanNames = ac.getBeanDefinitionNames();
		for (String name : beanNames) {
			System.out.println("---->" + name);
		}
	}
}

输出的结果是:

com.csdn.dh.Filter.MyTypeFilter
com.csdn.dh.pojo.User
com.csdn.dh.pojo.UserPojo
com.csdn.dh.test.TestMain


---->org.springframework.context.annotation.internalConfigurationAnnotationProcessor
---->org.springframework.context.annotation.internalAutowiredAnnotationProcessor
---->org.springframework.context.annotation.internalRequiredAnnotationProcessor
---->org.springframework.context.annotation.internalCommonAnnotationProcessor
---->org.springframework.context.event.internalEventListenerProcessor
---->org.springframework.context.event.internalEventListenerFactory
---->mainConfig
---->user

从结果中可以看到,UserPojo类已经被排除了。就是定义的MyTypeFilter起的作用。其他剩下的4个类型读者有兴趣的可自行尝试。

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

useDefaultFilters  这个属性是指是否自动扫描带有@Component、@Repository、@Service、@Controller注解的类,默认是true,是开启的。


@Scpoe

用于调整作用域,默认是singleton,单例的。来看一下源码里面怎么说吧:

        /**
	 * Specifies the name of the scope to use for the annotated component/bean.
	 * <p>Defaults to an empty string ({@code ""}) which implies
	 * {@link ConfigurableBeanFactory#SCOPE_SINGLETON SCOPE_SINGLETON}.
	 * @since 4.2
	 * @see ConfigurableBeanFactory#SCOPE_PROTOTYPE
	 * @see ConfigurableBeanFactory#SCOPE_SINGLETON
	 * @see org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST
	 * @see org.springframework.web.context.WebApplicationContext#SCOPE_SESSION
	 * @see #value
	 */
	@AliasFor("value")
	String scopeName() default "";

看着是有4种可选的

prototype:多实例的:ioc容器启动并不会去调用方法创建对象放在容器中。每次获取的时候才会调用方法创建对象;

singleton:单实例的(默认值):ioc容器启动会调用方法创建对象放到ioc容器中。以后每次获取就是直接从容器(map.get())中拿,

request:同一次请求创建一个实例

session:同一个session创建一个实例


@Lazy

懒加载:
单实例的Bean:默认在容器启动的时候创建对象
懒加载:容器启动不创建对象。第一次使用(获取)Bean创建对象,并初始化


@Conditional

按照一定的条件进行判断,给容器中注册满组条件的Bean

来看一下源码:看一下他的value,我们需要自定义自己的条件,实现Condition,并重写matches方法,是不是眼熟这个方法?没错,刚才我们就写过了是吧?方法的意思

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {

	/**
	 * All {@link Condition}s that must {@linkplain Condition#matches match}
	 * in order for the component to be registered.
	 */
	Class<? extends Condition>[] value();

}
public class MyCondition implements Condition {
	/**
	 * ConditionContext:判断条件能使用的上下文(环境)
	 * AnnotatedTypeMetadata:注释信息
	 */
	@Override
	public boolean matches(ConditionContext context,AnnotatedTypeMetadata metadata) {
		//获取到容器使用的beanfactory
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
		//获取类加载器
		ClassLoader classLoader = context.getClassLoader();
		//获取当前环境信息
		Environment environment = context.getEnvironment();
		//获取到Bean定义的注册类
		BeanDefinitionRegistry registry = context.getRegistry();
		return true;
	}
}

具体的例子我就不写上,相信认真的读者也会方法跟刚才的TypeFilter的实现是差不多的。有兴趣读者可以看看这个两个参数的具体一些方法,来实现一下满足自己设定条件的Bean吧。


@Import

快速给容器中导入一个组件。看一下源码:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
	/**
	 * {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}
	 * or regular component classes to import.
	 */
	Class<?>[] value();
}
属性是Class<?>的数组,这个注解的作用是导入一个组件,导入的组件默认是类的全类名,即@Import(User.class)    输出的BeanName是com.csdn.dh.pojo.User

认真看源码的读者能发现,它还能用ImportSelector和ImportBeanDefinitionRegistrar

我们可以自定义需要返回的组件:

public class MyImportSelector implements ImportSelector {
	//返回的String数组就是到导入到容器中的组件全类名
	//AnnotationMetadata:当前标注@Import注解的类的所有注解信息
	@Override
	public String[] selectImports(AnnotationMetadata importingClassMetadata) {
		//方法不要返回null值,否则会报空指针异常(读者可以自行debug看一下原因)
		return new String[]{"com.csdn.dh.pojo.User"};
	}
}
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
	/**
	 * AnnotationMetadata:当前类的注解信息
	 * BeanDefinitionRegistry:BeanDefinition注册类;
	 * 		把所有需要添加到容器中的bean;调用BeanDefinitionRegistry.registerBeanDefinition手工注册进来
	 */
	@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		//指定Bean定义信息;(Bean的类型)
		RootBeanDefinition beanDefinition = new RootBeanDefinition(User.class);
		//注册一个Bean,指定bean名
		registry.registerBeanDefinition("user", beanDefinition);
		}
	}
}


@Profile

指定组件在哪个环境的情况下才能被注册到容器中,不指定则在任何环境下都能注册这个组件。

①加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中。默认是default环境
②写在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能开始生效
③没有标注环境标识的bean在任何环境下都是加载的;

@PropertySource("classpath:/db.properties")
@Configuration
public class TestDHProfile implements EmbeddedValueResolverAware{
	@Value("${db.user}")
	private String user;
	
	private StringValueResolver resolver;
	private String  driverClass;
	
	@Profile("test")
	@Bean("testDataSource")
	public DataSource dataSourceTest(@Value("${db.password}")String pwd) throws Exception{
		ComboPooledDataSource dataSource = new ComboPooledDataSource();
		dataSource.setUser(user);
		dataSource.setPassword(pwd);
		dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
		dataSource.setDriverClass(driverClass);
		return dataSource;
	}
	
	@Profile("dev")
	@Bean("devDataSource")
	public DataSource dataSourceDev(@Value("${db.password}")String pwd) throws Exception{
		ComboPooledDataSource dataSource = new ComboPooledDataSource();
		dataSource.setUser(user);
		dataSource.setPassword(pwd);
		dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/dev");
		dataSource.setDriverClass(driverClass);
		return dataSource;
	}

	@Override
	public void setEmbeddedValueResolver(StringValueResolver resolver) {
		this.resolver = resolver;
		driverClass = resolver.resolveStringValue("${db.driverClass}");
	}

}
public class TestProfileMain {
	//1、使用命令行动态参数: 在虚拟机参数位置加载 -Dspring.profiles.active=test
	//2、代码的方式激活某种环境;
	@Test
	public void testMain(){
		//1、创建一个applicationContext
		AnnotationConfigApplicationContext applicationContext = 
				new AnnotationConfigApplicationContext();
		//2、设置需要激活的环境
		applicationContext.getEnvironment().setActiveProfiles("dev");
		//3、注册主配置类
		applicationContext.register(TestDHProfile.class);
		//4、启动刷新容器
		applicationContext.refresh();
		applicationContext.close();
	}
}
相信认真看的读者也看到上面TestDHProfile中实现了EmbeddedValueResolverAware的接口。这边我说一下这个接口的作用吧,实现这个接口的setEmbeddedValueResolver方法可以读取配置文件,拿到配置文件中属性对应的值,如我代码中的driverClass就是利用resolver来获取的。

@Value

这个注解不讲了。


@Autowired

自动注入。

@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
	/**
	 * Declares whether the annotated dependency is required.
	 * <p>Defaults to {@code true}.
	 */
	boolean required() default true;
}

(1)、[标注在方法位置]:参数从容器中获取,默认不写@Autowired效果是一样的,都能自动装配
(2)、[标在构造器上]:如果组件只有一个有参构造器,这个有参构造器的@Autowired可以省略,参数位置的组件还是可以自动从容器中获取
(3)、放在参数位置:默认按照类型去容器查找对应组件,如果找到多个相同类型的会按照组件的id到容器中查找。

注意:自动注入一定要将属性值赋好值,否则会报错。因为注解中required默认是true的,即必须的。可以进行修改。

@Qualifier

使用@Qualifier指定需要装配的组件的id。

例如:当你有两个相同类型的service时,为@Service("userService1"),@Service("userService2");然后你

@Autowired注解了private UserService userService;这样子Spring不知道你需要哪个service,所以你可以在userService上加上@Qualifier("userService1")与自动注入搭配使用。

@Primary

让Spring进行自动装配的时候,默认使用首选的Bean。这个不说了,不过这个注解是加到Bean上的


@Resource

可以和@Autowired一样实现自动装配功能;默认是按照组件名称进行装配的;

但是没有能支持@Primary功能没有支持@Autowired(reqiured=false);


@Inject

需要导入javax.inject的包,和Autowired的功能一样。没有required=false的功能;
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值