Spring 常用注解解析
1. 常用注解
1.1 @Configuration
@Configurationpublic class MainConfig {}
@Configuration注解表明这个类是一个配置类,该类中应该包含如何在Spring应用上下文中创建bean的细节
1.2 @ComponentScan
@Configuration@ComponentScan("per.ym")public class MainConfig {}
@ComponentScan注解用于启用组件扫描,其作用同xml中配置
@Configuration@ComponentScan("per.ym, per.mm")public class MainConfig {}
在上面的例子中,所设置的基础包是以String类型表示的。我认为这是可以的,但这种方法是类型不安全(not type-safe)的。如果你重构代码的话,那么所指定的基础包可能就会出现错误了。除了将包设置为简单的String类型之外,@ComponentScan还提供了另外一种方法,那就是将其指定为包中所包含的类或接口:
@Configuration@ComponentScan(basePackageClasses = {MyService.class, MyDao.class})public class MainConfig {}
你可以考虑在包中创建一个用来进行扫描的空标记接口(marker interface)。通过标记接口的方式,你依然能够保
持对重构友好的接口引用,但是可以避免引用任何实际的应用程序代码
1.3 @Controller, @Service, @ Repository, @Component
这几个自然不用多说,算是见得最多的了,它们锁修饰的类在被spring容器扫描到时会被加入到spring的管理之中。
@Controller对应表现层的Bean
@Service对应的是业务层Bean
@Repository对应数据访问层Bean
@Component,当你不能明确的选择上述3中,就用这个
Spring应用上下文中所有的bean都会给定一个ID,如果没有明确指定,Spring会根据类名为其指定一个ID,也就是将类名的第一个字母变为小写。你也可以这样显示的指定一个ID:
@Component("ymm")public class Person() {}
Spring支持将@Named(Java依赖注入规范中所提供的)作为@Component注解的替代方案。两者之间有一些细微的差异,但是在大多数场景中,它们是可以互相替换的。
1.4 @Bean
@Bean注解会告诉Spring这个方法将会返回一个对象,该对象要注册为Spring应用上下文中
的bean。方法体中包含了最终产生bean实例的逻辑。
@Configurationpublic class MainConifg { @Bean public BookDao bookDao(){ return new BookDao(); }}
@Bean注解会告诉Spring这个方法将会返回一个对象,该对象要注册为Spring应用上下文中
的bean。方法体中包含了最终产生bean实例的逻辑。
默认情况下,bean的ID与带有@Bean注解的方法名是一样的。在上例中,bean的名字将会是bookDao。如果你想为其设置成一个不同的名字的话,那么可以重命名该方法,也可以通过name属性指定一个不同的名字:
@Configurationpublic class MainConifg { @Bean(" ymBookDao") public BookDao bookDao(){ return new BookDao(); }}
还可以像这样设置初始化方法和销毁方法:
@Configurationpublic class MainConifg { @Bean(initMethod = "init", destroyMethod = "destroy") public BookDao bookDao(){ return new BookDao(); }}
这如同在xml中配置init-method="init" destroy-method="destory"一样
1.5 @Autowired
借助@Autowired注解可以实现spring的自动装配,自动装配就是让Spring自动满足bean依赖的一种方法,在满足依赖的过程中,会在Spring应用上下文中寻找匹配某个bean需求的其他bean
@Servicepublic class BookService { @Autowired(required=false) private BookDao bookDao;}
你可以把@Autowired标注在属性、构造器、setter方法上。实际上,Setter方法并没有什么特殊之处,@Autowired注解甚至可以用在类的任何方法上。
除了自带的@Autowired,spring还支持Java规范中的@Resource(JSR250)和@Inject(JSR330),它们之间的区别如下:
@Autowired:
a,默认优先按照类型去容器中找对应的组件:applicationContext.getBean(BookDao.class),如果有且只有一个 bean匹配依赖需求的话,那么这个bean将会被装配进来;如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找applicationContext.getBean("bookDao");
b,@Qualifier("bookDao"):使用@Qualifier明确指定需要装配的组件的id,而不是使用属性名;
c,自动装配默认一定要将属性赋值好,没有就会报错;可以使用@Autowired(required=false);
d,@Primary:让Spring进行自动装配的时候,默认使用首选的bean;也可以使用@Qualifier指定需要装配的bean的名字;
@Resource:
a,和@Autowired一样实现自动装配功能;默认是按照组件名称进行装配的;
b,不支持@Primary功能;
c,不支持required=false的功能;
@Inject:
a,和@Autowired一样实现自动装配功能;
b,不支持required=false的功能;
一个可能的例子:
@Configuration@ComponentScan("per.ym.service")public class MainConifgOfAutowired { @Primary @Bean("bookDao1") public BookDao bookDao1(){ BookDao bookDao = new BookDao(); bookDao.setLable("1"); return bookDao; } @Bean("bookDao2") public BookDao bookDao2(){ BookDao bookDao = new BookDao(); bookDao.setLable("2"); return bookDao; }} @Servicepublic class BookService { @Qualifier("bookDao1") @Autowired(required=false) private BookDao bookDao; //@Resource(name="bookDao2") //private BookDao bookDao; //@Qualifier("bookDao2") //@Inject //private BookDao bookDao;}public class BookDao { private String lable = "0"; public String getLable() { return lable; } public void setLable(String lable) { this.lable = lable; }}
1.6 @Import
通过导入的方式实现快速给容器中导入组件,其上可以配置3种类型的值,分别是普通bean,ImportSelector,ImportBeanDefinitionRegistrar。特别的,你可以导入一个被@Configuration注解修饰的bean,这和在一个spring配置文件中使用
@Configuration@Import({MainConfig2.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})public class MainConfig {}
MainConfig2可以是另一个配置类或者普通的bean:
@Configurationpublic class MainConfig2 { @Bean public Person person(){ return new Person(); }}
ImportSelector,其selectImports方法返回的数组中应包含要导入容器的bean的全类名
public class MyImportSelector implements ImportSelector { //返回值,就是到导入到容器中的组件全类名 //AnnotationMetadata:当前标注@Import注解的类的所有注解信息 @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { //方法不要返回null值,否则会有NPE return new String[]{"per.ym.bean.Car","per.ym.bean.Dog"}; }}
ImportBeanDefinitionRegistrar,调用BeanDefinitionRegistry.registerBeanDefinition手工注册想要添加到容器中的bean
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { /** * AnnotationMetadata:当前类的注解信息 * BeanDefinitionRegistry:BeanDefinition注册类; */ @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { //指定Bean定义信息 RootBeanDefinition beanDefinition = new RootBeanDefinition(Red.class); //注册一个Bean,指定bean名 registry.registerBeanDefinition("red", beanDefinition); }}
1.7 @Conditional
放在类上,当满足条件时,这个类中配置的所有bean注册才能生效
放在方法上,当满足条件时,才向容器中注册当前bean
@Conditional({WindowsCondition.class})@Configurationpublic class MainConfig { @Conditional(LinuxCondition.class) @Bean("linus") public Person person(){ return new Person("linus"); }}/*** ConditionContext:判断条件能使用的上下文(环境)* AnnotatedTypeMetadata:注释信息*///判断是否windows系统public class WindowsCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { //1、能获取到ioc使用的beanfactory ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); //2、获取类加载器 ClassLoader classLoader = context.getClassLoader(); //3、获取到bean定义的注册类 BeanDefinitionRegistry registry = context.getRegistry(); //4、获取当前环境信息 Environment environment = context.getEnvironment(); String property = environment.getProperty("os.name"); if(property.contains("Windows")){ return true; } return false; }}
1.8 @Profile
根据当前环境,动态的激活和切换一系列组件的功能;指定组件在哪个环境的情况下才能被注册到容器中,不指定,任何环境下都能注册这个组件
加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中
写在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能开始生效
@PropertySource("classpath:/dbconfig.properties")@Configurationpublic class MainConfigOfProfile implements EmbeddedValueResolverAware{ @Value("${db.user}") private String user; private StringValueResolver valueResolver; 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; } @Profile("prod") @Bean("prodDataSource") public DataSource dataSourceProd(@Value("${db.password}")String pwd) throws Exception{ ComboPooledDataSource dataSource = new ComboPooledDataSource(); dataSource.setUser(user); dataSource.setPassword(pwd); dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/prod"); dataSource.setDriverClass(driverClass); return dataSource; } @Override public void setEmbeddedValueResolver(StringValueResolver resolver) { this.valueResolver = resolver; driverClass = valueResolver.resolveStringValue("${db.driverClass}"); }}db.properties:db.user=rootdb.password=123456db.driverClass=com.mysql.jdbc.Driver
启动命令行中设置环境参数:
-Dspring.profiles.active=test
代码方式设置:
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfProfile.class);applicationContext.getEnvironment().setActiveProfiles("dev");