模拟spring IoC基于注解底層解耦過程
在xml中開啟包掃描,spring就會去掃描指定包下面的元件
通過反射機制去過濾元件上是否有spring提供的注解(Controller、service)
如果有注解,spring會通過反射機制建立執行個體儲存到容器中
然後spring會根據反射機制檢測該類中是否有屬性需要注入(也就是屬性上是否有spring提供的注解)
如果有注解,spring會根據反射機制進行注入
反射+設計模式+注解(自定義)
Dao
package com.guoy.spring.ioc.account.dao;
public interface AccountDao {
void saveAccount();
}
package com.guoy.spring.ioc.account.dao;
import com.guoy.spring.ioc.annotation.Repository;
@Repository
public class AccountDaoImpl implements AccountDao {
@Override
public void saveAccount() {
System.out.println("成功儲存了賬戶");
}
}
Service
package com.guoy.spring.ioc.account.service;
public interface AccountService {
void saveAccount();
}
package com.guoy.spring.ioc.account.service;
import com.guoy.spring.ioc.account.dao.AccountDao;
import com.guoy.spring.ioc.annotation.Autowired;
import com.guoy.spring.ioc.annotation.Service;
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
@Override
public void saveAccount() {
accountDao.saveAccount();
}
}
Servlet
package com.guoy.spring.ioc.account.servlet;
import com.guoy.spring.ioc.account.service.AccountService;
import com.guoy.spring.ioc.annotation.Autowired;
import com.guoy.spring.ioc.annotation.Controller;
@Controller
public class AccountServlet {
@Autowired
private AccountService accountService;
public void saveAccount(){
accountService.saveAccount();
}
}
Annotation
- 自定義注解Autowired
package com.guoy.spring.ioc.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
}
- 自定義注解Controller
package com.guoy.spring.ioc.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
}
- 自定義持久層注解Repository
package com.guoy.spring.ioc.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Repository {
}
- 自定義業務層注解Service
package com.guoy.spring.ioc.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Service {}
BeanFactory
package com.guoy.spring.ioc.factory;
import java.util.HashMap;
import java.util.Map;
/**
* 工廠對象,用于存放元件的執行個體
*/
public class BeanFactory {
private static Map<String,Object> beanMap = new HashMap<>();
public static Map<String, Object> getBeanMap() {
return beanMap;
}
public static void setBeanMap(Map<String, Object> beanMap) {
BeanFactory.beanMap = beanMap;
}
/**
* //使用者用于擷取執行個體的方法
* @return
*/
public static Object getBean(String beanId){
Object obj = beanMap.get(beanId);
return obj;
}
}
ComponentScan
package com.guoy.spring.ioc.scan;
import com.guoy.spring.ioc.annotation.Autowired;
import com.guoy.spring.ioc.annotation.Controller;
import com.guoy.spring.ioc.annotation.Repository;
import com.guoy.spring.ioc.annotation.Service;
import com.guoy.spring.ioc.factory.BeanFactory;
import java.io.File;
import java.io.FileFilter;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 模拟spring底層基于注解的ioc解耦
*
* @author ShuaiGUO
*/
public class ComponentScan {
//聲明一個集合,用于儲存全類名
private static List<String> fileList = new ArrayList<>();
/**
* 模拟裝配執行個體、注入執行個體的整個過程
* pathName 掃描的包
*/
public static void getCompoentScan(String pathName){
pathName = pathName.replace(".","/");
//擷取目前工程的絕對路徑
String path = ClassLoader.getSystemResource("").getPath()+pathName;
//建立file對象,file對象可以表示一個檔案夾、或者是一個檔案、或者是檔案所對應的目錄
File file = new File(path);
//調用遞歸方法,用于所有的檔案夾過濾
addFiles(file);
//周遊集合
for (String className : fileList) {
try {
//建立位元組碼對象
Class aClass = Class.forName(className);
//根據反射機制檢測類上有沒有自定義注解
Controller controller = (Controller)aClass.getAnnotation(Controller.class);
Service service = (Service)aClass.getAnnotation(Service.class);
Repository repository = (Repository)aClass.getAnnotation(Repository.class);
//判斷是否三個注解中任意注解不為空,将執行個體添加到工廠
if (controller !=null || service != null || repository != null){
//根據反射建立執行個體對象
Object obj = aClass.newInstance();
//擷取類的簡類名
String simpleName = aClass.getSimpleName();
//添加執行個體到工廠
BeanFactory.getBeanMap().put(simpleName,obj);
}
} catch (Exception e) {
e.printStackTrace();
}
}
//再次周遊集合,将需要注入的屬性進行注入
for (String className : fileList) {
try {
//建立位元組碼對象
Class aClass = Class.forName(className);
//根據反射機制檢測類上有沒有自定義注解
Controller controller = (Controller)aClass.getAnnotation(Controller.class);
Service service = (Service)aClass.getAnnotation(Service.class);
Repository repository = (Repository)aClass.getAnnotation(Repository.class);
//如果類上有其中一個注解,就把該類執行個體添加到容器中
if (controller != null || service != null || repository != null){
//根據反射擷取類中的屬性
Field[] declaredFields = aClass.getDeclaredFields();
//周遊屬性數組
for (Field declaredField : declaredFields) {
//檢測屬性上是否有自定義的注解(Autowired)
Autowired autowired = declaredField.getAnnotation(Autowired.class);
//如果這個注解不為空,那就意味着這個屬性需要被注入
if (autowired != null){
//擷取map集合
Map<String, Object> beanMap = BeanFactory.getBeanMap();
//将map集合轉成set集合
Set<Map.Entry<String, Object>> entries = beanMap.entrySet();
//周遊map集合
for (Map.Entry<String, Object> entry : entries) {
//因為我們聚合的執行個體是接口,擷取接口集合
Class[] interfaces = entry.getValue().getClass().getInterfaces();
//周遊集合
for (Class anInterface : interfaces) {
//接口與屬性名相同
if (anInterface == declaredField.getType()){
//打破封裝,因為是private
declaredField.setAccessible(true);
//給屬性指派
declaredField.set(BeanFactory.getBean(aClass.getSimpleName()),entry.getValue());
}
}
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 定義一個遞歸的方法
* file.listFiles() 将所有檔案或者檔案夾切成一個數組
* FileFilter接口, 用于檔案過濾的接口,可以将檔案夾過濾掉
*/
public static void addFiles(File file){
File[] files = file.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
//isDirectory()判斷目前檔案是否是檔案夾
if (pathname.isDirectory()){
//繼續調用遞歸方法
addFiles(pathname);
}
//擷取符合要求的檔案,應該是.class結尾的檔案
return pathname.getPath().endsWith(".class");
}
});
//周遊檔案數組,将符合要求的檔案路徑切割成全類名
for (File f : files) {
String path = f.getPath();
//System.out.println(path);//E:\dev\spring_Ioc\out\production\spring_Ioc\com\guoy\spring\ioc\scan\CompoentScan.class
//将斜杠替(\)換成點(.)
path = path.replace("\\",".");
//System.out.println(path);//E:.dev.spring_Ioc.out.production.spring_Ioc.com.guoy.spring.ioc.scan.CompoentScan.class
//将com包前面的字元串删掉
path = path.substring(path.lastIndexOf("com"),path.length());
//System.out.println(path);//com.guoy.spring.ioc.scan.CompoentScan.class
//将.class删除
path = path.replace(".class","");
//System.out.println(path);//com.guoy.spring.ioc.scan.CompoentScan
//将符合要求的全類名放入集合
fileList.add(path);
}
}
}
Test
package com.guoy.spring.ioc.scan;
import com.guoy.spring.ioc.account.servlet.AccountServlet;
import com.guoy.spring.ioc.factory.BeanFactory;
public class Test {
public static void main(String[] args) {
//開啟掃描包
ComponentScan.getCompoentScan("com.guoy");
//從工廠擷取執行個體
AccountServlet accountServlet =(AccountServlet) BeanFactory.getBean("AccountServlet");
//調用儲存賬戶的方法
accountServlet.saveAccount();
}
}
結果:
成功儲存了賬戶