![cover](https://img-blog.csdnimg.cn/9f04acb0b6754df3beec8547e8fd0295.png)
Spring框架
Spring框架后续还会更新
目录
Application Context获取bean对象的方式
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![](https://img-blog.csdnimg.cn/08256948bc5349dd99e9f1d9166655ca.png)
@Autowired注入歧义问题
-
若注入的类型对应的实例存在多个,则编译报错/运行时报错,解决办法有2种:
-
注入对象的名称对应某个beanId,则可以注入成功
-
@Autowired的注入机制:
-
先根据类型匹配
-
若没有匹配类型,报错
-
有匹配类型,对应的实例有多个,则自动根据name匹配
-
-
-
-
添加@Qualifer注解指定beanId
-
自动装配和消除歧义 - 2
使用 @Qualifer 注解
@Qualifer也适用于方法注入和属性注入。
组件的名称应该不体现实现细节,除非同一个接口有2个相同的实现(如上所示)
自动装配和消除歧义 - 3
-
自动装配的规则
-
查找与所需类型匹配的唯一bean
-
如果提供了 @Qualifer 则使用
-
尝试根据名称查找匹配的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都应该可以使用组件扫描进行配置,并且是事务性的
-
更多推荐
所有评论(0)