文章目录
前言
在日常开发中,我们会遇到这么个场景:某些老项目中会出现原始jdbc或者线程池等工具的使用,在繁忙的开发中,在每个功能模块手动连接、释放对于开发人员来说,这简直就是一件丧尽天良的事情。
需求分析
对此我们预想通过配置或者实现某些接口,让spring自动完成对这些类对象自动完成类初始化以及资源销毁。
解决方案
思路概述
对此我们想到两种实现方式。
一种是基于配置的方案,即只需要用户在自己的类中编写好自己的初始化方法和销毁方法,然后在xml中配置好这些方法名,这样我们就可以在resourceLoader读取xml的时候发现这些配置,从而将信息加载到beanDefinition中,在创建bean的时候就可以通过反射的方式完成初始化方法调用和销毁方法注册到虚拟机钩子中。
第二种方法是通过继承接口的方式完成用户初始化方法和销毁的方法编写,让容器在bean创建过程中判断这个bean是否继承了初始化接口和销毁接口从而完成初始化方法调用和销毁方法注册到虚拟机钩子中。
类图设计
通过类图我们就可以看到spring框架的设计思路,首先将最顶层的可进行扩展配置的bean工厂接口定义一个可以销毁单例bean的新行为让抽象bean工厂继承,这时候,又出现一个非常奇妙的设计,AbstractBeanFactory并没有实现destorySingletons,而是这个方法交给其父类DefalultSingletonBeanRegistry,这样做的好处是既保证了bean工厂销毁bean方法这个行为的实现,又保证了扩展性。还符合设计原则中的单一职责原则,即把该做的事情交给该做这个事的类实现。
之后的设计也很容易理解AbstractAutowireCapableBeanFactory
通过依赖初始化bean、和销毁bean这两个接口,增加了initializeBean、invokeInitMethods、registerDisposableBeanIfNecessary三个方法,并交由createBean进行调用,可以看出这里也符合《clean code》的设计原则:同一个函数中的代码应该属于同一层级,一个函数只做一件事,后续扩展也很方便。
后续一个重要的扩展,就是在AbstractApplicationContext定义一个注册销毁方法将各种行为注册的虚拟机钩子中。
之后就是各种继承关系,将经过扩展后的bean工厂交给classPathXmlApplicationContext使用即可
代码结构
在对应目录使用命令
tree /f > structure.txt
可得
├─main
│ ├─java
│ │ └─cn
│ │ └─shark
│ │ └─springframework
│ │ ├─beans
│ │ │ │ BeansException.java
│ │ │ │ PropertyValue.java
│ │ │ │ PropertyValues.java
│ │ │ │
│ │ │ └─factory
│ │ │ │ BeanFactory.java
│ │ │ │ ConfigurableListableBeanFactory.java
│ │ │ │ DisposableBean.java
│ │ │ │ HierarchicalBeanFactory.java
│ │ │ │ InitializingBean.java
│ │ │ │ ListableBeanFactory.java
│ │ │ │
│ │ │ ├─config
│ │ │ │ AutowireCapableBeanFactory.java
│ │ │ │ BeanDefinition.java
│ │ │ │ BeanFactoryPostProcessor.java
│ │ │ │ BeanPostProcessor.java
│ │ │ │ BeanReference.java
│ │ │ │ ConfigurableBeanFactory.java
│ │ │ │ SingletonBeanRegistry.java
│ │ │ │
│ │ │ ├─core
│ │ │ │ └─io
│ │ │ │ ClassPathResource.java
│ │ │ │ DefaultResourceLoader.java
│ │ │ │ FileSystemResource.java
│ │ │ │ Resource.java
│ │ │ │ ResourceLoader.java
│ │ │ │ UrlResource.java
│ │ │ │
│ │ │ ├─support
│ │ │ │ AbstractAutowireCapableBeanFactory.java
│ │ │ │ AbstractBeanDefinitionReader.java
│ │ │ │ AbstractBeanFactory.java
│ │ │ │ BeanDefinitionReader.java
│ │ │ │ BeanDefinitionRegistry.java
│ │ │ │ CglibSubclassingInstantiationStrategy.java
│ │ │ │ DefaultListableBeanFactory.java
│ │ │ │ DefaultSingletonBeanRegistry.java
│ │ │ │ DisposableBeanAdapter.java
│ │ │ │ InstantiationStrategy.java
│ │ │ │ SimpleInstantiationStrategy.java
│ │ │ │
│ │ │ ├─util
│ │ │ │ ClassUtils.java
│ │ │ │
│ │ │ └─xml
│ │ │ XmlBeanDefinitionReader.java
│ │ │
│ │ └─context
│ │ │ ApplicationContext.java
│ │ │ ConfigurableApplicationContext.java
│ │ │
│ │ └─support
│ │ AbstractApplicationContext.java
│ │ AbstractRefreshableApplicationContext.java
│ │ AbstractXmlApplicationContext.java
│ │ ClassPathXmlApplicationContext.java
│ │
│ └─resources
└─test
├─java
│ └─cn
│ └─shark
│ └─springframework
│ │ ApiTest.java
│ │
│ ├─bean
│ │ UserDao.java
│ │ UserService.java
│ │
│ └─common
│ MyBeanFactoryPostProcessor.java
│ MyBeanPostProcessor.java
│
└─resources
important.properties
spring.xml
相关代码
BeanDefinition
增加initMethodName
、destroyMethodName
两个字段
package cn.shark.springframework.beans.factory.config;
import cn.shark.springframework.beans.PropertyValues;
public class BeanDefinition {
private Class beanClass;
private PropertyValues propertyValues;
private String initMethodName;
private String destroyMethodName;
public BeanDefinition(Class beanClass) {
this.beanClass = beanClass;
this.propertyValues = new PropertyValues();
}
public BeanDefinition(Class beanClass, PropertyValues propertyValues) {
this.beanClass = beanClass;
this.propertyValues = propertyValues != null ? propertyValues : new PropertyValues();
}
public Class getBeanClass() {
return beanClass;
}
public void setBeanClass(Class beanClass) {
this.beanClass = beanClass;
}
public PropertyValues getPropertyValues() {
return propertyValues;
}
public void setPropertyValues(PropertyValues propertyValues) {
this.propertyValues = propertyValues;
}
public String getInitMethodName() {
return initMethodName;
}
public void setInitMethodName(String initMethodName) {
this.initMethodName = initMethodName;
}
public String getDestroyMethodName() {
return destroyMethodName;
}
public void setDestroyMethodName(String destroyMethodName) {
this.destroyMethodName = destroyMethodName;
}
}
ConfigurableBeanFactory
定义一个新行为 void destroySingletons();

package cn.shark.springframework.beans.factory.config;
import cn.shark.springframework.beans.factory.HierarchicalBeanFactory;
public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, SingletonBeanRegistry {
String SCOPE_SINGLETON = "singleton";
String SCOPE_PROTYPE = "protype";
void addBeanPostProcessor(BeanPostProcessor beanPostProcessor);
void destroySingletons();
}
DefaultSingletonBeanRegistry
DefaultSingletonBeanRegistry 增加了一个disposableBeans的map集合用来存储需要销毁的bean对象,并增加一个destroySingletons()方法用来遍历这个map不断执行每个bean对象的销毁行为,而这个行为我们在上述解释类图时候就说过了AbstractBeanFactory
从ConfigurableBeanFactory
继承过来了这个行为,DefaultSingletonBeanRegistry
作为AbstractBeanFactory
的父类却实现了这个方法,这样的做法巧妙了保证的单一职责。
package cn.shark.springframework.beans.factory.support;
import cn.shark.springframework.beans.BeansException;
import cn.shark.springframework.beans.factory.DisposableBean;
import cn.shark.springframework.beans.factory.config.SingletonBeanRegistry;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public abstract class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {
private Map<String, Object> singletonObjects = new HashMap<String, Object>();
private final Map<String, DisposableBean> disposableBeans=new HashMap<>();
@Override
public Object getSingleton(String beanName) {
return singletonObjects.get(beanName);
}
protected void addSingleton(String beanName, Object singletonObject) {
singletonObjects.put(beanName, singletonObject);
}
public void registerDisposableBean(String beanName,DisposableBean bean){
disposableBeans.put(beanName,bean);
}
public abstract <T> T getBean(String name, Class<T> requiredType) throws BeansException;
public void destroySingletons(){
Set<String> keySet = this.disposableBeans.keySet();
Object[] disposableBeanNames = keySet.toArray();
for (int i = disposableBeanNames.length-1; i >=0 ; i--) {
Object beanName = disposableBeanNames[i];
DisposableBean disposableBean = disposableBeans.remove(beanName);
try {
disposableBean.destroy();
} catch (Exception e) {
throw new BeansException("Destroy method on bean with name '" + beanName + "' threw an exception", e);
}
}
}
}
DisposableBeanAdapter
考虑到实现销毁的方式有两种,故我们使用适配器模式保证注册销毁方法的时候无需关心这些细节,我们假设有这么些开发者作死在配置文件和bean中都实现了同一个销毁行为,如果我们不做特殊处理,两次销毁中肯定有一次会崩所以就如以下代码所示,如果这个bean继承了销毁行为的接口我们就执行,然后在判断bean定义传进来的方法名是否为空若不为空,我们先判断以下这个bean是否是继承了销毁行为接口以及配置的方法名是否是destory,若是这样说明开发者在作死,直接无视。
package cn.shark.springframework.beans.factory.support;
import cn.hutool.core.util.StrUtil;
import cn.shark.springframework.beans.BeansException;
import cn.shark.springframework.beans.factory.DisposableBean;
import cn.shark.springframework.beans.factory.config.BeanDefinition;
import java.lang.reflect.Method;
public class DisposableBeanAdapter implements DisposableBean {
private final Object bean;
private final String beanName;
private String destroyMethodName;
public DisposableBeanAdapter(Object bean, String beanName, BeanDefinition beanDefinition) {
this.bean = bean;
this.beanName = beanName;
this.destroyMethodName = beanDefinition.getDestroyMethodName();
}
@Override
public void destroy() throws Exception {
if (bean instanceof DisposableBean) {
((DisposableBean) bean).destroy();
}
//避免二次销毁
if (StrUtil.isNotEmpty(destroyMethodName)&&!(bean instanceof DisposableBean &&"destroy".equals(this.destroyMethodName))){
Method destroyMethod = bean.getClass().getMethod(destroyMethodName);
if (null==destroyMethod){
throw new BeansException("Couldn't find a destroy method named '" + destroyMethodName + "' on bean with name '" + beanName + "'");
}
destroyMethod.invoke(bean);
}
}
}
AbstractAutowireCapableBeanFactory
增加调用初始化行为和注册销毁行为
package cn.shark.springframework.beans.factory.support;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.shark.springframework.beans.BeansException;
import cn.shark.springframework.beans.PropertyValue;
import cn.shark.springframework.beans.PropertyValues;
import cn.shark.springframework.beans.factory.DisposableBean;
import cn.shark.springframework.beans.factory.InitializingBean;
import cn.shark.springframework.beans.factory.config.AutowireCapableBeanFactory;
import cn.shark.springframework.beans.factory.config.BeanDefinition;
import cn.shark.springframework.beans.factory.config.BeanPostProcessor;
import cn.shark.springframework.beans.factory.config.BeanReference;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
/**
* AutowireCapableBeanFactory 前后置处理器操作
* AbstractBeanFactory 前后置处理器操作
*/
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {
private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();
@Override
protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException {
Object bean = null;
try {
bean = createBeanInstance(beanName, beanDefinition, args);
applyPropertyValues(beanName, bean, beanDefinition);
bean = initializeBean(beanName, bean, beanDefinition);
} catch (Exception e) {
throw new BeansException("Instantiation of bean failed", e);
}
registerDisposableBeanIfNecessary(beanName, bean, beanDefinition);
addSingleton(beanName, bean);
return bean;
}
private Object initializeBean(String beanName, Object bean, BeanDefinition beanDefinition) {
Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);
try {
invokeInitMethods(beanName, wrappedBean, beanDefinition);
} catch (Exception e) {
throw new BeansException("Invocation of init method of bean[" + beanName + "] failed", e);
}
wrappedBean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
return wrappedBean;
}
private void invokeInitMethods(String beanName, Object bean, BeanDefinition beanDefinition) throws Exception {
if (bean instanceof InitializingBean) {
((InitializingBean) bean).afterPropertiesSet();
}
String initMethodName = beanDefinition.getInitMethodName();
if (StrUtil.isNotEmpty(initMethodName)) {
Method initMethod = beanDefinition.getBeanClass().getMethod(initMethodName);
if (null == initMethod) {
throw new BeansException("Could not find an init method named '" + initMethodName + "' on bean with name '" + beanName + "'");
}
initMethod.invoke(bean);
}
}
protected void registerDisposableBeanIfNecessary(String beanName, Object bean, BeanDefinition beanDefinition) {
if (bean instanceof DisposableBean || StrUtil.isNotEmpty(beanDefinition.getDestroyMethodName())) {
registerDisposableBean(beanName, new DisposableBeanAdapter(bean, beanName, beanDefinition));
}
}
protected Object createBeanInstance(String beanName, BeanDefinition beanDefinition, Object[] args) {
Constructor constructorToUse = null;
Class<?> beanClass = beanDefinition.getBeanClass();
Constructor<?>[] declaredConstructors = beanClass.getDeclaredConstructors();
for (Constructor<?> ctor : declaredConstructors) {
if (null != args && ctor.getParameterTypes().length == args.length) {
constructorToUse = ctor;
break;
}
}
return getInstantiationStrategy().instantiate(beanName, beanDefinition, constructorToUse, args);
}
protected void applyPropertyValues(String beanName, Object bean, BeanDefinition beanDefinition) {
try {
for (PropertyValue pv : beanDefinition.getPropertyValues().getPropertyValues()) {
String name = pv.getName();
Object value = pv.getValue();
if (value instanceof BeanReference) {
BeanReference beanReference = (BeanReference) value;
value = getBean(beanReference.getBeanName());
}
BeanUtil.setFieldValue(bean, name, value);
}
} catch (Exception e) {
throw new BeansException("Error setting property values:" + beanName);
}
}
public InstantiationStrategy getInstantiationStrategy() {
return instantiationStrategy;
}
public void setInstantiationStrategy(InstantiationStrategy instantiationStrategy) {
this.instantiationStrategy = instantiationStrategy;
}
@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessBeforeInitialization(result, beanName);
if (null == current) {
return result;
}
result = current;
}
return result;
}
@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessAfterInitialization(result, beanName);
if (null == current) {
return result;
}
result = current;
}
return result;
}
}
XmlBeanDefinitionReader
因为bean定义类增加了两个字段,故这里读取就要多加两个字段了
package cn.shark.springframework.beans.factory.xml;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.XmlUtil;
import cn.shark.springframework.beans.BeansException;
import cn.shark.springframework.beans.PropertyValue;
import cn.shark.springframework.beans.factory.config.BeanDefinition;
import cn.shark.springframework.beans.factory.config.BeanReference;
import cn.shark.springframework.beans.factory.core.io.Resource;
import cn.shark.springframework.beans.factory.core.io.ResourceLoader;
import cn.shark.springframework.beans.factory.support.AbstractBeanDefinitionReader;
import cn.shark.springframework.beans.factory.support.BeanDefinitionRegistry;
import com.sun.deploy.util.StringUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import java.io.IOException;
import java.io.InputStream;
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
super(registry);
}
public XmlBeanDefinitionReader(BeanDefinitionRegistry registry, ResourceLoader resourceLoader) {
super(registry, resourceLoader);
}
@Override
public void loadBeanDefinitions(Resource resource) throws BeansException {
try {
try (InputStream inputStream = resource.getInputStream()) {
doLoadBeanDefinitions(inputStream);
}
} catch (IOException | ClassNotFoundException e) {
throw new BeansException("IOException parsing XML document from " + resource, e);
}
}
@Override
public void loadBeanDefinitions(Resource... resources) throws BeansException {
for (Resource resource : resources) {
loadBeanDefinitions(resource);
}
}
@Override
public void loadBeanDefinitions(String... locations) throws BeansException {
for (String location : locations) {
loadBeanDefinitions(location);
}
}
@Override
public void loadBeanDefinitions(String location) throws BeansException {
ResourceLoader resourceLoader = getResourceLoader();
Resource resource = resourceLoader.getResource(location);
loadBeanDefinitions(resource);
}
protected void doLoadBeanDefinitions(InputStream inputStream) throws ClassNotFoundException {
Document doc = XmlUtil.readXML(inputStream);
Element root = doc.getDocumentElement();
NodeList childNodes = root.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
if (!((childNodes.item(i)) instanceof Element)) {
continue;
}
if (!("bean".equals(childNodes.item(i).getNodeName()))) {
continue;
}
Element bean = (Element) childNodes.item(i);
String id = bean.getAttribute("id");
String name = bean.getAttribute("name");
String className = bean.getAttribute("class");
String initMethod = bean.getAttribute("init-method");
String destroyMethod = bean.getAttribute("destroy-method");
Class<?> clazz = Class.forName(className);
String beanName = StrUtil.isNotEmpty(id) ? id : name;
if (StrUtil.isEmpty(beanName)) {
beanName = StrUtil.lowerFirst(clazz.getSimpleName());
}
BeanDefinition beanDefinition = new BeanDefinition(clazz);
beanDefinition.setInitMethodName(initMethod);
beanDefinition.setDestroyMethodName(destroyMethod);
for (int j = 0; j < bean.getChildNodes().getLength(); j++) {
if (!(bean.getChildNodes().item(j) instanceof Element)) {
continue;
}
if (!("property".equals((bean.getChildNodes().item(j).getNodeName())))) {
continue;
}
Element property = (Element) bean.getChildNodes().item(j);
String attrName = property.getAttribute("name");
String attrValue = property.getAttribute("value");
String attrRef = property.getAttribute("ref");
Object value = StrUtil.isNotEmpty(attrRef) ? new BeanReference(attrRef) : attrValue;
PropertyValue propertyValue = new PropertyValue(attrName, value);
beanDefinition.getPropertyValues().addPropertyValue(propertyValue);
}
if (getRegistry().containsBeanDefinition(beanName)) {
throw new BeansException("Duplicate beanName[" + beanName + "] is not allowed");
}
getRegistry().registerBeanDefinition(beanName, beanDefinition);
}
}
}
ConfigurableApplicationContext
可进行配置bean的接口定义一个注册销毁的行为
package cn.shark.springframework.context;
import cn.shark.springframework.beans.BeansException;
public interface ConfigurableApplicationContext extends ApplicationContext {
/**
* 刷新容器
*
* @throws BeansException
*/
void refresh() throws BeansException;
void registerShutdownHook();
void close();
}
AbstractApplicationContext
package cn.shark.springframework.context.support;
import cn.shark.springframework.beans.BeansException;
import cn.shark.springframework.beans.factory.ConfigurableListableBeanFactory;
import cn.shark.springframework.beans.factory.config.BeanFactoryPostProcessor;
import cn.shark.springframework.beans.factory.config.BeanPostProcessor;
import cn.shark.springframework.beans.factory.core.io.DefaultResourceLoader;
import cn.shark.springframework.context.ConfigurableApplicationContext;
import java.util.Map;
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
/**
* 刷新容器
*
* @throws BeansException
*/
@Override
public void refresh() throws BeansException {
// 1. 创建 BeanFactory,并加载 BeanDefinition
refreshBeanFactory();
// 2. 获取 BeanFactory
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
// 3. 在 Bean 实例化之前,执行 BeanFactoryPostProcessor (Invoke factory processors registered as beans in the context.)
invokeBeanFactoryPostProcessors(beanFactory);
// 4. BeanPostProcessor 需要提前于其他 Bean 对象实例化之前执行注册操作
registerBeanPostProcessors(beanFactory);
// 5. 提前实例化单例Bean对象
beanFactory.preInstantiateSingletons();
}
protected abstract void refreshBeanFactory() throws BeansException;
protected abstract ConfigurableListableBeanFactory getBeanFactory();
private void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
Map<String, BeanFactoryPostProcessor> beanFactoryPostProcessorMap = beanFactory.getBeansOfType(BeanFactoryPostProcessor.class);
for (BeanFactoryPostProcessor beanFactoryPostProcessor : beanFactoryPostProcessorMap.values()) {
beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
}
}
private void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
Map<String, BeanPostProcessor> beanPostProcessorMap = beanFactory.getBeansOfType(BeanPostProcessor.class);
for (BeanPostProcessor beanPostProcessor : beanPostProcessorMap.values()) {
beanFactory.addBeanPostProcessor(beanPostProcessor);
}
}
/**
* 按照类型返回bean实例
*
* @param type
* @return
* @throws BeansException
*/
@Override
public <T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException {
return getBeanFactory().getBeansOfType(type);
}
/**
* 返回注册表中所有的bean名称
*
* @return
*/
@Override
public String[] getBeanDefinitionNames() {
return getBeanFactory().getBeanDefinitionNames();
}
@Override
public Object getBean(String name) throws BeansException {
return getBeanFactory().getBean(name);
}
@Override
public Object getBean(String name, Object... agrs) throws BeansException {
return getBeanFactory().getBean(name, agrs);
}
@Override
public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
return getBeanFactory().getBean(name, requiredType);
}
@Override
public void registerShutdownHook() {
Runtime.getRuntime().addShutdownHook(new Thread(this::close));
}
@Override
public void close() {
getBeanFactory().destroySingletons();
}
}
测试
UserDao
package cn.shark.springframework.bean;
import java.util.HashMap;
import java.util.Map;
public class UserDao {
private static Map<String, String> hashMap = new HashMap<>();
public void initDataMethod() {
System.out.println("执行:init-method");
hashMap.put("10001", "巨大情");
hashMap.put("10002", "巨二情");
hashMap.put("10003", "巨三情");
}
public void destroyDataMethod() {
System.out.println("执行:destroy-method");
hashMap.clear();
}
public String queryUserName(String uId) {
return hashMap.get(uId);
}
}
UserService
package cn.shark.springframework.bean;
import cn.shark.springframework.beans.factory.DisposableBean;
import cn.shark.springframework.beans.factory.InitializingBean;
/**
* 测试bean
*
* @author Zsy
* @date 2021-08-30 21:41
*/
public class UserService implements InitializingBean, DisposableBean {
private String uId;
private String company;
private String location;
private UserDao userDao;
public String queryUserInfo() {
return userDao.queryUserName(uId) + "," + company + "," + location;
}
public String getuId() {
return uId;
}
public void setuId(String uId) {
this.uId = uId;
}
public String getCompany() {
return company;
}
public void setCompany(String company) {
this.company = company;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public UserDao getUserDao() {
return userDao;
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void destroy() throws Exception {
System.out.println("执行:UserService.destroy");
}
/**
* bean处理了属性填充后调用
*
* @throws Exception
*/
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("执行:UserService.afterPropertiesSet");
}
}
测试单元
@Test
public void test_Processor(){
ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("classpath:spring.xml");
applicationContext.registerShutdownHook();
UserService userService = applicationContext.getBean("userService", UserService.class);
String result = userService.queryUserInfo();
System.out.println(result);
}