主要的注解使用:
本文用的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);