package com.etoak.student.framework;
import com.etoak.student.entity.School;
import com.etoak.student.entity.Student;
import com.etoak.student.service.IStudentService;
import com.mysql.jdbc.StringUtils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.beans.PropertyDescriptor;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
//Bean工厂 ==》生产Bean的
public class BeanFactory {
public static void main(String[] args) {
//new BeanFactory();
BeanFactory f = new BeanFactory();
Student stu = (Student )f.getBean("stu");
System.out.println(stu);
/* School sch = (School)f.getBean("sch");
System.out.println(sch);
IStudentService servcie = (IStudentService)f.getBean("service");
System.out.println(servcie);*/
}
private static Map<String,BeanDefinition> beanDefinitions = new HashMap<>();
public BeanFactory(){
//读取配置文件
//从包里边寻找
//BeanFactory.class.getResourceAsStream("../../../../../bean.xml");
//从包外边寻找
InputStream is = BeanFactory.class.getClassLoader().
getResourceAsStream("beans.xml");
SAXReader reader = new SAXReader();
try {
Document doc = reader.read(is);
//获得根元素<beans>
Element root = doc.getRootElement();
List<Element> beans = root.elements("bean");
for (Element bean : beans) {
//id 获得bean元素中的id属性的值
String id = bean.attributeValue("id");
//类名字
String className = bean.attributeValue("class");
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setId(id);
beanDefinition.setClassName(className);
//获得bean的子元素
List<Element> constructorArgsElements = bean.elements("constructor-args");
if(constructorArgsElements!=null && constructorArgsElements.size()>0){
//需要带参数构造方法构造对象
List<ConstructorArgs> constructorArgsList = new ArrayList<>();
for(Element consEle:constructorArgsElements){
ConstructorArgs ca = new ConstructorArgs();
String name = consEle.attributeValue("name");
//不一定有值
ca.setName(name);
//取类型
String type = consEle.attributeValue("type");
//ConstructorArgs类中的type的类型就是Class
ca.setType(type!=null?Class.forName(type):null);
ca.setRef(consEle.attributeValue("ref"));
ca.setValue(consEle.attributeValue("value"));
constructorArgsList.add(ca);
}
beanDefinition.setConstructorArgsList(constructorArgsList);
}else{
//不需要带参数的构造方法 1.无参构造 2.3.4.
List<Element> pros = bean.elements("property");
if(pros!=null && pros.size()>0){
//有<property>标签
List<PropertyDefinition> propertyDefinitions = new ArrayList<>();
for(Element pro:pros){
PropertyDefinition pd = new PropertyDefinition();
pd.setName(pro.attributeValue("name"));
pd.setValue(pro.attributeValue("value"));
pd.setRef(pro.attributeValue("ref"));
propertyDefinitions.add(pd);
}
beanDefinition.setProps(propertyDefinitions);
}
}
beanDefinitions.put(id,beanDefinition);
}
}catch(Exception e){
e.printStackTrace();
}
}
//该方法对外生产Bean对象 Student Dao Service QueryRunner
public Object getBean(String id){
//1.new 2.newInstance()
//newInstance()===>cls==>Class.forName()==>stu /sch
BeanDefinition beanDefinition = beanDefinitions.get(id);
String className = beanDefinition.getClassName();
try {
Class cls = Class.forName(className);
Object target = null;
List<ConstructorArgs> constructorArgsList = beanDefinition.getConstructorArgsList();
if(constructorArgsList!=null && constructorArgsList.size()>0){
Class[] parameterTypes = new Class[constructorArgsList.size()];
Object[] params = new Object[constructorArgsList.size()];
int k=0;
for(ConstructorArgs constructorArgs:constructorArgsList){
//获得参数类型
Class type = constructorArgs.getType();
//这里type和name只能存在一个
if(type!=null){
parameterTypes[k]=type;
//如果<constructor-args name="" type="" value="" ref="" />
//是否存在value,不存在就拿ref(value和ref只会存在一个),如果用ref,递归这个bean,为什么递归?这里ref引用的bean很可能引用其他的bean,所以需要递归
params[k] = constructorArgs.getValue()==null?this.getBean(constructorArgs.getRef()):constructorArgs.getValue();
}else{
//形式参数的名字,也就是name有值而type为null的情况
}
k++;
}
//带参数的构方法构造对象
Constructor con = cls.getDeclaredConstructor(parameterTypes);
target = con.newInstance(params);
//else是无参构造但是传值的
}else {
//if beanFactory BeanFactory
//使用无参构造方法构造对象
target = cls.newInstance();
List<PropertyDefinition> pds = beanDefinition.getProps();
if(pds!=null && pds.size()>0) {
for(PropertyDefinition pd:pds) {
//<property name="xxxx"
String name = pd.getName();
Class fieldType = cls.getDeclaredField(name).getType();
Object value = pd.getValue();
//name setName age setAge birth setBirth
String setter0 = "set"+name.substring(0,1).toUpperCase()+name.substring(1);
//setName setAge
//这里的fieldType是参数的类型,如int和Integer是并不一样的,存在方法重载,需要判断出哪个方法
Method setter = cls.getDeclaredMethod(setter0, fieldType);
//给属性赋值,value存在直接赋值,不存在再递归其他的Bean
setter.invoke(target, value==null?this.getBean(pd.getRef()):value);
}
}
}
return target;
}catch(Exception e){
e.printStackTrace();return null;
}
}
}
<?xml version="1.0" encoding="GBK"?>
<beans>
<bean id="stu" class="com.etoak.student.entity.Student">
<constructor-args type="com.etoak.student.entity.School" ref="sch"></constructor-args>
</bean>
<bean id="sch" class="com.etoak.student.entity.School">
<property name="name" value="山东大学"></property>
<property name="phone" value="110"></property>
</bean>
<bean id="service" class="com.etoak.student.service.StudentServiceImpl">
<property name="dao" ref="dao"></property>
</bean>
<bean id="dao" class="com.etoak.student.dao.StudentDaoImpl">
<property name="qr" ref="qr"></property>
</bean>
<bean id="qr" class="org.apache.commons.dbutils.QueryRunner">
<constructor-args type="javax.sql.DataSource" ref="ds"></constructor-args>
</bean>
<bean id="ds" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql:///et2101"></property>
<property name="username" value="root"></property>
<property name="password" value="etoak"></property>
</bean>
</beans>