目录

Spring

Spring容器是如何工作的

定义Spring Bean

关于@Configuration和@Bean的配置说明

在Application Context中访问Bean

Application Context获取bean对象的方式

@Bean方法参数注入

Bean的配置

显式配置Bean - @Bean

隐式配置Bean

​编辑组件扫描和BeanId的命名规则

隐式配置之组件类注解

关于组件扫描-@ComponentScan

组件扫描最佳实践

什么时候使用哪一种?

Bean的作用域

Bean作用域:默认

Bean作用域:prototype

常用Spring作用域

引用:可用的作用域

依赖注入Bean

@Autowired注入

@Autowired注入方式-3种

@Autowired依赖:是必须的还是可选的?

自动装配和消除歧义 - 1​编辑

自动装配和消除歧义 - 2

自动装配和消除歧义 - 3

@Resource

关于@Resource

组合注解和元注解

Stereotype注解(组合注解)

预定义的Stereotype注解

Meta注解(元注解)


Spring

Spring容器是如何工作的

定义Spring Bean

关于@Configuration和@Bean的配置说明

@Configuration
public class ApplicationConfig {
        @Bean 
        public TransferService transferService() {
                 return new TransferServiceImpl(accountRepository()); 
        }
        @Bean 
        public AccountRepository accountRepository() { 
                return new JdbcAccountRepository(dataSource());
        }
        @Bean 

        public DataSource dataSource() {
                BasicDataSource dataSource = new BasicDataSource();                                   
                dataSource.setDriverClassName("org.postgresql.Driver"); 
                dataSource.setUrl("jdbc:postgresql://localhost/transfer"); 
                dataSource.setUsername("transfer-app"); 
                dataSource.setPassword("secret45");
                return dataSource; 
        }
}

在Application Context中访问Bean

Application Context获取bean对象的方式

ApplicationContext context = SpringApplication.run(...);
​
// 通过bean id,需要进行类型转换
TransferService ts1 =  (TransferService) context.getBean("transferService");
​
// 使用带类型参数的方法,以避免类型转换
TransferService ts2 = context.getBean("transferService", TransferService.class);
​
// 如果该类型是唯一的,则是不需要bean id
TransferService ts3 = context.getBean(TransferService.class);

@Bean方法参数注入

  • 定义@Bean方法的参数

    • Spring会找到匹配类型的bean,并注入参数

Bean的配置

显式配置Bean - @Bean

@Configuration
public class TransferModuleConfig {
        @Bean
        public TransferService transferService() {                              
                return new TransferServiceImpl(accountRepository());
        }
        @Bean
        public AccountRepository accountRepository() {                                  
                ...
        }
}

隐式配置Bean

  • 在类的上方添加组件类注解 - @Component

  • 在配置类的上方添加注解@ComponentScan指定扫描的包,从而让Spring到指定包下扫描添加了组件类注解的类,将其装配为Bean 组件扫描

组件扫描和BeanId的命名规则

  • 默认情况下,beanid取决于类名

    • 类名首字母大写,第二个字母小写,则beanid为首字母小写后的名称

    • 否则beanid为类全名

  • 若想自定义beanid,可以在注解中定义 @Component("mybeanid")

隐式配置之组件类注解

  • @Componnent 通用组件注解

  • @Controller 控制器组件

  • @Service 业务层组件

  • @Repository 持久层组件

注意:这四个注解我们一般是按需添加,但是如果分不清,随便添加至少不会出错

关于组件扫描-@ComponentScan

  • 各组件会在启动时被扫描

    • JAR依赖也会被扫描!

    • 如果需要扫描的文件太多,可能导致启动变慢

      • 特别是对于大型应用程序

      • 在最坏的情况下会慢几秒

  • 最佳实践是什么样的?

组件扫描最佳实践

  • 非常糟糕:

@ComponentScan({"org", "com"})
  • 仍然很糟糕:

@ComponentScan("com")
  • 还不错:(平时开发使用)

@ComponentScan("com.bank.app")
  • 最优的:(Spring 推荐使用)

@ComponentScan({"com.bank.app.repository", "com.bank.app.service", "com.bank.app.controller"})

从严谨的角度上来说,我们应该把包配得很细致,从实践的角度上来说,可能不会很在意启动上的一点点你效率,同时在项目中也会出现遗漏的问题,所以可以把包配得略大一些,只要项目中取得名字不会和其他冲突,慢的那点时间是可以接受的

什么时候使用哪一种?

显式配置

  • 优点:

    • 集中在一个(或几个)地方

    • 编写任何你需要的Java代码

    • 可以对配置类进行单元测试

    • 可用于所有类(不只是你自己的类)

  • 缺点:

    • 比注解更加冗余

隐式配置-组件扫描

  • 对你自己的Bean非常友好

  • 优点:

    • 编辑位置单一(就在类中)

    • 允许非常快速的开发

  • 缺点:

    • 配置分布在你的代码库中

      • 难以调试/维护(可以通过规范的命名和规范的分包等手段来让问题变的更小,当达到某种共识时问题可以说不存在了)

    • 只适用于你自己的代码(不可改变的缺点)

显式配置与隐式配置的混合使用

  • 你可以通过多种方式进行混合

  • 常用方法

    • 将隐式配置用于:

      • 你自己的类

  • 将显式配置用于:

    • 没有添加注解的第三方Bean

    • 不能更改的遗留代码

    • 当在单一逻辑位置管理配置是一个很重要的问题的时候

Bean的作用域

Bean作用域:默认

  • 默认的作用域是单例的

当一个数据有单例特征,那同时也拥有常驻内存的效果,所以我们可以随时随地的去调用它

Bean作用域:prototype

  • 作用域“prototype”(原型:可以理解为非单例的)

    • 每次引到bean时都会创建新的实例

@Scope 可以加到方法上面 也可以加到类上面 .默认值是singleton ,可以省略,所以一般用于非单例里面

常用Spring作用域

  • 最常用的作用域有:

singleton只使用1个实例
prototype每次引用到bean时都会创建新的实例
session每个用户会话创建新的实例 - 仅限Web环境
request每个请求创建新的实例 - 仅限Web环境

引用:可用的作用域

作用域描述
singleton持续时间与 ApplicationContext 一致
prototype每次调用 getBean() 都会返回一个新的对象 持续时间与持有引用的时间一致,没有引用后,会被作为垃圾而回收
session持续时间与用户的HTTP会话一致
request持续时间与用户的HTTP请求一致
application持续时间与ServletContext一致(Spring 4.0)
global持续时间与Portlet应用程序中的全局HttpSession一致(Spring 5开始过期
thread持续时间与所在的线程一致,在Spring中已定义,但默认未注册
websocket持续时间与websocket一致(Spring 4.2)
refresh可以超过其application context的重新加载时间 难以确保效果,假设Spring Cloud配置服务器

依赖注入Bean

  • @Autowired注入

  • @Resource注入

@Autowired注入

  • 该注入是Spring框架提供的注解

  • 可用于构造方法注入,set方法注入,字段注入

  • @Autowired默认是根据类型来匹配的

@Autowired注入方式-3种

  • 构造方法注入(Spring推荐的做法,但实际上说一套做一套)

@Autowired // 如果这是唯一的构造方法,该注解是可选的
public TransferServiceImpl(AccountRepository repo) {                              
        this.accountRepository = repo;
}

必须存在唯一的匹配类型的依赖

  • set方法注入(Spring 会为参数赋值)

@Autowired
public void setAccountRepository(AccountRepository repo) {                              
        this.accountRepository = repo;
}

注意:构造方法注入和setter注入均支持多参数注入

Spring会从容器中查找符合的值去为参数赋值

  • 字段注入

@Autowired
private AccountRepository accountRepository;

即便是private字段也可以注入

但是,不易于单元测试

Spring会从容器中查找符合的值去为属性赋值

  • 若需要注入的依赖项不存在,此时会发生什么?

@Autowired依赖:是必须的还是可选的?

  • 默认行为:必须的

@Autowired
public void setAccountRepository(AccountRepository repo) {                              
        this.accountRepository = repo;
}

如果依赖项不存在则会出现异常

  • 使用required属性覆盖默认行为

@Autowired(required=false)
public void setAccountRepository(AccountRepository repo) {                              
        this.accountRepository = repo;
}

仅当依赖项存在时才会注入

  • 若需要注入的依赖类型对应的实例存在多个,会发生什么?

自动装配和消除歧义 - 1

@Autowired注入歧义问题

  • 若注入的类型对应的实例存在多个,则编译报错/运行时报错,解决办法有2种:

    • 注入对象的名称对应某个beanId,则可以注入成功

      • @Autowired的注入机制:

        • 先根据类型匹配

          1. 若没有匹配类型,报错

          2. 有匹配类型,对应的实例有多个,则自动根据name匹配

    • 添加@Qualifer注解指定beanId

自动装配和消除歧义 - 2

使用 @Qualifer 注解

@Qualifer也适用于方法注入和属性注入。

组件的名称应该不体现实现细节,除非同一个接口有2个相同的实现(如上所示)

自动装配和消除歧义 - 3

  • 自动装配的规则

    1. 查找与所需类型匹配的唯一bean

    2. 如果提供了 @Qualifer 则使用

    3. 尝试根据名称查找匹配的bean

  • 示例

    • 我们有多个Queue bean

    • Spring会查找id被设置为“ack”的bean

@Resource

  • 来自JSR-250,能被EJB 3.0和Spring支持

    • 根据名称,而不是类型来识别依赖项

      • 名称是Spring的Bean名称

      • @Autowired是根据类型来匹配的

  • 仅支持Setter和字段注入

1.Setter注入

@Resource(name="jdbcAccountRepository")
public void setAccountRepository(AccountRepository repo) {                              
        this.accountRepository = repo;
}

2. 字段注入

@Resource(name="jdbcAccountRepository")
private AccountRepository accountRepository;

关于@Resource

  • 当没有提供名称时:

    • 根据属性/字段的名称进行推断

    • 若找不到匹配的name,则直接回退到根据类型注入

  • 示例

    • 查找名为accountRepository的Bean

      • 因为方法名称是setAccountRepository

    • 然后,查找类型为AccountRepository的Bean

@Resource
public void setAccountRepository(AccountRepository repo) {                              
        this.accountRepository = repo;
}

@Autowired:先根据类型,再根据名称

@Resource:先根据名称,再根据类型

组合注解和元注解

  • Stereotype注解

  • Meta注解

Stereotype注解(组合注解)

  • 组件扫描会检查自身就带有@Component注解的那些注解

    • 也就是所谓的Stereotype注解

@Service注解是Spring框架的一部分。

预定义的Stereotype注解

  • Spring框架的Stereotype注解

注意:其它Spring项目(Spring Web-Service,Spring Integration)提供了它们自己的Stereotype注解。

Meta注解(元注解)

  • 可添加在其它注解上的注解

    • 例如:所有的业务Bean都应该可以使用组件扫描进行配置,并且是事务性的

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐