资料来源:https://www.bilibili.com/video/BV1hE411o7w7?p=144

一、设计模式-工厂模式

工厂模式是我们最常用的实例化对象模式了,它是用工厂中的方法代替new创建对象的一种设计模式
我们以Mybatis的SqlSession接口为例,它有一个实现类DefaultSqlSession,如果我们要创建该接口的实例对象:SqlSession sqlSession=new DefaultSqlSession();
可是,实际情况是,通常我们都要在创建SqlSession实例时做点初始化的工作,比如解析XML,封装连接数据库的信息等等。
在创建对象时,如果有一些不得不做的初始化操作时,我们首先到的是,可以使用构造函数,这样生成实例就写成:SqlSession sqlSession=new DefaultSqlSession(传入配置文件的路径)
但是,如果创建sqlSession实例时所做的初始化工作不是像赋值这样简单的事,可能是很长一段代码,如果也写入构造函数中,那你的代码很难看了(就需要Refactor重构)。
为什么说代码很难看,初学者可能没有这种感觉,我们分析如下,初始化工作如果是很长一段代码,说明要做的工作很多,将很多工作装入一个方法中,相当于将很多鸡蛋放在一个篮子里,是很危险的,这也是有悖于Java面向对象的原则,面向对象的封装(Encapsulation)和分派(Delegation)告诉我们,尽量将长的代码分派“切割”成每段,将每段再“封装”起来(减少段和段之间耦合联系性),这样,就会将风险分散,以后如果需要修改,只要更改每段,不会再发生牵一动百的事情。
所以,Mybatis框架在使用时为我们提供了SqlSessionFactory工厂类,通过方法获取到SqlSession对象。同时方法有很多重载,用于实现不同的需求。这个方法就是openSession(),它支持传入Connection参数来保证连接的一致性;支持传入true|false来保证事务的提交时机等等。

二、IOC和DI

1、IOC-Inversion of Control

  1. 它的含义为:**控制反转**。它不是一个技术,而是一种思想。其作用是用于削减代码间的耦合。它的实现思想就是**利用了工厂设计模式,把创建对象代码从具体类中剥离出去,交由工厂来完成,从而降低代码间的依赖关系**

耦合有如下分类:
(1)内容耦合。当一个模块直接修改或操作另一个模块的数据时,或一个模块不通过正常入口而转入另一个模块时,这样的耦合被称为内容耦合。内容耦合是最高程度的耦合,应该避免使用之
(2)公共耦合。两个或两个以上的模块共同引用一个全局数据项,这种耦合被称为公共耦合。在具有大量公共耦合的结构中,确定究竟是哪个模块给全局变量赋了一个特定的值是十分困难的。
(3)外部耦合。一组模块都访问同一全局简单变量而不是同一全局数据结构,而且不是通过参数表传递该全局变量的信息,则称之为外部耦合。
(4)控制耦合。一个模块通过接口向另一个模块传递一个控制信号,接受信号的模块根据信号值而进行适当的动作,这种耦合被称为控制耦合。
(5)标记耦合。若一个模块A通过接口向两个模块B和C传递一个公共参数,那么称模块B和C之间存在一个标记耦合。
(6)数据耦合。模块之间通过参数来传递数据,那么被称为数据耦合。数据耦合是最低的一种耦合形式,系统中一般都存在这种类型的耦合(不需要解耦的部分),因为为了完成一些有意义的功能,往往需要将某些模块的输出数据作为另一些模块的输入数据。
(7)非直接耦合。两个模块之间没有直接关系,它们之间的联系完全是通过主模块的控制和调用来实现

为什么要解耦:
耦合是影响软件复杂程度和设计质量的一个重要因素,在设计上我们应采用以下原则:如果模块间必须存在耦合,就尽量使用数据耦合,少用控制耦合,限制公共耦合的范围,尽量避免使用内容耦合

2、DI-Dependency Injection

  1. 它的全称是依赖注入。**是spring框架核心ioc的具体实现**。<br /> 举例说明:我们的程序在编写时,通过控制反转,把对象的创建交给了spring,但是代码中不可能出现没有依赖的情况。**ioc解耦只是降低他们的依赖关系,但不会消除**。例如:我们的业务层仍会调用持久层的方法。那这种业务层和持久层的依赖关系,在使用spring之后,就让spring来维护了。<br /> 简单的说,就是坐等框架把持久层对象传入业务层,而不用我们自己去获取。

三、Spring注解驱动开发入门

1、写在最前

spring在2.5版本引入了注解配置的支持,同时从Spring 3版本开始,Spring JavaConfig项目提供的许多特性成为核心Spring框架的一部分。因此可以使用Java而不是XML文件来定义应用程序类外部的bean。在这里面官方文档为我们提供了四个基本注解@Configuration,@Bean,@Import,@DependsOn
image.png

2、注解驱动入门案例介绍

  1. 1. 需求:
  2. 实现保存一条数据到数据库。
  3. 2. 表结构:
  4. create table account(
  5. id int primary key auto_increment,
  6. name varchar(50),
  7. money double(7,2)
  8. );
  9. 3. 要求:
  10. 使用spring框架中的JdbcTemplateDriverManagerDataSource
  11. 使用纯注解配置springioc

3、案例实现

3.1、导入坐标

  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework</groupId>
  4. <artifactId>spring-context</artifactId>
  5. <version>5.1.6.RELEASE</version>
  6. </dependency>
  7. <dependency>
  8. <groupId>org.springframework</groupId>
  9. <artifactId>spring-jdbc</artifactId>
  10. <version>5.1.6.RELEASE</version>
  11. </dependency>
  12. <dependency>
  13. <groupId>mysql</groupId>
  14. <artifactId>mysql-connector-java</artifactId>
  15. <version>5.1.45</version>
  16. </dependency>
  17. </dependencies>

3.2、编写配置类

  1. /**
  2. * spring的配置类,相当于applicationContext.xml的作用
  3. */
  4. @Import(JdbcConfig.class) // 建立JdbcConfig与主配置文件SpringConfiguration的关联
  5. @PropertySource(value="classpath:jdbc.properties") // 读取配置文件内容,也可以放到JdbcConfig上
  6. @Configuration // spring的配置类,相当于applicationContext.xml的作用
  7. //@Component
  8. public class SpringConfiguration {
  9. }
  1. /**
  2. * 和Jdbc操作相关的配置类
  3. */
  4. // 读取配置文件内容,也可以放到SpringConfiguration配置类上
  5. // @PropertySource(value="classpath:jdbc.properties")
  6. public class JdbcConfig {
  7. @Value("${jdbc.driver}")
  8. private String driver;
  9. @Value("${jdbc.url}")
  10. private String url;
  11. @Value("${jdbc.username}")
  12. private String username;
  13. @Value("${jdbc.password}")
  14. private String password;
  15. /**
  16. * 创建JdbcTemplate对象,并且存入Ioc容器
  17. * @return
  18. */
  19. @Bean("jdbcTemplate") // @Bean:把当前对象的返回值存入Ioc容器,指定bean的ID为jdbcTemplate,若不指定则默认为方法名
  20. // @Autowired:自动按照类型注入,会从ioc容器中找到跟DataSource参数类型相匹配的对象。此处可以不用标注@Autowired,会自动在容器中注入
  21. public JdbcTemplate createJdbcTemplate(@Autowired DataSource dataSource){
  22. return new JdbcTemplate(dataSource);
  23. }
  24. /**
  25. * 创建数据源,并存入ioc容器
  26. * @return
  27. */
  28. @Bean
  29. public DataSource createDataSource(){
  30. // 1.创建Spring的内置数据源对象
  31. DriverManagerDataSource dataSource = new DriverManagerDataSource();
  32. // 2.给数据源提供必要的参数
  33. dataSource.setDriverClassName(driver);
  34. dataSource.setUrl(url);
  35. dataSource.setUsername(username);
  36. dataSource.setPassword(password);
  37. // 3.返回
  38. return dataSource;
  39. }
  40. }

3.3、编写配置文件

  1. jdbc.driver=com.mysql.jdbc.Driver
  2. jdbc.url=jdbc:mysql://localhost:3306/spring_day01
  3. jdbc.username=root
  4. jdbc.password=1234

3.4、编写测试类

  1. /**
  2. * 测试spring注解驱动开发的入门案例
  3. */
  4. public class SpringAnnotationTest {
  5. public static void main(String[] args) {
  6. // 1.创建容器(基于注解的创建方式)
  7. // AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
  8. // 首先扫描config包下存在@Configuration注解的类
  9. AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext("config");
  10. // 2.根据bean的Id获取对象
  11. JdbcTemplate jdbcTemplate = ac.getBean("jdbcTemplate", JdbcTemplate.class);
  12. // 3.执行操作
  13. jdbcTemplate.update("insert into account(name, money) values(?, ?)","zzz",54321);
  14. }
  15. }

四、IOC的常用注解分析

1、用于注解驱动的注解

1.1、@Configuration

1.1.1、源码
  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Component // 将Configuration修饰的对象存在IOC容器
  5. public @interface Configuration {
  6. @AliasFor(annotation = Component.class)
  7. String value() default ""; // 支持设置Bean的ID
  8. }

1.1.2、说明

1、作用:
它是在spring3.0版本之后加入的。此注解是spring支持注解驱动开发的一个标志。表明当前类是spring的一个配置类,作用是替代spring的applicationContext.xml。但其本质就是@Component注解,被此注解修饰的类,也会被存入spring的IOC容器。
2、属性:
value:用于存入spring的Ioc容器中Bean的id。
3、使用场景:
在注解驱动开发时,用于编写配置的类,通常可以使用此注解。一般情况下,我们的配置也会分为主从配置,@Configuration一般出现在主配置类上。例如,入门案例中的SpringConfiguration类上。值得注意的是,如果我们在注解驱动开发时,构建ioc容器使用的是传入字节码的构造函数,此注解可以省略。但是如果传入的是一个包,此注解则不能省略。
image.png

  1. // 方式一:传入要扫描包的方式,需要在主配置类上加@Configuration
  2. // 若主配置类上没有加@Configuration("springConfiguration"),则报错:NoSuchBeanDefinitionException: No bean named 'springConfiguration' available
  3. // 1.创建容器
  4. AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext("config");
  5. // 2.获取对象
  6. // SpringConfiguration springConfiguration = ac.getBean(SpringConfiguration.class);
  7. SpringConfiguration springConfiguration = ac.getBean("springConfiguration", SpringConfiguration.class);
  8. // 3.输出结果
  9. System.out.println(springConfiguration); // config.SpringConfiguration$$EnhancerBySpringCGLIB$$55ee2d39@3439f68d
  10. // 方式二:传入被注解的类的字节码的方式,不需要在主配置类上加@Configuration
  11. // 1.创建容器
  12. AnnotationConfigApplicationContext ac1 = new AnnotationConfigApplicationContext(SpringConfiguration.class);
  13. // 2.获取对象
  14. SpringConfiguration springConfiguration1 = ac1.getBean(SpringConfiguration.class);
  15. // 3.输出结果
  16. System.out.println(springConfiguration1); // config.SpringConfiguration$$EnhancerBySpringCGLIB$$55ee2d39@74294adb

1.1.3、示例
  1. 在注解驱动的入门案例中,由于没有了applicationContext.xml,就没法在xml中配置spring创建容器要扫描的包了。那么,我们自己写的一些类,通过注解配置到ioc容器中也无法实现了。此时就可以使用此注解来代替spring的配置文件。
  1. /**
  2. * spring的配置类
  3. * 用于替代xml配置
  4. */
  5. @Configuration
  6. @Import(JdbcConfig.class)
  7. @PropertySource("classpath:jdbc.properties")
  8. public class SpringConfiguration {
  9. }

1.2、@ComponentScan

1.2.1、源码
  1. @Retention(RetentionPolicy.RUNTIME)
  2. @Target(ElementType.TYPE)
  3. @Documented
  4. @Repeatable(ComponentScans.class)
  5. public @interface ComponentScan {
  6. @AliasFor("basePackages")
  7. String[] value() default {};
  8. @AliasFor("value")
  9. String[] basePackages() default {};
  10. Class<?>[] basePackageClasses() default {};
  11. Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
  12. Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
  13. ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
  14. String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;
  15. boolean useDefaultFilters() default true;
  16. Filter[] includeFilters() default {};
  17. Filter[] excludeFilters() default {};
  18. boolean lazyInit() default false;
  19. @Retention(RetentionPolicy.RUNTIME)
  20. @Target({})
  21. @interface Filter {
  22. FilterType type() default FilterType.ANNOTATION;
  23. @AliasFor("classes")
  24. Class<?>[] value() default {};
  25. @AliasFor("value")
  26. Class<?>[] classes() default {};
  27. String[] pattern() default {};
  28. }
  29. }

1.2.2、说明

1、作用:
用于指定创建容器时要扫描的包。该注解在指定扫描的位置时,可以指定包名,也可以指定扫描的类。同时支持定义扫描规则,例如包含哪些或者排除哪些。同时,它还支持自定义Bean的命名规则
2、属性:
1)value:
用于指定要扫描的包。当指定了包的名称之后,spring会扫描指定的包及其子包下的所有类。
2)basePackages:
它和value作用是一样的。
image.png
3)basePackageClasses
指定具体要扫描的类的字节码。
image.png
4)nameGenrator:
指定扫描bean对象存入容器时的命名规则。详情请参考第五章第4小节的BeanNameGenerator及其实现类。
image.png

  1. /**
  2. * 自定义BeanName生成规则
  3. */
  4. public class CustomeBeanNameGenerator implements BeanNameGenerator {
  5. @Override
  6. public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
  7. //定义Bean的名称
  8. String beanName = null;
  9. //1.判断当前bean的定义信息是否是注解的
  10. if(definition instanceof AnnotatedBeanDefinition){
  11. //2.把definition转成注解的bean定义信息
  12. AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition)definition;
  13. //2.获取注解Bean定义的元信息
  14. AnnotationMetadata annotationMetadata = annotatedBeanDefinition.getMetadata();
  15. //3.获取定义信息中的所有注解
  16. Set<String> types = annotationMetadata.getAnnotationTypes();
  17. //4.遍历types集合
  18. for(String type : types){
  19. //5.得到注解的属性
  20. AnnotationAttributes attributes = AnnotationAttributes.fromMap(annotationMetadata.getAnnotationAttributes(type, false));
  21. //6.判断attributes是否为null,同时必须是@Component及其衍生注解
  22. if (attributes != null && isStereotypeWithNameValue(type, annotationMetadata.getMetaAnnotationTypes(type), attributes)) {
  23. //7.获取value属性的值
  24. Object value = attributes.get("value");
  25. //8.判断value属性是否为String类型
  26. if (value instanceof String) {
  27. String strVal = (String) value;
  28. //9.判断value属性是否有值
  29. if (StringUtils.hasLength(strVal)) {
  30. if (beanName != null && !strVal.equals(beanName)) {
  31. throw new IllegalStateException("Stereotype annotations suggest inconsistent " +
  32. "component names: '" + beanName + "' versus '" + strVal + "'");
  33. }
  34. beanName = strVal;
  35. }
  36. }
  37. }
  38. }
  39. }
  40. return beanName != null ? "my"+beanName : "my"+buildDefaultBeanName(definition);
  41. }
  42. private static final String COMPONENT_ANNOTATION_CLASSNAME = "org.springframework.stereotype.Component";
  43. private boolean isStereotypeWithNameValue(String annotationType,
  44. Set<String> metaAnnotationTypes, @Nullable Map<String, Object> attributes) {
  45. boolean isStereotype = annotationType.equals(COMPONENT_ANNOTATION_CLASSNAME) ||
  46. metaAnnotationTypes.contains(COMPONENT_ANNOTATION_CLASSNAME) ||
  47. annotationType.equals("javax.annotation.ManagedBean") ||
  48. annotationType.equals("javax.inject.Named");
  49. return (isStereotype && attributes != null && attributes.containsKey("value"));
  50. }
  51. private String buildDefaultBeanName(BeanDefinition definition) {
  52. String beanClassName = definition.getBeanClassName();
  53. Assert.state(beanClassName != null, "No bean class name set");
  54. String shortClassName = ClassUtils.getShortName(beanClassName);
  55. return Introspector.decapitalize(shortClassName);
  56. }
  57. }
  1. 5scopeResolver:(基本不用)<br /> 用于处理并转换检测到的Bean的作用范围。<br /> 6soperdProxy:(基本不用)<br /> 用于指定bean生成时的代理方式。默认是Default,则不使用代理。详情请参考第五章第5小节ScopedProxyMode枚举。<br /> 7resourcePattern:<br /> 用于指定符合组件检测条件的类文件,默认是包扫描下的 **/*.class<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/22523384/1637385234615-79da3d29-8f52-4c96-ae2c-191dd712f3e2.png#clientId=u3a62f1d3-507b-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=200&id=u2c75357e&margin=%5Bobject%20Object%5D&name=image.png&originHeight=200&originWidth=1116&originalType=binary&ratio=1&rotation=0&showTitle=false&size=28131&status=done&style=none&taskId=ube6bbbbe-56e3-45f5-8bff-5082322cedf&title=&width=1116)<br /> 8)useDefaultFilters:<br /> 是否对带有@Component @Repository @Service @Controller注解的类开启检测,默认是开启的。<br /> 9)includeFilters:<br /> 自定义组件扫描的过滤规则,用以扫描组件。不会排除其他的,比如设置了包含service,不会排除controller<br /> FilterType有5种类型:<br /> ANNOTATION:注解类型,默认<br /> ASSIGNABLE_TYPE:指定固定类<br /> ASPECTJ:ASPECTJ类型<br /> REGEX:正则表达式<br /> CUSTOM:自定义类型<br /> 详细用法请参考第五章第6小节自定义组件扫描过滤规则<br /> 10)excludeFilters:<br /> 自定义组件扫描的排除规则。<br /> 11)lazyInit:<br /> 组件扫描时是否采用懒加载 ,默认不开启。<br />3、使用场景:<br /> 在注解驱动开发时,我们自己编写的类都使用注解的方式进行配置,要想让spring添加到ioc容器中,就需要使用此注解来实现组件的扫描。<br />4、细节:<br /> 在spring4.3版本之后还加入了一个@ComponentScans的注解,该注解就是支持配置多个@ComponentScan。

1.2.3、示例(P12-P17)
  1. 在入门案例中,如果我们加入了dao或者记录日志的工具类,这些使用了@Component或其衍生注解配置的bean,要想让他们进入ioc容器,就少不了使用@ComponentScan
  1. @Repository("accountDao")
  2. public class AccountDaoImpl implements AccountDao{
  3. //持久层开发(此处没有考虑mybatis代理dao方式或者其他持久层技术,因为不希望和其他框架技术关联)
  4. }
  5. /**
  6. * spring的配置类
  7. * 用于替代xml配置
  8. */
  9. @Configuration
  10. @Import(JdbcConfig.class)
  11. @PropertySource("classpath:jdbc.properties")
  12. @ComponentScan("com.itheima")
  13. public class SpringConfiguration {
  14. }

1.3、@Bean

1.3.1、源码
  1. @Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @interface Bean {
  5. @AliasFor("name")
  6. String[] value() default {};
  7. @AliasFor("value")
  8. String[] name() default {};
  9. @Deprecated
  10. Autowire autowire() default Autowire.NO;
  11. boolean autowireCandidate() default true;
  12. String initMethod() default "";
  13. String destroyMethod() default AbstractBeanDefinition.INFER_METHOD;
  14. }

1.3.2、说明

1、作用:
它写在方法上,表示把当前方法的返回值存入spring的ioc容器。
同时还可以出现在注解上,作为元注解来使用。

  1. // 自定义Bean注解
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Target({ElementType.METHOD})
  4. @Bean(name = "abcd") // 初始化bean的标识
  5. public @interface MyBean {
  6. }

2、属性:
1)name:
用于指定存入spring容器中bean的标识。支持指定多个标识。当不指定该属性时,默认值是当前方法的名称
2)value:
此属性是在4.3.3版本之后加入的。它和name属性的作用是一样的。
3)autowireCandidate:
用于指定是否支持自动按类型注入到其他bean中。只影响@Autowired注解的使用。不影响@Resource注解注入。默认值为true,意为允许使用自动按类型注入。
image.png
image.png
4)initMethod:
用于指定初始化方法(通常使用编程的方式进行初始化,所以不用initMethod)
5)destroyMethod:
用于指定销毁方法(通常没使用)
3、使用场景:
通常情况下,在基于注解的配置中,我们用于把一个类存入spring的ioc容器中,首先考虑的是使用@Component以及他的衍生注解。但是如果遇到要存入容器的Bean对象不是我们写的类,此时无法在类上添加@Component注解,这时就需要此注解了。
4、示例:
例如,在我们配置JdbcTemplate使用Spring内置数据源DriverManagerDataSource时,数据源类是spring-jdbc这个jar包中类,此时我们无法编辑,在上面加注解,此时就可以使用@Bean注解配置

总结:@Component只适用于我们新建的类,@Bean适用于第三方提供的类
5、@Bean写到注解上,自定义注解,也会存到IOC容器中
image.png
6、使用细节
当@Bean没有指定value或name时,仍然会存入IOC容器,在存入容器的时候是使用方法名称作为Bean的标识。若出现方法重载,则按照定义的顺序,只会将最后一个@Bean放入IOC容器创建对象。若返回值是void,执行会报错
image.png
image.png

1.3.3、示例代码
  1. @Configuration
  2. public class SpringConfiguration {
  3. // 创建一个数据源对象
  4. // @Bean:把当前对象的返回值存入容器
  5. @Bean(value="dataSource1", autowireCandidate = false)
  6. public DataSource createDataSource(){
  7. // 1.创建数据源对象
  8. DriverManagerDataSource dataSource = new DriverManagerDataSource();
  9. // 2.调用初始化方法(省略)
  10. // 3.返回bean对象
  11. return dataSource;
  12. }
  13. // 方式一:@Resource注入DataSource,针对autowireCandidate = false,不受影响
  14. @Resource(name="dataSource1")
  15. private DataSource dataSource;
  16. // 创建JdbcTemplate对象
  17. @MyBean // 自定义Bean注解
  18. public JdbcTemplate createJdbcTemplate(){
  19. System.out.println("执行了没有参数的createJdbcTemplate");
  20. return new JdbcTemplate(dataSource);
  21. }
  22. // 方式二:@Autowired注入DataSource,若autowireCandidate = false,则报错
  23. // 创建JdbcTemplate对象
  24. @Bean //(value="jdbcTemplate")
  25. public JdbcTemplate createJdbcTemplate(@Autowired DataSource dataSource){
  26. System.out.println("创建了JdbcTemplate");
  27. return new JdbcTemplate(dataSource);
  28. }
  29. }

1.4、@Import

1.4.1、源码
  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @interface Import {
  5. Class<?>[] value();
  6. }

image.png
image.png
image.png

1.4.2、说明

1、作用:
该注解是写在类上的,通常都是和注解驱动的配置类一起使用的。其作用是引入其他的配置类。使用了此注解之后,可以使我们的注解驱动开发和早期xml配置一样,分别配置不同的内容,使配置更加清晰。同时指定了此注解之后,被引入的类上可以不再使用@Configuration、@Component等注解
image.png
2、属性:
value:
用于指定其他配置类的字节码。它支持指定多个配置类。
关于ImportSelector和ImportBeanDefinitionRegistrar请参考第五章第7小节@Import注解的高级分析
3、使用场景:
当我们在使用注解驱动开发时,由于配置项过多,如果都写在一个类里面,配置结构和内容将杂乱不堪,此时使用此注解可以把配置项进行分门别类进行配置。
4、使用细节
Bean的唯一标识是类的全限定类名。
image.png

1.4.3、示例(P24-P28)
  1. 在入门案例中,我们使用了SpringConfiguration做为主配置类,而连接数据库相关的配置被分配到了JdbcConfig配置类中,此时使用在SpringConfiguration类上使用@Import注解把JdbcConfig导入进来就可以了。
  1. /**
  2. * spring的配置类
  3. * 用于替代xml配置
  4. */
  5. @Configuration
  6. @Import(JdbcConfig.class)
  7. @PropertySource("classpath:jdbc.properties")
  8. @ComponentScan("com.itheima")
  9. public class SpringConfiguration {
  10. }
  1. // 连接数据库的配置
  2. public class JdbcConfig {
  3. @Value("${jdbc.driver}")
  4. private String driver;
  5. @Value("${jdbc.url}")
  6. private String url;
  7. @Value("${jdbc.username}")
  8. private String username;
  9. @Value("${jdbc.password}")
  10. private String password;
  11. @Bean("jdbcTemplate")
  12. public JdbcTemplate createJdbcTemplate(DataSource dataSource){
  13. return new JdbcTemplate(dataSource);
  14. }
  15. @Bean("dataSource")
  16. public DataSource createDataSource(){
  17. DriverManagerDataSource dataSource = new DriverManagerDataSource();
  18. dataSource.setDriverClassName(driver);
  19. dataSource.setUrl(url);
  20. dataSource.setUsername(username);
  21. dataSource.setPassword(password);
  22. return dataSource;
  23. }
  24. }

1.5、@PropertySource

1.5.1、源码
  1. @Retention(RetentionPolicy.RUNTIME)
  2. @Documented
  3. @Repeatable(PropertySources.class)
  4. public @interface PropertySource {
  5. String name() default "";
  6. String[] value();
  7. boolean ignoreResourceNotFound() default false;
  8. String encoding() default "";
  9. Class<? extends PropertySourceFactory> factory() default PropertySourceFactory.class;
  10. }

1.5.2、说明

1、作用:
用于指定读取资源文件的位置。注意,它不仅支持properties,也支持xml文件,并且通过YAML解析器,配合自定义PropertySourceFactory实现解析yml配置文件(详情请参考第五章第8小节自定义PropertySourceFactory实现YAML文件解析)。
2、属性:
1)name:
指定资源的名称。如果没有指定,将根据基础资源描述生成。
2)value:
指定资源的位置。可以是类路径,也可以是文件路径。
3)ignoreResourceNotFound:
指定是否忽略资源文件有没有,默认是false,也就是说当资源文件不存在时spring启动将会报错。
4)encoding:
指定解析资源文件使用的字符集。当有中文的时候,需要指定中文的字符集。
5)factory: P31(执行过程分析)
指定读取对应资源文件的工厂类,默认的是PropertySourceFactory。
3、使用场景:
我们实际开发中,使用注解驱动后,xml配置文件就没有了,此时一些配置如果直接写在类中,会造成和java源码的紧密耦合,修改起来不方法。此时一些配置可以使用properties或者yml来配置就变得很灵活方便。
image.png

1.5.3、示例(P31-P34)

在入门案例中,我们连接数据库的信息如果直接写到JdbcConfig类中,当需要修改时,就面临修改源码的问题,此时使用@PropertySource和SpringEL表达式,就可以把配置放到properties文件中了。

  1. // jdbc.properties
  2. jdbc.driver=com.mysql.jdbc.Driver
  3. jdbc.url=jdbc:mysql://localhost:3306/spring_ioc
  4. jdbc.username=root
  5. jdbc.password=1234
  1. // spring的主配置类,用于替代xml配置
  2. @Configuration
  3. @Import(JdbcConfig.class)
  4. @PropertySource("classpath:jdbc.xml") // 用于指定配置文件的位置
  5. //@PropertySource("classpath:jdbc.properties")
  6. //@PropertySource("file:///I:/spring_ioc/spring_annotation_demo/anno08_propertysource/src/main/resources/jdbc.properties") //基于文件协议的方式
  7. public class SpringConfiguration {
  8. // 针对Spring4.3版本之前的properties文件解析器,需要注册资源文件解析器的bean到ioc容器中
  9. // 在4.3版本之后使用PropertySourceFactory接口的唯一实现类:DefaultPropertySourceFactory(已自动注册)
  10. //@Bean
  11. //public static PropertySourcesPlaceholderConfigurer createPropertySourcesPlaceholderConfigurer(){
  12. //return new PropertySourcesPlaceholderConfigurer();
  13. //}
  14. }
  1. // JDBC的配置类
  2. public class JdbcConfig {
  3. // 需要在主配置文件中指定资源配置文件的路径,否则根据${jdbc.driver}找不到任何内容
  4. @Value("${jdbc.driver}")
  5. private String driver;
  6. @Value("${jdbc.url}")
  7. private String url;
  8. @Value("${jdbc.username}")
  9. private String username;
  10. @Value("${jdbc.password}")
  11. private String password;
  12. @Bean(name="dataSource")
  13. public DataSource createDataSource(){
  14. System.out.println("驱动类是:" + driver);
  15. //1.创建Spring内置数据源
  16. DriverManagerDataSource dataSource = new DriverManagerDataSource();
  17. //2.给数据源填充属性
  18. dataSource.setDriverClassName(driver);
  19. dataSource.setUrl(url);
  20. dataSource.setUsername(username);
  21. dataSource.setPassword(password);
  22. //3.返回
  23. return dataSource;
  24. }
  25. }
  1. //1.创建容器
  2. AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext("config");
  3. //2.获取对象
  4. DataSource dataSource = ac.getBean("dataSource",DataSource.class);
  5. //3.获取连接
  6. Connection connection = dataSource.getConnection();
  7. System.out.println("连接成功.....");
  8. connection.close();

2、注解驱动开发之注入时机和设定注入条件的注解

2.1、@DependsOn

2.1.1、源码
  1. @Target({ElementType.TYPE, ElementType.METHOD})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @interface DependsOn {
  5. String[] value() default {};
  6. }

2.1.2、说明

1、作用:
用于指定在创建某个类时,其依赖的bean对象先创建。spring中没有特定bean的加载顺序,使用此注解则可指定bean的加载顺序。(在基于注解配置中,是按照类中方法的书写顺序决定的。默认情况下在扫描包config时,会按照包名、类名按序逐个扫描里面的类,注入容器中创建对象)
image.png
image.png
2、属性:
value: 用于指定bean的唯一标识。被指定的bean会在当前bean创建之前加载。
3、使用场景:
在观察者模式中,分为事件,事件源和监听器。一般情况下,我们的监听器负责监听事件源,当事件源触发了事件之后,监听器就要捕获,并且做出相应的处理。以此为前提,我们肯定希望监听器的创建时间在事件源之前,此时就可以使用此注解。

2.1.3、示例
  1. package com.itheima.event;
  2. // 监听器
  3. @Component
  4. public class EventZListener {
  5. public EventZListener(){
  6. System.out.println("监听器创建了");
  7. }
  8. }
  1. package com.itheima.event;
  2. // 事件源对象
  3. @Component
  4. @DependsOn("eventZListener") // 监听器EventZListener类的唯一标识:eventZListener(被@Component修饰后的结果)
  5. public class EventSource {
  6. public EventSource(){
  7. System.out.println("事件源对象创建了");
  8. }
  9. }
  1. package config;
  2. @Configuration
  3. @ComponentScan("com.itheima") // 扫描com.itheima包下所有的文件,注入容器
  4. public class SpringConfiguration {
  5. }
  1. public class SpringDependsOnTest {
  2. public static void main(String[] args) {
  3. //1.获取容器
  4. AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext("config");
  5. //2.启动容器
  6. ac.start();
  7. }
  8. }
  9. 执行结果:
  10. 监听器创建了
  11. 事件源对象创建了

2.2、@Lazy

2.2.1、源码
  1. @Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.FIELD})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @interface Lazy {
  5. boolean value() default true;
  6. }

2.2.2、说明

1、作用:
用于指定单例bean对象的创建时机。在没有使用此注解时,单例bean的生命周期与容器相同(创建容器时创建Bean对象)。但是当使用了此注解之后,单例对象的创建时机变成了第一次使用时创建,后续也不再创建了。注意:这不是延迟加载思想(因为不是每次使用时都创建,只是第一次创建的时机改变了)。该注解不适于多例bean,多例bean是每次使用时创建新的Bean对象
image.png
image.png
2、属性:
value:指定是否采用延迟加载。默认值为true,表示开启。
3、使用场景:
在实际开发中,当我们的Bean是单例对象时,并不是每个都需要一开始都加载到ioc容器之中,有些对象可以在真正使用的时候再加载,当有此需求时,即可使用此注解。值得注意的是,此注解只对单例bean对象起作用,当指定了@Scope注解的prototype取值后,此注解不起作用。

2.2.3、示例
  1. // 模拟记录日志的类
  2. @Component
  3. //@Scope("prototype") // 设置多例模式
  4. // 只对单例模式有用,设置延迟加载,将一些不需要立即注入容器的类(或不常用的类)实现延迟加载,避免容器中的类过多
  5. @Lazy
  6. public class LogUtil {
  7. public LogUtil(){
  8. System.out.println("LogUtil对象创建了");
  9. }
  10. public void printLog(){
  11. System.out.println("模拟记录日志");
  12. }
  13. }
  1. // spring的配置类
  2. @Configuration
  3. @ComponentScan("com.itheima")
  4. public class SpringConfiguration {
  5. }
  1. // 1.创建容器
  2. AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext("config");
  3. // 2.获取对象
  4. LogUtil logUtil = ac.getBean("logUtil",LogUtil.class);
  5. // 3.执行方法
  6. logUtil.printLog();
  7. LogUtil logUtil2 = ac.getBean("logUtil",LogUtil.class);
  8. System.out.println(logUtil == logUtil2);

image.png

2.3、@Conditional

2.3.1、源码
  1. @Target({ElementType.TYPE, ElementType.METHOD})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @interface Conditional {
  5. /**
  6. * All {@link Condition Conditions} that must {@linkplain Condition#matches match}
  7. * in order for the component to be registered.
  8. */
  9. Class<? extends Condition>[] value();
  10. }

2.3.2、说明

1、作用:
它的作用是根据条件选择注入的bean对象。
2、属性:
value:用于提供一个Condition接口的实现类,实现类中需要编写具体代码实现注入的条件。
3、使用场景:
当我们在开发时,可能会使用多平台来测试,例如我们的测试数据库分别部署到了linux和windows两个操作系统上面,现在根据我们的工程运行环境选择连接的数据库。此时就可以使用此注解。同时基于此注解引出的@Profile注解,就是根据不同的环境,加载不同的配置信息,详情请参考第五章第9小节@Profile的使用。

2.3.3、示例
  1. // 连接数据库的配置
  2. public class JdbcConfig {
  3. @Value("${jdbc.driver}")
  4. private String driver;
  5. @Value("${jdbc.url}")
  6. private String url;
  7. @Value("${jdbc.username}")
  8. private String username;
  9. @Value("${jdbc.password}")
  10. private String password;
  11. // linux系统注入的数据源
  12. @Bean("dataSource")
  13. @Conditional(LinuxCondition.class)
  14. public DataSource createLinuxDataSource(@Value("${linux.driver}") String lDriver,
  15. @Value("${linux.url}")String lUrl,
  16. @Value("${linux.username}")String lUsername,
  17. @Value("${linux.password}")String lPassword){
  18. DriverManagerDataSource dataSource = new DriverManagerDataSource(lUrl,lUsername,lPassword);
  19. dataSource.setDriverClassName(lDriver);
  20. System.out.println(lUrl);
  21. return dataSource;
  22. }
  23. /**
  24. * windows系统注入的数据源
  25. * @return
  26. */
  27. @Bean("dataSource")
  28. @Conditional(WindowsCondition.class)
  29. public DataSource createWindowsDataSource(){
  30. DriverManagerDataSource dataSource = new DriverManagerDataSource();
  31. dataSource.setDriverClassName(driver);
  32. dataSource.setUrl(url);
  33. dataSource.setUsername(username);
  34. dataSource.setPassword(password);
  35. System.out.println(url);
  36. return dataSource;
  37. }
  38. }
  1. package com.itheima.condition;
  2. /**
  3. * 自定义规则
  4. */
  5. public class LinuxCondition implements Condition {
  6. @Override
  7. public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
  8. //1.获取环境信息(为了取出当前操作系统是windows还是linux)
  9. Environment environment = context.getEnvironment();
  10. //2.获取当前系统的名称
  11. String osName = environment.getProperty("os.name");
  12. //3.判断是否包含windos规则
  13. osName="os.Linux";
  14. if(osName.contains("Linux")){
  15. //需要注册到ioc容器中
  16. return true;
  17. }
  18. //不需要注册到Ioc容器中
  19. return false;
  20. }
  21. }
  1. package com.itheima.condition;
  2. /**
  3. * 自定义注册bean的条件,windows操作系统注入
  4. */
  5. public class WindowsCondition implements Condition {
  6. /**
  7. * 是否注册到ioc容器中的核心方法
  8. * @param context ioc上下文对象
  9. * @param metadata
  10. * @return 是true的时候,表示注册到ioc容器中,否则不注册
  11. */
  12. @Override
  13. public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
  14. //1.获取ioc使用的BeanFactory对象
  15. ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
  16. //2.获取类加载器
  17. ClassLoader classLoader = context.getClassLoader();
  18. //3.获取环境信息(为了取出当前操作系统是windows还是linux)
  19. Environment environment = context.getEnvironment();
  20. //输出所有的系统环境信息
  21. if(environment instanceof StandardEnvironment){
  22. //转换环境信息
  23. StandardEnvironment standardEnvironment = (StandardEnvironment)environment;
  24. Map<String,Object> map = standardEnvironment.getSystemProperties();
  25. for(Map.Entry<String,Object> me : map.entrySet()){
  26. System.out.println(me.getKey()+","+me.getValue());
  27. }
  28. }
  29. //4.获取bean定义信息的注册器
  30. BeanDefinitionRegistry registry = context.getRegistry();
  31. //5.获取当前系统的名称
  32. String osName = environment.getProperty("os.name");
  33. osName="os.Linux";
  34. //6.判断是否包含windos规则
  35. if(osName.contains("Windows")){
  36. //需要注册到ioc容器中
  37. return true;
  38. }
  39. //不需要注册到Ioc容器中
  40. return false;
  41. }
  42. }
  1. linux.driver=com.mysql.jdbc.Driver
  2. linux.url=jdbc:mysql://localhost:3306/ssm
  3. linux.username=root
  4. linux.password=1234
  5. -------------------------------------------------------------------------------------
  6. jdbc.driver=com.mysql.jdbc.Driver
  7. jdbc.url=jdbc:mysql://localhost:3306/spring_day01
  8. jdbc.username=root
  9. jdbc.password=1234

3、用于创建对象的注解

3.1、@Component和三个衍生注解

3.1.1、源码
  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Indexed
  5. public @interface Component {
  6. // 原生注解,其他注解(controller、service、Repository)都衍生于它,常用于工具类上
  7. String value() default "";
  8. }
  1. @Target({ElementType.TYPE})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Component
  5. public @interface Controller { // 常用于表现层上
  6. @AliasFor(annotation = Component.class)
  7. String value() default "";
  8. }
  1. @Target({ElementType.TYPE})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Component
  5. public @interface Service { // 常用于业务层上
  6. @AliasFor(annotation = Component.class)
  7. String value() default "";
  8. }
  1. @Target({ElementType.TYPE})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Component
  5. public @interface Repository { // 常用于持久层上
  6. @AliasFor(annotation = Component.class)
  7. String value() default "";
  8. }

3.1.2、说明

1、作用:
这四个注解都是用于修饰类的。是用于把当前类创建一个对象,存入spring的ioc容器中。在实例化时,首选默认无参构造函数。同时支持带参构造,前提是构造函数的参数依赖必须要有值。否则抛异常
2、属性:
value:用于指定存入容器时bean的id。当不指定时,默认值为当前类的名称。
image.png
image.png
3、使用场景:
当我们需要把自己编写的类注入到Ioc容器中,就可以使用以上四个注解实现。以上四个注解中@Component注解通常用在非三层对象中。而@Controller,@Service,@Repository三个注解一般是针对三层对象使用的,提供更加精确的语义化配置。
需要注意的是,spring在注解驱动开发时,要求必须先接管类对象,然后会处理类中的属性和方法。如果类没有被spring接管,那么里面的属性和方法上的注解都不会被解析

  1. package config;
  2. /**
  3. * spring的配置类
  4. */
  5. @Configuration
  6. @ComponentScan("com.itheima")
  7. @Import(JdbcConfig.class)
  8. @PropertySource(value = "classpath:jdbc.yml", factory = YmlPropertySourceFactory.class)
  9. public class SpringConfiguration{
  10. }
  1. package support.factory;
  2. /**
  3. * 自定义yml文件解析的工厂类
  4. */
  5. public class YmlPropertySourceFactory implements PropertySourceFactory {
  6. @Override
  7. public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
  8. //1.创建yaml文件解析工厂
  9. YamlPropertiesFactoryBean factoryBean = new YamlPropertiesFactoryBean();
  10. //2.设置要解析的内容
  11. factoryBean.setResources(resource.getResource());
  12. //3.把资源解析成properties文件
  13. Properties properties = factoryBean.getObject();
  14. //4.返回spring提供的PropertySource对象
  15. return (name != null ? new PropertiesPropertySource(name,properties)
  16. : new PropertiesPropertySource(resource.getResource().getFilename(),properties));
  17. }
  18. }
  1. package config;
  2. /**
  3. * 和jdbc操作相关的配置:
  4. * JdbcTemplate创建
  5. * DataSource创建
  6. */
  7. public class JdbcConfig {
  8. @Value("${jdbc.driver}")
  9. private String driver;
  10. @Value("${jdbc.url}")
  11. private String url;
  12. @Value("${jdbc.username}")
  13. private String username;
  14. @Value("${jdbc.password}")
  15. private String password;
  16. @Bean("jdbcTemplate1")
  17. public JdbcTemplate createJdbcTemplateOne(@Autowired DataSource dataSource){ // 可以不用加@Autowired
  18. return new JdbcTemplate(dataSource);
  19. }
  20. @Bean("jdbcTemplate2")
  21. public JdbcTemplate createJdbcTemplateTwo(@Autowired DataSource dataSource){
  22. return new JdbcTemplate(dataSource);
  23. }
  24. @Bean("dataSource")
  25. public DataSource createDataSource(){
  26. //1.创建数据源对象
  27. DriverManagerDataSource dataSource = new DriverManagerDataSource();
  28. //2.给属性赋值
  29. dataSource.setDriverClassName(driver);
  30. dataSource.setUrl(url);
  31. dataSource.setUsername(username);
  32. dataSource.setPassword(password);
  33. //3.返回
  34. return dataSource;
  35. }
  36. }

3.1.3、示例

正确的方式1:使用默认构造函数

  1. /**
  2. * 用于记录系统日志
  3. * @author 黑马程序员
  4. * @Company http://www.itheima.com
  5. */
  6. @Component
  7. public class LogUtil {
  8. /**
  9. * 默认无参构造函数
  10. */
  11. public LogUtil(){
  12. }
  13. //可以使用aop思想实现系统日志的记录
  14. }

正确的方式2:在构造函数中注入一个已经在容器中的bean对象。

  1. // 此处只是举例:使用JdbcTemplate作为持久层中的操作数据库对象
  2. @Repository("userDao")
  3. public class UserDaoImpl implements UserDao{
  4. private JdbcTemplate jdbcTemplate ;
  5. /**
  6. * 此时要求容器中必须有JdbcTemplate对象
  7. * @param jdbcTemplate
  8. */
  9. public UserDaoImpl(JdbcTemplate jdbcTemplate){
  10. this.jdbcTemplate = jdbcTemplate;
  11. }
  12. }

正确的方式3:在构造函数中注入一个读取配置文件获取到的值。

  1. // 用于记录系统日志
  2. @Component
  3. public class LogUtil {
  4. /**
  5. * 构造时,注入日志级别
  6. * @param logLevel
  7. */
  8. public LogUtil(@Value("${log.level}")String logLevel){
  9. System.out.println(logLevel);
  10. }
  11. //可以使用aop思想实现系统日志的记录
  12. }

错误的方式:由于logLevel没有值,所以运行会报错。

  1. // 用于记录系统日志
  2. @Component
  3. public class LogUtil {
  4. /**
  5. * 构造时,注入日志级别
  6. * @param logLevel
  7. */
  8. public LogUtil(String logLevel){
  9. System.out.println(logLevel);
  10. }
  11. //可以使用aop思想实现系统日志的记录
  12. }

4、用于注入数据的注解

4.1、@Autowired

4.1.1、源码
  1. @Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @interface Autowired {
  5. boolean required() default true;
  6. }

4.1.2、说明

1、作用:
自动按照类型注入。当ioc容器中有且只有一个类型匹配时可以直接注入成功。当有超过一个匹配时,则使用变量名称(写在方法上就是方法名称)作为bean的id,在符合类型的bean中再次匹配,能匹配上就可以注入成功。当匹配不上时,是否报错要看required属性的取值。
1)若容器中不存在Bean对象,同时设置@Autowired(required = true),则报错
image.png
2)若容器中存在多个Bean对象,使用变量名称作为bean的id在容器中再次匹配,若能匹配到相同的,则也会正常执行。若未匹配到,则报错:系统中不存在唯一的对象
image.png
image.png
2、属性:
required:是否必须注入成功。默认值是true,表示必须注入成功。当取值为true的时候,注入不成功会报错
3、使用场景:
此注解的使用场景非常之多,在实际开发中应用广泛。通常情况下我们自己写的类中注入依赖bean对象时,都可以采用此注解。

4.1.3、示例
  1. /**
  2. * 此处只是举例:使用Jdbc作为持久层中的操作数据库对象
  3. */
  4. @Repository("accountDao")
  5. public class AccountDaoImpl implements AccountDao{
  6. @Autowired
  7. private JdbcTemplate jdbcTemplate ;
  8. }

4.2、@Qualifier

4.2.1、源码
  1. @Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Inherited
  4. @Documented
  5. public @interface Qualifier {
  6. String value() default "";
  7. }

4.2.2、说明

1、作用:
当使用自动按类型注入时,遇到有多个类型匹配的时候,就可以使用此注解来明确注入哪个bean对象。注意它通常情况下都必须配置@Autowired注解一起使用
image.png
2、属性:
value:用于指定bean的唯一标识。
3、使用场景:
在我们的项目开发中,很多时候都会用到消息队列,我们以ActiveMQ为例。当和spring整合之后,Spring框架提供了一个JmsTemplate对象,它既可以用于发送点对点模型消息也可以发送主题模型消息。如果项目中两种消息模型都用上了,那么针对不同的代码,将会注入不同的JmsTemplate,而容器中出现两个之后,就可以使用此注解注入。当然不用也可以,我们只需要把要注入的变量名称改为和要注入的bean的id一致即可。

4.3、@Resource

4.3.1、源码
  1. @Target({TYPE, FIELD, METHOD})
  2. @Retention(RUNTIME)
  3. public @interface Resource {
  4. String name() default "";
  5. String lookup() default "";
  6. Class<?> type() default java.lang.Object.class;
  7. enum AuthenticationType {
  8. CONTAINER,
  9. APPLICATION
  10. }
  11. AuthenticationType authenticationType() default AuthenticationType.CONTAINER;
  12. boolean shareable() default true;
  13. String mappedName() default "";
  14. String description() default "";
  15. }

4.3.2、说明

1、作用:
此注解来源于JSR规范(Java Specification Requests),其作用是找到依赖的组件注入到应用来,它利用了JNDI(Java Naming and Directory Interface Java命名目录接口 J2EE规范之一)技术查找所需的资源
1)默认情况下,即所有属性都不指定,它默认按照byType的方式装配bean对象
2)如果没有指定name,而是指定了type,则按照byType装配bean对象。即跟默认情况一致
image.png
3)如果指定了name,没有指定type,则采用byName。即根据bean的唯一标识进行匹配
image.png
4)当byName和byType都指定了,两个都会校验,有任何一个不符合条件就会报错。
image.png
2、属性:
1)name:
资源的JNDI名称。在spring的注入时,指定bean的唯一标识。
2)type:
指定bean的类型。
3)lookup:
引用指向的资源的名称。它可以使用全局JNDI名称链接到任何兼容的资源。
4)authenticationType:
指定资源的身份验证类型。它只能为任何受支持类型的连接工厂的资源指定此选项,而不能为其他类型的资源指定此选项。
5)shareable:
指定此资源是否可以在此组件和其他组件之间共享。
6)mappedName:
指定资源的映射名称。
7)description:
指定资源的描述。
3、使用场景:
当我们某个类的依赖bean在ioc容器中存在多个的时候,可以使用此注解指定特定的bean对象注入。当然我们也可以使用@Autowired配合@Qualifier注入。

4.4、@Value

4.4.1、源码
  1. @Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @interface Value {
  5. String value();
  6. }

4.4.2、说明

1、作用:
用于注入基本类型和String类型的数据。它支持spring的EL表达式,可以通过${} 的方式获取配置文件中的数据。配置文件支持properties、xml和yml文件。
2、属性:
value:指定注入的数据或者spring的el表达式。
3、使用场景:
在实际开发中,像连接数据库的配置,发送邮件的配置等等,都可以使用配置文件配置起来。此时读取配置文件就可以借助spring的el表达式读取。

4.4.3、示例
  1. /**
  2. * 连接数据库的配置
  3. */
  4. public class JdbcConfig {
  5. @Value("${jdbc.driver}")
  6. private String driver;
  7. @Value("${jdbc.url}")
  8. private String url;
  9. @Value("${jdbc.username}")
  10. private String username;
  11. @Value("${jdbc.password}")
  12. private String password;
  13. /**
  14. * windows系统注入的数据源
  15. * @return
  16. */
  17. @Bean("dataSource")
  18. public DataSource createWindowsDataSource(){
  19. DriverManagerDataSource dataSource = new DriverManagerDataSource();
  20. dataSource.setDriverClassName(driver);
  21. dataSource.setUrl(url);
  22. dataSource.setUsername(username);
  23. dataSource.setPassword(password);
  24. return dataSource;
  25. }
  26. }
  1. jdbc.driver=com.mysql.jdbc.Driver
  2. jdbc.url=jdbc:mysql://localhost:3306/spring_day01
  3. jdbc.username=root
  4. jdbc.password=1234

4.5、@Inject

4.5.1、源码
  1. @Target({ METHOD, CONSTRUCTOR, FIELD })
  2. @Retention(RUNTIME)
  3. @Documented
  4. public @interface Inject {
  5. }

4.5.2、说明

1、作用:
它也是用于建立依赖关系的。和@Resource和@Autowired的作用是一样。在使用之前需要先导入坐标:

  1. <!-- https://mvnrepository.com/artifact/javax.inject/javax.inject -->
  2. <dependency>
  3. <groupId>javax.inject</groupId>
  4. <artifactId>javax.inject</artifactId>
  5. <version>1</version>
  6. </dependency>

但是他们之前也有区别:
1)@Autowired:来源于spring框架自身。
默认是byType自动装配,当配合了@Qualifier注解之后,由@Qualifier实现byName装配。它有一个required属性,用于指定是否必须注入成功。
2)@Resource:来源于JSR-250规范。
在没有指定name属性时是byType自动装配,当指定了name属性之后,采用byName方式自动装配;此时若根据名称装配不存在,也不会再根据类型装配
3)@Inject:来源于JSR-330规范。(JSR330是Jcp给出的官方标准反向依赖注入规范)
它不支持任何属性,但是可以配合@Qualifier或者@Primary注解使用。
同时,它默认是采用byType装配,当指定了JSR-330规范中的@Named注解之后,变成byName装配
4)@Qualifier不能单独使用,必须配合@Autowired或@Inject使用
5)@Named也不能单独使用,也必须配合@Autowired或@Inject使用
image.png

  1. @Service("accountService") //若没有指定id,则存入容器中的id为短类名accountServiceImpl
  2. public class AccountServiceImpl implements AccountService {
  3. // 根据类型进行匹配,可以根据接口或接口的实现类匹配
  4. // 若采用接口进行匹配,需保证当前接口只有一个实现类注入到Ioc容器中,否则报错找不到唯一的Bean对象
  5. // @Resource 默认按照类型AccountDao进行匹配,若AccountDao的多个实现类均注入到Ioc容器中,会报错找不到唯一的Bean对象
  6. // @Resource(type = AccountDao.class)
  7. // @Resource(type = AccountDaoImplOne.class, name = "accountDaoImplOne")
  8. @Inject //默认按照类型AccountDao进行匹配,若AccountDao的多个实现类均注入到Ioc容器中,会报错找不到唯一的Bean对象
  9. @Named("accountDaoImplOne") // 按照名称accountDaoImplOne进行匹配,与@Inject或@Autowired配合使用
  10. @Autowired //默认按照类型AccountDao进行匹配,若AccountDao的多个实现类均注入到Ioc容器中,会报错找不到唯一的Bean对象
  11. @Qualifier("accountDaoImplOne") // 按照名称accountDaoImplOne进行匹配,与@Inject或@Autowired配合使用
  12. private AccountDao accountDao;
  13. @Override
  14. public void saveAccount() {
  15. accountDao.saveAccount();
  16. }
  17. }
  1. @Repository //若没有指定id,则存入容器中的id为短类名accountDaoImplOne
  2. public class AccountDaoImplOne implements AccountDao {
  3. @Override
  4. public void saveAccount() {
  5. System.out.println("AccountDaoImplOne 保存了账户");
  6. }
  7. }
  8. @Repository //若没有指定id,则存入容器中的id为短类名accountDaoImplTwo
  9. public class AccountDaoImplTwo implements AccountDao {
  10. @Override
  11. public void saveAccount() {
  12. System.out.println("AccountDaoImplTwo 保存了账户");
  13. }
  14. }


2、属性:无
3、使用场景:
在使用@Autowired注解的地方,都可以替换成@Inject。它也可以出现在方法上,构造函数上和字段上,但是需要注意的是:因为JRE无法决定构造方法注入的优先级,所以规范中规定类中只能有一个构造方法带@Inject注解。

4.5.3、示例
  1. /**
  2. * 第一种写法: 写在字段上
  3. * 此处只是举例:使用Jdbc作为持久层中的操作数据库对象
  4. */
  5. @Repository("userDao")
  6. public class UserDaoImpl implements UserDao{
  7. @Inject
  8. private JdbcTemplate jdbcTemplate ;
  9. }
  10. /**
  11. * 第二种写法:写在构造函数上
  12. */
  13. @Repository("accountDao")
  14. public class AccountDaoImpl implements AccountDao{
  15. private JdbcTemplate jdbcTemplate;
  16. /**
  17. * 此时要求容器中必须有JdbcTemplate对象
  18. * @param jdbcTemplate
  19. */
  20. @Inject
  21. public AccountDaoImpl(JdbcTemplate jdbcTemplate){
  22. this.jdbcTemplate = jdbcTemplate;
  23. }
  24. }
  25. /**
  26. * 第三种写法:配合@Named注解使用
  27. */
  28. @Repository("accountDao")
  29. public class AccountDaoImpl implements AccountDao{
  30. @Inject
  31. @Named("jdbcTemplate")
  32. private JdbcTemplate jdbcTemplate ;
  33. }

4.6、@Primary

4.6.1、源码
  1. @Target({ElementType.TYPE, ElementType.METHOD})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @interface Primary {
  5. }

4.6.2、说明

1、作用:用于指定bean的注入优先级。被@Primary修饰的bean对象优先注入
2、属性:无
3、使用场景:
当我们的依赖对象,有多个存在时,@Autowired注解已经无法完成功能,此时我们首先想到的是@Qualifier注解指定依赖bean的id。但是此时就产生了,无论有多少个bean,每次都会使用指定的bean注入。但是当我们使用@Primary,表示优先使用被@Primary注解的bean,但是当不存在时还会使用其他的

4.6.3、示例

image.png

5、和生命周期以及作用范围相关的注解

5.1、@Scope

5.1.1、源码
  1. @Target({ElementType.TYPE, ElementType.METHOD})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @interface Scope {
  5. @AliasFor("scopeName")
  6. String value() default "";
  7. @AliasFor("value")
  8. String scopeName() default "";
  9. ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;
  10. }

5.1.2、说明

1、作用:用于指定bean对象的作用范围
2、属性:
1)value:
指定作用范围的取值。在注解中默认值是””。
但是在spring初始化容器时,会借助ConfigurableBeanFactory接口中的类成员:

  1. String SCOPE_SINGLETON = "singleton";

2)scopeName:
它和value的作用是一样的。
3)proxyMode:
它是指定bean对象的代理方式的。指定的是ScopedProxyMode枚举的值
DEFAULT:默认值。(就是NO)
NO:不使用代理。
INTERFACES:使用JDK官方的基于接口的代理。
TARGET_CLASS:使用CGLIB基于目标类的子类创建代理对象。
3、使用场景:在实际开发中,我们的bean对象默认都是单例的。通常情况下,被spring管理的bean都使用单例模式来创建。但是也有例外,例如Struts2框架中的Action,由于有模型驱动和OGNL表达式的原因,就必须配置成多例的。

  1. @Component
  2. //@Scope("singleton") //单例模式:创建容器时,会创建对象,且只创建一次,关闭容器会同时销毁对象
  3. @Scope("prototype") //多例模式:创建容器时,不会创建对象;每次使用对象时创建一次对象,关闭容器不会销毁对象。由垃圾回收器来回收
  4. public class LogUtil {
  5. public LogUtil(){
  6. System.out.println("对象创建了");
  7. }
  8. }

5.2、@PostConstruct

5.2.1、源码
  1. @Documented
  2. @Retention (RUNTIME)
  3. @Target(METHOD)
  4. public @interface PostConstruct {
  5. }

5.2.2、说明

1、作用:用于指定bean对象的初始化方法。只能写到方法上
2、属性:无
3、使用场景:在bean对象创建完成后,需要对bean中的成员进行一些初始化的操作时,就可以使用此注解配置一个初始化方法,完成一些初始化的操作。

5.3、@PreDestroy

5.3.1、源码
  1. @Documented
  2. @Retention (RUNTIME)
  3. @Target(METHOD)
  4. public @interface PreDestroy {
  5. }

5.3.2、说明

1、作用:用于指定bean对象的销毁方法。只能写到方法上
2、属性:无
3、使用场景:在bean对象销毁之前,可以进行一些清理操作。

5.3.3、单例模式,关闭容器时销毁对象

image.png

5.3.4、多例模式,关闭容器不会销毁对象,需要等待垃圾回收器来回收

image.png