首先介绍一下关于Spring的几个重要概念
Spring
Spring容器的概念,容器这个名字起的非常好,容器可以放很多的东西,我们的程序启动的时候会创建spring容器,会给spring容器一个清单,清单中列出了需要创建的对象以及对象依赖关系,spring容器会创建和组装好清单中的对象,然后将这些对象存放在spring容器中,当程序中需要使用的时候,可以到容器中查找获取,然后直接使用。
IOC:控制反转
对象A依赖于对象B,之前使用B对象的时候都需要自己去创建和组装,而现在这些创建和组装都交给spring容器去给完成了,使用者只需要去spring容器中查找需要使用的对象就可以了;这个过程中B对象的创建和组装过程反转了,所以叫控制反转,IOC是面向对象编程中的一种设计原则,主要是为了降低系统代码的耦合度,让系统利于维护和扩展。
DI:依赖注入
依赖注入是spring容器中创建对象时给其设置依赖对象的方式,比如给spring一个清单,清单中列出了
需要创建B对象以及其他的一些对象(可能包含了B类型中需要依赖对象),此时spring在创建B对象的
时候,会看B对象需要依赖于哪些对象,然后去查找一下清单中有没有包含这些被依赖的对象,如果有
就去将其创建好,然后将其传递给B对象;可能B需要依赖于很多对象,B创建之前完全不需要知道其他
对象是否存在或者其他对象在哪里以及被他们是如何创建,而spring容器会将B依赖对象主动创建好并将
其注入到B中去,比如spring容器创建B的时候,发现B需要依赖于A,那么spring容器在清单中找到A的
定义并将其创建好之后,注入到B对象中。
总之
- IOC控制反转,是一种设计理念,将对象创建和组装的主动控制权利交给了spring容器去做,控制的动作被反转了,降低了系统的耦合度,利于系统维护和扩展,主要就是指需要使用的对象的组装控制权被反转了,之前是自己要做的,现在交给spring容器做了。
- DI依赖注入,表示spring容器中创建对象时给其设置依赖对象的方式,通过某些注入方式可以让系统更灵活,比如自动注入等可以让系统变得很灵活。
- spring容器:主要负责容器中对象得创建、组装、对象查找、对象生命周期得管理等操作。
容器创建bean实例的方法
- 通过反射调用构造方法创建bean对象
- 通过静态工厂方法创建bean对象
- 通过实例工厂方法创建bean对象
- 通过FactoryBean创建bean对象
测试bean
public class Blog {
private String date;
private String title;
private String author;
private int num;
public Blog() {
}
public Blog(String date, String title, String author, int num) {
this.date = date;
this.title = title;
this.author = author;
this.num = num;
}
//省略了getter 和 setter方法
@Override
public String toString() {
return "Blog{" +
"date='" + date + '\'' +
", title='" + title + '\'' +
", author='" + author + '\'' +
", num=" + num +
'}';
}
}
通过反射调用构造方法创建bean对象
调用类的构造方法获取对应的bean实例,是使用最多的方式,这种方式只需要在xml bean元素中指定擦class属性,spring容器内部会自动调用该类型的构造方法来创建bean对象,将其放在容器中以供使用。
<!--通过无参构造创建bean-->
<bean id="noArgBlog" class="com.demo.blog.Blog"/>
<!--通过有参构造创建bean-->
<bean id="argBlog" class="com.demo.blog.Blog">
<constructor-arg index="0" value="2022-06-10"/>
<constructor-arg index="1" value="spring系列-01"/>
<constructor-arg index="2" value="wangms"/>
<constructor-arg index="3" value="1"/>
</bean>
constructor-arg:用于指定构造方法的参数值
index:构造方法中参数的位置,从0开始,依次递增
value:指定参数的值
ref:当插入的值为容器内其他bean的时候,这个值为容器中对应bean的名称
测试用例
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beansBlog.xml");
Blog noArgBlog = context.getBean("noArgBlog", Blog.class);
System.out.println(noArgBlog);
Blog argBlog = context.getBean("argBlog", Blog.class);
System.out.println(argBlog);
测试结果
通过无参构造函数创建的bean
Blog{date='null', title='null', author='null', num=0}
Blog{date='2022-06-10', title='spring系列-01', author='wangms', num=1}
通过静态工厂方法创建bean对象
创建一个静态工厂类,用于生成Blog对象。
public class BlogStaticFactory {
/**静态无参方法创建*/
public static Blog noArgBlogBuilder()
{
Blog blog = new Blog();
blog.setTitle("无参静态方法创建的...");
return blog;
}
/**静态有参方法创建*/
public static Blog argBlogBuilder(String date, String title, String author, int num)
{
return new Blog(date, title, author, num);
}
}
spring容器会自动调用静态工厂的静态方法获取指定的对象,将其放在容器中以供使用
<!-- 通过工厂静态无参方法创建bean对象 -->
<bean id="noArgStaticBlogBuilder" class="com.demo.blog.BlogStaticFactory" factory-method="noArgBlogBuilder"/>
<!-- 通过工厂静态有参方法创建bean对象 -->
<bean id="argStaticBlogBuilder" class="com.demo.blog.BlogStaticFactory" factory-method="argBlogBuilder">
<constructor-arg index="0" value="2022-06-10"/>
<constructor-arg index="1" value="有参静态方法创建的..."/>
<constructor-arg index="2" value="wangms2"/>
<constructor-arg index="3" value="2"/>
</bean>
class:指定静态工厂完整的类名
factory-method:静态工厂中的静态方法,返回需要的对象
constructor-arg:用于指定静态方法参数的值,用法和上面介绍的构造方法一样
spring容器启动的时候会自动调用BlogStaticFactory中的noArgBlogBuilder静态方法获取Blog对象,将其作为noArgStaticBlogBuilder名称对应的bean对象放在spring容器中。
会调用BlogStaticFactory的argBlogBuilder方法,并且会传入需要的参数,得到返回的Blog对象,将其作为argStaticBlogBuilder名称对应的bean对象放在spring容器中。
测试用例
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beansBlog.xml");
Blog noArgBlog = context.getBean("noArgStaticBlogBuilder", Blog.class);
System.out.println(noArgBlog);
Blog argBlog = context.getBean("argStaticBlogBuilder", Blog.class);
System.out.println(argBlog);
测试结果
Blog{date='null', title='无参静态方法创建的...', author='null', num=0}
Blog{date='2022-06-10', title='有参静态方法创建的...', author='wangms2', num=2}
通过实例工厂方法创建bean对象
让spring容器去调用某些对象的某些实例方法来生成bean对象放在容器中以供使用。
定义一个实例工厂
public class BlogFactory {
/**无参方法*/
public Blog noArgBuilder()
{
return new Blog();
}
/**有参方法*/
public Blog argBuilder(String date, String title, String author, int num)
{
return new Blog(date, title, author, num);
}
}
<!-- 定义一个工厂实例 -->
<bean id="blogFactory" class="com.demo.blog.BlogFactory"/>
<!-- 通过实例工厂无参方法创建bean对象 -->
<bean id="blog1" class="com.demo.blog.BlogStaticFactory" factory-bean="blogFactory" factory-method="noArgBuilder"/>
<!-- 通过实例工厂有参方法创建bean对象 -->
<bean id="blog2" class="com.demo.blog.BlogStaticFactory" factory-bean="blogFactory" factory-method="argBuilder">
<constructor-arg index="0" value="2022-06-10"/>
<constructor-arg index="1" value="实例工厂有参方法创建的..."/>
<constructor-arg index="2" value="wangms2"/>
<constructor-arg index="3" value="2"/>
</bean>
spring容器以factory-bean的值为bean名称查找对应的bean对象,然后调用该对象中factory-method
属性值指定的方法,将这个方法返回的对象作为当前bean对象放在容器中供使用
blog1对应的bean是通过BlogFactory的noArgBuilder方法生成的;
blog2对应的bean是通过BlogFactory的argBuilder方法生成的。
测试用例
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beansBlog.xml");
Blog noArgBlog = context.getBean("blog1", Blog.class);
System.out.println(noArgBlog);
Blog argBlog = context.getBean("blog2", Blog.class);
System.out.println(argBlog);
测试结果
Blog{date='null', title='null', author='null', num=0}
Blog{date='2022-06-10', title='实例工厂有参方法创建的...', author='wangms2', num=2}
通过FactoryBean来创建bean对象
前面我们学过了BeanFactory接口,BeanFactory是spring容器的顶层接口,而这里要说的是
FactoryBean,也是一个接口,这两个接口很容易搞混淆,FactoryBean可以让spring容器通过这个接口
的实现来创建我们需要的bean对象。
源码如下
public interface FactoryBean<T> {
/*** 返回创建好的对象 */
@Nullable T getObject() throws Exception;
/*** 返回需要创建的对象的类型 */
@Nullable Class<?> getObjectType();
/** * bean是否是单例的 **/
default boolean isSingleton() {
return true;
}
}
接口中有3个方法,前面2个方法需要我们去实现,getObject方法内部由开发者自己去实现对象的创
建,然后将创建好的对象返回给Spring容器,getObjectType需要指定我们创建的bean的类型;最后一
个方法isSingleton表示通过这个接口创建的对象是否是单例的,如果返回false,那么每次从容器中获取
对象的时候都会调用这个接口的getObject() 去生成bean对象。
最后一种方式因平常不怎么用,所以没有贴代码,感兴趣的可以实现接口试试。