Spring 注解驱动开发-IOC (精华版)
Spring 注解驱动开发-IOC
组件注册
- 包扫描+组件标注注解(@ComponetScan/ @Controller/@Service/@Repository/@Component)
- @Bean[导入的第三方包里面的组件]
- @Import[快速给容器中导入一个组件]
- @Import(要导入到容器中的组件);容器中就会自动注册这个组件,id默认是全类名。
- ImportSelector:返回需要导入的组件的全类名数组;
- ImportBeanDefinitionRegistrar:手动注册bean到容器中
- 使用Spring提供的FactoryBean(工厂Bean);
@Configraution,@Bean
//配置类==配置文件
@Configuration //告诉Spring这是要给配置类
public class MainConfig {
//给容器中注册一个Bean,类型为返回值类型,id默认是方法名作为id
@Bean("person")
public Person person(){
return new Person("zzp",18);
}
}
@ComponentScan
@Configuration
@ComponentScan(basePackages = "com.zzp",
useDefaultFilters = false,includeFilters =
@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Service.class})
// excludeFilters =
// @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Service.class, Controller.class})
)
//@ComponentScan value:指定要扫描的包
//excludeFilters = @Filter(..)指定扫描的时候按什么规则排除哪些组件
//includeFilters = @Filter(..) 指定扫描的时候只扫哪些组件 前提需要加上 userDefaultFiters = false
public class MainConfig {
}
自定义TypFilter
MainConfig.java
@Configuration
@ComponentScan(basePackages = "com.zzp",
useDefaultFilters = false,includeFilters =
@ComponentScan.Filter(type = FilterType.CUSTOM,classes = {MyFilterType.class})
)
//@ComponentScan value:指定要扫描的包
//excludeFilters = @Filter(..)指定扫描的时候按什么规则排除哪些组件
//includeFilters = @Filter(..) 指定扫描的时候只扫哪些组件 前提需要加上 userDefaultFiters = false
//FilterType.ANNOTATION 根据注解类型来过滤
//FilterType.ASSIGNABLE_TYPE 根据类的类型来过滤
//FilterType.ASPECTJ 根据表达式来过滤
//FilterType.REGEX 根据正则来过滤
//FilterType.CUSTOM 自定义过滤规则
public class MainConfig {
}
MyFilterType.java
public class MyFilterType implements TypeFilter {
/**
*
* @param metadataReader 读取到的当前正在扫描类的信息
* @param metadataReaderFactory 可以获取到其他任何类的信息
* @return
* @throws IOException
*/
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
//获取当前扫描到的类注解的信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
//获取当前扫描到的类的类信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
//获取当前扫描到的类的类资源(类的路径)
Resource resource = metadataReader.getResource();
String className = classMetadata.getClassName();
if (className.contains("er")){
return true; //返回true 合格
}
return false; //false 不合格
}
}
@Scope
@Configuration
public class MainConfig2 {
@Scope //调整作用域
// PROTOTYPE 多实例的:ioc容器启动并不会去调用方法创建对象放在容器中。
// 每次获取的时候才会创建对象
// SINGLETON 单例的(默认值): ioc容器启动会调用方法创建对象到ioc容器中,以后每次获取就直接从容器中拿
// REQUEST 同一次请求创建一个实例
// SESSION 同一个session创建一个实例
@Bean
public Person person(){
return new Person("zzp",18);
}
}
@Lazy
主要针对于单实例bean, 默认在容器启动时创建对象;
懒加载:启动容器不创建对象,第一次使用(获取)Bean的时候创建对象,并进行初始化。
@Configuration
public class MainConfig2 {
@Scope
@Lazy
@Bean
public Person person(){
return new Person("zzp",18);
}
}
@Conditional
MainConfig2.java
//满足当前条件,该配置类才会生效
@Conditional(WindowsCondition.class)
@Configuration
public class MainConfig2 {
@Scope
@Lazy
@Bean
public Person person(){
return new Person("zzp",18);
}
//满足当前条件,该bean才会生效
@Conditional(WindowsCondition.class)
@Bean("bill")
public Person person01(){
return new Person("bill Gates",62);
}
@Conditional(LinuxCondition.class)
@Bean("linux")
public Person person02(){
return new Person("linux",49);
}
}
自定义Condition
WindowsCondition.java
public class WindowsCondition implements Condition {
/**
*
* @param context 当前使用的上下文
* @param metadata 注释信息
* @return
*/
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//获取到ioc容器使用的beanFactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//获取到类加载器
ClassLoader classLoader = context.getClassLoader();
//获取到bean定义的注册类
BeanDefinitionRegistry registry = context.getRegistry();
//获取到当前环境信息
Environment environment = context.getEnvironment();
String property = environment.getProperty("os.name");
if (property.contains("Windows")){
return true;
}
return false;
}
}
LinuxCondition.java
public class LinuxCondition implements Condition {
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
String property = environment.getProperty("os.name");
if (property.contains("linux")){
return true;
}
return false;
}
}
@Import
@Configuration
//将Blue导入到ioc容器中,id为全类名com.zzp.domain.Blue
//ImportSelector:返回需要导入的组件的全类名数组
//@Import({Blue.class, Red.class})
//ImportBeanDefinitionRegistrar手动注册进容器中
@Import({MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})
public class MainConfig2 {
}
自定义ImportSelector
MyImportSelector.java
public class MyImportSelector implements ImportSelector {
//返回值就是导入到容器中的组件全类名
//importingClassMetadata :当前标注@Import注解的类的所有注解信息
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.zzp.domain.Blue","com.zzp.domain.Red"};
}
}
自定义ImportBeanDefinitionRegistrar
MyImportBeanDefinitionRegistrar.java
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
*
* @param importingClassMetadata 当前标注@Import的注解的类的所有注解信息
* @param registry BeanDefinition注册类
* 把所有需要添加到容器的bean 调用registry.registerBeanDefinition()方法手工注册进去
*
*/
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//判断BeanDefinition注册类 中是否包含 id为com.zzp.domain.Blue 的bean
boolean blue = registry.containsBeanDefinition("com.zzp.domain.Blue");
boolean red = registry.containsBeanDefinition("com.zzp.domain.Red");
if (blue && red){
//手工注册进ioc容器中
//1.实例化一个beanDefinition
RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class);
//2.并注册进 BeanDefinition注册类中
registry.registerBeanDefinition("rainBow",beanDefinition);
}
}
}
工厂模式 FactoryBean 组件注册
@Configuration
public class MainConfig3 {
/**
* 使用Spring 提供的FactoryBean(工厂Bean)
* 1.默认获取到的是工厂 bean调用getObject创建对象
* 2.要获取工厂Bean本身,需要在id前面加上&
* eg: ac.getBean("&color") 获取到的对象类型是 class com.zzp.domain.ColorFactoryBean
* ac.getBean("color") 获取到的对象类型是 class com.zzp.domain.Color
*/
@Bean("color")
public ColorFactoryBean colorFactoryBean(){
return new ColorFactoryBean();
}
}
ColorFactoryBean.java
public class ColorFactoryBean implements FactoryBean<Color> {
public Color getObject() throws Exception {
return new Color();
}
public Class<?> getObjectType() {
return Color.class;
}
public boolean isSingleton() {
return true;
}
}
生命周期
bean的声明周期:
bean创建----> 初始化 ---->销毁的过程
销毁:
- 单实例: 容器关闭的时候 //application.close()
- 多实例:容器不会管理这个bean,容器不会调用销毁方法。
1.通过@Bean指定init-method和destory-method
/**
* 指定初始化和销毁方法: 通过@Bean指定init-method和destory-method
*/
@Configuration
public class MainConfigOfLifeCycle {
@Bean(initMethod = "init",destroyMethod = "destory")
public Red red(){
return new Red();
}
}
Red.java
public class Red {
public Red() {
System.out.println("red constructor .....");
}
public void init(){
System.out.println("red init...");
}
public void destory(){
System.out.println("red destory....");
}
}
2.通过实现InitializingBean和DisposableBean接口的方法
@Configuration
public class MainConfigOfLifeCycle {
@Bean
public Black black(){
return new Black();
}
}
Black.java
public class Black implements InitializingBean, DisposableBean {
public Black() {
System.out.println("Black constroctor...");
}
//destory-method
public void destroy() throws Exception {
System.out.println("Black destory ....");
}
//init-method
public void afterPropertiesSet() throws Exception {
System.out.println("Black afterPropertiesSet ...");
}
}
3.通过JSR250 @PostConstruct注解和@PreDestroy注解
@Configuration
public class MainConfigOfLifeCycle {
@Bean
public Green green(){
return new Green();
}
}
Green.java
public class Green {
public Green() {
System.out.println("Green constroctor...");
}
//init-method
@PostConstruct
public void init(){
System.out.println("Green init...");
}
//destory-method
@PreDestroy
public void destory(){
System.out.println("Green destory...");
}
}
4.BeanPostProcessor接口(bean的后置处理器)
注意:这个是围绕在init初始化前后
/**
* BeanPostProcessor【interface】 bean的后置处理器
* 在bean初始化前后进行一些处理工作
* postProcessBeforeInitialization:在初始化之前
* postProcessAfterInitialization:在初始化之后
*/
@Configuration
@ComponentScan("com.zzp.condition")
public class MainConfigOfLifeCycle {
@Bean
public Green green(){
return new Green();
}
}
MyBeanPostProcessor.java
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
/**
* @param bean 当前bean对象
* @param beanName 当前bean的 id名字
* @return
* @throws BeansException
*/
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessBeforeInitialization :beanName:"+ beanName + " " + "bean对象:" + bean);
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization:beanName:"+ beanName + " " + "bean对象:" + bean);
return bean;
}
}
属性赋值
@Value
@Configuration
//加载配置文件到环境中
@PropertySource("classpath:person.properties")
public class MainConfigValue {
@Bean
public Person person(){
return new Person();
}
}
Person.java
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Person {
/**
* 使用Value赋值
*1.基本数值
*2.可以写SpEL: #{}
*3.可以写${}:取出配置文件中的值(在运行环境变量里面的值)
*/
@Value("zhangsan")
private String name;
@Value("#{20-2}")
private Integer age;
@Value("${person.nickname}")
private String nickname;
}
自动装配
1.@Autowired 自动注入
/**
* 搭配使用:
* 1)、默认优先按照类型去容器中找相对应的组件:applicationContext.getBean(BookDao.class);
*
* 2)、如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找
*
* 3)、@Qualifier("bookDao"):使用@Qualifier指定需要装配的组件的id
*
* 4)、自动装配默认一定要将属性赋值好,没有就会报错
* 允许没有该装配的组件 @Autowired(required=false)
*
* 5)、@Primary: 如果有多个同类型的组件 可以让Spring进行自动装配的时候,默认首选的bean 注意:这个注解是放在需要装配的组件上的
*
*/
2.Spring还支持使用@Resource(JSR250) 和@Inject(JSR330) 【java规范的注解】
/**
* @Resource:
* 可以和@Autowired一样实现自动装配功能,默认是按照组件名称进行装配的。
* 缺点: 1.没有能支持@Primary功能 2.没有支持@Autowired(required=false)的功能
*
* @Inject:
* 需要导入javax.inject包,和@Autowired的功能一样
* 缺点: 没有支持@Autowired(required=false)的功能
*
* @Autowired是Spring定义的, @Resource和@Inject都是Java规范
*
* AutowiredAnnotationBeanPostProcessor(增强器): 解析上面这些注解以及@Autowired注解并完成自动装配功能的
*/
3.xxxAware
自定义组件想要使用Spring容器底层的一些组件(ApplicationContext,BeanFactory,xxx)
自定义组件实现xxxAware接口:在创建对象的时候,会调用接口规定的方法注入相关组件
把Spring底层一些组件注入到自定义的Bean中;
xxxAware的功能: 底层是使用xxxProcessor 来处理的 该xxxProcessor也是实现了BeanPostProcessor增强器
例子: 实现ApplicationContextAware 底层使用 class ApplicationContextAwareProcessor implements **BeanPostProcessor ** 增强器
Rea.java
@Component
public class Rea implements ApplicationContextAware {
private ApplicationContext applicationContext;
public ApplicationContext getApplicationContext() {
return applicationContext;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("context:" + applicationContext);
this.applicationContext = applicationContext;
}
}
4.@Profile
Spring为我们提供的可以根据当前环境,动态的激活和切换一系列组件的功能。
@Profile:指定组件在哪个环境的情况下才能被注册到容器中,不指定,任何环境下都能注册这个组件。
案例:
MyConfigOfProfile.java
/**
*
* @Profile:指定组件在哪个环境的情况下才能被注册到容器中,不指定则任何环境下都能注册这个组件。
* 1)、加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中。默认是default环境
* 2)、写在配置类上,只有指定的环境的时候,整个配置类里面的所有配置才能生效
* 3)、没有标注环境标识的bean在任何环境下都是加载的。
*/
@Profile("test")
@PropertySource("classpath:jdbc.properties")
@Configuration
public class MyConfigOfProfile implements EmbeddedValueResolverAware {
@Value("${jdbc.user}")
private String user;
@Value("${jdbc.password}")
private String password;
private String driverClass;
@Profile("test")
@Bean("dataSourceTest")
public DataSource dataSourceTest() throws Exception {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(password);
dataSource.setJdbcUrl("jdbc:mysql:///test");
dataSource.setDriverClass(driverClass);
return dataSource;
}
@Profile("prod")
@Bean("dataSourceProd")
public DataSource dataSource1() throws Exception {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(password);
dataSource.setJdbcUrl("jdbc:mysql:///luffy");
dataSource.setDriverClass(driverClass);
return dataSource;
}
@Profile("dev")
@Bean("dataSourceDev")
public DataSource dataSourceDev() throws Exception {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(password);
dataSource.setJdbcUrl("jdbc:mysql:///staf");
dataSource.setDriverClass(driverClass);
return dataSource;
}
//采用实现Aware接口的方式来解析值
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.driverClass = resolver.resolveStringValue("${jdbc.driver}");
}
}
运行时怎么去切换环境呢?
1.通过代码去控制
MyTest.java
public class MyTest {
@Test
public void test(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
//获取环境
ConfigurableEnvironment environment = applicationContext.getEnvironment();
//设置环境的profile值
environment.setActiveProfiles("test");
//注册配置类
applicationContext.register(MyConfigOfProfile.class);
applicationContext.refresh();
String[] names = applicationContext.getBeanNamesForType(DataSource.class);
for (String name : names) {
System.out.println(name);
}
}
}
2.通过命令行参数来控制
-Dspring.profiles.active=test
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)