Spring笔记(1) - 组件注册及部分源码解析
-
@Bean:类注入容器
- xml方式:
<bean id="person" class="com.hrh.bean.Person"> <property name="name" value="张三"></property> <property name="age" value="20"></property> </bean> public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); Person person = (Person) context.getBean("person"); System.out.println(person); }
- @Bean+@Configuration方式:
@Configuration//配置类==配置文件 public class BeanConfig { @Bean public Person person(){ return new Person("张三",20); } } AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class); Person person = (Person)context.getBean("person");
- xml方式:
-
@ComponentScan:扫描类注入容器
- xml方式:
<!-- component-scan包扫描,只要标注了@Controller、@Service、@Repository、@Component的类会注入容器中 use-default-filters=false禁用默认过滤规则,使用自定义过滤规则 --> <context:component-scan base-package="com.hrh" use-default-filters="false"/>
- @ComponentScan方式:JDK1.8的ComponentScan是Repeatable的,即可以在类上定义多个@ComponentScan
@Configuration//配置类==配置文件 /** * value:指定要扫描的包 * includeFilters:指定扫描时只需要包含哪些组件,需要使用useDefaultFilters = false使规则生效 * excludeFilters:指定扫描时按照什么规则排除哪些组件 */ @ComponentScan(value = "com.hrh",includeFilters = { @Filter(type = FilterType.ANNOTATION, classes = {Controller.class}) },useDefaultFilters = false) public class BeanConfig { @Bean public Person person(){ return new Person("张三",20); } } //====结果==== org.springframework.context.annotation.internalConfigurationAnnotationProcessor org.springframework.context.annotation.internalAutowiredAnnotationProcessor org.springframework.context.annotation.internalCommonAnnotationProcessor org.springframework.context.event.internalEventListenerProcessor org.springframework.context.event.internalEventListenerFactory beanConfig userController person
- @ComponentScans:如果不是JDK1.8,可以使用该注解定义多个@ComponentScan
@ComponentScans({ @ComponentScan(value = "com.hrh",includeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class}) },useDefaultFilters = false) }) public class BeanConfig { @Bean public Person person(){ return new Person("张三",20); } }
- @Filter:过滤规则
@Configuration//配置类==配置文件 /** * FilterType.ANNOTATION:根据注解扫描指定的类 * FilterType.ASSIGNABLE_TYPE:按照给定的类型,如果是接口类型,其实现类和子类注入容器;如果是类,本类和子类注入容器 * FilterType.ASPECTJ:使用ASPECTJ表达式 * FilterType.REGEX:使用正则表达式 * FilterType.CUSTOM:使用自定义规则,自定义实现TypeFilter接口,重写match方法 */ @ComponentScan(value = "com.hrh", includeFilters = { // @ComponentScan.Filter(type = FilterType.ANNOTATION, // classes = {IUserService.class}), // @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, // classes = {IUserService.class}), @ComponentScan.Filter(type = FilterType.CUSTOM,classes ={MyTypeFilter.class} ) }, useDefaultFilters = false) public class BeanConfig { @Bean public Person person() { return new Person("张三", 20); } } public class MyTypeFilter implements TypeFilter { /** * * @param metadataReader 读取到的当前正在扫描的类的信息 * @param metadataReaderFactory 可以获取到其他任何类的信息 * @return ture则将类加入容器 * @throws IOException */ public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { //获取当前类注解的信息 AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata(); //获取当前类资源(类路径) Resource resource = metadataReader.getResource(); //获取当前正在扫描的类的类信息 ClassMetadata classMetadata = metadataReader.getClassMetadata(); String className = classMetadata.getClassName(); System.out.println("className:"+className); if(className.contains("Controller")){ return true; } return false; } } //====结果==== className:com.hrh.bean.Person className:com.hrh.config.MyTypeFilter className:com.hrh.controller.UserController className:com.hrh.dao.UserDao className:com.hrh.service.IUserService className:com.hrh.service.SubUserService className:com.hrh.service.UserService org.springframework.context.annotation.internalConfigurationAnnotationProcessor org.springframework.context.annotation.internalAutowiredAnnotationProcessor org.springframework.context.annotation.internalCommonAnnotationProcessor org.springframework.context.event.internalEventListenerProcessor org.springframework.context.event.internalEventListenerFactory beanConfig userController person
- xml方式:
-
@Scope:作用域
@Configuration @ComponentScan(value = "com.hrh") public class BeanConfig { /** * ConfigurableBeanFactory#SCOPE_PROTOTYPE:prototype * ConfigurableBeanFactory#SCOPE_SINGLETON:singleton * org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST:request * org.springframework.web.context.WebApplicationContext#SCOPE_SESSION:session * prototype:多实例(原型),只有在获取的时候才会注入容器,每次获取都会进行创建对象注入不同的实例,每次获取的对象都不同(涉及线程安全时可以用到) * singleton:单实例(默认值),ioc容器启动会调用该方法创建对象注入容器,每次获取直接中容器中拿 * request:同一次请求创建一个实例 * session:同一个session创建一个实例 */ @Scope(value = "prototype") @Bean public Person person() { System.out.println("注入容器。。。。。"); return new Person("张三", 20); }
- @Lazy:懒加载,项目启动时不创建对象注入容器,只在第一次调用时才创建对象并初始化
@Lazy @Bean public Person person() { System.out.println("注入容器。。。。。"); return new Person("张三", 20); }
-
在Spring的诸多应用场景中bean都是单例形式,当一个单例bean需要和一个非单例bean组合使用或者一个非单例bean和另一个非单例bean组合使用时,我们通常都是将依赖以属性的方式放到bean中来引用,然后以@Autowired来标记需要注入的属性。但是这种方式在bean的生命周期不同时将会出现很明显的问题,假设单例bean A需要一个非单例bean B(原型),我们在A中注入bean B,每次调用bean A中的方法时都会用到bean B,我们知道Spring Ioc容器只在容器初始化时执行一次,也就是bean A中的依赖bean B只有一次注入的机会,但是实际上bean B我们需要的是每次调用方法时都获取一个新的对象(原型)所以问题明显就是:我们需要bean B是一个原型bean,而事实上bean B的依赖只注入了一次变成了事实上的单例bean。
@Component @Scope("prototype") public class PrototypeBean { private static final Logger logger= LoggerFactory.getLogger(PrototypeBean.class); public void say() { logger.info("say something..."); } } @Component public class SingletonBean { private static final Logger logger = LoggerFactory.getLogger(SingletonBean.class); @Autowired private PrototypeBean bean; public void print() { logger.info("Bean SingletonBean's HashCode : {}",bean.hashCode()); bean.say(); } } @SpringBootApplication public class SampleApplication { private static final Logger logger = LoggerFactory.getLogger(SampleApplication.class); public static void main(String[] args) { SpringApplication.run(SampleApplication.class, args); } @Bean public CommandLineRunner test(final SingletonBean bean) { return (args)-> { logger.info("测试单例bean和原型bean的调用"); int i =0; while(i<3) { i++; bean.print(); } }; } }
1)在bean A中引入ApplicationContext每次调用方法时用上下文的getBean(name,class)方法去重新获取bean B的实例。
2)使用@Lookup注解。
这两种解决方案都能解决我们遇到的问题,但是第二种相对而言更简单。@Component public class SingletonBean { private static final Logger logger = LoggerFactory.getLogger(SingletonBean.class); @Autowired private ApplicationContext context; public void print() { PrototypeBean bean = getFromApplicationContext(); logger.info("Bean SingletonBean's HashCode : {}",bean.hashCode()); bean.say(); } /** * 每次都从ApplicatonContext中获取新的bean引用 * @return PrototypeBean instance */ PrototypeBean getFromApplicationContext() { return this.context.getBean("prototypeBean",PrototypeBean.class); } } =================== @Component public abstract class SingletonBean { private static final Logger logger = LoggerFactory.getLogger(SingletonBean.class); public void print() { PrototypeBean bean = methodInject(); logger.info("Bean SingletonBean's HashCode : {}",bean.hashCode()); bean.say(); } // 也可以写成 @Lookup("prototypeBean") 来指定需要注入的bean @Lookup protected abstract PrototypeBean methodInject(); }
- @Lazy:懒加载,项目启动时不创建对象注入容器,只在第一次调用时才创建对象并初始化
-
@Conditional:按照条件注入容器,实现Condition的matches()
@Configuration @ComponentScan(value = "com.hrh") public class BeanConfig { @Conditional(WindowsConfig.class) @Bean(value = "Bill") public Person person1() { System.out.println("创建Windows。。。。。"); return new Person("Bill", 20); } @Conditional(LinuxConfig.class) @Bean(value = "Linux") public Person person2() { System.out.println("创建Linux。。。。。"); return new Person("Linux", 20); } } public class WindowsConfig implements Condition { /** * * @param context 判断条件能使用的上下文 * @param metadata 注释信息 * @return */ public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { //获取ioc使用的beanFactory ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); //获取类加载器 ClassLoader classLoader = beanFactory.getBeanClassLoader(); Environment environment = context.getEnvironment(); String property = environment.getProperty("os.name"); if("Windows 10".equals(property)){ return true;//当前系统环境是windows 10,将条件下的bean注入容器 } return false; } } public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { //获取ioc使用的beanFactory ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); //获取类加载器 ClassLoader classLoader = beanFactory.getBeanClassLoader(); Environment environment = context.getEnvironment(); String property = environment.getProperty("os.name"); if("Linux".equals(property)){ return true; } return false; }
-
@Import:快速导入组件进容器
public class Color { } @Configuration @ComponentScan(value = "com.hrh") @Import(Color.class) //@Import({Color.class,Person.class})导入多个 public class BeanConfig {} public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class); String[] beanDefinitionNames = context.getBeanDefinitionNames(); for(String beanName:beanDefinitionNames){ System.out.println(beanName); } }
-
ImportSelector:实现该接口,重写selectImports()
public class MyImportSelector implements ImportSelector { /** * * @param importingClassMetadata 当前标注@Import的类的全部注解信息,比如该类还标注了@Configuration也会被获取到 * @return 返回的组件全类名加入到容器中 */ public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[]{"com.hrh.bean.Color","com.hrh.bean.Person"}; } } @Configuration @ComponentScan(value = "com.hrh") @Import({MyImportSelector.class}) public class BeanConfig {}
-
ImportBeanDefinitionRegistrar:实现ImportBeanDefinitionRegistrar,重写registerBeanDefinitions()
@Configuration @Import({Color.class,MyImportBeanDefinitionRegistrar.class}) public class BeanConfig {} public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { /** * * @param importingClassMetadata 当前类的注解信息 * @param registry BeanDefinition注册类,把所有需要添加到容器中的bean,调用BeanDefinitionRegistryregisterBeanDefinition注册进来 */ @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { boolean color = registry.containsBeanDefinition("com.hrh.bean.Color"); //容器中有Color类才将Person类进行注入 if(color) { RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Person.class); registry.registerBeanDefinition("person",rootBeanDefinition); } } }
-
-
FactoryBean
public class MyFactoryBean implements FactoryBean<Color> { //返回一个对象,并且该对象会注入容器 @Override public Color getObject() throws Exception { return new Color(); } @Override public Class<?> getObjectType() { return Color.class; } @Override public boolean isSingleton() { return true; } } @Configuration public class BeanConfig { /** * 1.默认获取的是MyFactoryBean调用getObject创建的对象 * 2.要获取MyFactoryBean本身,需要给id前加&,比如&myBeanFactory */ @Bean public MyFactoryBean myBeanFactory(){ return new MyFactoryBean(); } } AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class); Object bean1=context.getBean("myBeanFactory"); System.out.println(bean1.getClass()); Object bean2=context.getBean("&myBeanFactory"); System.out.println(bean2.getClass());
作者:huangrenhui
欢迎任何形式的转载,但请务必注明出处。
如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的【推荐】。
如果,您希望更容易地发现我的新博客,不妨点击一下左下角的【关注我】。
如果,您对我的博客所讲述的内容有兴趣,请继续关注我的后续博客,我是【码猿手】。
如果,您希望更容易地发现我的新博客,不妨点击一下左下角的【关注我】。
如果,您对我的博客所讲述的内容有兴趣,请继续关注我的后续博客,我是【码猿手】。
限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。