Spring基于注解二
上一次介绍了很多的关于spring的基本的注解,这篇文章描述一下关于Spring注解的基本的原理,从简单的例子入手
@Configuration
@Import({Color.class,Red.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})
/**
* @Import 导入组件,id默认为组件的全类名
*/
public class MainConfig2 {
/**
* Scope 取值
*
* SCOPE_PROTOTYPE 多实例的
*
* SCOPE_SINGLETON 单实例的(默认值)
*
*
*
* SCOPE_REQUEST
* SCOPE_SESSION
*
* @return
*
*
* prototype
*
* 如果是多实例的时候,IOC容器并不会创建对象,而是在获取对象的时候需要创建对象
*
* singleton
* IOC容器启动会创建方法创建对象到IOC容器中,
* 以后每次使用获取对象就直接在IOC容器中拿
* request 同一次请求创建要给实例
* session 同一个session 创建一个实例
*
* 懒加载
* 单实例:默认在容器启动的时候创建对象
* 懒加载:容器启动的时候不创建对象,在第一次使用的时候获取到Bean对象,并初始化
*/
@Bean
// @Scope(value = "prototype") //调整作用域
@Lazy //标注实现懒加载
public Person person(){
System.out.println("给容器中添加person。。。。。。");
return new Person("zhangsan",25);
}
/**
* 按照一定条件进行判断,满足条件给定容器中注册Bean
*
* 如果系统是Windows 给容器中注册 @Bean("bill")
* 如果系统是Linux 给容器中注册 @Bean("linus")
*
* -Dos.name=linux
*/
// @Conditional({Condition}) 也可以添加到类上,如果当前容器满足当前条件才会被注册,类中注解统一设置
@Conditional({WindowsCondition.class})
@Bean("bill")
public Person person01(){
return new Person("BillGats",63);
}
@Conditional({LinuxCondition.class})
@Bean("linus")
public Person person02(){
return new Person("linus",59);
}
/***
* 给容器注册组件
* 1)包扫描+组件标注注解 自己写的类
*
* 2)@Bean[导入第三方包里面的组件]
*
* 3)@Import [快速给容器中导入组件]
* 1、@Import(要导入到容器中的组件,容器中就会自动注册这个组件,id默认为全类名)
* 2、@ImportSelector:返回需要导入到组件中的类名数组
* 3、@ImportBeanDefinitionRegistrar:通过调用一个方法可以给容器中添加一些组件。
* 手动注入
* 4)使用Spring提供的FactoryBean(工厂Bean)
* 1、默认获取到的是工厂Bean调用的getObject创建的对象
* 2、要获取工厂bean本省,我们需要给ID前面加一个&符号
* &colorFactoryBean
*
*
*/
@Bean
public ColorFactoryBean colorFactoryBean(){
return new ColorFactoryBean();
}
}
首先这里介绍一下简单常用的两种将组建注入类的方式
1.使用@Bean 的方式
2.使用@Component的方式
第一种方式可以直接在配置类中注入,第二种方式使用包扫描的方式将组建加入到容器中。
3.使用@Import
4.使用Spring提供的FactoryBean(工厂模式)
对于工厂模式,大家可以自己找找有关的资料。当我们需要引入到外部的组件的时候可以使用@Import注解
-
1、@Import(要导入到容器中的组件,容器中就会自动注册这个组件,id默认为全类名)
-
2、@ImportSelector:返回需要导入到组件中的类名数组
-
3、@ImportBeanDefinitionRegistrar:通过调用一个方法可以给容器中添加一些组件。
还可以通过手动注入的方式
这里我们提供一个通过工厂方式注册组件的方式
继承FactoryBean接口
//创建一个Spring定义的工厂bean
public class ColorFactoryBean implements FactoryBean {
/**
*
* @return 返回一个对象,这个对象添加到容器中
* @throws Exception
*/
@Override
public Object getObject() throws Exception {
System.out.println("======getObject========");
return new Color();
}
@Override
public Class<?> getObjectType() {
return Color.class;
}
/**
* 是否为单实例
* @return
* true 这个Bean是单实例,在容器中保存一份
* false 表示这个Bean是一个多实例的方式,
*/
@Override
public boolean isSingleton() {
return false;
}
}
在这里我们看到我们在实现接口方法的时候在getObject() 方法中注入的是一个Color对象,在我们后期使用的时候就会看到
private static void test06() {
ApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
printBean(annotationConfigApplicationContext);
Blue blue = annotationConfigApplicationContext.getBean(Blue.class);
System.out.println(blue);
//工厂Bean获取的是getObject创建对象
Object object = annotationConfigApplicationContext.getBean("colorFactoryBean");
Object object2 = annotationConfigApplicationContext.getBean("colorFactoryBean");
System.out.println("bean类型"+object.getClass());
System.out.println(object==object2);
Object object4 = annotationConfigApplicationContext.getBean("&colorFactoryBean");
System.out.println(object4.getClass());
}
我们在获取两个对象的时候时候拿到的同一个对象,就可以证明我们在使用的时候真正的获取到的是在我们容器加载的时候创建的对象。如果我们想要获取到我们工厂类对象只需要在对象前面加上&符号即可。这种方式在我们的BeanFactory中也提到了。
public interface BeanFactory {
String FACTORY_BEAN_PREFIX = "&";
Object getBean(String var1) throws BeansException;
<T> T getBean(String var1, Class<T> var2) throws BeansException;
Object getBean(String var1, Object... var2) throws BeansException;
这里需要重点介绍几个注解
@Lazy
@Conditional
@Scope
首先我们需要介绍一下在Spring容器中的对象加载过程,在spring容器中,我们有几种对象加载的方式,这就是我们在代码注释中提到的 @Scope 注解的几个参数, singleton 在IOC容器启动的时候创建对象,以后每次使用对象都是从容器中获取。 prototype 使用多实例的时候,在组件使用的时候创建对象。还有两个分别是 request 和 session 这个两个在我们使用的时候是不经常使用的。表示在一次请求中创建一个对象或者在一次回话中创建一个对象。这里就引入了一个新的概念,当然在使用的时候需要注意的就是我们所说的单例模式,这个也是23种设计模式,在这里我们不做太多的描述。
通过以上我们引入一个新的概念就是懒加载,对于懒加载来说也是也可以说是单例模式中的一个思想,简单的说就是在使用的时候去创建对象,不需要的时候就不会创建对象,这样可以节约不少的资源。很多对象我们暂时不需要使用,我们就不需要出现在java内存中。
@Conditional注解表示我们在使用的是时候可以根据具体的条件来向Spring容器中注入对应的组件。在使用这个注解的时候我们后面的参数类需要继承Condtion接口实现接口中的方法
在代码中我们可以看出**matches()**方法的两个参数的意思
Condition 接口
import org.springframework.core.type.AnnotatedTypeMetadata;
public interface Condition {
boolean matches(ConditionContext var1, AnnotatedTypeMetadata var2);
}
实现Condition接口
//判断是Windows系统
public class WindowsCondition implements Condition {
/**
*
* @param conditionContext 判断条件使用的上下文
* @param annotatedTypeMetadata 当前标注注解的注释信息
* @return
*/
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
//是否为Windows系统
//获取到IOC使用的Bean工厂
ConfigurableBeanFactory beanFactory= conditionContext.getBeanFactory();
//获取类加载
ClassLoader classLoader = conditionContext.getClassLoader();
//获取当前环境信息
Environment environment = conditionContext.getEnvironment();
BeanDefinitionRegistry registry = conditionContext.getRegistry();
String property = environment.getProperty("os.name");
if (property.contains("Windows")){
return true;
}
return false;
}
}
实现Condition接口
//判断是Linux系统
public class LinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
//是否为Windows系统
//获取到IOC使用的Bean工厂
ConfigurableBeanFactory beanFactory= conditionContext.getBeanFactory();
//获取类加载
ClassLoader classLoader = conditionContext.getClassLoader();
//获取当前环境信息
Environment environment = conditionContext.getEnvironment();
BeanDefinitionRegistry registry = conditionContext.getRegistry();
String property = environment.getProperty("os.name");
//可以判断容器中的bean的注册情况,也可以在容器中注册Bean
registry.containsBeanDefinition("person");
// registry.registerBeanDefinition(registerBeanDefinition);
if (property.contains("linux")){
return true;
}
return false;
}
}
通过这种方式就可以实现在我们不同的操作环境向容器中注入对应的组件。