Spring教程动画文字版2

9 篇文章 0 订阅

接上篇文章: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;
    • proxyBeanMethodsfalse,表示是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动态代理和CGLIB
  • ProxyFactoryBean:利用FactoryBean机制将代理对象作为一个Bean
  • BeanNameAutoProxyCreator:指定某个beanName,让Spring对其进行AOP
  • DefaultAdvisorAutoProxyCreator:指定某个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不仅可以在代码运行的过程中读取相应的信息,还可以修改字节码,是一个相当不错的框架。


仅学习使用,侵权速删!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值