Spring 是一个为了解决企业应用开发的复杂性而创建的开源框架。是一个轻量级的控制反转( IOC)和面向切面( AOP )的容器框架:
- 从大小与开销两方面而言 Spring 都是轻量的
- 通过控制反转( IOC )的技术达到松耦合的目的
- 提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务进行内聚性的开发
- 包含并管理应用对象的配置和生命周期的容器
- 将简单组件组合称为复杂的框架
1、创建maven项目
首先通过IDEA创建一个Maven类型的Spring项目,点击File->new->Project弹出如下界面,选择maven,点击next
之后为项目选择名称和位置,并且可以设置项目所属公司名GroupId、输出名ArtfactId、版本名Version,最后生成的目录结构如下右图所示, 在src目录下有main和test两个文件夹,main用于存放源文件,test/java测试模块,通过JUnit生成的测试文件会自动保存到该目录。main下的java文件夹为Source Root,resources为Resources Root文件夹存放配置文件。最外层pom.xml为maven的配置文件。
2、使用接口降低耦合度
如下所示我创建了两个汽车类Audi和Toyota,分别有三个方法start()、run()、stop()。还有一个人XiaoWang,驾驶汽车回家goHome(),首先需要创建汽车类对象audi,然后再通过audi调用方法回家。
public class Audi {
public void start(){
System.out.println("奥迪启动!");
}
public void run(){
System.out.println("奥迪行驶中...");
}
public void stop(){
System.out.println("奥迪停车!");
}
}
public class XiaoWang {
private Audi audi=new Audi();
public void goHome(){
audi.start();
audi.run();
audi.stop();
}
}
这时如果Xiaowang希望更换车辆Toyota,他就需要再创建一个新的对象toyota,并且重写一个goHome方法去调用相同的start()、run()、stop()方法,这是十分繁琐与不便的。造成这种情况的原因是XiaoWang与汽车类Audi之间的耦合度过高。通过使用接口将耦合在一起的整体拆分为两部分,可以将汽车Audi、Toyota抽象为一个接口Car,然后XiaoWang通过传入Car对象来使用不同的汽车。
//公共接口
public interface Car {
void start();
void run();
void stop();
}
//汽车类实现接口
public class Toyota implements Car{
public void start(){
System.out.println("丰田启动!");
}
public void run(){
System.out.println("丰田行驶中...");
}
public void stop(){
System.out.println("丰田停车!");
}
}
//使用接口调用汽车
public class XiaoWang {
private Car car;
public XiaoWang(Car car) {
this.car = car;
}
public void goHome(){ //通过Car接口来调用汽车类的方法
car.start();
car.run();
car.stop();
}
}
public void main() {
Car car =new Toyota(); //创建传入不同的car对象来使用
XiaoWang xiaoWang=new XiaoWang(car);
xiaoWang.goHome();
}
3、IOC
控制反转(IOC,Inversion of Controll)是一个重要的面向对象编程的规则,用以削弱计算机程序的耦合问题,也是轻量级 Spring 框架的核心。所谓控制反转是指应用程序本身不负责所需对象的创建和维护,而是由外部容器完成,应用程序直接拿来使用。如下图所示,我们将一个普通Java对象(POJOs,Plain Ordinary Java Objects)传入Spring容器,通过读取配置元数据,之后就会生产一个符合我们系统需要的并且可以直接使用的对象。通过使用IOC,我们不必再手动创建和管理对象了,并且可以直接使用一个对象
依赖注入(Dependency Injection)是Spring实现IOC的一种方式,即在容器运行期间,动态地将依赖关系注入到对象。
手动实现IoC容器
如下所示为手动实现的Ioc容器,它利用一个ConcurrentHashMap来保存字符串类型的beanId到对应Bean类的映射关系。其getBean()方法即通过beanId查找Map中对应的Bean并且返回。setBean()方法来创建新的bean存入到Map中,参数除了需要传入bean所属类型clazz和beanId标识外,还需要传入其构造方法所需要的bean,例如构造一个XiaoWang对象,需要传入一个Car作为参数。之后根据参数beanId从Map中取出对应的bean,将其传入clazz的构造方法,遍历clazz所有的构造方法找到合适的实例化bean。最后将实例化的bean存入Map。
public class IocContainer {
private Map<String, Object> beans = new ConcurrentHashMap<String, Object>();
/**
* 根据beanId返回一个Bean
* @param beanId 要获取的bean的Id
* @return 返回找到的bean
*/
public Object getBean(String beanId) {
return beans.get(beanId);
}
/**
* 创建一个bean
* @param clazz 要创建的bean的类型
* @param beanId 对应的beanId
* @param paramBeanIds 对应构造方法所需要的参数的beanId列表
*/
public void setBeans(Class clazz, String beanId, String... paramBeanIds) {
//1、获取构造方法所需的参数bean
Object[] paramBeans = new Object[paramBeanIds.length];
for (int i = 0; i < paramBeanIds.length; i++) {
paramBeans[i] = beans.get(paramBeanIds[i]);
}
//2、调用构造方法实例化bean
Object bean = null;
for (Constructor constructor : clazz.getConstructors()) {
try { // 依次遍历clazz类的所有构造方法找到合适的来实例化bean
bean = constructor.newInstance(paramBeans);
} catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
e.printStackTrace();
}
}
if (bean == null)
throw new RuntimeException("没有合适的构造方法实例化" + beanId);
//3、将bean保存到beans中
beans.put(beanId, bean);
}
}
通过一个Test来测试上面的IOC容器,在使用之前需要创建bean,然后在testIoc()方法中就可以直接使用bean而不需要创建和维护对象。
class IocContainerTest {
private IocContainer iocContainer=new IocContainer();
@BeforeEach
void setUp() {
//在使用之前创建bean
iocContainer.setBeans(Audi.class,"audi");
iocContainer.setBeans(Toyota.class,"toyota");
//传入Bean参数audi构造XiaoWang
iocContainer.setBeans(XiaoWang.class,"xiaowang","audi");
}
@Test
void testIoc() {
XiaoWang x1=(XiaoWang)iocContainer.getBean("xiaowang"); //直接获取并使用bean
x1.goHome();
}
}
Spring中的IOC
spring中已经集成了Ioc容器供我们使用。其使用步骤如下:
1、首先在maven的配置文件pom.xml中引入Spring的依赖,只需要输入<artifactId>标签spring-core、spring-context,IDEA会提示补全对应的<groupId>和<version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.4.RELEASE</version>
</dependency>
......
2、在项目的src/main/resources文件夹下创建配置文件spring-ioc.xml,在其中注册所需要交友Ioc容器管理的Bean类,在<bean>标签内输入bean的id和对应的类
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="audi" class="com.spring.ioc.Audi"/>
</beans>
3、获取并使用bean。首先通过ApplicationContext加载resources目录下的配置文件spring-ioc.xml,然后通过getBean()从Ioc容器获取bean,需要传入bean的Id和Class作为参数
class AudiTest {
@Test
void run() {
//通过上下文管理类获取配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("spring-ioc.xml");
//获取Bean并使用
Audi audi = context.getBean("audi", Audi.class);
audi.run();
}
}
4、使用Bean
实例化
Spring实例化一个bean有三种方法:
- 第一种是通过构造方法实例化Bean,即之前使用的方法。如下所示,首先创建一个Bean类
public class Bean {
public Bean() {
System.out.println("Bean被创建");
}
}
之后在配置文件中注册:
<bean id="bean1" class="com.spring.ioc.Bean"/>
最后在代码中使用Bean如下
ApplicationContext context = new ClassPathXmlApplicationContext("spring-ioc.xml");
Bean bean1=context.getBean("bean1",Bean.class);
- 第二种是通过工厂的静态方法来实例化Bean。在Bean的基础上再创建一个BeanFactory来返回bean对象
public class BeanFactory {
public static Bean getBean(){
return new Bean();
}
}
接着在配置文件中注册,这里的factory-method要求必须为静态方法
<bean class="com.spring.ioc.BeanFactory" factory-method="getBean" id="bean2"/>
最后采用相同的方式获取bean
Bean bean2=context.getBean("bean2",Bean.class);
- 第三种方法是通过实例化工厂来实例Bean,首先工厂的getBean()方法是非静态的
public class BeanFactory {
public Bean getBean(){
return new Bean();
}
}
接着在配置文件中同时注册Bean和BeanFactory,并在bean中配置其对应的factory-bean和factory-method
<bean id="bean3Factory" class="com.spring.ioc.BeanFactory"/>
<bean id="bean3" class="com.spring.ioc.Bean" factory-bean="bean3Factory" factory-method="getBean"/>
最后采用相同的方法获取bean
Bean bean3=context.getBean("bean3",Bean.class);
为Bean添加别名可以通过name属性,也可以通过<alias>标签。通过别名创建的bean其实指向同一个bean对象。
<bean id="bean1" class="com.spring.ioc.Bean" name="bean1_1,bean1_2"/>
<alias name="bean1" alias="bean1_3"/>
属性注入
对Bean的属性进行注入有两种方法,第一种是通过构造函数进行注入,第二种是通过setXxxv()设置属性。
如下所示为一个bean类XiaoWang,他有两个属性age、audi,其中audi为自定义的汽车Audi对象,并且设置其构造方法和get/set方法如下
public class XiaoWang {
private int age;
private Audi audi;
public XiaoWang(int age, Audi audi) {
this.age = age;
this.audi = audi;
}
public int getAge() { return age; }
public void setAge(int age) {this.age = age; }
public Audi getAudi() {return audi; }
public void setAudi(Audi audi) {this.audi = audi; }
}
由于这里的构造方法需要传入两个参数,因此需要在配置文件中通过<constructor-arg>完成参数的传入。其中属性index代表是第几个参数,name代表成员变量的名称,type指定参数的类型,这里使用index即可指明对应哪个属性,因此name和type可以省略。在为参数赋值时,如果是Java自带数据类型,则使用value,如果是我们自己创建的类,则需要使用ref="",并传入对应的bean。这里第一个为age赋值为24,第二个赋值bean为之前创建的audi1
除了使用构造方法,还可以使用<property>对Bean的属性赋值。它会调用bean的setXxx()方法完成对属性的赋值。
<bean id="audi1" class="com.spring.ioc.Audi"/>
<bean id="xiaowang" class="com.spring.ioc.XiaoWang">
<constructor-arg index="0" name="age" value="24"/>
<constructor-arg index="1" name="audi" type="com.spring.ioc.Audi" ref="audi1"/>
<property name="age" value="21"/>
<property name="audi" ref="audi1"/>
</bean>
可以采用<constructor>和<property>的简便写法,首先在xml配置文件头中引入xmlns:c、xmlns:p,接着就可以在<bean>的属性中使用c:、p:来表示构造方法和属性。例如c:age代表传入属性age的构造方法,c:audi-ref代表传入属性audi的构造方法,p:age代表传入age的setAge()方法完成对age的赋值。注意由于audi并非Java的基本数据类型,所以其使用一般会带有“ref”之类的标识。
<bean id="audi1" class="com.spring.ioc.Audi"/>
<bean id="xiaowang" class="com.spring.ioc.XiaoWang"
c:age="22" c:audi-ref="audi1"
p:age="23" p:audi-ref="audi1"/>
Spring可以通过自动装配完成属性的注入,设置<beans default-autowire="byName" ...>代表通过beanId名完成自动装配,例如XiaoWang有一个属性audi,spring会自动查找Id为audi的Bean并且通过setAudi()方法注入到属性。如果beanId为audi111则查找失败。如果default-autowire="byType"代表依据类型查找,这时会查找类型为Audi的bean并完成注入。
如果Bean的属性是Java复合数据类型使用<property>赋值如下。在Bean XiaoWang中有string类型的List和Car类型的List属性,并且具有对应的get/set方法。在配置文件中赋值如下,常规类型的值使用<value>,自定义类型用<ref>。Set类型和List类似,只需要将<list>换为<set>即可。
<property name="stringList">
<list>
<value>strig1</value>
<value>字符串2</value>
</list>
</property>
<property name="carList">
<list>
<ref bean="audi1"/>
<ref bean="toyota1"/>
</list>
</property>
Map类型赋值如下,Bean中有Map类型的属性stringMap和carMap,分别使用<map>标签赋值
<property name="stringMap">
<map>
<entry key="sk1" value="string1"/>
</map>
</property>
<property name="carMap">
<map>
<entry key="car1" value-ref="audi1"/>
</map>
</property>
如果希望赋空值,只需要shiyong<null/>即可,<property name="carMap"> <null/> </property>
在使用属性时可以创建内部Bean,类似Java的内部类,供临时bean属性赋值使用
<bean id="xiaowang" class="com.spring.ioc.XiaoWang">
<property name="age" value="21"/>
<property name="audi">
<bean class="com.spring.ioc.Audi"/> <!--内部bean-->
</property>
</bean>
属性继承
如果两个Bean之间存在继承关系,可以使用parent属性指明Bean的父标签,从而可以继承父类的属性。例如下面有父类、子类两个Bean:
public class ParentBean {
protected String parentString;
public String getParentString() {
return parentString;
}
public void setParentString(String parentString) {
this.parentString = parentString;
}
}
public class ChildBean extends ParentBean { //继承自父类
private String childString;
public String getChildString() {
return childString;
}
public void setChildString(String childString) {
this.childString = childString;
}
@Override
public String toString() { //打印父类和子类的属性
return "ChildBean{" +
"childString='" + childString + '\'' +
", parentString='" + parentString + '\'' +
'}';
}
}
在配置文件中配置继承关系,父类<bean>设置abstract="true"代表不需要实例化该Bean,子类<bean>通过parent="paren"将父类指向了id为parent的<bean>。打印输出子类child:ChildBean{childString='子类字符串', parentString='父类字符串'},可以看到父类和子类的属性都被输出。
<bean id="parent" class="com.spring.ioc.ParentBean" abstract="true">
<property name="parentString" value="父类字符串"/>
</bean>
<bean id="child" class="com.spring.ioc.ChildBean" parent="parent">
<property name="childString" value="子类字符串"/>
</bean>
初始化和销毁
如果我们希望在Bean的初始化和销毁之前执行一些操作有两种方法,
第一是在<bean>中使用init-method、destroy-method来指定Bean在初始化和销毁时执行的方法,也可以在<beans>中通过属性default-init-method、default-destroy-method为所用的bean设置默认的方法。例如在Bean类中定义onInit()、onDestroy()方法,并在spring文件中进行配置:
<bean id="bean" class="com.spring.ioc.Bean" init-method="onInit" destroy-method="onDestroy" />
之后通过上下文对象context获取bean对象并且在关闭context时销毁
AbstractApplicationContext context=new ClassPathXmlApplicationContext("spring-ioc.xml");
Bean bean1=context.getBean("bean",Bean.class);
System.out.println(bean1);
context.close(); //关闭上下文对象context时会调用bean的销毁方法
第二种方法是通过实现InitializingBean、DisposableBean接口中的方法来执行创建、销毁之前的操作
public class Bean implements DisposableBean, InitializingBean {
@Override
public void destroy() throws Exception {
System.out.println("实现接口,Bean销毁之前");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("实现接口,Bean创建之前");
}
}
5、作用域
单例/多例模式
在<bean>标签中的scope属性可以设置bean的作用域,其默认值为singleton单例模式,与之对应的是prototype多例模式。所谓单例模式就是指在一个上下文对象context中只会实例化一个bean,而多例模式会创建多个不同的bean。
如下所示为一个Bean类,它有一个InnerBean类的成员变量
public class Bean {
private InnerBean innerBean;
public Bean() {
System.out.println("Bean被创建");
}
public InnerBean getInnerBean() {
return innerBean;
}
public void setInnerBean(InnerBean innerBean) {
this.innerBean = innerBean;
System.out.println("内部bean:"+innerBean);
}
}
public class InnerBean {
public InnerBean() {
System.out.println("InnerBean被创建");
}
}
在配置文件中配置innerBean和bean,并且将innerBean设置为bean的子属性,这里innerBean的scope为单例模式singleton
<bean id="innerBean" class="com.spring.ioc.InnerBean" scope="singleton"/>
<bean id="bean" class="com.spring.ioc.Bean">
<property name="innerBean" ref="innerBean"/>
</bean>
在测试模块中先创建两个innerBean实例,之后再创建一个bean实例
@Test
void testScope() {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-ioc.xml");
InnerBean inner1 = context.getBean("innerBean", InnerBean.class);
System.out.println("innerBean1:" + inner1);
InnerBean inner2 = context.getBean("innerBean", InnerBean.class);
System.out.println("innerBean2:" + inner2);
Bean bean = context.getBean("bean", Bean.class);
System.out.println("bean:" + bean);
}
运行结果如下所示,InnerBean被创建了一次,并且inner1、inner2以及bean内部innerBean所使用的都是同一个InnerBean
将配置文件中的innerBean设置为多例模式scope="prototype",再次运行代码结果如下,可见InnerBean被创建了三次,并且inner1、inner2以及bean内部innerBean所使用的是不同的InnerBean对象
Web作用域
web中常用的为request、session、application三个作用域。如下所示为定义一个Servlet的Controller,定义请求映射的路径为requestScope,并向页面返回本对象的toString()结果
@Controller
public class RequestController {
@RequestMapping("requestScope")
@ResponseBody
public String testScope(){
return this.toString();
}
}
将上面的Controller交由Spring管理并配置其作用域为request,部署服务器后访问该Controller页面,刷新页面可以看到两次request请求访问返回的对象不同,这是因为RequestController的作用域为request
<bean class="com.spring.ioc.controller.RequestController" scope="request"/>
同理定义一个SessionController,并定义其作用域为session。刷新页面返回的SessionController对象相同,但是在另一个浏览器访问sessionScope对象不同,这样由于同一个浏览器session相同,换一个浏览器会打开一个新的session
<bean class="com.spring.ioc.controller.RequestController" scope="request"/>
定义一个ApplicationController,其作用域为application。不论是刷新页面还是在不同的浏览器,都会返回相同的对象,因为只要服务器没有关闭,就属于同一个application
<bean class="com.spring.ioc.controller.ApplicationController" scope="application"/>
自定义作用域
spring支持用户自定义作用域,类需要实现springframework的Scope接口。接口中的get()方法用于从作用域中返回对象,remove()方法用于从作用于删除并返回对象。之后在配置文件中注册自定义scope,例如spring为我们提供了一个自定义好的scope--SimpleThreadScope用于在一个线程中返回一个实例,其注册如下:
<!--引入自定义scope-->
<bean id="simpleThreadScope" class="org.springframework.context.support.SimpleThreadScope"/>
<!--配置scope-->
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="simpleThread" value-ref="simpleThreadScope"/>
</map>
</property>
</bean>
<!--使用自定义scope-->
<bean id="bean1" class="com.spring.ioc.Bean" scope="simpleThread"/>
Bean的懒加载
如果bean的作用域为默认的singleton,则spring会在启动时就会实例化该bean。可以通过设置lazy-init="true"来使Bean懒加载,即当我们使用getBean()方法获取时才会创建bean。如果希望所有的bean都是懒加载,在<beans>标签中设置default-lazy-init="true"
<bean id="bean" class="com.spring.ioc.Bean" lazy-init="true"/>
6、使用注解
为了避免使用繁琐的配置文件对Bean进行管理,Spring在2.5版本之后引入了注解的方式对Bean进行配置和管理。例如有一个类Bean1,我们首先创建Spring的上下文管理器BeanConfiguration用于管理Bean,为其添加注解@Configuration。在其中定义方法myBean1用于返回一个被管理的Bean1类,这里方法名myBean1就是默认的bean的Id,也可以通过注解@Bean的属性value为其添加别名,并且将新创建的Bean1对象作为返回值。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class BeanConfiguration {
@Bean(value = "myBeanId")
public Bean1 myBean1() {
return new Bean1();
}
}
在代码中获取Bean1如下,首先通过AnnotationConfigApplicationContext获取上下文对象context,其传入参数为刚定义的管理类BeanConfiguration。之后通过getBean()得到bean对象b1。
ApplicationContext context=new AnnotationConfigApplicationContext(BeanConfiguration.class);
Bean1 b1=context.getBean("myBean1",Bean1.class);
上面的方法为每一个Bean都要定义一个返回方法,这样也很繁琐。可以添加注解@ComponentScan来扫描包的方式将Bean自动添加到BeanConfiguration类中,这时它会自动扫描指定包目录下添加了@Component注解的Bean,并将其类名首字母小写作为默认bean的Id。例如下面BeanConfiguration会扫描com.spring.ioc.annotation,添加将其中的Bean1,并将bean1作为其id。也可以通过@Component的value属性为其添加自定义Id--”myBeanId“。类似@Component的注解还有@Controller用于标注控制层组件、@Service标注服务处、@Repository标注Dao层
//被管理的类
package com.spring.ioc.annotation;
import org.springframework.stereotype.Component;
@Component(value = "myBeanId")
public class Bean1 {
}
//Bean的管理类
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(value = "com.spring.ioc.annotation") //自动扫描包
public class BeanConfiguration {
}
属性注入
简单数据类型
简单的数据类型可以通过@Value()注解来进行属性注入,并且完成从string到对应类型的转换,结果为:
@Component
public class MyBean {
@Value("101")
private int simpleInt;
自定义类型的属性注入
对Bean的属性进行注入有三种方法,第一种是通过构造函数,如下所示,使用@Autowired注解修饰MyBean的构造方法,它会自动去BeanConfiguration管理的上下文中查找ParamBean并完成对属性paramBean的注入。
@Component
public class MyBean {
private ParamBean paramBean;
@Autowired
public MyBean(ParamBean paramBean) {
this.paramBean = paramBean;
}
}
第二种方法是通过setXxx()方法完成对属性paramBean的注入
@Component
public class MyBean {
private ParamBean paramBean;
@Autowired
public void setParamBean(ParamBean paramBean) {
this.paramBean = paramBean;
}
}
第三种方法是直接为属性添加@Autowired,Spring会自动查找该类型的Bean完成注入
@Component
public class MyBean {
@Autowired
private ParamBean paramBean;
}
复合类型的属性注入
复合类型的属性注入也是通过@Autowired修饰,不同的是需要定义对应的Bean来返回相关类型。例如下面定义了一个String类型的List属性,并在Spring上下文管理器中定义了一个Bean来返回stringList,Spring会将返回的List注入到stringList属性。我们还可以通过@Component注解创建一个Bean返回stringList,效果相同。
@Component
public class MyBean {
private List<String> stringList;
@Autowired
public void setStringList(List<String> stringList) {
this.stringList = stringList;
}
}
@Configuration
@ComponentScan(value = "com.spring.ioc.annotation")
public class BeanConfiguration {
@Bean
public List<String> stringList(){
List<String> list=new ArrayList<String>();
list.add("string1");
list.add("字符串2");
return list;
}
}
如果在上下文管理器中存在String类型的Bean,那么会被自动作为元素添加到String类型的List属性中,并且可以通过@Order注解来指定元素添加的顺序,顺序的数字不必连续,也不必从1开始。运行结果如下所示:
@Configuration
@ComponentScan(value = "com.spring.ioc.annotation")
public class BeanConfiguration {
@Bean
@Order(36)
public String stringBean1() {
return "string3";
}
@Bean
@Order(21)
public String stringBean2() {
return "字符串4";
}
}
如果同时存在stringList、string类型的Bean,则会优先将String类型的Bean作为元素添加到stringList属性。这时如果仍然希望使用stringList注入,则需要@Qualifier来指定需要注入的beanId
@Autowired
@Qualifier("stringList")
public void setStringList(List<String> stringList) {
this.stringList = stringList;
}
同理可以使用@Autowired来修饰Map数据类型,并定义返回Map类型的Bean来完成注入。
作用域
通过注解的方式为Bean规定作用域只需要通过@Scope()即可,如下定义MyBean作用域为多例模式
@Component
@Scope("prototype")
public class MyBean {
......
可以通过CustomScopeConfigurer来添加自定义作用域,如下所示添加自定义作用域SimpleThreadScope
@Bean
public SimpleThreadScope MyScope() {
return new SimpleThreadScope();
}
@Bean
public CustomScopeConfigurer customScopeConfigurer() {
CustomScopeConfigurer configurer = new CustomScopeConfigurer();
configurer.addScope("simpleThread", MyScope());
return configurer;
}
初始化和销毁
通过注解的方式定义在Bean的初始化和销毁之前的执行函数有三种方法,第一种是实现InitializingBean、DisposalBean接口,这种方法和之前的XML配置一样。第二种方法是通过@PostConstruct、@PreDestroy注解来标注方法
@Component
public class MyBean {
......
@PostConstruct
public void onInit(){
System.out.println("创建Bean...");
}
@PreDestroy
public void onDestroy(){
System.out.println("销毁Bean...");
}
第三种方法是通过@Bean的属性initMethod和destroyMethod来指定方法
@Bean(initMethod = "onInit",destroyMethod = "onDestroy")
public MyBean myBean(){
return new MyBean();
}
在注解中可以通过@lazy来实现Bean的懒加载功能