对于一个以前从事Android的开发者来说,现在转到java后台开发,就直接上手SpringBoot,虽然平时的工作中大多数是进行的增删查改,但是我也想自己提升,就看了SpringBoot的源码,但是在看源码的过程中,发现很多的原理还是不懂,于是现在回头从基础的Spring注解开始学习起,以下是我利用闲暇时间通过看视频所学习到的,在这儿只是为了记录一下,以后想回顾的时候,好随时查找,如果有一些错误的认知,烦请指教,谢谢!
Spring的重要特性之一是IOC容器和DI,Spring是把所有的组件都统一放到IOC容器中,然后组件之间的关系通过容器来进行自动装配,也就是DI。所以下面整体要记录的主要是用纯注解的方式来(顺便回顾下以往)完成容器的组件注册、管理以及依赖注入功能
1、以前用bean.xml配置文件的方式
- 在resources下新建一个bean.xml,用<bean>标签注册组件
Person类就不写了,就是一个普通的POJO类
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<bean id="person" class="com.atguigu.bean.Person" >
<property name="age" value="20"></property>
<property name="name" value="zhangsan"></property>
</bean>
</beans>
- 如何知道这个组件是否已经被加入到容器中
public class MainTest {
@SuppressWarnings("resource")
public static void main(String[] args) {
//获取容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
//通过组件名在容器中获取组件
Person bean = (Person) applicationContext.getBean("person");
System.out.println(bean);
}
}
-
结果
image.png
2、用注解@Configuration和@Bean的方式
- @Configuration是3.0发布的,用于定义配置类,可替换bean.xml配置文件
@Bean就相当于xml中的<bean>标签
//给容器中注册一个Bean;类型为返回值的类型,id默认是用方法名作为id,也可以自己在@Bean中自己写@Bean("person")
@Bean("person")
public Person person(){
return new Person("lisi", 20);
}
- 如何知道这个组件是否已经被加入到容器中
public class MainTest {
@SuppressWarnings("resource")
public static void main(String[] args) {
//获取容器
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
//通过组件类型在容器中获取组件
Person bean = applicationContext.getBean(Person.class);
System.out.println(bean);
}
}
-
结果
image.png
3、包扫描@ComponentScan+标注(@Controller、@Service、@Repository,@Component)的方式
- 对于以前bean.xml方式
1)、在xml中写上这个
<context:component-scan base-package="com.atguigu" use-default-filters="false"></context:component-scan>
2)、只要在POJO类上标注了@Controller、@Service、@Repository,@Component就可以注册到容器中
3)、访问容器用ClassPathXmlApplicationContext就行 - 在配置类上加入注解@ComponentScan(value="com.atguigu")
1)、只要在value="com.atguigu"这个包路径下的所有含有标注的POJO都会被加载
2)、访问容器用AnnotationConfigApplicationContext就行
4、@Import[快速给容器中导入一个组件]
4.1、通过下面注解的定义可以看到,此注解只能放到类上,value是一个类数组,但是value是可以有多种方式的,所有下面将对这三种 {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}方式分别进行一个记录描述
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
/**
* {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}
* or regular component classes to import.
*/
Class<?>[] value();
}
4.2、给容器导入组件
- 1)、@Import(要导入到容器中的组件);容器中就会自动注册这个组件,id默认是全类名。
@Import({Color.class,Red.class})
public class MainConfig2 {
}
//把所有注册进去容器的组件打印一遍
@Test
public void testImport(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
String[] definitionNames = applicationContext.getBeanDefinitionNames();
for (String name : definitionNames) {
System.out.println(name);
}
}
Import导入的两个,配置类mainConfig2也是组件,是因为可以看到@Configuration里面是有@Component标注的,其余的是系统自动注册的一些组件
- 2)、ImportSelector:返回需要导入的组件的全类名数组;在SpringBoot底层使用很多
//可以看到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.
*/
String[] selectImports(AnnotationMetadata importingClassMetadata);
}
//自定义逻辑返回需要导入的组件
public class MyImportSelector implements ImportSelector {
//返回值,就是到导入到容器中的组件全类名
//AnnotationMetadata:当前标注@Import注解的类的所有注解信息
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// TODO Auto-generated method stub
//importingClassMetadata
//方法不要返回null值
return new String[]{"com.atguigu.bean.Blue","com.atguigu.bean.Yellow"};
}
}
//具体的使用方式
@Import({Color.class,Red.class,MyImportSelector.class})
结果
- 3)、ImportBeanDefinitionRegistrar:手动注册bean到容器中
//可以看到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.
* @param importingClassMetadata annotation metadata of the importing class
* @param registry current bean definition registry
*/
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
}
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
* AnnotationMetadata:当前类的注解信息
* BeanDefinitionRegistry:BeanDefinition注册类;
* 把所有需要添加到容器中的bean;调用
* BeanDefinitionRegistry.registerBeanDefinition手工注册进来
*/
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean definition = registry.containsBeanDefinition("com.atguigu.bean.Red");
boolean definition2 = registry.containsBeanDefinition("com.atguigu.bean.Blue");
if(definition && definition2){
//指定Bean定义信息;(Bean的类型,Bean。。。)
RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class);
//注册一个Bean,指定bean名
registry.registerBeanDefinition("rainBow", beanDefinition);
}
}
}
//具体的使用方式
@Import({Color.class,Red.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})
结果
5、使用Spring提供的 FactoryBean(工厂Bean)
- 1)、FactoryBean的接口有三个方法需要实现
public interface FactoryBean<T> {
/**
* Return an instance (possibly shared or independent) of the object
* managed by this factory.
* <p>As with a {@link BeanFactory}, this allows support for both the
* Singleton and Prototype design pattern.
* <p>If this FactoryBean is not fully initialized yet at the time of
* the call (for example because it is involved in a circular reference),
* throw a corresponding {@link FactoryBeanNotInitializedException}.
* <p>As of Spring 2.0, FactoryBeans are allowed to return {@code null}
* objects. The factory will consider this as normal value to be used; it
* will not throw a FactoryBeanNotInitializedException in this case anymore.
* FactoryBean implementations are encouraged to throw
* FactoryBeanNotInitializedException themselves now, as appropriate.
* @return an instance of the bean (can be {@code null})
* @throws Exception in case of creation errors
* @see FactoryBeanNotInitializedException
*/
T getObject() throws Exception;
/**
* Return the type of object that this FactoryBean creates,
* or {@code null} if not known in advance.
* <p>This allows one to check for specific types of beans without
* instantiating objects, for example on autowiring.
* <p>In the case of implementations that are creating a singleton object,
* this method should try to avoid singleton creation as far as possible;
* it should rather estimate the type in advance.
* For prototypes, returning a meaningful type here is advisable too.
* <p>This method can be called <i>before</i> this FactoryBean has
* been fully initialized. It must not rely on state created during
* initialization; of course, it can still use such state if available.
* <p><b>NOTE:</b> Autowiring will simply ignore FactoryBeans that return
* {@code null} here. Therefore it is highly recommended to implement
* this method properly, using the current state of the FactoryBean.
* @return the type of object that this FactoryBean creates,
* or {@code null} if not known at the time of the call
* @see ListableBeanFactory#getBeansOfType
*/
Class<?> getObjectType();
/**
* Is the object managed by this factory a singleton? That is,
* will {@link #getObject()} always return the same object
* (a reference that can be cached)?
* <p><b>NOTE:</b> If a FactoryBean indicates to hold a singleton object,
* the object returned from {@code getObject()} might get cached
* by the owning BeanFactory. Hence, do not return {@code true}
* unless the FactoryBean always exposes the same reference.
* <p>The singleton status of the FactoryBean itself will generally
* be provided by the owning BeanFactory; usually, it has to be
* defined as singleton there.
* <p><b>NOTE:</b> This method returning {@code false} does not
* necessarily indicate that returned objects are independent instances.
* An implementation of the extended {@link SmartFactoryBean} interface
* may explicitly indicate independent instances through its
* {@link SmartFactoryBean#isPrototype()} method. Plain {@link FactoryBean}
* implementations which do not implement this extended interface are
* simply assumed to always return independent instances if the
* {@code isSingleton()} implementation returns {@code false}.
* @return whether the exposed object is a singleton
* @see #getObject()
* @see SmartFactoryBean#isPrototype()
*/
boolean isSingleton();
}
- 2)、一个类实现此接口
//创建一个Spring定义的FactoryBean
public class ColorFactoryBean implements FactoryBean<Color> {
//返回一个Color对象,这个对象会添加到容器中
public Color getObject() throws Exception {
System.out.println("ColorFactoryBean...getObject...");
return new Color();
}
public Class<?> getObjectType() {
return Color.class;
}
//是单例?
//true:这个bean是单实例,在容器中保存一份
//false:多实例,每次获取都会创建一个新的bean;
public boolean isSingleton() {
return false;
}
}
- 3)、在配置类中引入使用
@Bean
public ColorFactoryBean colorFactoryBean(){
return new ColorFactoryBean();
}
总结点:
1)默认获取到的是工厂bean调用getObject创建的Color对象com.atguigu.bean.Color
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
//工厂Bean获取的是调用getObject创建的对象
Object bean2 = applicationContext.getBean("colorFactoryBean");
System.out.println("bean的类型:"+bean2.getClass());
工厂Bean获取的是调用getObject创建的对象
2)要获取工厂Bean本身,我们需要给id前面加一个"&" &colorFactoryBean
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
//获取工厂Bean本身
Object bean4 = applicationContext.getBean("&colorFactoryBean");
System.out.println(bean4.getClass());
获取工厂Bean本身
可以看到&是定义在BeanFacory中的,加上这个前缀就是返回FacoryBean自己,而不是工厂返回的实例
public interface BeanFactory {
/**
* Used to dereference a {@link FactoryBean} instance and distinguish it from
* beans <i>created</i> by the FactoryBean. For example, if the bean named
* {@code myJndiObject} is a FactoryBean, getting {@code &myJndiObject}
* will return the factory, not the instance returned by the factory.
*/
String FACTORY_BEAN_PREFIX = "&";
}
3)在Spring和其他框架进行整合的时候,FacotyBean使用得非常多,这里还要区分FacotyBean和BeanFactory
网友评论