Spring注解开发之Spring常用注解
https://blog.csdn.net/Adrian_Dai/article/details/80287557
主要的注解使用:
本文用的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的功能;
---------------------
作者:Adrian_Dai
来源:CSDN
原文:https://blog.csdn.net/adrian_dai/article/details/80287557
版权声明:本文为博主原创文章,转载请附上博文链接!