Spring除了提供基于xml配置还提供了基于注解的配置,开发中经常用基于注解的方式。Spring提供了很多注解掌握其使用方法与实现原理是非常有必要的。
@Required
最新Spring5中该注解标记为过时的,此注解应用于JavaBean的setter方法上。
@Deprecated
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Required {
}
这个注解表明Bean设置属性的时候必须装配明确的属性值,下面演示其用法。
- 新增JavaBean
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Setter
public class Student {
private String name;
private Integer age;
private Grade grade;
@Required
public void setGrade(Grade grade) {
this.grade = grade;
}
}
@Data
public class Grade {
private String gradeLevel;
}
- xml中配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="student" class="com.codegeek.day7.Student"/>
<bean id="grade" class="com.codegeek.day7.Grade"/>
</beans>
我们在 Student
的 setGrade
方法加入了注解,表明当容器初始化创建Student
类的时候必须也要初始化 Grade
类然后将 Grade
值注入 Student
类中,如果我们像上面那样编辑器会给我们一个错误提示:
然后我们对xml配置文件做以下改动:
然后发现编辑器不在告警。
@Autowired
相信大家对此注解一定不会陌生,在日常开发中最常见的使用场景就是:如果类A需要调用类B的方法,此时我们可以将类B做为类A的成员属性然后在这个成员属性加上此注解即可。
@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;
}
下面演示其用法 我们在Student
类注入 Grade
类。
然后为Grade类属性进行赋值:
运行测试类:
注入原理
当要注入 Grade
对象 @Autowired 注解首先会跟着类型去匹配IOC容器中类型为Grade
的类,找到就返回并设置否则返回 NoSuchBeanDefinitionException
异常。
如果有多个类型的Bean那么IOC容器会注入那个呢?让我们先定义两个同类型的Bean如下所示:
然后在跑一次测试的代码:我们发现注入的依然是id为 grade
的那个 Bean
对象。
原因是:当发现多个类型的Bean对象的时候,Spring会以注入对象的变量名称做为 id 去IOC容器中匹配该id。
若是找到就返回否则会报以下错误:
例如我们将变量名称改为 grade1
如下:
@Autowired
private Grade grade1;
然后我们运行以下测试类的代码就抛出异常如下所示:
@Qualifier
上面我们发现注入类类型有多个时,Spring容器会进行抛异常处理。我们可以指定id的类此时就要使用到 @Qualifier
注解。
我们再次运行测试类我们发现grade2对象已经注入成功。
@Resource
Spring也支持JSR-250注解 @Resource默认情况下其也是根据类的成员变量名去IOC容器中查找类,如下我们使用 @Resource 注解 Spring容器会在IOC容器查找 id 为 grade1
的 Grade
,与上面类似找到就返回对象并注入否则还是抛出异常。
@Resource
private Grade grade1;
我们对注入对象进行如下修改:
@Resource(name = "grade2")
private Grade grade1;
然后Spring容器会在容器查找 id 为 grade2
的 Grade
类对象。此时指定了 name 属性的 @Resource
等价与@Autowired
、@Qualifier
组合。
@Configuration @Bean
@Configuration
注解表明该类为配置类其功能相当于我们常常配置的xml数据源配置.
@Bean
声明一个受Spring管理的JavaBean对象其相当于我们xml配置中的<bean> 标签。我们定义以下配置类:
@Configuration
public class ApplicationConfiguration {
@Bean(name = "student2")
public Student createStudent() {
return new Student("张三", 23);
}
}
然后xml中配置可以扫描此注解的功能:
跑一下测试的代码如下:
@Primary
一般按照如下根据类型获取Bean会获取多个类型时,此时可以对某个Bean设置 @Primary
这样Spring优先返回指定了此注解的Bean。
我们对配置类进行如下更改:
然后我们在跑一下测试类的代码:
我们发现Spring容器优先返回了加 @Primary
的Bean对象。
类路径扫描注解@Component及其同义注解
相信开发人员对 @Service
@Controller
@Repository
已经很熟悉了,我们都知道其分别对应着服务层、表现层、持久层而 @Component
属于Spring管理组件的通用注解。通过查看源码我们可以看到其他三个注解都是构建在 @Component
之上
开发中如果想要Spring可以自动检测加了注解的类并注入其相应的Bean,需要我们加入 @ComponentScan
也可以通过xml配置 <context:component-scan>
,两者效果是一致的。
@Import
与我们常常在某个Xml中 import
其他模块xml配置一样,我们也可以使用某个配置类import
其他配置类正向下面这样。
- xml中导入其他配置xml信息
- 配置类导入其他配置类
@Configuration
public class DataConfiguration {
@Bean
public Grade createGrade() {
return new Grade("大学一年级");
}
}
@ImportResource
我们在xml配置了一个id为 grade2
的 Grade
类如下所示:
然后我们使用 AnnotationConfigApplicationContext
这个类去获取上面Grade类代码如下所示:
@Test
public void testRequired() {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
annotationConfigApplicationContext.register(ApplicationConfiguration.class);
annotationConfigApplicationContext.refresh();
Grade bean = annotationConfigApplicationContext.getBean("grade2", Grade.class);
System.out.println("\n" + bean);
}
运行确报错 No bean named 'grade2' available
的异常。
出现这个原因是我们的 ApplicationConfiguration 这个类没有配置名称为grande2 的对象但我们的xml配置文件已经定义了该Bean,此时我们可以使用@ImportResource 将配置文件内容加入到 ApplicationConfiguration 这个类中解决问题。
@Profile
在开发中经常有这样的需求:不同开发环境下注册不同的Bean对象以方便我们在不同环境下完成不同的业务逻辑如:开发环境、测试环境、生产环境等。此时我们可借助@Profile帮助我们在不同的环境注册不同的Bean,下面以一个简单的案例演示其使用。
- 新增三个不同环境配置类
@Configuration
@Profile(value = "dev")
public class DevConfiguration {
@Bean(name = "dataSource")
public DataSource createDataSource(@Qualifier("dataConfig") DataConfig config) throws SQLException {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setUrl(config.getUrl());
dataSource.setDriver(config.getDriver());
dataSource.setUsername(config.getUserName());
dataSource.setPassword(config.getPassword());
return dataSource;
}
@Bean(name = "dataConfig")
public DataConfig devData() throws SQLException {
String url = "jdbc:mysql://localhost/test?createDatabaseIfNotExist=true&useUnicode=true&characterEncoding=utf8&autoReconnect=true";
Driver driver = new Driver();
String userName = "root";
String password = "123456";
return new DataConfig(driver, url, userName, password);
}
}
@Configuration
@Profile(value = "prop")
public class PropConfiguration {
@Bean(name = "dataSource")
public DataSource createDataSource(@Qualifier("dataConfig") DataConfig config) throws SQLException {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setUrl(config.getUrl());
dataSource.setDriver(config.getDriver());
dataSource.setUsername(config.getUserName());
dataSource.setPassword(config.getPassword());
return dataSource;
}
@Bean(name = "dataConfig")
public DataConfig devData() throws SQLException {
String url = "jdbc:mysql://10.211.55.1/test?createDatabaseIfNotExist=true&useUnicode=true&characterEncoding=utf8&autoReconnect=true";
Driver driver = new Driver();
String userName = "root";
String password = "123456";
return new DataConfig(driver, url, userName, password);
}
}
@Configuration
@Profile(value = "qa")
public class QAConfiguration {
@Bean(name = "dataSource")
public DataSource createDataSource(@Qualifier("dataConfig") DataConfig config) throws SQLException {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setUrl(config.getUrl());
dataSource.setDriver(config.getDriver());
dataSource.setUsername(config.getUserName());
dataSource.setPassword(config.getPassword());
return dataSource;
}
@Bean(name = "dataConfig")
public DataConfig devData() throws SQLException {
String url = "jdbc:mysql://10.211.55.5/test?createDatabaseIfNotExist=true&useUnicode=true&characterEncoding=utf8&autoReconnect=true";
Driver driver = new Driver();
String userName = "root";
String password = "123456";
return new DataConfig(driver, url, userName, password);
}
}
@PropertySource
Spring中有个接口Environment,它对应用环境建立了一个Profile
与<kbdProperty
两个概念。我们可以设置额外的Properties文件并将其加入到Spring配置文件中,如下是一个简单的使用案例:
- 新增JavaBean对象
@Data
@AllArgsConstructor
@NoArgsConstructor
public class DeployConfiguration {
private String deployUrl;
private String deployUser;
private String deployPath;
private String deployEnvironment;
}
@Configuration
@PropertySource(value = "classpath:day7/customize.properties")
public class PropertySourceConfiguration {
@Autowired
private Environment environment;
@Bean(name = "deployConfiguration")
public DeployConfiguration create() {
DeployConfiguration configuration = new DeployConfiguration();
configuration.setDeployEnvironment(environment.getProperty("deploy.environment"));
configuration.setDeployPath(environment.getProperty("deploy.path"));
configuration.setDeployUrl(environment.getProperty("deploy.url"));
configuration.setDeployUser(environment.getProperty("deploy.user"));
return configuration;
}
}
- 新增 customize.properties
deploy.environment=linux
deploy.path=/soft/service/spring-tomcat
deploy.user=codegeekgao
deploy.url=10.211.55.1