Spring及其各项功能(二)

十、依赖注入之手工装配—注解方式

注入依赖对象可以采用手工装配自动装配,在实际应用中建议使用手工装配,因为自动装配会产生未知情况,开发人员无法预见最终的装配结果。

手工装配依赖对象,在这种方式中又有两种编程方式:

1,在xml配置文件中,通过在bean节点下配置,如之前的例子

2,在Java代码中,使用@Autowired或@Resource注解方式进行装配。但我们需要xml配置文件中配置以下信息:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-2.5.xsd">
    <context:annotation-config/>
</beans>

这个配置,隐式注册了多个对注释进行解析处理的处理器:

AutowireAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor, PersistenceAnnotationBeanPostProcessor, RequireAnnotationBeanPostProcessoor

@Autowired和@Resource的区别:

@Autowired是Spring(org.springframework.beans.factory.annotation.Autowired)提供的,默认按类型装配;默认情况下它要求依赖对象必须存在,如果运行null值,可以设置它required属性为false。如果我们想使用按名称装配,可以结合@Qualifier注解一起使用。如下:

package cn.sjmz.service.impl;

import cn.sjmz.dao.PersonDao;
import cn.sjmz.service.PersonService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

public class PersonServiceBean implements PersonService {
    @Autowired @Qualifier("personDaoxxxx")
    private PersonDao personDao;    //用在字段上
    private String name = "xxxx";

    public void save() {
        personDao.add();
        System.out.println(name);
    }
}
package cn.sjmz.service.impl;

import cn.sjmz.dao.PersonDao;
import cn.sjmz.service.PersonService;
import org.springframework.beans.factory.annotation.Autowired;

public class PersonServiceBean implements PersonService {
    private PersonDao personDao;
    private String name = "xxxx";

    @Autowired
    public void setPersonDao(PersonDao personDao) { //用于属性的Setter方法上
        this.personDao = personDao;
    }

    public void save() {
        personDao.add();
        System.out.println(name);
    }
}

@Resource是J2EE(javax.annocation.Resource)提供的(推荐,因为没有和框架紧密耦合),也可以标注在字段或属性的Setter方法上,但它默认按名称装配,当找不到与名称匹配的bean才会按类型装配。当注解标注在字段上,即默认取字段的名称作为bean名称寻找依赖对象,当注解标注在属性的Setter方法上,即默认取属性名作为bean名称寻找依赖对象。

配置示例:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-2.5.xsd">
    <context:annotation-config/>

    <bean id="personDao" class="cn.sjmz.dao.impl.PersonDaoBean"></bean>
    <bean id="personService" class="cn.sjmz.service.impl.PersonServiceBean"></bean>
</beans>

代码示例:

package cn.sjmz.service.impl;

import cn.sjmz.dao.PersonDao;
import cn.sjmz.service.PersonService;

import javax.annotation.Resource;

public class PersonServiceBean implements PersonService {
    @Resource
    private PersonDao personDao;
    private String name = "xxxx";

    public void save() {
        personDao.add();
        System.out.println(name);
    }
}

测试代码:

import cn.sjmz.service.PersonService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringTest {
    public void setUp() throws Exception {
    }

    @Test
    public void instanceSpring() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
        PersonService personService = (PersonService) ctx.getBean("personService");
        personService.save();
    }
}

执行OK!

配置文件改为如下(id="personDaoxxxx"),也可正常注入。因为按名称匹配不到,会按类型匹配。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-2.5.xsd">
    <context:annotation-config/>

    <bean id="personDaoxxxx" class="cn.sjmz.dao.impl.PersonDaoBean"></bean>
    <bean id="personService" class="cn.sjmz.service.impl.PersonServiceBean"></bean>
</beans>

注入也可以指定name属性,一旦制定了name属性,就只能按名称装配了。如果name属性值在配置文件中存在,会提示Cannot resolve bean 'personDaoyyy'错误

package cn.sjmz.service.impl;

import cn.sjmz.dao.PersonDao;
import cn.sjmz.service.PersonService;

import javax.annotation.Resource;

public class PersonServiceBean implements PersonService {
    @Resource(name = "personDaoxxxx")
    private PersonDao personDao;
    private String name = "xxxx";

    public void save() {
        personDao.add();
        System.out.println(name);
    }
}

注入也可以设定到set方法上

package cn.sjmz.service.impl;

import cn.sjmz.dao.PersonDao;
import cn.sjmz.service.PersonService;

import javax.annotation.Resource;

public class PersonServiceBean implements PersonService {
    private PersonDao personDao;
    private String name = "xxxx";

    @Resource
    public void setPersonDao(PersonDao personDao) {
        this.personDao = personDao;
    }

    public void save() {
        personDao.add();
        System.out.println(name);
    }
}


十一、注解方式的实现原理

自定义注解代码:

package cn.sjmz.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

//自定义Resource注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
public @interface MyAnnotation {
    String name() default "";
}

注解注入实现代码:

import cn.sjmz.annotation.MyAnnotation;
import org.apache.commons.beanutils.ConvertUtils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.XPath;
import org.dom4j.io.SAXReader;

import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ItcastClassPathXmlApplicationContext {
    private List<BeanDefinition> beanDefinitions = new ArrayList<BeanDefinition>();

    private Map<String, Object> singletons = new HashMap<String, Object>();

    public ItcastClassPathXmlApplicationContext(String fileName) {
        this.readXML(fileName);
        this.instanceBeans();
        this.annotationInject();    //注解注入
        this.injectObject();
    }

    /**
     * 注解注入实现
     */
    private void annotationInject() {
        //1.循环所有的bean对象
        for (String beanName : singletons.keySet()) {
            Object bean = singletons.get(beanName); //获取bean对象
            //判断bean对象是否存在
            if (bean != null) {
                try {
                    //bean对象存在,获取bean对象的属性
                    PropertyDescriptor[] ps = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();
                    for (PropertyDescriptor propertyDesc : ps) {
                        Method setter = propertyDesc.getWriteMethod();  //获取属性的setter方法
                        //setter方法存在,运用反射技术判断setter方法上是否有MyAnnotation注解
                        if (setter != null && setter.isAnnotationPresent(MyAnnotation.class)) {
                            MyAnnotation annotation = setter.getAnnotation(MyAnnotation.class);
                            Object value = null;
                            if (annotation.name() != null && !"".equals(annotation.name())) {
                                value = singletons.containsKey(annotation.name());
                            } else {//没有设定name属性
                                value = singletons.get(propertyDesc.getName());
                                if (value == null) {    //按类型寻找
                                    for (String key : singletons.keySet()) {
                                        //判断类型是否匹配
                                        if (propertyDesc.getPropertyType().isAssignableFrom(singletons.get(key).getClass())) {
                                            value = singletons.get(key);
                                            break;
                                        }
                                    }
                                }
                            }
                            setter.setAccessible(true);
                            setter.invoke(bean, value);  //把引用对象注入到属性
                        }
                    }

                    //获取bean对象的字段
                    Field[] fields = bean.getClass().getDeclaredFields();
                    for (Field field : fields) {
                        //判断字段上是否存在注解
                        if (field.isAnnotationPresent(MyAnnotation.class)) {
                            MyAnnotation annotation = field.getAnnotation(MyAnnotation.class);
                            Object value = null;
                            if (annotation.name() != null && !"".equals(annotation.name())) {
                                value = singletons.containsKey(annotation.name());
                            } else {
                                value = singletons.get(field.getName());
                                if (value == null) {    //按类型寻找
                                    for (String key : singletons.keySet()) {
                                        //判断类型是否匹配
                                        if (field.getType().isAssignableFrom(singletons.get(key).getClass())) {
                                            value = singletons.get(key);
                                            break;
                                        }
                                    }
                                }
                            }
                            field.setAccessible(true);  //运行访问private字段
                            field.set(bean, value);  //把引用对象注入到属性
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 为bean对象的属性注入值
     */
    private void injectObject() {
        for (BeanDefinition beanDefinition : beanDefinitions) {
            Object bean = singletons.get(beanDefinition.getId());
            if (bean != null) {
                try {
                    PropertyDescriptor[] ps = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();
                    for (PropertyDefinition propertyDefinition : beanDefinition.getPropertyDefinitionList()) {
                        for (PropertyDescriptor propertyDesc : ps) {
                            if (propertyDefinition.getName().equals(propertyDesc.getName())) {
                                Method setter = propertyDesc.getWriteMethod();  //获取属性的setter方法
                                if (setter != null) {
                                    Object value;
                                    // ref注入
                                    if (propertyDefinition.getRef() != null && !"".equals(propertyDefinition.getRef().trim())) {
                                        value = singletons.get(propertyDefinition.getRef());
                                    } else {    //基本类型注入
                                        value = ConvertUtils.convert(propertyDefinition.getValue(), propertyDesc.getPropertyType());
                                    }
                                    setter.setAccessible(true); //setter方法是private时,需要增加这条语句
                                    setter.invoke(bean, value); //把引用对象注入到属性
                                }
                                break;
                            }
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 完成bean的实例化
     */
    private void instanceBeans() {
        for (BeanDefinition beanDefinition : beanDefinitions) {
            if (beanDefinition.getClassName() != null && (!"".equals(beanDefinition.getClassName().trim()))) {
                try {
                    singletons.put(beanDefinition.getId(), Class.forName(beanDefinition.getClassName()).newInstance());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 读取xml配置文件
     *
     * @param fileName
     */
    public void readXML(String fileName) {
        SAXReader saxReader = new SAXReader();
        Document document = null;
        try {
            URL xmlPath = this.getClass().getClassLoader().getResource(fileName);
            document = saxReader.read(xmlPath);
            Map<String, String> nsMap = new HashMap<String, String>();
            nsMap.put("ns", "http://www.springframework.org/schema/beans");     //加入命名空间
            XPath xsub = document.createXPath("//ns:beans/ns:bean");        //创建beans/bean查询路径
            xsub.setNamespaceURIs(nsMap);   //设置命名空间
            List<Element> beans = xsub.selectNodes(document);   //获取文档下所有bean节点
            for (Element element : beans) {
                String id = element.attributeValue("id");       //获取id属性值
                String clazz = element.attributeValue("class"); //获取class属性值
                BeanDefinition beanDefinition = new BeanDefinition(id, clazz);
                XPath propertySub = element.createXPath("ns:property");
                propertySub.setNamespaceURIs(nsMap);    //设置命名空间
                List<Element> properties = propertySub.selectNodes(element);
                for (Element property : properties) {
                    String propertyName = property.attributeValue("name");
                    String propertyRef = property.attributeValue("ref");
                    String propertyValue = property.attributeValue("value");
                    PropertyDefinition propertyDefinition = new PropertyDefinition(propertyName, propertyRef, propertyValue);
                    beanDefinition.getPropertyDefinitionList().add(propertyDefinition);
                }
                beanDefinitions.add(beanDefinition);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取bean实例
     *
     * @param beanName
     * @return
     */
    public Object getBean(String beanName) {
        return this.singletons.get(beanName);
    }
}

配置文件示例:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-2.5.xsd">
    <context:annotation-config/>

    <bean id="personDaoxxxx" class="cn.sjmz.dao.impl.PersonDaoBean"></bean>
    <bean id="personService" class="cn.sjmz.service.impl.PersonServiceBean"></bean>
</beans>

注解使用示例:

package cn.sjmz.service.impl;

import cn.sjmz.annotation.MyAnnotation;
import cn.sjmz.dao.PersonDao;
import cn.sjmz.service.PersonService;

public class PersonServiceBean implements PersonService {
    @MyAnnotation
    private PersonDao personDao;
    private String name = "xxxx";

    public void save() {
        personDao.add();
        System.out.println(name);
    }
}

测试代码:

import cn.sjmz.service.PersonService;
import org.junit.Test;

public class SpringTest {
    public void setUp() throws Exception {
    }

    @Test
    public void instanceSpring() {
//        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
        ItcastClassPathXmlApplicationContext ctx = new ItcastClassPathXmlApplicationContext("beans.xml");
        PersonService personService = (PersonService) ctx.getBean("personService");
        personService.save();
    }
}

正常执行,符合预期结果!

十二、依赖注入之自动装配

对于自动装配,了解一下就可以,不推荐大家使用。

使用示例: <bean id="..." class="..." autowire="byType">

autowire的属性取值如下:

1)byType:按类型装配,可以根据属性的类型,在容器中寻找跟该类型匹配的bean。如果发现多个,那么将会抛出异常。如果没有找到,即属性值为null。

2) byName:按名称装配,可以根据属性的名称,在容器中寻找跟该属性名相同的bean,如果没有找到,即属性值为null。

3) constructor:与byType的方式类似,不同之处在于它应用于构造器参数。如果在容器中没有找到与构造器参数类型一致的bean,那么将会抛出异常。

4) autodetect:通过bean的自省机制(introspection)来决定是使用constructor还是byType方式进行自动装配。如果发现默认的构造器,那么将使用byType方式。

自动装配配置文件示例:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-2.5.xsd">
    <context:annotation-config/>

    <bean id="personDao" class="cn.sjmz.dao.impl.PersonDaoBean"></bean>
    <bean id="personService" class="cn.sjmz.service.impl.PersonServiceBean" autowire="byType"></bean>
</beans>

代码示例:(注意:需要有Set方法,否则会报空指针异常

package cn.sjmz.service.impl;

import cn.sjmz.dao.PersonDao;
import cn.sjmz.service.PersonService;

public class PersonServiceBean implements PersonService {
    private PersonDao personDao;
    private String name = "xxxx";

    public void setPersonDao(PersonDao personDao) {
        this.personDao = personDao;
    }

    public void save() {
        personDao.add();
        System.out.println(name);
    }
}

测试代码示例:

import cn.sjmz.service.PersonService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringTest {
    public void setUp() throws Exception {
    }

    @Test
    public void instanceSpring() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
        PersonService personService = (PersonService) ctx.getBean("personService");
        personService.save();
    }
}

测试结构OK!

十三、通过在classpath自动扫描的方式把组件纳入spring容器中管理

前面的例子我们都是使用XML的bean定义来配置组件。在稍大的项目中,通常会有上百个组件,如果这些组件采用xml的bean定义来配置,显然会增加配置文件的体积,查找及维护起来也不方便。Spring2.5为我们引入了组件自动扫描机制。他可以在类路径底下寻找标注了@Component、@service、@Controller、@Repository注解的类,并把这些类纳入进spring容器中管理。他的作用和在xml文件中使用bean节点配置组件是一样的。要使用自动扫描机制,我们需要打开一下配置信息:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-2.5.xsd">
    <context:component-scan base-package="cn.sjmz"/>
</beans>

其中base-package为需要扫描的包(含子包),另外有了<context:component-scan>的配置,就可以不用配置<context:annotation-config>了,因为前者已经包含了后者注册的所有注解处理器

@Service 用于标注业务层组件

@Controller 用于标注控制层组件(如Struts中的action)

@Repository 用于标注数据访问组件,即DAO组件

@Component 泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注

实现代码示例:

package cn.sjmz.dao.impl;

import cn.sjmz.dao.PersonDao;
import org.springframework.stereotype.Repository;

@Repository
public class PersonDaoBean implements PersonDao {
    public void add(){
        System.out.println("执行PersonDaoBean中的add()方法");
    }
}
package cn.sjmz.service.impl;

import cn.sjmz.dao.PersonDao;
import cn.sjmz.service.PersonService;
import org.springframework.stereotype.Service;

@Service
public class PersonServiceBean implements PersonService {
    private PersonDao personDao;
    private String name = "xxxx";

    public void setPersonDao(PersonDao personDao) {
        this.personDao = personDao;
    }

    public void save() {
        personDao.add();
        System.out.println(name);
    }
}

测试代码示例:(注:默认getBean的名称为首字母小写的bean全名

import cn.sjmz.dao.PersonDao;
import cn.sjmz.service.PersonService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringTest {
    public void setUp() throws Exception {
    }

    @Test
    public void instanceSpring() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
        PersonService personService = (PersonService) ctx.getBean("personServiceBean");
        PersonDao personDao = (PersonDao) ctx.getBean("personDaoBean");
        System.out.println(personService);
        System.out.println(personDao);
    }
}

输出结果:

cn.sjmz.service.impl.PersonServiceBean@255b53dc
cn.sjmz.dao.impl.PersonDaoBean@1dd92fe2

也可以更改getBean使用的bean名称,如下(更改getBean使用名称为personService):

package cn.sjmz.service.impl;

import cn.sjmz.dao.PersonDao;
import cn.sjmz.service.PersonService;
import org.springframework.stereotype.Service;

@Service("personService")
public class PersonServiceBean implements PersonService {
    private PersonDao personDao;
    private String name = "xxxx";

    public void setPersonDao(PersonDao personDao) {
        this.personDao = personDao;
    }

    public void save() {
        personDao.add();
        System.out.println(name);
    }
}

测试代码:

import cn.sjmz.dao.PersonDao;
import cn.sjmz.service.PersonService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringTest {
    public void setUp() throws Exception {
    }

    @Test
    public void instanceSpring() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
        PersonService personService = (PersonService) ctx.getBean("personService");
        PersonDao personDao = (PersonDao) ctx.getBean("personDaoBean");
        System.out.println(personService);
        System.out.println(personDao);
    }
}

测试结果(OK):

cn.sjmz.service.impl.PersonServiceBean@255b53dc

cn.sjmz.dao.impl.PersonDaoBean@1dd92fe2


通过@Scope修改bean的作用域范围,默认为singleton,即getBean多次返回的实例为相同实例。如果改为@Scope("prototype"),则每次实例化返回为不同实例。

另外,还可以通过@PostConstruct注解指定初始化方法、通过@PreDestory指定销毁方法

代码示例:

package cn.sjmz.service.impl;

import cn.sjmz.dao.PersonDao;
import cn.sjmz.service.PersonService;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

@Service("personService") @Scope("prototype")
public class PersonServiceBean implements PersonService {
    private PersonDao personDao;
    private String name = "xxxx";

//    public void setPersonDao(PersonDao personDao) {
//        this.personDao = personDao;
//    }

    @PostConstruct
    public void initMethod() {
        System.out.println("PersonServiceBean中的initMethod.");
    }

    @PreDestroy
    public void destroyMethod() {
        System.out.println("PersonServiceBean中的destroyMethod.");
    }

    public void save() {
        personDao.add();
        System.out.println(name);
    }
}

测试代码:

import cn.sjmz.dao.PersonDao;
import cn.sjmz.service.PersonService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringTest {
    public void setUp() throws Exception {
    }

    @Test
    public void instanceSpring() {
//        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
        AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
        PersonService personService = (PersonService) ctx.getBean("personService");
        PersonService personService2 = (PersonService) ctx.getBean("personService");
        PersonDao personDao = (PersonDao) ctx.getBean("personDaoBean");
        System.out.println(personService);
        System.out.println(personService2);
        System.out.println(personDao);

        ctx.close();    //关闭容器
    }
}

输出结果:(结果不符合预期,发现destroyMethod没有执行。 测试发现不设置@Scope("prototype")输出结果符合预期,destroyMethod可以正常打印,根本原因待查!!!

PersonServiceBean中的initMethod.
PersonServiceBean中的initMethod.
cn.sjmz.service.impl.PersonServiceBean@62e136d3
cn.sjmz.service.impl.PersonServiceBean@c8e4bb0
cn.sjmz.dao.impl.PersonDaoBean@6279cee3

猜你喜欢

转载自blog.csdn.net/sjmz30071360/article/details/80370438