Spring主要是IoC和Aop,本文主要讲解IoC的简单使用。IoC,即控制反转,即通过配置文件来创建对象,当然还通过注解来创建对象。总的来说,不使用new来创建对象等。
一、对象的创建
(1)对象的获取
对象的创建过程实际上是将对象注册到IoC容器中,再通过getBean()取出的过程。根据配置不同分为基于xml配置文件和基于注解方式。但不管怎么样,两者都是为了将Bean对象配置到IoC容器中。
//基于XML
ApplicationContext context = new ClassPathXmlApplicationContext("xml文件路径");
//基于注解
ApplicationContext context = new AnnotationConfigApplicationContext(注解配置类.class);
无论哪种方式,最终我们都将获得IoC容器对象,即ApplicationContext对象(当然也可以直接使用其子类),来获得所需要的Bean实例(之所以需要强转,是因为getBean()本身使用了反射机制,newInstance()返回值为Object)。
Student student = (Student) context.getBean("Student");
需要注意的是,当获得context时(快加载),Spring已经将所有的需要注册的bean都进行了实例化和属性注入,然后放到了IoC容器中(ConcurrentHashMap),getBean()只是从该容器中获得对应对象而已
(2)基于xml文件来配置实例化对象
前面我们只讲解了如何从IoC获取实例化的对象,但是如何配置才能够将对象放入IoC容器。先介绍基于XML文件的实现方法,主要是三种:①普通的创建方法(调用构造函数)、②静态工厂方法、③动态工厂方法。(注:原理上来讲,工厂方法与①创建的Bean并不是一样的,它们会被放在不同的ConcurrentHashMap中,但由于继承的关系,ApplicationContext中含有这两个Map,且getBean()并没有区分,因此倒也不用过于区分,只是说如果配置文件中对①这种方法的配置对②③可能无效)
①普通的创建方法
<!-- id是这个bean的名称,也是IoC容器中的key,class是类所在的全路径(包名.类名) -->
<bean id="Student" class="Test.Student"></bean>
其原理简单来讲(包括后面的原理只是大概实现了思想,表明各个属性的含义,源码实现更为复杂)是:通过读取配置文件,获得id的值和class的值,那么通过反射获取对象,再将id和Object以键值对放入容器
className = ... //获得xml配置内容
id = ... //获得xml配置内容
Class c = Class.forName(className);//反射
Object obj = c.newInstance();//反射
map.put(id,Object);//伪代码,将id与Object放入IoC容器中
②静态工厂
首先,得有个工厂类,里面有个静态方法。
public class StaticFactory {
public static Cat newCat() {
return new Cat("MMMI");}}
接下来就是在配置文件中配置该工厂,与①不同的是,我们需要使用factory-method来指定哪一个方法是工厂方法
<!-- factory-method 指定静态工厂方法 -->
<bean id="Cat" class="Test.StaticFactory" factory-method="newCat"></bean>
这样就会将newCat()产生的Cat对象,以id值为key加入到IoC容器中。原理依旧是反射
Class c = Class.forName(className);//反射获取class对象
Method method = c.getMethod(methodName);//获得指定的方法
Object objec = method.invoke(null);//由于是静态方法,因此使用null来调用获得返回值对象
③动态工厂创建
首先要有一个动态工厂类,就把静态工厂类的newCat()的static去掉即可。(代码略)
接下来就是配置,由于是动态工厂,因此还必须指定工厂的对象,即增加了factory-bean属性来指定工厂的对象,因此需要先将动态工厂注册进入IoC容器。
<!-- 注册动态工厂 -->
<bean id="factory" class="Test.StaticFactory"></bean>
<!-- factory-bean指定动态工厂对象 -->
<bean id="Cat" class="Test.StaticFactory" factory-method="newCat" factory-bean="factory"></bean>
原理与静态工厂类似,只不过需要通过factory-bean来从IoC容器中获得factory对象,再调用方法
String className = ...;
String methodName = ...;
Object factory = ...;//通过factory-bean从IoC容器中获得
Class c = Class.forName(className);//反射
Method method = c.getMethod(methodName);//反射
Object objec = method.invoke(factory);//反射获得返回值
(3)基于XML+注解的实例化对象配置
首先,基于注解分为两种,一种是纯注解,即使用AnnotationConfigApplicationContext来创建IoC容器,一种是使用XML文件,使用ClassPathXmlApplicationContext来获得IoC容器,但是使用了部分注解。因此,后者需要在配置文件中来开启注解(导入aop的jar包,且xml文件要引入context空间约束)
<!-- base-package 表示要扫描注解的包,只要该包下的注解才会生效 -->
<context:component-scan base-package="Test"></context:component-scan>
但是此时只是开启了注解,真正将对象加入IoC容器的注解是@Component("id值");如果未指定id值则默认为类名的首字符小写
@Component("Cat")
public class Cat implements Pet{
...
}
这是普通方法的配置过程,对于两种工厂方法,Spring3和Spring4版本引入了新的注解@Bean(),name值为存入的id,无论是动态工厂还是静态工厂都需要在工厂类上使用@Component来出创建
@Component
public class StaticFactory {
@Bean(name="Cat")
public static Cat newCat() {
return new Cat("MMMI");
}
}
如果工厂方法有参数,系统会自动注入(注入操作会在后面讲解)
(4)完全基于注解
完全基于注解,实际上就是将xml配置文件去除。但是因此,我们需要增加一个或者多个配置类来将(3)中残留在配置文件中一些配置来转移到配置类中,这个类的class对象也就是AnnotationConfigApplicationContext的参数。
在配置类中,首先需要使用@Configuration来表示该类是一个配置类。
在xml文件中,我们还残留一个开启注解的配置,在配置类中使用@ComponentScan("要扫描的包")来代替
如果有多个配置类,@import导入,且子配置类不需要@Configuration和@ComponentScan
也可以使用@PropertySource("文件位置")来加载指定位置的properties文件
@Configuration
@ComponentScan("Test")
public class Config {
}
至此,我们已经能够通过Spring来创建对象了