接上篇文章: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 {
/**
* Explicitly specify the name of the Spring bean definition associated with the
* {@code @Configuration} class. If left unspecified (the common case), a bean
* name will be automatically generated.
* <p>The custom name applies only if the {@code @Configuration} class is picked
* up via component scanning or supplied directly to an
* {@link AnnotationConfigApplicationContext}. If the {@code @Configuration} class
* is registered as a traditional XML bean definition, the name/id of the bean
* element will take precedence.
* @return the explicit component name, if any (or empty String otherwise)
* @see AnnotationBeanNameGenerator
*/
@AliasFor(annotation = Component.class)
String value() default "";
/**
* Specify whether {@code @Bean} methods should get proxied in order to enforce
* bean lifecycle behavior, e.g. to return shared singleton bean instances even
* in case of direct {@code @Bean} method calls in user code. This feature
* requires method interception, implemented through a runtime-generated CGLIB
* subclass which comes with limitations such as the configuration class and
* its methods not being allowed to declare {@code final}.
* <p>The default is {@code true}, allowing for 'inter-bean references' via direct
* method calls within the configuration class as well as for external calls to
* this configuration's {@code @Bean} methods, e.g. from another configuration class.
* If this is not needed since each of this particular configuration's {@code @Bean}
* methods is self-contained and designed as a plain factory method for container use,
* switch this flag to {@code false} in order to avoid CGLIB subclass processing.
* <p>Turning off bean method interception effectively processes {@code @Bean}
* methods individually like when declared on non-{@code @Configuration} classes,
* a.k.a. "@Bean Lite Mode" (see {@link Bean @Bean's javadoc}). It is therefore
* behaviorally equivalent to removing the {@code @Configuration} stereotype.
* @since 5.2
*/
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 {
//这种情况下 是Full配置Bean,AppConfig是一个代理对象
//Lite配置Bean,AppConfig是一个普通对象
}
P39 @Import你真的会用吗?
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
/**
* {@link Configuration @Configuration}, {@link ImportSelector},
* {@link ImportBeanDefinitionRegistrar}, or regular component classes to 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 {
/**
* Select and return the names of which class(es) should be imported based on
* the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
* @return the class names, or an empty array if none
*/
String[] selectImports(AnnotationMetadata importingClassMetadata);
/**
* Return a predicate for excluding classes from the import candidates, to be
* transitively applied to all classes found through this selector's imports.
* <p>If this predicate returns {@code true} for a given fully-qualified
* class name, said class will not be considered as an imported configuration
* class, bypassing class file loading as well as metadata introspection.
* @return the filter predicate for fully-qualified candidate class names
* of transitively imported configuration classes, or {@code null} if none
* @since 5.2.4
*/
@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) {
//返回的类列表(类名称)会成为Bean
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 {
/**
* Register bean definitions as necessary based on the given annotation metadata of
* the importing {@code @Configuration} class.
* <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be
* registered here, due to lifecycle constraints related to {@code @Configuration}
* class processing.
* <p>The default implementation delegates to
* {@link #registerBeanDefinitions(AnnotationMetadata, BeanDefinitionRegistry)}.
* @param importingClassMetadata annotation metadata of the importing class
* @param registry current bean definition registry
* @param importBeanNameGenerator the bean name generator strategy for imported beans:
* {@link ConfigurationClassPostProcessor#IMPORT_BEAN_NAME_GENERATOR} by default, or a
* user-provided one if {@link ConfigurationClassPostProcessor#setBeanNameGenerator}
* has been set. In the latter case, the passed-in strategy will be the same used for
* component scanning in the containing application context (otherwise, the default
* component-scan naming strategy is {@link AnnotationBeanNameGenerator#INSTANCE}).
* @since 5.2
* @see ConfigurationClassPostProcessor#IMPORT_BEAN_NAME_GENERATOR
* @see ConfigurationClassPostProcessor#setBeanNameGenerator
*/
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
BeanNameGenerator importBeanNameGenerator) {
registerBeanDefinitions(importingClassMetadata, registry);
}
/**
* Register bean definitions as necessary based on the given annotation metadata of
* the importing {@code @Configuration} class.
* <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be
* registered here, due to lifecycle constraints related to {@code @Configuration}
* class processing.
* <p>The default implementation is empty.
* @param importingClassMetadata annotation metadata of the importing class
* @param registry current bean definition registry
*/
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
}
}
举个例子:
@Import(ZhouyuImportBeanDefinitionRegistrar.class)
public class AppConfig {
}
public class OrderService {
}
public class ZhouyuImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
//注册一个BeanDefinitions 也就是一个Bean
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
BeanNameGenerator importBeanNameGenerator) {
//生成一个BeanDefinition
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition();
AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
//指定类
beanDefinition.setBeanClass(OrderService.class);
//注册到Spring容器中,这里可以指定beanName
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
//这里要指定这个实现了BeanDefinitionRegistryPostProcessor接口的类是一个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里面的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;
}
}
//上面的类是有两个Bean:ZhouyuFactoryBean这个Bean对象,UserService的Bean对象
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> {
/**
* Is the object managed by this factory a prototype? That is,
* will {@link #getObject()} always return an independent instance?
* <p>The prototype status of the FactoryBean itself will generally
* be provided by the owning {@link BeanFactory}; usually, it has to be
* defined as singleton there.
* <p>This method is supposed to strictly check for independent instances;
* it should not return {@code true} for scoped objects or other
* kinds of non-singleton, non-independent objects. For this reason,
* this is not simply the inverted form of {@link #isSingleton()}.
* <p>The default implementation returns {@code false}.
* @return whether the exposed object is a prototype
* @see #getObject()
* @see #isSingleton()
*/
default boolean isPrototype() {
return false;
}
/**
* Does this FactoryBean expect eager initialization, that is,
* eagerly initialize itself as well as expect eager initialization
* of its singleton object (if any)?
* <p>A standard FactoryBean is not expected to initialize eagerly:
* Its {@link #getObject()} will only be called for actual access, even
* in case of a singleton object. Returning {@code true} from this
* method suggests that {@link #getObject()} should be called eagerly,
* also applying post-processors eagerly. This may make sense in case
* of a {@link #isSingleton() singleton} object, in particular if
* post-processors expect to be applied on startup.
* <p>The default implementation returns {@code false}.
* @return whether eager initialization applies
* @see org.springframework.beans.factory.config.ConfigurableListableBeanFactory#preInstantiateSingletons()
*/
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.register(UserService.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);
//applicationContext.register(UserService.class);
/* applicationContext.registerBean("userService", UserService.class, new Supplier<UserService>() {
@Override
public UserService get() {
return new UserService();
}
});*/
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
P48 依赖注册有哪些方式?
autowire mode
:可以理解Spring自带的自动注入功能,和@Autowired注解没什么关系,主要利用的是类的Set方法:by type 、by name
@Autowired
:用@Autowired注解来标识要给哪个属性进行注入
- 可以加载字段上:先by type,再by name
- 可以加载方法上:先by type,再by name
@Resource
:用@Resource注解来标志要给哪个属性进行自动注入
- 可以加载字段上:如果没指定name, 先by name,再by type;如果指定了name,直接根据name查询,找不到,就报错;
- 可以加载方法上:如果没指定name, 先by name,再by type;如果指定了name,直接根据name查询,找不到,就报错;
@Value
:用@value注解来给属性赋值 :可以加载字段、方法上
自定义BeanPostProcessor
:处理自定义注解
autowire mode
查看 [P19 @Bean的autowired属性](## P19 @Bean的autowired属性)
原理是在生成UserService这个bean的过程中,回去找这个bean中属性的setter方法,进行根据type或者name进行注入。
@ComponentScan("com.zhouyu")
public class AppConfig {
@Bean(autowire = Autowire.BY_NAME)
//@Bean(autowire = Autowire.BY_TYPE)
public UserService userService(){
return new UserService();
}
}
P49 BeanPostProcessor自定义依赖注入
举个例子,自定义注解,实现@Autowired功能,只根据名称匹配。
//自定义注解
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Zhouyu2Autowired {
}
@Component
public class OrderService {
}
@Component
public class UserService {
//这里是自定义注解
@Zhouyu2Autowired
private OrderService orderService;
public void test(){
System.out.println(orderService);
}
}
//实现接口BeanPostProcessor,可以在UserService这个Bean生成过程中操作,也就是对其中属性赋值,
//实现接口ApplicationContextAware,可以获取Spring容器的上下文对象:ApplicationContext
@Component
public class ZhouyuBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware {
//这里用上下文获取OrderService的Bean对象
private ApplicationContext applicationContext;
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
Class<?> aClass = bean.getClass();
for (Field field: aClass.getDeclaredFields()) {
//假设bean就是UserService,首先找到属性中有这个自定义注解的属性
if(field.isAnnotationPresent(Zhouyu2Autowired.class)){
field.setAccessible(true);
//根据ApplicationContext 拿到这个属性对应的Bean
Object autowiredBean = applicationContext.getBean(field.getName());
try {
//把这个属性对应的Bean注入到这个UserService的bean中
field.set(bean,autowiredBean);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
//UserService 这个时候这个属性就有值了
return bean;
}
//ApplicationContextAware 的方法会把Spring容器的ApplicationContext传进来
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
public class MainTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService =(UserService) applicationContext.getBean("userService");
System.out.println(userService);
userService.test();
}
}
//结果:
com.zhouyu.springdemo.service.UserService@34bde49d
com.zhouyu.springdemo.service.OrderService@1b1cfb87
Process finished with exit code 0
过程
实现接口
BeanPostProcessor
,可以在UserService这个Bean生成过程中操作,也就是对其中属性赋值;实现接口
ApplicationContextAware
,可以获取Spring容器的上下文对象:ApplicationContext;用上下文获取属性–OrderService的Bean对象;
假设bean就是UserService,首先找到属性中有这个自定义注解的属性;
根据ApplicationContext 拿到这个属性对应的Bean;
把这个属性对应的Bean注入到这个UserService的bean中;
返回;
P50 获取ApplicationContext有哪些方式?
总览
ApplicationContextAware
:通过ApplicationContextAware回调,Spring会把ApplicationContext传入setApplicationContext()方法
@Autowired
:因为Spring在进行依赖注入时做了特殊处理,也支持ApplicationContext对象注入给某个字段
@Autowired
@Component
public class UserService {
@Autowired
private OrderService orderService;
@Autowired
private ApplicationContext applicationContext;
public void test(){
System.out.println(orderService);
}
}
ApplicationContextAware
接口
@Component
public class UserService implements ApplicationContextAware {
@Autowired
private OrderService orderService;
private ApplicationContext applicationContext;
public void test(){
System.out.println(orderService);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
Spring容器在创建UserService这个Bean的时候,检测是 实现了ApplicationContextAware接口,就会传进起来上下文,这样就可以获取到Spring容器的上下文
P51 有哪些作用域,底层原理分析
总览
单例
----------单例池 ------------通过一个Map实现:ConcurrentHashMap多例
------prototype----- 无需额外支持,每次创建一个Bean直接返回Request
:
- request.getAttribute()
- request.setAttribute()
Session
:
- session.getAttribute()
- session.setAttribute()
application
:
- servlet.getAttribute()
- servlet.setAttribute()
P52 Spring类型转化
有哪些方式?
总览
PropertyEditor -------- 利用JDK自带的
ConversionService --------- 利用Spring中实现的
TypeConverter ---------- 合二为一
XML中定义的bean实现,类名(“com.zhouyu.service.UserService”)其实是一个字符串,那么Spring如何将字符串转化成类呢
<bean id = "userService" class = "com.zhouyu.service.UserService"></bean>
PropertyEditor
举个例子
public class OrderService {
private String orderName;
public String getOrderName() {
return orderName;
}
public void setOrderName(String orderName) {
this.orderName = orderName;
}
}
public class StringToUserPropertyEditor extends PropertyEditorSupport implements PropertyEditor {
@Override
public void setAsText(String text) throws IllegalArgumentException {
OrderService orderService = new OrderService();
orderService.setOrderName(text);
this.setValue(orderService);
}
}
public class MainTest {
public static void main(String[] args) {
StringToUserPropertyEditor propertyEditor = new StringToUserPropertyEditor();
propertyEditor.setAsText("111");
OrderService value = (OrderService) propertyEditor.getValue();
System.out.println(value.getOrderName());
}
}
//结果:
"C:\Program Files\Java\jdk1.8.0_281\bin\java.exe"...
111
Process finished with exit code 0
上面的例子中不是在Spring容器中,那么如何注入到容器中呢
接上面例子
//增加一个配置bean
@ComponentScan(value = "com.zhouyu")
public class AppConfig {
@Bean
public CustomEditorConfigurer customEditorConfigurer(){
CustomEditorConfigurer customEditorConfigurer = new CustomEditorConfigurer();
Map<Class<?>,Class<? extends PropertyEditor>> propertyEditorMap = new HashMap<>();
//表示StringToUserPropertyEditor可以将String转化为OrderService类型,在Spring源码中,如果需要发现的对象时String,而需要的类型是OrderService,
// 就会使用这个转换器
propertyEditorMap.put(OrderService.class,StringToUserPropertyEditor.class);
customEditorConfigurer.setCustomEditors(propertyEditorMap);
return customEditorConfigurer;
}
}
public class MainTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService =(UserService) applicationContext.getBean("userService");
System.out.println(userService);
userService.test();
}
}
//结果:
com.zhouyu.springdemo.service.UserService@4f0f2942
com.zhouyu.springdemo.service.OrderService@2657d4dd
Process finished with exit code 0
P53 Spring 类型转换服务 ConversionService
//转换器实现类 String.class----->OrderService.class
public class StringToUserConverter implements ConditionalGenericConverter {
@Override
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return sourceType.getType().equals(String.class) && targetType.getType().equals(OrderService.class);
}
@Override
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(String.class,OrderService.class));
}
@Override
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
OrderService orderService = new OrderService();
orderService.setOrderName((String) source);
return orderService;
}
}
//配置类中添加一个Bean
@Bean
public ConversionServiceFactoryBean conversionServiceFactoryBean(){
ConversionServiceFactoryBean conversionServiceFactoryBean = new ConversionServiceFactoryBean();
conversionServiceFactoryBean.setConverters(Collections.singleton(new StringToUserConverter()));
return conversionServiceFactoryBean;
}
P54 Spring 类型转换服务 TypeConverter
二合一
P55 Spring AOP有哪些使用方式?
总览
ProxyFactory
:代理对象工程,封装了JDK动态代理和CGLIBProxyFactoryBean
:利用FactoryBean机制将代理对象作为一个BeanBeanNameAutoProxyCreator
:指定某个beanName,让Spring对其进行AOPDefaultAdvisorAutoProxyCreator
:指定某个Advisor,让Spring对其匹配的Bean对象进行AOP@EnableAspectJAutoProxy
:开启支持AspectJ
ProxyFactory
JDK动态代理和CGLIB动态代理技术,Spring中对其进行了封装,封装出来的类叫做ProxyFactory,标识创建代理对象的一个工厂,使用起来会比上面的更加方便,比如:
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.framework.ProxyFactory;
public class MainTest {
public static void main(String[] args) {
UserService userService = new UserService();
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(userService);
proxyFactory.addAdvice(new MethodInterceptor() {
//拦截器
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("before.........");
Object proceed = methodInvocation.proceed();
System.out.println("after.........");
return proceed;
}
});
UserService proxy = (UserService) proxyFactory.getProxy();
proxy.test();
}
}
public class UserService {
public void test(){
System.out.println("userService.test()方法");
}
}
//结果:
before.........
userService.test()方法
after.........
Process finished with exit code 0
P56 Spring AOP之 ProxyFactoryBean
利用FactoryBean机制将代理对象作为一个Bean
//增加一个bean
@Bean
public ProxyFactoryBean userServiceProxy(){
UserService userService = new UserService();
ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
proxyFactoryBean.setTarget(userService);
proxyFactoryBean.addAdvice(new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("before.........");
Object proceed = methodInvocation.proceed();
System.out.println("after.........");
return proceed;
}
});
return proxyFactoryBean;
}
//获取代理对象
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = (UserService) applicationContext.getBean("userServiceProxy");
userService.test();
}
//结果:
before.........
userService.test()方法
after.........
Process finished with exit code 0
通过这种方法来定义一个UserService的Bean,并且是经过了AOP的,但是这种方式只能针对某一个Bean,它是一个FactoryBean,所以利用的就是FactoryBean技术,间接得将UserService的代理对象作为了Bean。
P57 Spring AOP之 BeanNameAutoProxyCreator
指定某个beanName,让Spring对其进行AOP
自动代理创建器
@ComponentScan(value = "com.zhouyu")
public class AppConfig {
//拦截器
@Bean
public MethodInterceptor zhouyuAroundAdvice(){
return new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("before.........");
Object proceed = methodInvocation.proceed();
System.out.println("after.........");
return proceed;
}
};
}
//自动代理创建器
@Bean
public BeanNameAutoProxyCreator beanNameAutoProxyCreator(){
BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();
beanNameAutoProxyCreator.setBeanNames("userSe*"); //匹配规则
beanNameAutoProxyCreator.setInterceptorNames("zhouyuAroundAdvice"); //指定拦截器的bean的名字
beanNameAutoProxyCreator.setProxyTargetClass(true); //true:使用cglib
return beanNameAutoProxyCreator;
}
}
public class MainTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
//获取userService这个bean ,实际上是生成的代理对象成为了userService的Bean,看打印结果
UserService userService = (UserService) applicationContext.getBean("userService");
userService.test();
}
}
//结果:
before.........
userService.test()方法
after.........
Process finished with exit code 0
获取的userService这个bean ,实际上是生成的代理对象成为了userService的Bean,看打印结果
通过BeanNameAutoProxyCreator可以批量的Bean进行AOP,并且指定了代理逻辑,指定了一个InterceptorName,也就是一个Advice,前提条件是这个Advice,也得是一个Bean,这样Spring才能找到,但是BeanNameAutoProxyCreator缺点也很明显,它只能根据beanName来指定想要代理的Bean。
P58 Spring AOP之 DefaultAdvisorAutoProxyCreator
指定某个Advisor,让Spring对其匹配的Bean对象进行AOP
Advisor:顾问
@Component
public class OrderService {
public void test(){
System.out.println("this is OrderService.test方法");
}
}
@Component
public class UserService {
public void test(){
System.out.println("userService.test()方法");
}
}
@ComponentScan(value = "com.zhouyu")
public class AppConfig {
@Bean
public DefaultPointcutAdvisor defaultPointcutAdvisor(){
//切点 代理叫做test的方法们,无论哪个bean,里面有test,就会被代理
NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
pointcut.addMethodName("test");
DefaultPointcutAdvisor defaultPointcutAdvisor = new DefaultPointcutAdvisor();
defaultPointcutAdvisor.setPointcut(pointcut);
//代理逻辑
defaultPointcutAdvisor.setAdvice(new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("before.........");
Object proceed = methodInvocation.proceed();
System.out.println("after.........");
return proceed;
}
});
return defaultPointcutAdvisor;
}
//默认的Advisor自动代理创建器
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
return new DefaultAdvisorAutoProxyCreator();
}
}
public class MainTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = (UserService) applicationContext.getBean("userService");
userService.test();
OrderService orderService = (OrderService) applicationContext.getBean("orderService");
orderService.test();
}
}
//结果:
before.........
userService.test()方法
after.........
before.........
this is OrderService.test方法
after.........
Process finished with exit code 0
可以看到这两个类都被代理了 。
另外
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
return new DefaultAdvisorAutoProxyCreator();
}
完全可以被如下替换:
@Import(DefaultAdvisorAutoProxyCreator.class)
因为我们只是生成了一个Bean而已,而且在换个类也是Spring提供的。
通过DefaultAdvisorAutoProxyCreator会直接去找所有Advisor类型的Bean,根据Advisor中的PointCut和Advisor信息,确定要代理的Bean以及代理逻辑。但是我们发现,通过这种方式,我们得依靠一个类来实现定义我们的Advisor,或者PointCut,那么这个步骤能不能简化一点呢?
可以通过注解!
。。。。
最终进化成
@EnableAspectJAutoProxy
P59 Spring中有哪些父子
总览
父子类
父子BeanDefinition: 父子Bean
父子BeanFactory
父子ApplicationContext
父子BeanFactory
public class MainTest {
public static void main(String[] args) {
/*AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = (UserService) applicationContext.getBean("userService");
userService.test();
OrderService orderService = (OrderService) applicationContext.getBean("orderService");
orderService.test();*/
DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition.setBeanClass(UserService.class);
defaultListableBeanFactory.registerBeanDefinition("userService",beanDefinition);
System.out.println(defaultListableBeanFactory.getBean("userService"));
DefaultListableBeanFactory defaultListableBeanFactory1 = new DefaultListableBeanFactory();
AbstractBeanDefinition beanDefinition1 = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition1.setBeanClass(UserService.class);
defaultListableBeanFactory1.registerBeanDefinition("orderService",beanDefinition1);
System.out.println(defaultListableBeanFactory1.getBean("orderService"));
//指定父BeanFactory,我们可以用defaultListableBeanFactory 从defaultListableBeanFactory1中获取"orderService"这个bean
defaultListableBeanFactory.setParentBeanFactory(defaultListableBeanFactory1);
//注意看这个代码:
System.out.println(defaultListableBeanFactory.getBean("orderService"));
}
}
//结果:
17:24:26.319 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'userService'
com.zhouyu.springdemo.service.UserService@19bb089b
17:24:26.377 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'orderService'
com.zhouyu.springdemo.service.UserService@4563e9ab
com.zhouyu.springdemo.service.UserService@4563e9ab
Process finished with exit code 0
通过设定父子BeanFactory关系,我们可以用defaultListableBeanFactory 从defaultListableBeanFactory1中获取"orderService"这个bean
父子ApplicationContext
public class MainTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
AnnotationConfigApplicationContext applicationContext1 = new AnnotationConfigApplicationContext(AppConfig.class);
//指定父子上下文
applicationContext1.setParent(applicationContext1);
}
}
Spring有哪些有用的工具类
- 国际化:MessageSource 更方便的进行国际化操作
- 资源加载:context.getResource() 更方便的读取某个资源:
- 文件资源
- 网络资源
- 获取运行时环境:context.getEnviroment() 更方便的获取环境变量:
- 操作系统环境变量
- JVM环境变量
- properties文件
- 事件发布:
- ApplicationListener: 某个类作为时间监听器
- @EventListener:某个方法作为事件监听器
- Order比较器:
- OrderComparator:利用Order接口指定顺序
- AnnotationAwareOrderComprator:利用@Ordered注解指定顺序
- 类的元数据读取器: SimpleMetadataReaderFactory:读取类上的各种信息:
- 类名
- 类上的注解
- 类中的方法
- …
- s
P60 Spring工具之国际化
resources目录下新建两个文件messages.properties,messages_en.properties,里面分别存放如下
# messages.properties
test=a
# messages_en.properties
test=b
//定义国际化资源的Bean
@Bean
public MessageSource messageSource(){
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("messages");
return messageSource;
}
//main方法里面获取
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
System.out.println(applicationContext.getMessage("test", null, new Locale("en")));
}
//结果:
b
Process finished with exit code 0
不同的文件对应不同的国家,加载不同数据。
P61 Spring工具之资源加载
ApplicationContext 还拥有资源加载的功能,比如可以直接获取某个文件的内容, 获取网络等资源操作
public static void main(String[] args) throws IOException {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
//获取本项目内的文件信息
Resource resource = applicationContext.getResource("file:D:\\IDEA-workSpace\\就是玩\\SpringDemo\\src\\main\\java\\com\\zhouyu\\springdemo\\service\\OrderService.java");
System.out.println(resource.contentLength());
Resource resource1 = applicationContext.getResource("https://www.baidu.com");
System.out.println(resource1.contentLength());
System.out.println(resource1.getURL());
}
//结果:
580
2443
https://www.baidu.com
Process finished with exit code 0
如果不使用ApplicationContext,单独实现的话,会比较费事,ApplicationContext可以直接注入,在你的自己的类里面使用。
P62 Spring工具之获取运行时环境
ApplicationContext 获取系统环境变量等信息:
public class MainTest {
public static void main(String[] args) throws IOException {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
ConfigurableEnvironment environment = applicationContext.getEnvironment();
System.out.println("environment.getSystemEnvironment():");
System.out.println(environment.getSystemEnvironment());
System.out.println("environment.getSystemProperties():");
System.out.println(environment.getSystemProperties());
System.out.println("environment.getPropertySources():");
System.out.println(environment.getPropertySources());
System.out.println("environment.getActiveProfiles():");
System.out.println(environment.getActiveProfiles());
}
}
//结果:
environment.getSystemEnvironment():
{USERDOMAIN_ROAMINGPROFILE=....略
environment.getSystemProperties():
{java.runtime.name=Java(TM) SE Runtime Environment, sun.boot.library.path=C:\Program Files\Java\jdk1.8.0_281\jre\bin, ...略
environment.getPropertySources():
[PropertiesPropertySource@459718907 {name='systemProperties', properties={java.runtime.name=Java(TM) SE Runtime Environment, ...略
environment.getActiveProfiles():
[Ljava.lang.String;@24c1b2d2
Process finished with exit code 0
P63 Spring工具之事件发布
ApplicationListener: 某个类作为时间监听器
Spring提供的事件发布机制:同步
//自定义监听器
@Component
public class ZhouyuApplicationListener implements ApplicationListener {
@Override
public void onApplicationEvent(ApplicationEvent event) {
if(event instanceof PayloadApplicationEvent){
PayloadApplicationEvent e = (PayloadApplicationEvent) event;
System.out.println(e.getPayload());
}
System.out.println(event);
}
}
@Component
public class UserService {
//事件发布广播器
@Autowired
private ApplicationEventMulticaster applicationEventMulticaster;
public void test(){
applicationEventMulticaster.multicastEvent(new PayloadApplicationEvent<String>(this,"广播器发布 test"));
System.out.println("userService.test()方法");
}
}
public static void main(String[] args) throws IOException {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
//Spring在启动中会发布事件,自定义监听器会打印消息
//我们再发布一个事件
applicationContext.publishEvent("自定义发布事件");
//执行 test
UserService userService = (UserService) applicationContext.getBean("userService");
userService.test();
}
//结果:
org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@71dac704, started on Wed Mar 16 11:07:11 CST 2022]
自定义发布事件
org.springframework.context.PayloadApplicationEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@71dac704, started on Wed Mar 16 11:07:11 CST 2022]
广播器发布 test
org.springframework.context.PayloadApplicationEvent[source=com.zhouyu.springdemo.service.UserService@13d73f29]
userService.test()方法
Process finished with exit code 0
可以看到这里有三个事件,Spring容器启动时发布的,自定义发布事件,以及UserService类中发布的事件。
@EventListener:某个方法作为事件监听器
接着上面的例子,删除自定义监听器ZhouyuApplicationListener,在AppConfig配置类中新增一个方法,作为监听器,这里使用@EventListener注解:
@EventListener
public void eventListener(ApplicationEvent event){
if(event instanceof PayloadApplicationEvent){
PayloadApplicationEvent e = (PayloadApplicationEvent) event;
System.out.println("@EventListener: "+e.getPayload());
}
System.out.println("@EventListener: "+event);
}
//再次执行main方法,结果如下:
@EventListener: org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@71dac704, started on Wed Mar 16 11:13:59 CST 2022]
@EventListener: 自定义发布事件
@EventListener: org.springframework.context.PayloadApplicationEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@71dac704, started on Wed Mar 16 11:13:59 CST 2022]
@EventListener: 广播器发布 test
@EventListener: org.springframework.context.PayloadApplicationEvent[source=com.zhouyu.springdemo.service.UserService@415b0b49]
userService.test()方法
Process finished with exit code 0
这是两种不同的监听器定义的方式。
P64 Spring工具之Order比较器
OrderComparator:利用Order接口指定顺序
OrderComparator是Spring提供了一种比较器,可以有从来根据@Order注解或实现Ordered接口来执行值比较,从而可以进行排序。
public class UserService implements Ordered {
private int order;
public void setOrder(int order) {
this.order = order;
}
public void test(){
System.out.println("userService.test()方法");
}
@Override
public int getOrder() {
return order;
}
@Override
public String toString() {
return "UserService{" +
"order=" + order +
'}';
}
}
public static void main(String[] args) throws IOException {
UserService userService = new UserService();
UserService userService1 = new UserService();
UserService userService2 = new UserService();
userService.setOrder(1);
userService1.setOrder(3);
userService2.setOrder(2);
List<UserService> list = new ArrayList<>();
list.add(userService);
list.add(userService1);
list.add(userService2);
list.sort(new OrderComparator());
System.out.println(list);
}
//结果
[UserService{order=1}, UserService{order=2}, UserService{order=3}]
Process finished with exit code 0
可以看到结果已经排序,按照指定的order字段排序
AnnotationAwareOrderComprator 替换OrderComparator也可以,因为是其一个子类。
P65 Spring工具之类的元数据读取器
在Spring中需要去解析类的信息,比如类名,类中的方法,类上的注解,这些都可以称之为类的元数据,所以Spring对类的元数据做了抽象,并提供了一些工具。
MetadataReader标识类的元数据读取器,默认实现类是simpleMetadataReader。比如:
@Component
public class UserService {
public void test(){
System.out.println("userService.test()方法");
}
}
public class MainTest {
public static void main(String[] args) throws IOException {
//元数据读取工厂
SimpleMetadataReaderFactory simpleMetadataReaderFactory = new SimpleMetadataReaderFactory();
//构造一个MetadataReader
MetadataReader metadataReader = simpleMetadataReaderFactory.getMetadataReader("com.zhouyu.springdemo.service.UserService");
//得到一个ClassMetadata 并获取了类名
ClassMetadata classMetadata = metadataReader.getClassMetadata();
System.out.println(classMetadata.getClassName());
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
for (String annotationType : annotationMetadata.getAnnotationTypes()) {
System.out.println(annotationType);
}
}
}
//结果:
com.zhouyu.springdemo.service.UserService
org.springframework.stereotype.Component
Process finished with exit code 0
需要注意的是:simpleMetadataReader去解析类的时候,使用的技术是ASM技术。
什么是ASM
ASM 是一个 Java 字节码操控框架。它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class 文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。
ASM不仅可以在代码运行的过程中读取相应的信息,还可以修改字节码,是一个相当不错的框架。
仅学习使用,侵权速删!