IoC和AOP是Spring的核心,可以說沒有他們就沒有龐大的Spring家族。我也是心血來潮,自己動手寫了一個簡易的Spring框架。可以通過使用註解來實現IoC容器和AOP。
先說IoC部分吧。源碼下載:http://download.csdn.net/detail/jobsandczj/9841126
IoC
先定義了兩個註解@MyBean和@MyAutowired,用來標記Bean和自動注入的對象。
package mySpring.autowired;
import java.lang.annotation.*;
/**
* Created by 10033 on 2017/5/9.
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface MyBean {
String value();
}
package mySpring.autowired;
import java.lang.annotation.*;
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface MyAutowired {
}
實現思路:
我的思路是在一個properties文件裏配置要掃描的包(Resource定位),通過掃描這些包獲得他們的Class對象存入List中(載入和解析),將list中的Class對象轉儲到Map中,以@Bean裏配置的Bean名爲key(註冊),再通過反射將Map裏的Class轉換成Bean(注入)。當然,注入的時候要判斷是否循環依賴,這個我是在注入過程中判斷的,也可以預判,但可能會稍麻煩些。下面是自動注入類的代碼:
package mySpring.autowired;
/**
* Created by 10033 on 2017/5/9.
*/
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 自動注入類
*/
public class AutomaticInjection {
public static void automaticInjection(String key, Map mmp) {
try {
List<Class> list = GetClass.getClassList(key);
for(Class classes:list) {
//註冊
Map<String, Object> judgeMap = new HashMap();
//注入
injection(mmp,classes,judgeMap);
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
//注入並判斷是否循環依賴
private static void injection(Map mmp, Class classes, Map judgeMap)
throws Exception {
boolean isExist = classes.isAnnotationPresent(MyBean.class);
//如果該註解存在
if(isExist) {
MyBean myBean = (MyBean) classes.getAnnotation(MyBean.class);
String beanName= myBean.value(); //獲得bean名稱
if(null==judgeMap.get(beanName))
judgeMap.put(beanName,true);
else { //又返回依賴他
throw new Exception("循環依賴");
}
if(null==mmp.get(beanName)) { //還沒有被注入
Object beanObj=classes.newInstance(); //獲得bean實例
Field[] fields=classes.getDeclaredFields();
boolean fieldExist;
for(Field field:fields) {
fieldExist=field.isAnnotationPresent(MyAutowired.class);
if(fieldExist) {
String classtype=field.getGenericType().toString();
Class fieldClass=Class.forName(classtype.substring(6));
//強制設置值 破壞了封裝性
field.setAccessible(true);
if(fieldClass.isAnnotationPresent(MyBean.class)) {//該屬性依賴其它Bean
MyBean tbean = (MyBean) fieldClass.getAnnotation(MyBean.class);
injection(mmp,fieldClass,judgeMap);
field.set(beanObj, mmp.get(tbean.value()));
}
else { //該屬性不依賴其它Bean
Object object=fieldClass.newInstance();
field.set(beanObj, object);
}
}
}
mmp.put(beanName, beanObj);
}
}
}
public static void reinjection(Map mmp, Class classes, Object obj) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
Field[] fields=classes.getDeclaredFields();
boolean fieldExist;
for(Field field:fields) {
fieldExist=field.isAnnotationPresent(MyAutowired.class);
if(fieldExist) {
String classtype=field.getGenericType().toString();
Class fieldClass=Class.forName(classtype.substring(6));
field.setAccessible(true);
//強制設置值 破壞了封裝性
field.setAccessible(true);
if(fieldClass.isAnnotationPresent(MyBean.class)) {//該屬性依賴其它Bean
MyBean tbean = (MyBean) fieldClass.getAnnotation(MyBean.class);
field.set(obj, mmp.get(tbean.value()));
}else { //該屬性不依賴其它Bean
Object object=fieldClass.newInstance();
field.set(obj, object);
}
}
}
}
}
接下來說AOP。
AOP
AOP我是選擇用CGLIB實現的。先是定義了兩個註解。@PointCut,@Ignore。
package mySpring.aop;
import java.lang.annotation.*;
/**
* Created by 10033 on 2017/5/12.
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface PointCut {
String value();
}
package mySpring.aop;
import java.lang.annotation.*;
/**
* Created by 10033 on 2017/5/12.
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Ignore {
}
我只實現了三種通知,Before,After,Surround(前面兩種結合,Spring裏沒有這種。。)。爲每種通知定義一個接口,每個接口都繼承Advice(空接口)。
實現原理:
AOP是在完成上述IoC注入後再實現的。就是爲每個Bean生成一個代理類,根據註解信息提供相應的方法攔截操作。Spring的AOP是會形成一條攔截器鏈的,我沒有做得那麼複雜。我寫了一個控制類來進行切面信息判斷來實現正確的攔截(替代攔截器鏈),這個控制器會根據註解選擇正確的操作執行。我將操作也獨立成了一個類,進行解耦。
下面是控制類:
package mySpring.aop;
/**
* Created by 10033 on 2017/5/12.
*/
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* 通過註解判斷執行哪個通知
*/
public class ProxyController {
//沒有類註解
public static Object doController
(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//有忽視註解
if(method.isAnnotationPresent(Ignore.class))
return methodProxy.invokeSuper(o, objects);
//沒有切入點
if(!method.isAnnotationPresent(PointCut.class)) {
return methodProxy.invokeSuper(o, objects);
}else { //有切入點
Advice advice=getAdvice(method);
return doAdvice(o,objects,methodProxy,advice);
}
}
//有類註解
public static Object doController
(Object o, Method method, Object[] objects, MethodProxy methodProxy, Advice advice) throws Throwable {
//有忽視註解
if(method.isAnnotationPresent(Ignore.class))
return methodProxy.invokeSuper(o, objects);
//有切入點
if(method.isAnnotationPresent(PointCut.class)) {
Advice advice2=getAdvice(method);
return doAdvice(o,objects,methodProxy,advice2);
} else { //沒有切入點
return doAdvice(o,objects,methodProxy,advice);
}
}
private static Object doAdvice(Object o, Object[] objects, MethodProxy methodProxy, Advice advice) throws Throwable {
if(advice instanceof AfterAdvice) {
return Execute.executeAfter(o,objects,methodProxy, (AfterAdvice) advice);
}else if(advice instanceof BeforeAdvice) {
return Execute.executeBefore(o,objects,methodProxy, (BeforeAdvice) advice);
}else if(advice instanceof SurroundAdvice) {
return Execute.executeSurround(o,objects,methodProxy, (SurroundAdvice) advice);
}
return null;
}
private static Advice getAdvice(Method method) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
String classPath=method.getAnnotation(PointCut.class).value();
Advice advice= (Advice) Class.forName(classPath).newInstance();
return advice;
}
}
下面是具體操作執行類
package mySpring.aop;
import net.sf.cglib.proxy.MethodProxy;
/**
* Created by 10033 on 2017/5/12.
* 執行通知
*/
public class Execute {
public static Object executeAfter
(Object o, Object[] objects, MethodProxy methodProxy, AfterAdvice advice) throws Throwable {
Object object=methodProxy.invokeSuper(o,objects);
advice.after();
return object;
}
public static Object executeBefore
(Object o, Object[] objects, MethodProxy methodProxy, BeforeAdvice advice) throws Throwable {
advice.before();
Object object=methodProxy.invokeSuper(o,objects);
return object;
}
public static Object executeSurround
(Object o, Object[] objects, MethodProxy methodProxy, SurroundAdvice advice) throws Throwable {
advice.before();
Object object=methodProxy.invokeSuper(o,objects);
advice.after();
return object;
}
}
執行完AOP後,我們要重新進行一遍注入,這就是上面那個自動注入類的reinjection方法要做的事。
啓動Spring也非常簡單,只要通過Class.forName("mySpring.autowired.BeanFactory");加載類就行。當然這麼做有一個壞處就是減少了靈活性,配置文件必須按照嚴格規範。
下面是BeanFactory類:
package mySpring.autowired;
import mySpring.aop.ProxyFactory;
import java.util.HashMap;
import java.util.Map;
/**
* Created by 10033 on 2017/5/9.
*/
public class BeanFactory {
public static Map<String, Object> map=new HashMap();
private static final String KEY="scan.package";
//初始化IoC容器
static {
AutomaticInjection.automaticInjection(KEY,map);
ProxyFactory.makeProxyBean(map);
//生成代理後重新注入
for(String key:map.keySet()) {
Class c=map.get(key).getClass().getSuperclass();
try {
AutomaticInjection.reinjection(map,c,map.get(key));
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static Object getBean(String name) {
return map.get(name);
}
}
獻醜了,但也算了了自己寫一個簡易Spring的心願,很多地方有待改進。望大牛指出。
對了,寫的時候遇到一個問題,那就是CGLIB生成的子類無法獲取到它父類的屬性,也因爲這個瞎忙活了很久,希望有人能爲我解答。
上面的問題已得到解答: