springIOC簡單模拟實作-----XML方式和注解方式
為了看懂spring底層代碼結構,模拟完成了一個依賴注入的小例子,僅供自己更好的了解springIoc。
XML方式
1、建立一個dao接口和對應的兩個實作類。
public interface UserService {
public void find();
}
public class UserDaoImpl implements UserDao {
@Override
public void query() {
System.out.println("user");
}
}
public class UserDaoImpl1 implements UserDao {
@Override
public void query() {
System.out.println("user1");
}
}
2、建立一個service接口和一個實作類,調用dao接口。
public class UserServiceImpl implements UserService {
UserDao dao;
TestDao test;
/*public UserServiceImpl(UserDao dao){//構造方法注入時使用
this.dao = dao;
}
*/
@Override
public void find() {
System.out.println("service");
dao.query();
test.query();
}
/*public void setDao(UserDao dao) {//setter方法注入時使用
this.dao = dao;
}*/
}
3、建立一個xml配置檔案,提供需要維護的對象和對象之間的依賴關系。
<?xml version="1.0" encoding="UTF-8"?>
<beans default-autowire="byType"><!-- 自動裝配時使用 -->
<!--<bean id="dao" class="com.spring.dao.UserDaoImpl"></bean>-->
<bean id="dao1" class="com.spring.dao.UserDaoImpl1"></bean>
<bean id="test" class="com.spring.dao.TestDaoImpl"></bean>
<bean id="service" class="com.spring.service.UserServiceImpl">
<!--setter方法注入-->
<!--<property name="dao" ref="dao"></property>-->
<!--構造方法注入-->
<!--<constructor-arg name="dao" ref="dao"></constructor-arg>-->
</bean>
</beans>
4、解析xml,建立對象并維護對象的依賴。(根據配置檔案中的要求實作)
public class BeanFactory {
//對象名稱和類的對應關系
Map<String,Object> map = new HashMap<String,Object>();
public BeanFactory(String xml){
parseXml(xml);
}
//解析xml中的映射關系
private void parseXml(String xml){
//讀取XML檔案
File xmlFile = new File(this.getClass().getResource("/").getPath()+"//"+xml);
SAXReader reader = new SAXReader();
try {
//獲得XML文檔
Document document = reader.read(xmlFile);
Element documentRoot = document.getRootElement();
//是否自動裝配
Attribute autowire = documentRoot.attribute("default-autowire");
Boolean flag = false;
if(autowire != null){
flag = true;
}
//bean清單
for (Iterator<Element> itFirst = documentRoot.elementIterator(); itFirst.hasNext();) {
//建立對象
Element elementFirst = itFirst.next();
//對象名
Attribute attributeId = elementFirst.attribute("id");
String attributeIdName = attributeId.getValue();
//對象
Attribute attributeClass = elementFirst.attribute("class");
String attributeClassName = attributeClass.getValue();
Class objectClazz = Class.forName(attributeClassName);
Object object = null;
//維護對象的依賴關系
for (Iterator<Element> itSecond = elementFirst.elementIterator(); itSecond.hasNext();) {
Element elementSecond = itSecond.next();
//用setter方法注入
if(elementSecond.getName().equals("property")){
//使用預設構造方法
object = objectClazz.newInstance();
//擷取ref屬性值
Attribute attributeRef = elementSecond.attribute("ref");
String refValue = attributeRef.getValue();
Object refObject = map.get(refValue);//根據ref值找到對應對象
//擷取name屬性值--對應對象中的set方法後面的值
Attribute attributeName = elementSecond.attribute("name");
String nameValue = attributeName.getValue();
Field field = objectClazz.getDeclaredField(nameValue);
//找不到對應方法
if(field == null){
throw new NyExecption("依賴在類中未被注入!");
}
field.setAccessible(true);//開啟屬性注入
field.set(object,refObject);
}else if(elementSecond.getName().equals("constructor-arg")){
//獲得依賴對象
String refValue = elementSecond.attribute("ref").getValue();
Object refObject = map.get(refValue);
//獲得構造參數需要的class
String nameValue = elementSecond.attribute("name").getValue();
Class fieldClazz = objectClazz.getDeclaredField(nameValue).getType();
//獲得構造對象
Constructor constructor = objectClazz.getConstructor(fieldClazz);
//使用特殊的構造方法
object = constructor.newInstance(refObject);
}else {
throw new NyExecption("配置檔案标簽錯誤,請修改。");
}
}
//沒有配置注入時使用自動裝配
if(flag && object == null){
String autoType = autowire.getValue();
if(autoType.equals("byType")){//按類型裝配
//獲得對象
object = objectClazz.newInstance();
//判斷是否有依賴
Field[] fields = objectClazz.getDeclaredFields();
for (Field field : fields) {
//得到屬性的類型
Class injectClazz = field.getType();
/**
* 由於是bytype 是以需要周遊map當中的所有對象
* 判斷對象的類型是不是和這個injectObjectClazz相同
*/
int count = 0;
Object injectObject = null;
for (String key : map.keySet()) {
Class injectObjectClazz = map.get(key).getClass().getInterfaces()[0];
if(injectObjectClazz.getName().equals(injectClazz.getName())){
injectObject = map.get(key);
//記錄找到一個,因為可能找到多個count
count++;
}
}
if(count > 1){
throw new NyExecption("需要一個對象,但是找到了"+count+"個對象");
}else {//注入對象依賴
field.setAccessible(true);
field.set(object,injectObject);
}
}
}else if(autoType.equals("byName")){//按名稱裝配
//判斷是否有依賴
Field[] fields = objectClazz.getDeclaredFields();
object = objectClazz.newInstance();//獲得對象
for (Field field : fields) {
String injectName = field.getName();
Object injectObject = map.get(injectName);
//找不到注入的對象
if(injectObject == null){
throw new NyExecption("依賴的對象不存在!");
}
//對象注入屬性--依賴對象
field.setAccessible(true);
field.set(object,injectObject);
}
}
}
//沒有子标簽
if(object == null){
object = objectClazz.newInstance();
}
map.put(attributeIdName,object);
}
} catch (Exception e) {
e.printStackTrace();
}
}
//擷取對應對象
public Object getBean(String beanName){
return map.get(beanName);
}
}
5、測試類(根據不同情況進行修改測試)
public class Test {
public static void main(String[] args) {
BeanFactory beanFactory = new BeanFactory("spring.xml");
UserService userService = (UserService) beanFactory.getBean("service");
userService.find();
}
}
注解方式
前兩步與第一種方法一樣。
3、自定義一個注解,用于标記掃描的類
@Retention(RetentionPolicy.RUNTIME)
public @interface MyService {
public String value();
}
4、解析注解
public class MyAnnotationConfigApplicationContext {
//對象名稱和類的對應關系
Map<String,Object> map = new HashMap<String,Object>();
public MyAnnotationConfigApplicationContext(String basePackage){
Scan(basePackage);
}
public Object Scan(String basePackage){
Object object = null;
String rootPath = this.getClass().getResource("/").getPath();
//将包路徑替換為/格式
String basePackagePath = basePackage.replaceAll("\\.","\\\\");
File file = new File(rootPath+"\\"+basePackagePath);
String[] fileList = file.list();//擷取路徑下所有類
for (String className : fileList) {
try {
className = className.substring(0,className.indexOf("."));
//擷取class對象
Class clazz = Class.forName(basePackage + "." + className);
//隻掃描有MyService注解的類
if (clazz.isAnnotationPresent(MyService.class)) {
//擷取注解中的value
MyService service = (MyService) clazz.getDeclaredAnnotation(MyService.class);
System.out.println(service.value());
//對象注入
object = clazz.newInstance();
System.out.println(object);
map.put(service.value(),object);
}
} catch (Exception e) {
e.printStackTrace();
}
}
//檢查類中是否有需要自動注入的屬性
//思路:1、建一個map存入上方注入的類名稱和class對象
//2、檢查注入的對象中屬性是否有@Autowired注解或@Resource注解
//3、如果有@Autowired注解按照屬性類型的class到map中找是否有對應類型進行注入,注意隻能有一個
//4、如果有@Resource注解,取裡面的name屬性,到map中的key中找是否有對應的key,進行注入。
if (!map.isEmpty()){
//周遊對象進行依賴注入
for (String name : map.keySet()) {
Object injectObject = map.get(name);
Class injectObjectClazz = injectObject.getClass();
Object innerObject = null;
//擷取所有屬性
Field[] fields = injectObjectClazz.getDeclaredFields();
if(fields.length > 0){
for (Field field : fields) {
//存在Autowired注解
if(field.isAnnotationPresent(Autowired.class)){
Class fieldClazz = field.getType();
int count = 0;
//周遊map找到對應class對象
for (String objName : map.keySet()) {
Class objClazz = map.get(objName).getClass();
if(fieldClazz.getName().equals(objClazz.getInterfaces()[0].getName())){
innerObject = map.get(objName);
count++;
}
}
//判斷是否有比對的注入對象
if(count == 0){
throw new NyExecption("沒有找到屬性值比對的類型對象。");
}else if(count == 1){
try {
field.setAccessible(true);
field.set(injectObject,innerObject);
} catch (Exception e) {
e.printStackTrace();
}
}else {
throw new NyExecption("需要一個對象,但是找到了"+count+"個對象");
}
}
//存在Resource注解
if(field.isAnnotationPresent(Resource.class)){
//擷取注解中的name
Resource resource = (Resource) field.getDeclaredAnnotation(Resource.class);
String daoName = resource.name();
//在map中找到對應class對象
Object fieldObject = map.get(daoName);
if(fieldObject == null){
throw new NyExecption("沒有找到屬性值比對的類型對象。");
}
try {
field.setAccessible(true);
field.set(injectObject,fieldObject);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
}
return object;
}
//擷取對應對象
public Object getBean(String beanName){
return map.get(beanName);
}
}
5、測試類,根據不同情況調整代碼進行測試
public class Test {
public static void main(String[] args) {
MyAnnotationConfigApplicationContext context = new MyAnnotationConfigApplicationContext("com.spring.dao");
TestDao testDao = (TestDao) context.getBean("testDaoImpl");
testDao.query();
}
}
以上就是簡易的SpringIoc實作小例子,需要根據不同情況就行調整測試,是新手了解springIoc的一個好方式。