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>
#
的Scopesspring 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" />
#
父级 Beanxml
中也可以用 <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
InitializingBean
andDisposableBean
callback interfacesCustom
init()
anddestroy()
methodsThe
@PostConstruct
and@PreDestroy
annotations. 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中决定执行顺序
#
Lifecyclepublic interface Lifecycle { void start(); void stop(); boolean isRunning();}
#
Phasedpublic interface Phased { int getPhase();}
#
SmartLifecyclepublic 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);
}
#
BeanClassLoaderAwarepublic interface BeanClassLoaderAware extends Aware { void setBeanClassLoader(ClassLoader classLoader);}
#
BeanFactoryAwarepublic interface BeanFactoryAware extends Aware { void setBeanFactory(BeanFactory beanFactory) throws BeansException;}
#
BootstrapContextAwarepublic interface BootstrapContextAware extends Aware { void setBootstrapContext(BootstrapContext bootstrapContext);}
#
LoadTimeWeaverAwarepublic interface LoadTimeWeaverAware extends Aware { void setLoadTimeWeaver(LoadTimeWeaver loadTimeWeaver);}
#
MessageSourceAwarepublic interface MessageSourceAware extends Aware { void setMessageSource(MessageSource messageSource);}
#
NotificationPublisherAwarepublic interface NotificationPublisherAware extends Aware { void setNotificationPublisher(NotificationPublisher notificationPublisher);}
#
ResourceLoaderAwarepublic interface ResourceLoaderAware extends Aware { void setResourceLoader(ResourceLoader resourceLoader);}
#
ServletConfigAwarepublic interface ServletConfigAware extends Aware { void setServletConfig(ServletConfig servletConfig);}
#
ServletContextAwarepublic interface ServletContextAware extends Aware { void setServletContext(ServletContext servletContext);}