Bean基础
本篇文章主要介绍Bean相关的知识点,让我们进一步去理解Spring 中 Bean的概念。
Spring Bean 基础#
Bean的名称-标识#
每个bean都有一个或多个标识符。这些标识符在bean的容器中必须是唯一的。一个bean通常只有一个标识符。当然,如果需要多个,则可以引用别名。
在xml中可以用id 和 name 来定义bean的标识,但是id只能设置一个名称,而name可以设置多个(用,号或者;号或者空格隔开)
<bean id="observer" name="observer_a,observer_b observer_c" class="com.lykos.ioc.chapter2.Observer"/>
不仅如此,创建bean的别名还有一种方式就是用alias
<alias name="observer_c" alias="observer_d"/><alias name="observer_c" alias="observer_e"/>Bean的创建方式#
bean支持构造方法和工场方法创建
构造方法创建#
当然工场方法创建与构造方法创建使用方法一样
@Datapublic class Observer { //无参构造方法 public Observer(){} //有参构造方法 @ConstructorProperties({"name"}) public Observer(String name){ this.name = name; } private String name;} <!-- 使用无参构造方法,创建--> <bean id="observer_constructor" class="com.lykos.ioc.chapter2.Observer"> </bean> <!-- 使用有参构造方法:属性名创建--> <bean id="observer_constructor_arg" class="com.lykos.ioc.chapter2.Observer"> <constructor-arg name="name" value="observer_constructor_arg_name"/> </bean> <!-- 使用有参构造方法:下标创建--> <bean id="observer_constructor_arg_index" class="com.lykos.ioc.chapter2.Observer"> <constructor-arg index="0" value="observer_constructor_arg_name_index"/> </bean>工场方法创建#
支持静态方法和非静态方法
静态工场#
public class ObserverStaticFactory { public static Observer createObserver(){ return new Observer(); }}非静态工场#
public class ObserverNonStaticFactory { public Observer createObserver(){ return new Observer(); }} <!-- 静态工场方法创建--> <bean id="observer_from_static_factory" class="com.lykos.ioc.chapter2.ObserverStaticFactory" factory-method="createObserver"></bean> <!-- 非静态工场方法创建--> <bean id="observerFactory" class="com.lykos.ioc.chapter2.ObserverNonStaticFactory"></bean> <bean id="observer_from_non_static_factory_b" factory-bean="observerFactory" factory-method="createObserver"> 依赖注入(Dependency Injection)#
依赖注入分为构造函数和setter两种方式
构造函数#
构造函数注入属性时有多种方式,分别为
直接注入不需要强调类型,名字或者下标的#
这种注入方式的前题是参数中没有歧义,不是基本类型
public class ConstructA { private ConstructB constructB; private ConstructC constructC; public ConstructA(ConstructB constructB,ConstructC constructC){ this.constructB = constructB; this.constructC = constructC; }}public class ConstructB {}public class ConstructC {} <bean id="constructA" class="com.lykos.ioc.chapter2.ConstructA"> <!--此处与顺序无关,也不需要指定名称和类型--> <constructor-arg ref="constructB"/> <constructor-arg ref="constructC"/> </bean> <bean id="constructB" class="com.lykos.ioc.chapter2.ConstructB"/> <bean id="constructC" class="com.lykos.ioc.chapter2.ConstructC"/>使用下标#
public class ConstructD { private int age; private String name; public ConstructD(int age,String name){ this.age = age; this.name = name; }}默认情况下就是配置顺序#
<!-- 不用指定下标,默认就是顺序就是我们配置顺序--> <bean id="constructD" class="com.lykos.ioc.chapter2.ConstructD"> <constructor-arg value="2"/> <constructor-arg value="this name"/> </bean>使用index指定下标#
<!-- 使用index指定下标,此时与我们配置的顺序无关,顺序可以随变--> <bean id="constructD_index" class="com.lykos.ioc.chapter2.ConstructD"> <constructor-arg index="1" value="this name 1"/> <constructor-arg index="0" value="2"/> </bean>使用type#
<!-- 使用type,此时与我们配置的顺序无关,顺序可以随变--> <bean id="constructD_type" class="com.lykos.ioc.chapter2.ConstructD"> <constructor-arg type="java.lang.String" value="this name 1 type"/> <constructor-arg type="int" value="112"/> </bean>使用name#
使用name是需要用到@ConstructorProperties注解。因为我们我们编译之后参数名会变成var1等,无法获取真实参数名,所以我们需要注解来标注。
//此处需要ConstructorProperties注释 @ConstructorProperties({"age", "name"}) public ConstructD(int var1, String var2) { this.age = var1; this.name = var2; }使用c:{}=xx#
在bean标签中使用c:{paramName}=xxx方式。要使用此方式配置文件中必须添加
xmlns:c="http://www.springframework.org/schema/c"
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 使用c:{},此时与我们配置的顺序无关,顺序可以随变--> <bean id="constructD_c_name" class="com.lykos.ioc.chapter2.ConstructD" c:name="cName" c:age="123"/>
</beans>setter 注入#
setter注入是在通过构造函数或者工场方法实例化对象后,使用对象setter方法调用的。
property方法#
public class SetterA { private String age; private String name; public void setAge(String age) { this.age = age; } public void setName(String name) { this.name = name; }} <bean id="setterA" class="com.lykos.ioc.chapter2.SetterA"> <property name="age" value="18"></property> <property name="name" value="lykos"></property> </bean>p:{}方法#
在bean标签中使用p:{propertyName}=xxx方式。要使用此方式配置文件中必须添加
xmlns:p="http://www.springframework.org/schema/p"
<bean id="setterB" class="com.lykos.ioc.chapter2.SetterA" p:age="17" p:name="pName"/>bean中其它概念#
depends-on#
实例化当前bean时需要依赖的对象,同时也决定了初始化和销毁顺序。多个用,隔开
<bean id="setterC" depends-on="setterB,setterA" class="com.lykos.ioc.chapter2.SetterA" p:age="19" p:name="dependName"/>懒加载 Lazy-initialized Beans#
正常情况在,IoC容器会在程序启动时全部初始化好singleton的对象,当然我们也可以使用lazy-init=true在指定当前对象不用着急加载,只需要在我们第一次使用时(也就是第一次getBean的时候)在初始化
<bean id="setterD" lazy-init="true" class="com.lykos.ioc.chapter2.SetterA"/>我们也可以在beans上设置default-lazy-init="true" 使其包含的所有bean默认都懒加载的
Lookup 查找型方法注入#
这个查找型方法,可以理解为就是一个工场方法,他主要是搭配原型-prototype 对象一起使用。主要原理是使用了CGLIB库生成字节码来动态生成覆盖该方法的子类来实现的。因此我们必须要先创建一个抽象类(抽象方法)或者接口(方法签名)
public abstract class LookUp { public abstract SetterA getSetterA();}或者
public interface LookUp { SetterA getSetterA();} <!-- 这里必须是scope="prototype" --> <bean id="setter_to_lookup" scope="prototype" class="com.lykos.ioc.chapter2.SetterA" p:age="17" p:name="pName"/> <bean id="lookup" class="com.lykos.ioc.chapter2.LookUp"> <!--lookup-method 定义后,每次getSetterA都会是一个新的对象--> <lookup-method name="getSetterA" bean="setter_to_lookup"></lookup-method> </bean>方法替换#
就是写一个通用方法替换原有的方法 定义一个被替换的方法
public class MethodReplace { public String say(String message){ return message; }}定义一个替换方法,必须实现MethodReplacer接口
public class MyMethodReplacer implements MethodReplacer { @Override public Object reimplement(Object obj, Method method, Object[] args) throws Throwable { System.out.println("do your thing"); //不能执行method.invoke(obj,args) return null; }}使用
<bean id="methodReplace" class="com.lykos.ioc.chapter2.MethodReplace"> <!-- 使用replaced-method替换原有的方法--> <replaced-method name="say" replacer="myMethodReplacer"></replaced-method> </bean> <bean id="myMethodReplacer" class="com.lykos.ioc.chapter2.MyMethodReplacer"></bean>的Scopes#
spring ioc中常用的是singleton和prototype。而singleton是默认的。
singleton 在ioc容器中有且仅有一个实例对象 prototype 在ioc容器中是多实例,也就是每当getBean时都会创建一个新的对象
当然在ioc中还有request,session,application,websocket不过这些scope只能在web应用容器中使用。
通常我们用scope="prototype"来设置bean的scope
<bean id="xxx" class="xxx" scope="prototype" />父级 Bean#
xml 中也可以用 <bean parent="parentBean">来继成父类bean的相关配置信息,例如:属性,构造参数,初始化方法等
parent既可以是具体的bean也可以是用于 <bean abstract="true"> 定义的抽象bean。
abstract=true 意味着在定义此类bean时我们可以不需要用class指定具体类限定名,当然容器在初始化过程中也会跳过带有abstract=true属性相关bean的实例化。
<bean id="parentBean2" abstract="true"> <property name="name" value="parentBean2"></property> <property name="age" value="18"></property> </bean>子类使用
<bean id="childBean" parent="parentBean2" class="com.lykos.ioc.chapter2.ChildBean"> <property name="desc" value="this is child"></property> </bean>此处com.lykos.ioc.chapter2.ChildBean需要有name和age属性
public class ChildBean { private String name; private int age; private String desc;}注意:子类也可以覆盖父类配置
<bean id="childBean2" parent="parentBean2" class="com.lykos.ioc.chapter2.ChildBean"> <property name="name" value="coverParentName"></property> <property name="desc" value="this is child"></property> </bean>Bean的自定义特性#
spring提供了许多接口,用于自定义Bean的特性
Lifecycle Callbacks#
从spring 2.5开始我们有三个选项来控制bean的生命周期行为
The
InitializingBeanandDisposableBeancallback interfacesCustom
init()anddestroy()methodsThe
@PostConstructand@PreDestroyannotations. You can combine these mechanisms to control a given bean.
InitializingBean#
实现此接口后。当容器中所有必要属性设置完成后,spring容器会回调afterPropertiesSet方法
定义initializingBean
public class MyInitializingBean implements InitializingBean { @Override public void afterPropertiesSet() throws Exception { System.out.println("所有属性设置完成!"); }}注册initializingBean
<bean id="myInitializingBean" class="com.lykos.ioc.chapter2.MyInitializingBean"></bean>DisposableBean#
实现此接口后,当容器被销毁后,可以得到回调。当然如果需要获取回调,我们还必须向容器中注册回调钩子。web容器中默认已加上 注册回调钩子
ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); // add a shutdown hook for the above context... ctx.registerShutdownHook();创建disposableBean
public class MyDisposableBean implements DisposableBean { @Override public void destroy() throws Exception { System.out.println("destroy"); }}注册disposableBean
<bean id="myDisposableBean" class="com.lykos.ioc.chapter2.MyDisposableBean"></bean>
Custom init() 和 destory()#
自定义初始化方法和销毁方法
在xml中可以在<beans default-init-method="init" default-destroy-method="destory"> 全局设置多个bean的初始化和销毁方法。当然也可以在<bean init-method="" destroy-method=""/>中覆盖全局的配置
当然在annotation中可以使用@Bean(initMethod = "",destroyMethod = "")设置
@PostContruct @PreDestroy#
这两个都是在使用annotation时使用
SmartLifecycle#
在讲解SmartLifecycle之前我们先认识一下
Lifecycle 接口可以为任何对象提供基本的生命周期方法 Phased 接口在多个SmartLifecycle中决定执行顺序
Lifecycle#
public interface Lifecycle { void start(); void stop(); boolean isRunning();}Phased#
public interface Phased { int getPhase();}SmartLifecycle#
public interface SmartLifecycle extends Lifecycle, Phased { boolean isAutoStartup(); void stop(Runnable callback);}可以看到SmartLifecycle继承了Lifecycle,Phased接口
下面根据一个自己实现了SmartLifecycle接口的类做详细说明
public class LifecycleBean implements SmartLifecycle { /** * 当所有对象已被实例化和初始化之后,将调用该方法 * 默认生命周期处理器将检查每个SmartLifecycle对象的 * isAutoStartup()方法和isRunning()方法返回值,如果都为true则被调用。 */ @Override public void start() { System.out.println("LifecycleBean start"); }
/** * isRunning()方法返回值为true则被调用。 */ @Override public void stop() { System.out.println("LifecycleBean stop"); }
/** * 1. 只有该方法返回false时,start方法才会被执行 * 2. 只有该方法返回true时,stop(Runnable callback)或stop()方法才会被执行。 */ @Override public boolean isRunning() { return true; }
/** * 如果工程中有多个实现接口SmartLifecycle的类,则这些类的start的执行顺序按getPhase方法返回值从小到大执行 * stop方法的执行顺序则相反,getPhase返回值较大类的stop方法先被调用,小的后被调用。 */ @Override public int getPhase(){ return 1; }
/** * 只有返回true star才会执行 * @return */ @Override public boolean isAutoStartup() { return true; }}ApplicationContextAware and BeanNameAware#
ApplicationContextAware#
此接口提供了为对象注入ApplicationContext的能力
public interface ApplicationContextAware { void setApplicationContext(ApplicationContext applicationContext) throws BeansException;}BeanNameAware#
在填充好bean属性之后且初始化回调(InitializingBean,Custom init)之前调用
public interface BeanNameAware { void setBeanName(String name) throws BeansException;}其它Aware接口#
spring还提供了多种Aware回调接口,这些接口使Bean向IoC容器表示它们需要某种基础结构依赖性
ApplicationEventPublisherAware#
事件发布
public interface ApplicationEventPublisherAware extends Aware { void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher);
}BeanClassLoaderAware#
public interface BeanClassLoaderAware extends Aware { void setBeanClassLoader(ClassLoader classLoader);}BeanFactoryAware#
public interface BeanFactoryAware extends Aware { void setBeanFactory(BeanFactory beanFactory) throws BeansException;}BootstrapContextAware#
public interface BootstrapContextAware extends Aware { void setBootstrapContext(BootstrapContext bootstrapContext);}LoadTimeWeaverAware#
public interface LoadTimeWeaverAware extends Aware { void setLoadTimeWeaver(LoadTimeWeaver loadTimeWeaver);}MessageSourceAware#
public interface MessageSourceAware extends Aware { void setMessageSource(MessageSource messageSource);}NotificationPublisherAware#
public interface NotificationPublisherAware extends Aware { void setNotificationPublisher(NotificationPublisher notificationPublisher);}ResourceLoaderAware#
public interface ResourceLoaderAware extends Aware { void setResourceLoader(ResourceLoader resourceLoader);}ServletConfigAware#
public interface ServletConfigAware extends Aware { void setServletConfig(ServletConfig servletConfig);}ServletContextAware#
public interface ServletContextAware extends Aware { void setServletContext(ServletContext servletContext);}