接上篇文章:https://blog.csdn.net/single_wolf_wolf/article/details/122948647
这个是笔者在B站看周瑜大神的相关视频时候的笔记 B站 周瑜大神
任意门:https://www.bilibili.com/video/BV1bY411b7Q6?p=48&spm_id_from=pageDriver
P37: @Resource注解你真的会用吗?
参考:[P5 @Resource如何工作的?](## P5 @Resource如何工作的?)
总览
- 没有指定name:先判断字段名字所对应的Bean是否存在,如果存在则把这个Bean赋值给属性,如果不存在则根据字段类型找Bean;
- 指定了name:指定了名字,就只会根据名字去查询,找到就用,找不到就报错;
- 注意:@Resource注解是JDK层面定义的,Spring负责实现,负责提供对于这个注解的支持
@Resource 和@Autowired注解都是用来依赖注入的。
P38 @Configuration你真的会用吗
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
@AliasFor(annotation = Component.class)
String value() default "";
boolean proxyBeanMethods() default true;
}
总览
被@Configuration注解修饰的类,首先是一个Bean,并且是一个配置Bean 。
什么是配置Bean?
proxyBeanMethods:代理Bean方法
- 有@Configuration注解
proxyBeanMethods 默认是true ,表示是Full配置Bean;proxyBeanMethods 为false ,表示是Lite 配置Bean(lite Mode:精简模式); - 无@Configuration注解
- 存在
@Component 注解就是Lite 配置Bean; - 存在
@ComponentScan 注解就是Lite 配置Bean; - 存在
@Import 注解就是Lite 配置Bean; - 存在
@ImportResource 注解就是Lite 配置Bean; - 存在
@Bean 注解的方法就是Lite 配置Bean;
配置Bean有什么用?
对于配置Bean,不仅仅只是放入容器中,Spring还会去解析配置Bean,
- 比如处理@ComponentScan就会去扫描
- 比如处理@Import注解就会将某个类导入成为Bean;
- 比如处理@Bean就会解析对应方法生成Bean;
proxyBeanMethods :
- true:配置Bean对应的配置类的代理对象
- false:配置Bean对应的配置类的普通对象
举个例子,上文也提到过,为什么有的配置类是代理对象,有的却是普通对象:
@Configuration
public class AppConfig {
}
P39 @Import你真的会用吗?
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
Class<?>[] value();
}
总览
普通类型 :直接将该类当做Bean;ImportSelector :将selectImport()方法返回的类当做Bean;DeferredImportSelector :
- 将selectImport()方法返回的类当做Bean;
- 与ImportSelector类型的却别在于selectImport()方法执行时机不同;
ImportBeanDefinitionRegistrar类型 :在registerBeanDefinitions()方法中注册BeanDefinition;
把某个类导入Spring容器作为一个Bean(配置Bean)
@ComponentScan(value = "com.zhouyu")
@Import(UserService.class)
public class AppConfig {
}
public class UserService {
}
public class MainTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
System.out.println(applicationContext.getBeanFactory().getBean(UserService.class));
UserService userService = (UserService)applicationContext.getBean("userService");
System.out.println(userService);
}
}
执行一下:
com.zhouyu.springdemo.service.UserService@30f842ca
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'userService' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:816)
at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1288)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1109)
at com.zhouyu.springdemo.service.MainTest.main(MainTest.java:16)
可以看到Spring容器里面是有UserService的Bean对象,至于报错,是因为这种导入方式的,Bean的name不再是默认的首字母小写也就是userService。
这个Import导入的Bean是配置Bean,也就是你可以再导入的类中定义Bean对象,比如@Bean,@ComponentScan。
举个例子
@Import(UserService.class)
public class AppConfig {
}
@Component
public class OrderService {
}
@ComponentScan(value = "com.zhouyu")
public class UserService {
}
public class MainTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
System.out.println(applicationContext.getBeanFactory().getBean(UserService.class));
System.out.println(applicationContext.getBeanFactory().getBean(OrderService.class));
}
}
执行:
com.zhouyu.springdemo.service.UserService@2dfaea86
com.zhouyu.springdemo.service.OrderService@15888343
Process finished with exit code 0
可以看到扫描到了OrderService这个Bean了,这里就是UserService被@Import导入以后,被视作一个配置Bean ,所以其上的@ComponentScan注解生效了。
P40 @Import导入之ImportSelector类型的作用
某个类(下面例子中的(ZhouyuImportSelector)实现ImportSelector接口,并且实现selectImports()方法,并且用@Import导入,那么这个类就会成为一个配置Bean,同时selectImports()方法,返回的字符串列表(类名称列表),里面的类都会成为Bean。
ImportSelector接口:
public interface ImportSelector {
String[] selectImports(AnnotationMetadata importingClassMetadata);
@Nullable
default Predicate<String> getExclusionFilter() {
return null;
}
}
举个例子:
@Import(ZhouyuImportSelector.class)
public class AppConfig {}
public class OrderService {}
public class UserService {}
public class ZhouyuImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{OrderService.class.getName(),UserService.class.getName()};
}
}
public class MainTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
System.out.println(applicationContext.getBeanFactory().getBean(UserService.class));
System.out.println(applicationContext.getBeanFactory().getBean(OrderService.class));
}
}
结果:
com.zhouyu.springdemo.service.UserService@166fa74d
com.zhouyu.springdemo.service.OrderService@40f08448
Process finished with exit code 0
DeferredImportSelector 接口和ImportSelector 接口,这里类似,就是执行时机不一样 。
P41 @Import导入之ImportBeanDefinitionRegistrar 类型的作用
某个类(下面例子中的(ZhouyuImportBeanDefinitionRegistrar)实现ImportBeanDefinitionRegistrar 接口,并且实现registerBeanDefinitions() 方法,并且用@Import导入,那么这个类就会成为一个配置Bean,同时registerBeanDefinitions()方法,里面生成一个BeanDefinition,然后注册这个BeanDefinition到Spring容器中,就会生成这个Bean。
注意:Spring整合Mybatis的时候,就用到了这个原理,将mybatis的代理对象注册到Spring容器中的时候,就是这个方式。
ImportBeanDefinitionRegistrar接口:
public interface ImportBeanDefinitionRegistrar {
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
BeanNameGenerator importBeanNameGenerator) {
registerBeanDefinitions(importingClassMetadata, registry);
}
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
}
}
举个例子:
@Import(ZhouyuImportBeanDefinitionRegistrar.class)
public class AppConfig {
}
public class OrderService {
}
public class ZhouyuImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
BeanNameGenerator importBeanNameGenerator) {
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition();
AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
beanDefinition.setBeanClass(OrderService.class);
registry.registerBeanDefinition("orderService",beanDefinition);
}
}
public class MainTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
System.out.println(applicationContext.getBeanFactory().getBean(OrderService.class));
}
}
结果:
16:16:09.978 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'appConfig'
16:16:09.984 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'orderService'
com.zhouyu.springdemo.service.OrderService@71623278
Process finished with exit code 0
P42 @Lookup注解你真的会用吗
抽象类因为无法实例化,所以如果在抽象类上面增加@Component类似的注解,不会生成一个Bean。
举个例子:
@Component
public abstract class AbstractBeanTest {
public abstract OrderService test();
}
public class MainTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
System.out.println(applicationContext.getBeanFactory().getBean(AbstractBeanTest.class));
}
}
结果:
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.zhouyu.springdemo.service.AbstractBeanTest' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:351)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:342)
at com.zhouyu.springdemo.service.MainTest.main(MainTest.java:16)
Process finished with exit code 1
如果这个抽象类的方法上面增加了,@Lookup注解,那么这个抽象类就会生成一个Bean,其实是一个CGLIB动态代理对象。
此外:
Spring提供了一个名为@Lookup的注解,这是一个作用在方法上的注解,被其标注的方法会被重写,然后根据其返回值的类型,容器调用BeanFactory的getBean()方法来返回一个bean。
还是上面的例子:
@Component
public class OrderService {
}
@Component
public abstract class AbstractBeanTest {
@Lookup("orderService")
public OrderService test(){
return null;
}
}
public class MainTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
AbstractBeanTest abstractBeanTest = (AbstractBeanTest)applicationContext.getBean("abstractBeanTest");
System.out.println(abstractBeanTest);
System.out.println(abstractBeanTest.test());
}
}
结果:
com.zhouyu.springdemo.service.AbstractBeanTest$$EnhancerBySpringCGLIB$$355b93b0@491b9b8
com.zhouyu.springdemo.service.OrderService@1a4927d6
Process finished with exit code 0
可以看到AbstractBeanTest的一个cglib的动态代理对象。
总览
@Lookup 注解的作用在官网上叫做方法注入 - @Autowired、@Resource、@Value是
属性注入 ,是给某个属性赋值 - @Lookup注解的作用是给某个方法赋值一个Bean,所以叫做方法注入(和Set方法,构造方法注入做区分),在调用这个方法是会返回所指定的Bean对象
- 正常情况下抽象类是不能成为Bean对象的,但是如果抽象类中的抽象方法用了@Lookup注解,那么最终也能产生一个Bean对象,并且该Bean对象可以调用抽象方法,并返回所指定的Bean对象。
- methodReplacer ------- 方法替换器
P43 @Primary注解你真的会用吗
在Spring容器中可能会存在一个类型的多个Bean对象,这时可以通过在某个Bean对象上使用@Primary注解来标识该Bean作为这个类型下的主Bean,在依赖注入时会优先使用。
P44 注册一个Bean有哪些方式?
总览
- @Component
- @Configuration
- @Service
- @Controller
- @Repository
- @Bean:通过解析某个方法作为Bean
- @Import:导入类或BeanDefinition作为Bean
- @ImportResource:导入一个spring.xml文件,通过解析该文件注册Bean
- BeanDefinitionRegistryPostProcessor:通过注册BeanDefinition来注册Bean
- FactoryBean:------SmartFactoryBean ------- 将自己new的对象注册为Bean
- applicationContext.registerBean():通过Supplier接口来提供一个对象作为Bean
- applicationContext.register():直接将某个类注册为Bean
- applicationContext.registerBeanDefinition():注册一个BeanDefinition,就相当于注册了一个Bean
BeanDefinitionRegistryPostProcessor举个例子:通过生成一个BeanDefinition来注册一个Bean
@Component
public class ZhouyuBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition();
AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
beanDefinition.setBeanClass(UserService.class);
registry.registerBeanDefinition("userService",beanDefinition);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
}
public class UserService {
}
public class MainTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
System.out.println(applicationContext.getBean("userService"));
}
}
11:15:45.531 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'userService'
com.zhouyu.springdemo.service.UserService@6f43c82
Process finished with exit code 0
P45 FactoryBean注册Bean的方式
看例子,ZhouyuFactoryBean这个name里面打印的对象实际是UserService的Bean对象,这里要注意,可以通过修改@Component里面的value属性来修改bean的name
@Component
public class ZhouyuFactoryBean implements FactoryBean {
@Override
public Object getObject() throws Exception {
return new UserService();
}
@Override
public Class<?> getObjectType() {
return UserService.class;
}
}
public class UserService {
}
public class MainTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
System.out.println(applicationContext.getBean("zhouyuFactoryBean"));
}
}
com.zhouyu.springdemo.service.UserService@3932c79a
Process finished with exit code 0
如果这个时候的UserService里面注入了一个Bean,这个Bean是null。
另外,如果上面的代码修改为
System.out.println(applicationContext.getBean("&zhouyuFactoryBean"));
注意,中间多了一个“&”,那么或获取ZhouyuFactoryBean这个Bean的Bean对象而不是UserService的Bean对象。
结果:
com.zhouyu.springdemo.service.ZhouyuFactoryBean@3932c79a
Process finished with exit code 0
P46 SmartFactoryBean定义Bean方式
debug可以发现实现FactoryBean 接口的时候,getObject()并不是在Spring容器启动的时候初始化阶段调用的,而是在执行
System.out.println(applicationContext.getBean("zhouyuFactoryBean"));
这个命令的时候,才调用的,也就是类似懒加载的情况。
SmartFactoryBean子接口集成了 FactoryBean接口,其中的方法isEagerInit()可以指定是否在Spring容器启动的时候加载这个Bean,默认是false。
public interface SmartFactoryBean<T> extends FactoryBean<T> {
default boolean isPrototype() {
return false;
}
default boolean isEagerInit() {
return false;
}
}
P47 ApplicationContext注册Bean的三种方式
applicationContext.register()
例子:
public class UserService {
@Autowired
private OrderService orderService;
public void test(){
System.out.println(orderService);
}
}
@Component
public class OrderService {
}
public class MainTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
applicationContext.register(UserService.class);
UserService userService =(UserService) applicationContext.getBean("userService");
System.out.println(userService);
userService.test();
}
}
:01.028 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'userService'
com.zhouyu.springdemo.service.UserService@20b2475a
com.zhouyu.springdemo.service.OrderService@7857fe2
Process finished with exit code 0
applicationContext.registerBean()
例子:OrderService UserService保持不变
public class MainTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
applicationContext.registerBean("userService", UserService.class, new Supplier<UserService>() {
@Override
public UserService get() {
return new UserService();
}
});
UserService userService =(UserService) applicationContext.getBean("userService");
System.out.println(userService);
userService.test();
}
}
14:34:09.454 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'userService'
com.zhouyu.springdemo.service.UserService@821330f
com.zhouyu.springdemo.service.OrderService@6f43c82
Process finished with exit code 0
这种方式和FactoryBean注册的Bean的区别就是UserService这个Bean里面的注入的属性:OrderService是有值的。
applicationContext.registerBeanDefinition()
例子:
public class MainTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition.setBeanClass(UserService.class);
applicationContext.registerBeanDefinition("userService",beanDefinition);
UserService userService =(UserService) applicationContext.getBean("userService");
System.out.println(userService);
userService.test();
}
}
14:36:52.413 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'userService'
com.zhouyu.springdemo.service.UserService@5db6b9cd
com.zhouyu.springdemo.service.OrderService@210ab13f
Process finished with exit code 0
未完待续
仅学习使用,侵权速删!
|