Spring内核研究-管理bean的声明周期一(InitializingBean和init-method)
InitializingBean
package research.spring.beanfactory.ch4; import org.springframework.beans.factory.InitializingBean; public class LifeCycleBean implements InitializingBean{ public void afterPropertiesSet() throws Exception { System. out .println( " LifeCycleBean initializing... " ); } }
xml version="1.0" encoding="UTF-8"?> DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean name="lifeBean" class="research.spring.beanfactory.ch4.LifeCycleBean"> bean> beans>
package research.spring.beanfactory.ch4; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.ClassPathResource; public class LifeCycleTest { public static void main(String[] args) { XmlBeanFactory factory = new XmlBeanFactory( new ClassPathResource(
" research/spring/beanfactory/ch4/context.xml " )); factory.getBean( " lifeBean " ); } }
SHAPE /* MERGEFORMAT
装配bean的合作者 |
查看bean是否实现InitializingBean接口 |
调用afterPropertiesSet方法 |
init-method
package research.spring.beanfactory.ch4; public class LifeCycleBean{ public void init(){ System. out .println( " LifeCycleBean.init... " ); } }
xml version="1.0" encoding="UTF-8"?> DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean name="lifeBean" class="research.spring.beanfactory.ch4.LifeCycleBean"
init-method="init"> bean> beans>
final protected void init() throws Exception{
System.out.println("init method...");
if(true) throw new Exception("init exception");
// …… // 在一个bean的合作者设备完成后,执行一个bean的初始化方法。 protected void invokeInitMethods(String beanName, Object bean, RootBeanDefinition mergedBeanDefinition)
throws Throwable { // 判断bean是否实现了InitializingBean接口 if (bean instanceof InitializingBean) { if (logger.isDebugEnabled()) { logger.debug( " Invoking afterPropertiesSet() on bean with name ' " + beanName + " ' " ); } // 调用afterPropertiesSet方法 ((InitializingBean) bean).afterPropertiesSet(); } // 判断bean是否定义了init-method if (mergedBeanDefinition != null && mergedBeanDefinition.getInitMethodName() != null ) { // 调用invokeCustomInitMethod方法来执行init-method定义的方法 invokeCustomInitMethod(beanName, bean, mergedBeanDefinition.getInitMethodName()); } } // 执行一个bean定义的init-method方法 protected void invokeCustomInitMethod(String beanName, Object bean, String initMethodName) throws Throwable { if (logger.isDebugEnabled()) { logger.debug( " Invoking custom init method ' " + initMethodName + " ' on bean with name ' " + beanName + " ' " ); } // 使用方法名,反射Method对象 Method initMethod = BeanUtils.findMethod(bean.getClass(), initMethodName, null ); if (initMethod == null ) { throw new NoSuchMethodException(
" Couldn't find an init method named ' " + initMethodName + " ' on bean with name ' " + beanName + " ' " ); } // 判断方法是否是public if ( ! Modifier.isPublic(initMethod.getModifiers())) { // 设置accessible为true,可以访问private方法。 initMethod.setAccessible( true ); } try { // 反射执行这个方法 initMethod.invoke(bean, (Object[]) null ); } catch (InvocationTargetException ex) { throw ex.getTargetException(); } } // ………..
Spring内核研究-管理bean之间的关系三(自动装配)
Spring BeanFactory提供了类似pico container中自动装配组件依赖的对象的功能。自动装配能应用在每个组件上,可以为一些组件定义自动装配,而另一些组件则不使用。
no
byName
SHAPE /* MERGEFORMAT
|
|
按照bean定义的名称自动装配 |
SHAPE /* MERGEFORMAT
|
这个Bean将被注入到dao中 |
byType
SHAPE /* MERGEFORMAT
|
|
按照bean定义的类型自动装配 |
SHAPE /* MERGEFORMAT
|
这个Bean将被注入到dao中 |
SHAPE /* MERGEFORMAT
|
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'dao' defined in class path resource [research/spring/beanfactory/ch3/context.xml]: Unsatisfied dependency expressed through bean property 'database': There are 2 beans of type [class research.spring.beanfactory.ch3.Database] for autowire by type. There should have been 1 to be able to autowire property 'database' of bean 'dao'...
constructor其实时按byType的方式进行构造函数的注入。
SHAPE /* MERGEFORMAT
|
|
按照bean定义的类型自动装配 |
constructor装配方式不关心构造参数的顺序,无论构造函数参数的顺序如何Spring都会按类型匹配到正确的合作者进行注入。
autodetect
Spring内核研究-管理bean之间的关系二(自动依赖检查)
package research.spring.beanfactory.ch3; public class Dao { private String name; public void setName(String name) { this .name = name; } }
<? xml version="1.0" encoding="UTF-8" ?> <! DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd" > < beans > < bean name ="dao" class ="research.spring.beanfactory.ch3.Dao" > </ bean > < bean id ="database" class ="research.spring.beanfactory.ch3.Database" > </ bean > </ beans >
<? xml version="1.0" encoding="UTF-8" ?> <! DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd" > < beans > < bean name ="dao" class ="research.spring.beanfactory.ch3.Dao" dependency-check ="all" > </ bean > < bean id ="database" class ="research.spring.beanfactory.ch3.Database" > </ bean > </ beans >
- none(默认)
- simple
- objects
- all
<? xml version="1.0" encoding="UTF-8" ?> <! DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd" > < beans > < bean name ="dao" class ="research.spring.beanfactory.ch3.Dao" dependency-check ="objects" > </ bean > < bean id ="database" class ="research.spring.beanfactory.ch3.Database" > </ bean > </ beans >
- 不能对构造函数中的参数进行检查。
- 即使属性中有默认值,只要包含了set方法,那么dependency-check仍然需要检查Spring中是否配置了这个属性。
package research.spring.beanfactory.ch3; public class Dao { private Database database; private String name = " chenjie " ; // dependency-check仍然会检查这个属性是否配置注入 public void setName(String name) { this .name = name; } public void setDatabase(Database database) { this .database = database; } }
Spring内核研究-管理bean之间的关系一(depends-on)
<? xml version="1.0" encoding="UTF-8" ?> <! DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd" > < beans > < bean name ="dao" class ="research.spring.beanfactory.ch3.Dao" depends-on ="database" > </ bean > < bean id ="database" class ="research.spring.beanfactory.ch3.Database" > </ bean > </ beans >
DAO depend-on Database时,也可以在DAO上定义setDatabase方法来接收一个Database的实例。这样Sping会保证DAO创建前先创建Database实例,然后在把实例化DAO后调用DAO的setDatabase方法把刚才创建的Database的实例注入给DAO。前提条件时Database必须定义成单例的。否则Spring在DAO depend-on Database时会创建一个Database的实例,在DAO.setDatabase时又会创建Database另外的一个实例。这种情况可能不是你想要的,而且很可能会造成比较隐蔽的错误。
<? xml version="1.0" encoding="UTF-8" ?> <! DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd" > < beans > < bean name ="dao" class ="research.spring.beanfactory.ch3.Dao" depends-on ="database " > < property name ="database" > < ref bean ="database" ></ ref > </ property > </ bean > < bean id ="database" class ="research.spring.beanfactory.ch3.Database" > </ bean > </ beans >
<? xml version="1.0" encoding="UTF-8" ?> <! DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd" > < beans > < bean name ="dao" class ="research.spring.beanfactory.ch3.Dao" depends-on ="database" > < constructor-arg > < ref bean ="database" ></ ref > </ constructor-arg > </ bean > < bean id ="database" class ="research.spring.beanfactory.ch3.Database" > </ bean > </ beans >
Spring允许Bean和Bean依赖的Bean(合作者)上同时定义depends-on。比如A depends-on B && B depends-on C && C depends-on D。下面这样定义是合法的。Sping实例化他们的顺序是D->C->B->A。
<? xml version="1.0" encoding="UTF-8" ?> <! DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd" > < beans > < bean name ="a" class ="research.spring.beanfactory.ch3.A" depends-on ="b" /> < bean name ="b" class ="research.spring.beanfactory.ch3.B" depends-on ="c" /> < bean name ="c" class ="research.spring.beanfactory.ch3.C" depends-on ="D" /> < bean name ="d" class ="research.spring.beanfactory.ch3.D" /> </ beans >
<? xml version="1.0" encoding="UTF-8" ?> <! DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd" > < beans > < bean name ="a" class ="research.spring.beanfactory.ch3.A" depends-on ="b" /> < bean name ="b" class ="research.spring.beanfactory.ch3.B" depends-on ="c" /> < bean name ="c" class ="research.spring.beanfactory.ch3.C" depends-on ="D" /> < bean name ="d" class ="research.spring.beanfactory.ch3.D" depends-on ="A" /> </ beans >
<? xml version="1.0" encoding="UTF-8" ?> <! DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd" > < beans > < bean name ="a" class ="research.spring.beanfactory.ch3.A" depends-on ="d,c,b" /> < bean name ="b" class ="research.spring.beanfactory.ch3.B" /> < bean name ="c" class ="research.spring.beanfactory.ch3.C" /> < bean name ="d" class ="research.spring.beanfactory.ch3.D" /> </ beans >
上面的例子中A的实例化需要先实例化D,C,B。Spring会按照depend-on中定义的顺序来处理Bean。在这个例子里Spring实例化对象的顺利是D->C->B->A。虽然实例化对象的顺序和前面“A depends-on B && B depends-on C && C depends-on D”的情况一下,但是这里的意义是完全不同的。不能用“A depends-on D,C,B”代替“A depends-on B && B depends-on C && C depends-on D”。
depends-on是一个非常又用的功能,借助depends-on我们可以管理那些依赖关系不明显或者没有直接依赖关系的对象。Spring内核研究-通过工厂注入
静态工厂注入
xml version="1.0" encoding="UTF-8"?> DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean name="userManager" class="research.spring.beanfactory.ch2.UserManager"> <lookup-method name="getUserDao" bean="userDao" /> bean> <bean name="userDao" class="research.spring.beanfactory.ch2.UserDao"
factory-method="getInstance" / > beans>
package research.spring.beanfactory.ch2; public class UserDao { public static UserDao getInstance() { return new UserDao( " static factory method " ); } private String name = "" ; public UserDao(String name) { this .name = name; } public void create() { System. out .println( " create user from - " + name); } }
package research.spring.beanfactory.ch2;
import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.ClassPathResource; public class Test { public static void main(String[] args) { XmlBeanFactory factory = new XmlBeanFactory( new ClassPathResource(
" research/spring/beanfactory/ch2/context.xml " )); UserManager manager = (UserManager) factory.getBean( " userManager " ); manager.createUser(); } }
create user from - static factory method
- 静态工厂方法上不能有参数,也不能在Spring种定义静态工厂方法的参数。
- 静态工厂方法只能是public的,不能是private或protected的。
- 静态工厂方法不能和构造函数注入一起使用。下面的定义时不能正常工作的:
package research.spring.beanfactory.ch2; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.ClassPathResource; public class Test { public static void main(String[] args) { XmlBeanFactory factory=new XmlBeanFactory(new ClassPathResource(
"research/spring/beanfactory/ch2/context.xml")); UserManager manager=(UserManager) factory.getBean("userManager"); manager.createUser(); } }
实例工厂注入
修改context.xml:package research.spring.beanfactory.ch2; public class UserDaoFactory{ public UserDao getUserDao(){ return new UserDao( " UserDaoFactory " ); } }
xml version="1.0" encoding="UTF-8"?> DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean name="userManager" class="research.spring.beanfactory.ch2.UserManager"> <lookup-method name="getUserDao" bean="userDao" /> bean> <bean name="userDao" class="research.spring.beanfactory.ch2.UserDao"
factory-bean="userDaoFactory" factory-method="getUserDao" > bean> <bean name="userDaoFactory" class="research.spring.beanfactory.ch2.UserDaoFactory"> bean> beans>
- factory-bean定义了工厂Bean
- factory-method定义了工厂方法
- 静态工厂方法上不能有参数,也不能在Spring种定义静态工厂方法的参数。
- 静态工厂方法只能是public的,不能是private或protected的。
- 静态工厂方法不能和构造函数注入一起使用。
- 实例工厂方法不能是静态的,而静态工厂方法必须是静态的。
Spring内核研究-Lookup方法注入
Lookup方法注入
package research.spring.beanfactory.ch2; public class UserDao { private String name = "" ; public UserDao(String name){ this .name = name; } public void create(){ System. out .println( " create user from - " + name); } }
package research.spring.beanfactory.ch2; public class UserManager { public UserDao getUserDao() { return new UserDao( " UserManager.getUserDao() " ); } public void createUser() { UserDao dao = getUserDao(); // 通过getUserDao获得userDao dao.create(); } }
context.xmlpackage research.spring.beanfactory.ch2; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.ClassPathResource; public class LookupMethodTest { public static void main(String[] args) { XmlBeanFactory factory = new XmlBeanFactory( new ClassPathResource( " research/spring/beanfactory/ch2/context.xml " )); UserManager manager = (UserManager) factory.getBean( " userManager " ); manager.createUser(); // create a User } }
xml version="1.0" encoding="UTF-8"?> DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean name="userManager" class="research.spring.beanfactory.ch2.UserManager"> bean> <bean name="userDao class="research.spring.beanfactory.ch2.UserDao" > bean> beans>
运行LookupMethodTest你会看到屏幕输入” create user from - UserManager.getUserDao()”。
在这个场景中我们就可以利用Spring提供的“Lookup方法”来替换原有的getUserDao方法,实现自动获取userDao的功能。修改context.xml:
xml version="1.0" encoding="UTF-8"?> DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean name="userManager" class="research.spring.beanfactory.ch2.UserManager"> <lookup-method name="getUserDao" bean="userDao" /> bean> <bean name="userDao" class="research.spring.beanfactory.ch2.UserDao" > <constructor-arg> <value>lookup methodvalue> constructor-arg> bean> <bean name="userDaoFactory" class="research.spring.beanfactory.ch2.UserDaoFactory"> bean> beans>
再次运行LookupMethodTest你会看到不同的输出结果“create user from - lookup method”。字符串“lookup method”是通过构造函数注入给userDao的。原来的userManager.java并没有作任何修改,仍然是通过UserDao dao = getUserDao();来获得userDao的。这说明Spring已经替换了原有的getUserDao方法的实现,当执行getUserDao时Spring会在容器中寻找指定的Bean,并返回这个Bean。
Lookup方法的工作机制
package research.spring.beanfactory.ch2; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.ClassPathResource; public class LookupMethodTest { public static void main(String[] args) { XmlBeanFactory factory = new XmlBeanFactory( new ClassPathResource(
" research/spring/beanfactory/ch2/context.xml " )); UserManager manager = (UserManager) factory.getBean( " userManager " ); System. out .println(manager.toString()); // 打印userManager的信息 manager.createUser(); // create a User } }
< bean name ="userManager" class ="research.spring.beanfactory.ch2.UserManager" > < lookup-method name ="getUserDao" bean ="userDao" /> < lookup-method name ="getOtherDao" bean ="otherDao" /> bean>
修改UserManager:
package research.spring.beanfactory.ch2; public class UserManager { private UserDao dao; public void setDao(UserDao dao) { this .dao = dao; } public UserDao getUserDao(String daoName) { return new UserDao( " UserManager.getUserDao() " ); } public void createUser() { UserDao dao = getUserDao(“userDao”); // 通过getUserDao获得userDao dao.create(); } }
- 方法不能是private的,但可以是protected的。
- 方法不能是静态的。
在抽象类和接口上应用Lookup方法
package research.spring.beanfactory.ch2; public abstract class Factory { public abstract UserDao getProduct(); }
package research.spring.beanfactory.ch2; public class UserDaoFactory extends Factory{ public UserDao getProduct(){ return new UserDao( " UserDaoFactory " ); } }
new UserDaoFactory().getProduce();
package research.spring.beanfactory.ch2; public abstract class Factory { public abstract Object getProduct(); }
xml version="1.0" encoding="UTF-8"?> DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean name="userManager" class="research.spring.beanfactory.ch2.UserManager"> <lookup-method name="getUserDao" bean="userDao" /> bean> <bean name="userDao" class="research.spring.beanfactory.ch2.UserDao" > <constructor-arg> <value>lookup methodvalue> constructor-arg> bean> <bean name="userDaoFactory" class="research.spring.beanfactory.ch2.Factory" singleton="false"> <lookup-method name="getProduct" bean="userDao" /> bean> beans>
package research.spring.beanfactory.ch2; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.ClassPathResource; public class LookupMethodTest { public static void main(String[] args) { XmlBeanFactory factory = new XmlBeanFactory( new ClassPathResource(
" research/spring/beanfactory/ch2/context.xml " )); // 获得抽象工厂 Factory abstractFactory = (Factory) factory.getBean( " userDaoFactory " ); UserDao userDao = (UserDao) abstractFactory.getProduct(); System. out .println(userDao.toString()); userDao.create(); } }
< bean name ="userDaoFactory" class ="research.spring.beanfactory.ch2.Factory" singleton ="false" > < lookup-method name ="getProduct" bean ="userDao" /> bean> <bean name="documentDaoFactory" class="research.spring.beanfactory.ch2.Factory" singleton="false"> <lookup-method name="getProduct" bean="documentDao" /> bean>