学习目标
- 学会Spring高级注解
- 对应项目
day3_anno_dependsOn
day3_anno_lazy
day3_anno_conditional
day3_anno_profile
day4_anno_basic
day4_anno_jsr
day4_anno_primary
day4_anno_lifecycle
学习笔记
1 注入时机和设定注入条件的注解
1.1 @DependsOn
1.1.1 作用
- 用于指定某个类的创建依赖的bean对象先创建。spring中没有特定bean的加载顺序,使用此注解则可指定bean的加载顺序。(在基于注解配置中,是按照类中方法的书写顺序决定的)
1.1.2 属性
- value:用于指定bean的唯一标识。被指定的bean会在当前bean创建之前加载
1.1.3 基本使用
@Configuration
@ComponentScan("com.example")
public class SpringConfiguration {
}
SpringConfiguration.java
@Component
@DependsOn("eventListener")
public class EventSource {
public EventSource(){
System.out.println("事件源对象创建");
}
}
EventSource.java
@Component
public class EventListener {
public EventListener(){
System.out.println("监听器创建");
}
}
EventListener.java
public class SpringDependsOnTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext("config");
ac.start();
}
}
SpringDependsOnTest.java
- EventSource依赖于EventListener,所以先创建EventListener,后创建EventSource
1.2 @Lazy
1.2.1 作用
- 用于指定单例bean对象的创建时机。在没有使用此注解时,单例bean的生命周期与容器相同。但是当使用了此注解之后,单例对象的创建时机变成了第一次使用时创建。注意:这不是延迟加载思想(因为不是每次使用时都创建,只是第一次创建的时机改变了)
1.2.2 属性
- value: 指定是否采用延迟加载。默认值为true,表示开启
1.2.3 基本使用
@Configuration
@ComponentScan("com.example")
public class SpringConfiguration {
}
SpringConfiguration.java
@Component
@Lazy
public class LogUtil {
public LogUtil(){
System.out.println("LogUtil创建了");
}
public void printLog(){
System.out.println("记录日志");
}
}
LogUtil.java
public class SpringLazyTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext("config");
LogUtil logUtil = ac.getBean("logUtil", LogUtil.class);
logUtil.printLog();
}
}
SpringLazyTest.java
1.3 @Conditional
1.3.1 作用
- 它的作用是根据条件选择注入的bean对象。
1.3.2 属性
- value: 用于提供一个Condition接口的实现类,实现类中需要编写具体代码实现注入的条件。
1.3.3 基本使用
- 自定义Condition实现bean的选择性注册
@Configuration
@Import(JdbcConfig.class)
@PropertySource({"classpath:jdbc.properties","classpath:linuxjdbc.properties"})
public class SpringConfiguration {
}
SpringConfiguration.java
/**
* 自定义注册bean的条件,windows操作系统注入
*/
public class WindowsCondition implements Condition {
/**
* 是否注册到ioc容器中的和新方法
*
* @param conditionContext
* @param annotatedTypeMetadata
* @return 是true表示注册,否则不注册
*/
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
//获取ioc使用的BeanFactory对象
ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
//获取类加载器
ClassLoader classLoader = beanFactory.getBeanClassLoader();
//获取环境信息,取出当前操作系统信息
Environment environment = conditionContext.getEnvironment();
//输出所有的系统环境信息
if (environment instanceof StandardEnvironment) {
//转换环境信息
StandardEnvironment standardEnvironment = (StandardEnvironment) environment;
Map<String, Object> map = standardEnvironment.getSystemEnvironment();
for (Map.Entry<String, Object> me : map.entrySet()) {
System.out.println(me.getKey() + " " + me.getValue());
}
}
//获取bean定义信息的注册器
BeanDefinitionRegistry registry = conditionContext.getRegistry();
//获取当前系统的名称
String os = environment.getProperty("os.name");
//判断是否包含Windows规则
if (os.contains("Windows")) {
//需要注册到ioc容器
return true;
}
//不需要注册到ioc容器
return false;
}
}
WindowsCondition.java
public class LinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
//获取环境信息,取出当前操作系统信息
Environment environment = conditionContext.getEnvironment();
//获取当前系统的名称
String os = environment.getProperty("os.name");
//判断是否包含Windows规则
if (os.contains("Linux")) {
//需要注册到ioc容器
return true;
}
//不需要注册到ioc容器
return false;
}
}
LinuxCondition.java
public class JdbcConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
/**
* 创建windows系统测试的数据源
* @return
*/
@Bean("dataSource")
@Conditional(WindowsCondition.class)
public DataSource createWindowsDataSource(){
//创建数据源
DriverManagerDataSource dataSource = new DriverManagerDataSource();
//给数据源填充属性
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
System.out.println("windows");
return dataSource;
}
/**
* 创建linux系统测试的数据源
* @return
*/
@Bean("dataSource")
@Conditional(LinuxCondition.class)
public DataSource createLinuxDataSource(@Value("${linuxjdbc.driver}") String linuxDriver,
@Value("${linuxjdbc.url}") String linuxUrl,
@Value("${linuxjdbc.username}") String linuxUsername,
@Value("${linuxjdbc.password}") String linuxPassword){
//创建数据源
DriverManagerDataSource dataSource = new DriverManagerDataSource();
//给数据源填充属性
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
System.out.println("linux");
return dataSource;
}
}
JdbcConfig.java
public class SpringConditionalTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext("config");
DataSource dataSource = ac.getBean("dataSource", DataSource.class);
System.out.println(dataSource);
}
}
SpringConditionalTest.java
1.4 @Profile
1.4.1 作用
- @Profile注解是spring提供的一个用来标明当前运行环境的注解。我们正常开发的过程中经常遇到的问题是,开发环境是一套环境,测试是一套环境,线上部署又是一套环境。这样从开发到测试再到部署,会对程序中的配置修改多次,尤其是从测试到上线这个环节,让测试的也不敢保证改了哪个配置之后能不能在线上运行。为了解决上面的问题,我们一般会使用一种方法,就是针对不同的环境进行不同的配置,从而在不同的场景中跑我们的程序。
- 而spring中的@Profile注解的作用就体现在这里。在spring使用DI来注入的时候,能够根据当前制定的运行环境来注入相应的bean。最常见的就是使用不同的DataSource了。
1.4.2 基本使用
@Configuration
@PropertySource("classpath:jdbc.properties")
@Import(JdbcConfig.class)
public class SpringConfiguration {
}
SpringConfiguration.java
public class JdbcConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
/**
* 开发环境的数据源
* @return
*/
@Bean("dataSource")
@Profile("dev")
public DruidDataSource createDevDataSource(){
//创建数据源对象
DruidDataSource dataSource = new DruidDataSource();
//设置属性
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
//开发环境的最大活动连接数
dataSource.setMaxActive(5);
return dataSource;
}
/**
* 测试环境的数据源
* @return
*/
@Bean("dataSource")
@Profile("test")
public DruidDataSource createTestDataSource(){
//创建数据源对象
DruidDataSource dataSource = new DruidDataSource();
//设置属性
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
//测试环境的最大活动连接数
dataSource.setMaxActive(50);
return dataSource;
}
/**
* 生产环境的数据源
* @return
*/
@Bean("dataSource")
@Profile("pro")
public DruidDataSource createProDataSource(){
//创建数据源对象
DruidDataSource dataSource = new DruidDataSource();
//设置属性
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
//生产环境的最大活动连接数
dataSource.setMaxActive(150);
return dataSource;
}
}
JdbcConfig.java
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
@ActiveProfiles("pro")
public class SpringProfileTest {
@Autowired
private DruidDataSource druidDataSource;
@Test
public void testDataSource(){
System.out.println(druidDataSource.getMaxActive());
}
}
SpringProfileTest.java
2 用于创建对象的注解
2.1 @Component和三个衍生注解@Controller @Service @Repository
2.1.1 作用
- 这四个注解都是用于修饰类的。是用于把当前类创建一个对象,存入spring的ioc容器中。在实例化时,首选默认无参构造函数。同时支持带参构造,前提是构造函数的参数依赖必须要有值。否则抛异常
2.1.2 属性
- value:用于指定存入容器时bean的id。当不指定时,默认值为当前类的名称
3 用于注入数据的注解
3.1 @Autowired
3.1.1 作用
- 自动按照类型注入。当ioc容器中有且只有一个类型匹配时可以直接注入成功。当有超过一个匹配时,则使用变量名称(写在方法上就是方法名称)作为bean的id,在符合类型的bean中再次匹配,能匹配上就可以注入成功。当匹配不上时,是否报错要看required属性的取值。
3.1.2 属性
- required:是否必须注入成功。默认值是true,表示必须注入成功。当取值为true的时候,注入不成功会报错。
3.1.3 @Qualifier
3.1.3.1 作用
- 当使用自动按类型注入时,遇到有多个类型匹配的时候,就可以使用此注解来明确注入哪个bean对象。注意它通常情况下都必须配置@Autowired注解一起使用
3.1.3.2 属性
- value:用于指定bean的唯一标识。
3.1.3.3 问题
- 必须要从容器中自动注入JdbcTemplate,否则在运行时会报空指针异常
@Autowired
private JdbcTemplate jdbcTemplate;
- 自动注入的前提是JdbcTemplate已经注入到容器中
@Bean("jdbcTemplate")
public JdbcTemplate createJdbcTemplate(DataSource dataSource){
return new JdbcTemplate(dataSource);
}
- 如果容器中有多个JdbcTemplate,优先找到与变量名与Bean的id相匹配的注入,如果不匹配,则会因为在容器中找到多个JdbcTemplate而报错
- 可以在@Autowired后使用@Qualifier指定唯一标识解决多个JdbcTemplate的问题
3.3 @Resource
3.3.1 作用
- 此注解来源于JSR规范(Java Specification Requests),其作用是找到依赖的组件注入到应用来,它利用了JNDI(Java Naming and Directory Interface Java命名目录接口 J2EE规范之一)技术查找所需的资源。
默认情况下,即所有属性都不指定,它默认按照byType的方式装配bean对象。如果指定了name,没有指定type,则采用byName。如果没有指定name,而是指定了type,则按照byType装配bean对象。当byName和byType都指定了,两个都会校验,有任何一个不符合条件就会报错。
3.3.2 属性
- name:资源的JNDI名称。在spring的注入时,指定bean的唯一标识。
- type:指定bean的类型。
- lookup:引用指向的资源的名称。它可以使用全局JNDI名称链接到任何兼容的资源。
- authenticationType:指定资源的身份验证类型。它只能为任何受支持类型的连接工厂的资源指定此选项,而不能为其他类型的资源指定此选项。
- shareable:指定此资源是否可以在此组件和其他组件之间共享。
- mappedName:指定资源的映射名称。
- description:指定资源的描述。
3.3.3 基本使用
@Service
public class AccountServiceImpl implements AccountService {
@Resource(type = AccountDao.class,name = "accountDaoImplOne")
private AccountDao accountDao;
@Override
public void save() {
accountDao.save();
}
}
AccountServiceImpl.java
@Repository
public class AccountDaoImplOne implements AccountDao {
@Override
public void save() {
System.out.println("1保存");
}
}
AccountDaoImplOne.java
@Repository
public class AccountDaoImplTwo implements AccountDao {
@Override
public void save() {
System.out.println("2保存");
}
}
AccountDaoImplTwo.java
public class SpringJsrTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext("config");
AccountService accountService = ac.getBean("accountServiceImpl", AccountService.class);
accountService.save();
}
}
SpringJsrTest.java
3.4 @Inject
3.4.1 作用
- 它也是用于建立依赖关系的。和@Resource和@Autowired的作用是一样,没有属性
3.4.2 基本使用
@Service
public class AccountServiceImpl implements AccountService {
//@Resource(type = AccountDao.class,name = "accountDaoImplOne")
@Inject
@Named("accountDaoImplOne")
private AccountDao accountDao;
@Override
public void save() {
accountDao.save();
}
}
AccountServiceImpl.java
3.5 几种注入数据注解的区别
- @Autowired:来源于spring框架自身。
默认是byType自动装配,当配合了@Qualifier注解之后,由@Qualifier实现byName装配。它有一个required属性,用于指定是否必须注入成功。 - @Resource:来源于JSR-250规范。
在没有指定name属性时是byType自动装配,当指定了name属性之后,采用byName方式自动装配。 - @Inject:来源于JSR-330规范。(JSR330是Jcp给出的官方标准反向依赖注入规范。)
它不支持任何属性,但是可以配合@Qualifier或者@Primary注解使用。
同时,它默认是采用byType装配,当指定了JSR-330规范中的@Named注解之后,变成byName装配。
3.6 @Primary
3.6.1 作用
- 用于指定bean的注入优先级。被@Primary修饰的bean对象优先注入,无属性
3.6.2 基本使用
- 如果注入时没有指定名称,则优先使用@Primary注解的AccountDaoImplTwo
@Repository
@Primary
public class AccountDaoImplTwo implements AccountDao {
@Override
public void save() {
System.out.println("2保存");
}
}
AccountDaoImplTwo.java
4 和生命周期以及作用范围相关的注解
4.1 @Scope
4.1.1 作用
- 用于指定bean对象的作用范围。
4.1.2 属性
- value:指定作用范围的取值。在注解中默认值是""。
但是在spring初始化容器时,会借助ConfigurableBeanFactory接口中的类成员:
String SCOPE_SINGLETON = “singleton”; - scopeName:它和value的作用是一样的。
- proxyMode:它是指定bean对象的代理方式的。指定的是ScopedProxyMode枚举的值
DEFAULT:默认值。(就是NO)
NO:不使用代理。
INTERFACES:使用JDK官方的基于接口的代理。
TARGET_CLASS:使用CGLIB基于目标类的子类创建代理对象。
4.2 @PostConstruct
4.2.1 作用
- 用于指定bean对象的初始化方法。无属性
4.3 @PreDestroy
4.3.1 作用
- 用于指定bean对象的销毁方法。无属性
4.3.2 基本使用
@Component
public class LogUtil {
public LogUtil(){
System.out.println("对象创建");
}
@PostConstruct
public void init(){
System.out.println("对象初始化");
}
@PreDestroy
public void destroy(){
System.out.println("对象销毁");
}
}
LogUtil.java
public class SpringLifecycleTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext("config");
LogUtil logUtil = ac.getBean("logUtil", LogUtil.class);
System.out.println(logUtil);
//关闭容器
ac.close();
}
}
SpringLifecycleTest.java
- 对于单例会随着Spring容器的关闭销毁单例对象