Spring高级注解来自于Spring的3.x版本之后提供支持的,在Spring4 & 5当中提供了很好的应用和支持,通过这些注解的学习和研究可以是我们在后续的Spring开发当中基于纯注解方式来完成。
第一章:@Configuration
类上加了@Configuration注解他就算是配置Bean了
Spring3.x当中提供的高级注解,用于替换xml配置文件,引入了配置Bean就以为彻底放弃xml这种配置方式了,这也是我们将来学习SpringBoot开发过程中的核心。
学习这个配置Bean是非常简单的,我们创建一个类AppConfig当前这仅仅是一个很普通的Java类,我们加入完成@Configuration这个注解之后,这个类就变成了配置Bean就不再是普通的配置类了。
在应用配置Bean替换XML的过程中,有两个问题,如下:
一:配置Bean替换XML细节
实际上我们在xml配置文件当中通过核心配置文件,我们配置的这个配置Bean实际上他的作用就是用来替换核心配置文件的。
我们在核心配置文件当中配置过这个Bean标签的来创建Bean,并且在这个Bean标签当中对这个属性进行注入,注入自定义类型和JDK类型的属性。当然我们也可以在Spring核心配置文件当中配置包扫描,通过配置包扫描来配置扫描包下的包含注解的类来创建他们的对象,这些功能都是在核心配置文件当中完成的。
但是现在引入了这个配置Bean之后呢,所有的配置和操作都是在这个配置Bean当中完成,具体怎么完成,具体怎么完成将来会有更多的详细细节, 原有的XML当中的配置内容都可以通过这种配置Bean的方式进行解决。
二:应用配置Bean工厂对象
应用配置Bean工厂对象也随之进行了改变。
我们来看一下在使用配置文件进行配置的时候,我们使用的工厂用的是,ClassPathXmlApplicationContext并且需要指定配置文件的位置。
对应的这个工厂对象已经不能够使用了,我们需要使用另一个对象,AnnotationConfigApplicationContext我们创建这个对象的同时也需要指定核心配置文件的位置,只不过指定的这个核心配置文件是一个Java类型的配置Bean的Class对象。
将配置Bean的这个类的Class对象作为参数传递进来,就可以依据这个核心配置文件创建Spring工厂对象,这个构造方法是由重载方法的,他的另一个参数是这个配置文件所在的包,可以指定这个配置参数所在的包,这样在这个包下或者子包下的配置文件就可以获取的到。这样的话,我们就可以根据这个配置文件创建你这个工厂对象的实例。
使用这个工厂对象的时候需要有两点注意事项:
1、创建工厂的代码,我们需要在创建工厂对象的过程当中使用这个工厂类变了
2、这个构造方法具有重载性。
三:配置Bean细节分析
在基于注解的开发过程中,我们想把所有的配置都配置到配置Bean当中,这时候对于日志配置来讲,这个时候我们是不能够集成Log4j的,甚至我们可以认为这个Log4J已经被淘汰了。
而在我们后续的开发过程中,Spring、甚至SpringBoot这样的话我们首推的是logback,下边呢,我们就看一下怎么继承这个logback呢?其实是非常简单,一个是引入Spring的jar包,一个是引入logback的核心配置文件。
将来我们使用Spring或者SpringBoot的时候都会使用这个logback的方式作为日志框架。
1:整合Logback
<!--整合log4j日志框架kaishi--> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.25</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>RELEASE</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.25</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> <version>1.7.25</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>org.logback-extensions</groupId> <artifactId>logback-ext-spring</artifactId> <version>0.1.4</version> </dependency>
<?xml version="1.0" encoding="UTF-8"?> <configuration> <!-- 控制台输出 --> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <!--格式化输出:%d表示⽇期,%thread表示线程名, %-5level:级别从左显示5个字符宽度%msg:⽇志消息,%n是换⾏符--> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n </pattern> </encoder> </appender> <root level="DEBUG"> <appender-ref ref="STDOUT"/> </root> </configuration>
三:@Component
加上这个注解之后就可以称为这个配置Bean,那么这个注解的本质是什么呢?这个也是@Component的衍生注解,点进去之后也有这个注解的上边有这@Component这个注解。
我们应用这个注解可以被标签进行扫描到,并创建对象,但是后续开发过程中我们没有人这么做,因为我们希望取代这个标签配置,彻底的放弃XML这种方式。
第二章:@Bean
我们通过配置Bean的方式替换了Spring的XML的核心配置文件,而作为xml的一个很重要的功能是配置Bean标签来配置Bean的创建。
然而我们当前如何在配置Bean当中完成标签Bean的配置呢,这就需要用到这个@Bean注解,这个就等同于xml当中的Bean标签
一:@Bean的使用
1:@Bean创建对象
1):@Bean创建简单对象
简单对象直接就是可以通过new的方式直接创建的对象
2):@Bean创建复杂对象
复杂对象就是Connection,SqlSessionFactory这样的对象,不能直接new的对象。
我们使用@Bean的前提就是使用配置Bean也就是说使用在@Configuration这样修饰的类中才能使用@Bean这个注解。我们添加一个@Bean注解,在对象的创建方法上,方法的返回值就是创建对象的类型。
@Bean注解修饰的方法名是有特殊的含义的,这个注解修饰的方法名就等同于,原来我们bean标签当中指定的id属性,这点需要额外注意。
方法体中的内容应该就是程序员想要创建对象的创建的过程。使用@Bean注解创建复杂对象也好,创建简单对象也好,直接将创建这个对象的方法写在@Bean修饰的方法的方法体中,这样的话,就完成了对象的创建,对象类型作为方法的返回体即可,然后方法名对应的就是具体的创建的Bean的id值,通过控制方法名的形式来控制创建出来的,程序员把对象创建的代码写在方法体中,这是Spring和程序员之间的协作
创建这个工厂的时候,我们可以传入配置Bean的class,也可以传入包的路径,Spring会扫描这个包查找包和子包下具有这个注解修饰的类型。
类似于接下来的复杂对象,我们是不能直接通过new的方式进行创建的,所以在Spring容器启动的时候,创建该对象的时候,需要Spring回调Spring的代码的书写,完成对象的创建,所以在Spring容器创建过程当中,我们可以看到一些复杂对象的创建的日志信息。比如连接对象,SQLSessionFactory这样的对象
3):@Bean创建复杂对象的注意事项
我们起初研究Spring的工厂的时候,我们创建复杂对象是将复杂对象实现一个Factory这样的一个接口来实现这样的功能,但是当我们现在基于配置Bean基于@Bean注解进行开发之后,我们使用@Bean这样完全没有问题,但是这样日后我们开发过程中,我们就使用了FactoryBean这种形式创建了复杂对象,这样是否可以和@Bean这种形式进行整合呢?
当然是可以的。只需要在配置Bean当中新提供一个@Bean注解,修饰一个新添加的方法,方法返回值为复杂对象,方法名为Bean的id,方法体中不需要进行创建对象的代码编写,只需要进行getObject方法的调用即可,这样就完成了Factory接口和二者的整合。
这个新的方法当中是写在配置类当中的,在这处理的过程当中,我们完成了将FactoryBean和@Bean的整合,也可以直接将复杂对象的创建写在配置类当中,仅仅使用这个@Bean的方式,这样的整合,也可以,但是我个人觉得有点多余。因为这样仅仅是一个FactoryBean和@Bean高级注解的一个联合使用。
正常情况下,我们直接在@Bean代码中写就行了,以上的这种联合使用,我们经常用来遗留系统的整合当中进行使用
4):@Bean注解自定义id值
就在@Bean当中添加一个参数就行了,@Bean(“name”)这样就实现了
5):@Bean控制对象创建次数
只需要在@Bean修饰的方法上,加上一个@Scope注解就可以了
当然这样的话,@Bean和FactoryBean联合用来整合遗留系统的这个方式,里边的isSingleton方法就失效了。默认不写的时候,是单例设计模式。
2:@Bean注入
1):自定义类型注入
在我们的配置Bean当中,我们通过@Bean注解配置一个UseDao对象,我们这一步完成的就是所要进行注入的对象的创建。
通过@Bean注解创建一个UseService对象,通过这个返回值 返回一个UseService对象,他里边的注入操作是通过形参来体现的,UseService依赖UserDao,将dao作为形参,创建对象的时候完成注入,使用set方法进行注入,这就等价于之前的propery标签,这样就完成了注入,最终我们把完整性UserService完成了注入,注入:一个是依赖作为形参,二是调用set方法进行注入即可。
@Configuration public class AppConfig1 { @Bean public UserDao userDao(){ return new UserDaoImpl(); } @Bean public UserService userService(UserDao userDao) { UserServiceImpl userService = new UserServiceImpl(); userService.setUserDao(userDao); return userService; } }
@Test public void testEight(){ ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig1.class); UserService userService = (UserService)ctx.getBean("userService"); userService.register(); }
2021-08-13 08:19:16.974 [main] INFO org.springframework.core.KotlinDetector - Kotlin reflection implementation not found at runtime, related features won't be available. 2021-08-13 08:19:17.050 [main] DEBUG o.s.c.a.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@4567f35d 2021-08-13 08:19:17.061 [main] DEBUG o.s.b.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor' 2021-08-13 08:19:17.197 [main] DEBUG o.s.b.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor' 2021-08-13 08:19:17.200 [main] DEBUG o.s.b.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory' 2021-08-13 08:19:17.202 [main] DEBUG o.s.b.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor' 2021-08-13 08:19:17.203 [main] DEBUG o.s.b.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor' 2021-08-13 08:19:17.209 [main] DEBUG o.s.b.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'appConfig1' 2021-08-13 08:19:17.214 [main] DEBUG o.s.b.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'userDao' 2021-08-13 08:19:17.236 [main] DEBUG o.s.b.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'userService' 2021-08-13 08:19:17.244 [main] DEBUG o.s.b.factory.support.DefaultListableBeanFactory - Autowiring by type from bean name 'userService' via factory method to bean named 'userDao' UserServiceImpl.register UserDaoImpl.save
用户自定义类型的注入就是通过这种类型来完成的,除了这种方式可以在@Bean当中进行注入的形式以外,我们还有更加简单的方式,开始还是需要先创建UserDao的对象,后边就是创建UserService的时候进行一个注入的时候,不采用这种形参的方式,而是采用userDao的方法的调用进行获取对象,进而通过set方法进行注入。因为相似度很高,我就不写了
2):JDK类型的注入
对于JDK类型的注入,我们我们之前直接通过property标签进行赋值,他的本质也是调用set方法,但是现在我们通过@Bean高级注解进行开发的时候,我们就需要时候用set方法进行赋值,现在的注入方式都是程序员显示的去调用set方法进行注入赋值操作,原来的时候,代码太简单,我们就不写了。这个和我们使用用户自定义类型进行注入的时候是没有任何区别的,本质上都是采用手工set注入的方式进行注入。要真的需要说明区别的话,就是用户自定义类型是需要生命形参的。
3):JDK类型注入细节分析
set注入的时候我们需要注意耦合的问题
我们通过@PropertySource(“classpath:/init.properties”)注解将配置文件引入到Spring当中,在@Value注解进行赋值注入操作,这样我们就解决了自定义类型的注入的问题
@Configuration @PropertySource("classpath:/init.properties") public class AppConfig1 { @Value("${id}") private Integer id; @Value("${name}") private String name; @Bean public Customer Customer(){ Customer customer = new Customer(); Customer.setId(); Customer.setName(); return cumtomer; } }
第三章:@ComponentScan
之前我们在核心配置文件当中context:component-san标签,用于进行包扫描然而现在我们包扫描注解就完全可以取代这个标签的作用
我们这个Component-scan注解是应用在配置Bean上边,引入他最终的目的就是为了来扫描Spring的基础注解,@Component及其衍生注解@Value@Autowired,这就是这个注解的核心作用。
一:@ComponentScan基本使用
<context:component-scan base-package = "com.dashu"/>
在这标签当中我们有一个属性base-package制定了要进行包扫描的包和子包,用注解进行替换之后,我们就一定是应用在配置Bean上边,也就是说在@Configuration注解修饰的类上加上这个注解之后,用于扫描对应包下基本注解,进而进行基本对象的创建。
有了这两个注解之后,这个配置Bean就可以称为真正意义上的配置Bean了。我们就不需要写任何标签了。因为在标签当中有一个base-packages属性,在注解当中也需要指定这个属性。
@Test public void testNine(){ ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig2.class); String[] beanDefinitionNames = ctx.getBeanDefinitionNames(); for (String beanDefinitionName : beanDefinitionNames) { System.out.println(beanDefinitionName); } //user1 //user2 }
/** * @Auther: DaShu * @Date: 2021/8/13 09:02 * @Description: */ @Component public class User1 { }
/** * @Auther: DaShu * @Date: 2021/8/13 09:02 * @Description: */ @Component public class User2 { }
二:@ComponentScan排除策略
当我们使用标签进行开发的时候,我们使用的排除策略就是通过这样的:在标签配置当中有一个字标签,这个子标签叫做<context:exclude-filter type =“”…通过这种排除策略可以排除定义包扫描之内的一些类进行排除操作。
当然我们基于注解呢,肯定也是进行一个排除策略的操作的,注解的属性和属性之间需要有逗号分割。排除策略的值是一个数组,因为排除策略是可以叠加的,我们可以通过配置多个排除策略完成排除策略的叠加。type属性对应之前的type标签,后边的value可以对应具体排除策略。也就是对应之前的expression,使用注解的和标签的这种形式的表现上有一点点差异,但是在要点上确实没啥区别。**
@Configuration @ComponentScan(basePackages = "com.dashu.scan", excludeFilters={@ComponentScan.Filter(type= FilterType.ANNOTATION,value ={ Service.class})}) public class AppConfig2 { }
通过这个操作,我们扫描com.dashu.scan包下的类,排除@Service注解修饰的类,这样我们就会创建User1的对象,不会创建User2的对象。作为排除类型一共有五种,五种排除方式的话自己进行复习就好了
@Configuration @ComponentScan(basePackages = "com.dashu.scan", excludeFilters={@ComponentScan.Filter(type= FilterType.ANNOTATION,value ={ Service.class}), @ComponentScan.Filter(type= FilterType.ASPECTJ,pattern ={"com.dashu..*"})}) public class AppConfig2 { }
这样写的话,就排除了注解的方式以及切面的方式,我们使用标签的方式定义排除策略的时候,我们通过expression的形式代表排除具体内容值,但是当我们使用注解的时候,我们的注解的时候就不是expression了,而是value,pattern基于注解排除的时候,待会需要进行一个完善
三:@ComponentScan包含策略
包含方式决定了只扫描哪些类上的注解,这些也对应当初标签配置开发的时候的属性,只需要有一个<componenet-scan这个属性就可以了。
但是我们使用这个标签的时候,必须先配置一个use-default-filters="false"这样的属性,来让Spring放弃他自己的扫描策略,而是使用我们自己的扫描策略。所以这个属性必须指定为false,之后我们就基于各种各样的方式进行配置扫描策略就可以了。
第四章:Spring工厂创建对象多种配置方式
一:多种配置方式的应用场景
我们创建一个User对象的话,我们有多种配置方式,比如使用@Component注解对他进行配置,也可以@Bean对他进行配置,也可以使用Bean标签对应进行配置,那么这些配置都适用于什么样的应用场景呢?
1:@Componenet注解,及其衍生注解,来讲主要应用程序员自己开发的类型上,程序员自己写的,程序要可以加上这些注解,也就可以进行创建对象和注入
2:@Bean这些对象,他也能创建对象,他用于框架提供的类型和别的程序员创建的类型,这样的类型都有一个特点,没有源码,所以,我们只能通过@Bean方法方法创建对象的形式来进行对象的创建,比如Connection对象和SqlSessionFactoryBean这个对象的创建,MapperScannerConfigure这个对象的创建。
3:bean标签这种形式我们基于纯注解开发过程中我们是基本不适用的,我们基本上只在遗留系统的整合上,可能用到这种形式,遗留提供很多年以前,没有使用注解,很多都是原生的创建。
4:Spring还为我们提供了一种基于@Import注解创建对象的方式,我们可以在配置Bean的上边使用这个注解,当我们在这个注解当中执行Class对象类型的时候,Spring创建配置Bean对象的同时也会把这个Class对象创建出来,这个@import注解也是可以让Spring工厂为我们进行对象创建的。
例如一个配置Bean当中,Spring会为我们创建他的对象,但是如果我们在他上边加了一个@Import(“User.class”)这样创建这个对象的同事也会把User这个对象创建出来。这个方式基本上是Spring底层自己使用的。
作为这个注解,我们很少用,什么时候会用呢,第一个场景是:一般是Spring框架的底层会使用,因为这种方式通过会集成其他的特性一起使用,Spring框架底层会使用,第二个场景是:这个经常会用在多配置Bean整合的时候,多配置Bean的整合上边,我们后续单独进行讲解分析。
@Test public void testNine(){ ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig2.class); String[] beanDefinitionNames = ctx.getBeanDefinitionNames(); for (String beanDefinitionName : beanDefinitionNames) { System.out.println(beanDefinitionName); } //appConfig2 //user1 //user2 //user }
@Configuration @Import(User.class) @ComponentScan("com.dashu.scan") public class AppConfig2 { }
二:多种配置方式优先级
多种配置方式优先级也是不一样的,@Component <@Bean< 配置文件中的Bean标签
优先级搞的配置覆盖优先级配置低的配置,当我们配置低的形式创建对象的内容不满意的时候,我们无需对他进行修改,只需要采用更高级的形式进行覆盖即可,进行配置覆盖的过程中,有一个前提,这个id值必须保持一致,只有保持一致才能够进行覆盖,只有当id配置一致的时候才能够进行覆盖。
三: 解决注解配置的耦合问题
注解配置的耦合问题来源于,我们创建复杂对象时直接在@Bean当中使用了new操作,当我们遇到这种耦合问题的时候。
我们如何进行解决呢,就是需要使用这样的一个高级的配置实现覆盖,比如所使用bean标签,我们可以在class标签当中使用新的注入,这样就解决了耦合的问题,只需要保持一个id值相同就可以了,只需要在Spring配置Bean当中引入Spring的核心配置文件,这样就可以实现对象的覆盖,也就解决了耦合的问题
但是这样也产生了一个新的问题,我们在配置Bean当中引入Spring核心配置文件之后,我们就修改原有的配置Bean,这不有增加了耦合性了么,实际上我们不引入这个配置Bean也是可以的。
我们可以这样实现,我们提供一个新的配置Bean,我们在新的配置Bean当中添加一个引入核心配置文件,这样设计是符合开闭设计原则的,后续我们让新的和旧的配置Bean一起起作用,这样的话,想让他们同时生效的话,只需要在Spring的工厂创建爱你的时候将两个配置Bean作为参数传递进去就好了,后续我们想让更多的配置Bean生效的话,我们甚至可以这样。**
第五章:整合多个配置信息
一:为什么会有多个配置信息呢?
真正的项目中会有N多个配置Bean的,根据内容进行拆分,拆分的原则是,按照功能进行查分,Spring和Mybatis、事务控制、都有自己独立的配置Bean
二:如何将多个配置信息整合在一起
1:整合要点
1:多配置Bean的整合
2:多配置Bean和@Component进行整合
3:多配置Bean和ApplicationContext.xml进行整合:配置覆盖,整合遗留系统
1、如何使多配置信息汇总成一个整体?
2、如何实现跨配置的注入?比如注解当中创建的对象注入给Bean标签当中
2:多配置Bean的整合
在整个处理的过程当中,我们需要关注两个要点:一个是如何整合,一个是如何夸配置注入。
整合其实很简单,只需要在创建Spring工厂对象的时候指定包扫描就可以了,当我们制定了扫描的包之后,创建工厂对象的时候就可以通过包扫描创建这些配置Bean的对象了,进而实现多个配置Bean的整合,这个解决方案,非常类似于我们当时多xml配置文件的整合。
@Test public void testTen(){ ApplicationContext ctx = new AnnotationConfigApplicationContext("com.dashu.config"); UserDao userDao = (UserDao) ctx.getBean("userDao"); UserService userService = (UserService) ctx.getBean("userService"); System.out.println(userDao); System.out.println(userService); //com.dashu.injection.UserDaoImpl@6e2aa843 //com.dashu.injection.UserServiceImpl@6f36c2f0 }
@Configuration public class Appconfig2 { @Bean public UserDao userDao (){ UserDaoImpl userDao = new UserDaoImpl(); return userDao; } }
@Configuration public class Appconfig1 { @Bean public UserService userService (){ UserServiceImpl userService = new UserServiceImpl(); return userService; } }
2021-08-13 21:16:06.479 [main] INFO org.springframework.core.KotlinDetector - Kotlin reflection implementation not found at runtime, related features won't be available. 2021-08-13 21:16:06.595 [main] DEBUG o.s.c.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [D:\giteesource\spring\spring-annotation\target\classes\com\dashu\config\Appconfig1.class] 2021-08-13 21:16:06.596 [main] DEBUG o.s.c.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [D:\giteesource\spring\spring-annotation\target\classes\com\dashu\config\Appconfig2.class] 2021-08-13 21:16:06.602 [main] DEBUG o.s.c.a.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@4567f35d 2021-08-13 21:16:06.627 [main] DEBUG o.s.b.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor' 2021-08-13 21:16:06.787 [main] DEBUG o.s.b.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor' 2021-08-13 21:16:06.789 [main] DEBUG o.s.b.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory' 2021-08-13 21:16:06.791 [main] DEBUG o.s.b.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor' 2021-08-13 21:16:06.792 [main] DEBUG o.s.b.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor' 2021-08-13 21:16:06.808 [main] DEBUG o.s.b.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'appconfig1' 2021-08-13 21:16:06.815 [main] DEBUG o.s.b.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'appconfig2' 2021-08-13 21:16:06.816 [main] DEBUG o.s.b.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'userService' 2021-08-13 21:16:06.865 [main] DEBUG o.s.b.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'userDao' com.dashu.injection.UserDaoImpl@6e2aa843 com.dashu.injection.UserServiceImpl@6f36c2f0
使用@Import注解完成多个配合Bean的整合,在Spring创建第一个对象的时候,也会把第二个配置Bean的对象进行创建,这样干偏底层,创建工厂对象的时候只需要指定第一个config配置Bean就可以了,这个跟XML多个整合的方式是一模一样的。
这里体现了@import的两个作用,第一个是创建对象,第二个是多配置Bean的整合。
@Test public void testEleten(){ ApplicationContext ctx = new AnnotationConfigApplicationContext(Appconfig1.class); UserDao userDao = (UserDao) ctx.getBean("userDao"); UserService userService = (UserService) ctx.getBean("userService"); System.out.println(userDao); System.out.println(userService); //com.dashu.injection.UserDaoImpl@6e2aa843 //com.dashu.injection.UserServiceImpl@6f36c2f0 }
/** * @Auther: DaShu * @Date: 2021/8/13 21:09 * @Description: */ @Configuration @Import(Appconfig2.class) public class Appconfig1 { @Bean public UserService userService (){ UserServiceImpl userService = new UserServiceImpl(); return userService; } }
/** * @Auther: DaShu * @Date: 2021/8/13 21:09 * @Description: */ @Configuration public class Appconfig2 { @Bean public UserDao userDao (){ UserDaoImpl userDao = new UserDaoImpl(); return userDao; } }
3:跨配置Bean进行注入
跨配置进行相应的注入,只需要将需要注入的内容在需要注入的配置Bean当中作为一个成员变量,然后使用一个@Autowired注解给她进行注入即可。这样就能完成一个跨配置的注入。这样的配置方式适用于使用配置Bean所有的场景。
@Test public void testEleten2() { ApplicationContext ctx = new AnnotationConfigApplicationContext(Appconfig1.class); UserDao userDao = (UserDao) ctx.getBean("userDao"); UserService userService = (UserService) ctx.getBean("userService"); userService.register(); //UserServiceImpl.register //UserDaoImpl.save }
@Configuration @Import(Appconfig2.class) public class Appconfig1 { @Autowired private UserDao userDao; @Bean public UserService userService (){ UserServiceImpl userService = new UserServiceImpl(); userService.setUserDao(userDao); return userService; } }
/** * @Auther: DaShu * @Date: 2021/8/13 21:09 * @Description: */ @Configuration public class Appconfig2 { @Bean public UserDao userDao (){ UserDaoImpl userDao = new UserDaoImpl(); return userDao; } }
4:配置Bean整合@Component
我们自己定义的类可以使用@Component及其衍生注解,这样我们的此时我们想把这二个进行整合,我们学过这个概念,我们只需要在配置Bean上边加上包扫描注解即可。在这里边实现跨配置进行注入的话和上边是没有任何区别的。都是采用@Autowired进行注即可。
5:配置Bean与配置文件的整合
1):应用场景
1:遗留系统
2:配置覆盖
2):@ImportResource
这里的整合是使用了@ImportResource注解,上边指定配置文件的路径,如果放在了跟路径下边,那么就是如下的写法。
注入的话和其他的地方是没有任何区别的,就是通过@Autowired注解加上一个,一个set注入即可。
三:Bean的底层实现原理
在创建一个配置Bean的时候,需要在配置Bean当中,添加一个@Configuration注解,后续工厂对象创建的时候,就会创建配置Bean和配置Bean当中的对象了。
实际上,这块有一个细节是需要关注的,作为Spring来讲,Spring在读取到配置Bean的时候,调用配置Bean当中程序员写好的创建对象的方法来创建对象。我们获取对象的话,获取的事Spring帮我们创建好的对象,那么Spring是如何做到只创建一次的呢?
我们书写了创建对象的功能,被Spring调用之后,Spring调用之后,他控制了对象的创建次数,创建对象初始功能,控制对象的创建次数是额外功能,也就是Spring采用了代理设计模式,进行了控制对象创建的额外功能。
整个配置Bean最为核心的功能采用了Aop的方式进行开发的,Aop的开发一种是基于JDK的,另外一种是采用Cglib的创建方式,显然配置Bean是采用Cglib这种形式的,整个配置Bean实际上采用的就是代理设计模式。
底下的这个图就证明了所谓的配置Bean对象就是Cglib创建的代理对象,可以控制对象的创建。整个配置Bean的底层就是代理设计模式。