IoC and AOP are at the heart of Spring, and without them there would be no large Spring family. On a whim, I wrote a simple Spring framework myself. The IoC container and AOP can be implemented using annotations.
Let’s start with the IoC part. The source code download: download.csdn.net/detail/jobs…
IoC
Two annotations, @myBean and @MyAutoWired, are defined to mark the Bean and the auto-injected object.
view plain
copy
?
- 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.*;
/**
* Created by 10033 on 2017/5/9.
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface MyBean {
String value();
}Copy the code
view plain
copy
?
- package mySpring.autowired;
- import java.lang.annotation.*;
- @Target({ElementType.FIELD, ElementType.METHOD})
- @Retention(RetentionPolicy.RUNTIME)
- @Inherited
- @Documented
- public @interface MyAutowired {
- }
package mySpring.autowired;
import java.lang.annotation.*;
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface MyAutowired {
}
Copy the code
Implementation idea:
The idea is to configure the package to scan in a Properties file (
The Resource location) by scanning these packages to get their Class objects into the List (
Load and parse), dump the Class object from list to Map, using the Bean named key configured in @bean (
registered), and use reflection to convert the Class in the Map into beans (
injection). Of course, it is necessary to determine whether circular dependencies are generated during injection, which I did in the process of injection and can also be predicted, but it may be a bit more troublesome.
Here is the code for the auto-injection class:
view plain
copy
?
- 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;
- / * *
- * Automatic injection of classes
- * /
- public class AutomaticInjection {
- public static void automaticInjection(String key, Map mmp) {
- try {
- List<Class> list = GetClass.getClassList(key);
- for(Class classes:list) {
- / / register
- Map<String, Object> judgeMap = new HashMap();
- / / injection
- injection(mmp,classes,judgeMap);
- }
- } catch (IOException e) {
- e.printStackTrace();
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- // Inject and determine whether cyclic dependencies exist
- private static void injection(Map mmp, Class classes, Map judgeMap)
- throws Exception {
- boolean isExist = classes.isAnnotationPresent(MyBean.class);
- // If the annotation exists
- if(isExist) {
- MyBean myBean = (MyBean) classes.getAnnotation(MyBean.class);
- String beanName= myBean.value(); // Get the bean name
- if(null==judgeMap.get(beanName))
- judgeMap.put(beanName,true);
- else { // Go back to relying on him
- throw new Exception( “Cyclic dependency”);
- }
- if(null==mmp.get(beanName)) { // Not yet injected
- Object beanObj=classes.newInstance(); // Get the bean instance
- 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));
- // Forcing a value breaks encapsulation
- field.setAccessible(true);
- if(fieldClass.isAnnotationPresent(MyBean. class)) {// This property depends on other beans
- MyBean tbean = (MyBean) fieldClass.getAnnotation(MyBean.class);
- injection(mmp,fieldClass,judgeMap);
- field.set(beanObj, mmp.get(tbean.value()));
- }
- else { // This property does not depend on other beans
- 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);
- // Forcing a value breaks encapsulation
- field.setAccessible(true);
- if(fieldClass.isAnnotationPresent(MyBean.class)) { // This property depends on other beans
- MyBean tbean = (MyBean) fieldClass.getAnnotation(MyBean.class);
- field.set(obj, mmp.get(tbean.value()));
- }else { // This property does not depend on other beans
- Object object=fieldClass.newInstance();
- field.set(obj, object);
- }
- }
- }
- }
- }
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 :list) {// Register Map<String, Object> judgeMap = new HashMap(); / / injection injection (MMPS, 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); // If (isExist) {MyBean MyBean = (MyBean) classes.getannotation (mybean.class); String beanName= myBean.value(); If (null== judgemap. get(beanName)) judgemap. put(beanName,true); Else {// throw new Exception(" loop dependency "); } if(null==mmp.get(beanName)) {// Object beanObj= classes.newinstance (); / / get the bean instance 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)); // Forcing a value breaks the encapsulation field.setaccessible (true); If (fieldClass. IsAnnotationPresent (MyBean. Class)) {/ / this property depends on other Bean MyBean tbean = (MyBean) fieldClass.getAnnotation(MyBean.class); injection(mmp,fieldClass,judgeMap); field.set(beanObj, mmp.get(tbean.value())); } else {// This property does not depend on other 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); // Forcing a value breaks the encapsulation field.setaccessible (true); If (fieldClass. IsAnnotationPresent (MyBean. Class)) {/ / this property depends on other Bean MyBean tbean = (MyBean) fieldClass.getAnnotation(MyBean.class); field.set(obj, mmp.get(tbean.value())); }else {// This property does not depend on other Bean Object Object = fieldClass.newinstance (); field.set(obj, object); } } } } }Copy the code
Next, AOP.
AOP
AOP I choose to use CGLIB implementation. First, two annotations are defined. The @pointcut, @ Ignore.
view plain
copy
?
- 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.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface PointCut {
String value();
}
Copy the code
view plain
copy
?
- 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 {
- }
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 {
}
Copy the code
I only implemented three notifications: Before, After, Surround. . Define an interface for each notification, and each interface inherits Advice (empty interface).
Implementation principle:
AOP is implemented after the above IoC injection. This is to generate a proxy class for each Bean that provides method interception operations based on annotation information. Spring’s AOP will form a chain of interceptors, but I’m not going to go that far. I have written a control class that performs section information judgment to implement the correct interception (instead of interceptor chain). This controller will select the correct action to perform according to the annotations. I decoupled the operations into a class as well.
Here are the control classes:
view plain
copy
?
- package mySpring.aop;
- / * *
- * Created by 10033 on 2017/5/12.
- * /
- import net.sf.cglib.proxy.MethodProxy;
- import java.lang.reflect.Method;
- / * *
- * Use annotations to determine which notification to execute
- * /
- public class ProxyController {
- // No class annotations
- public static Object doController
- (Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
- // There are ignoring notes
- if(method.isAnnotationPresent(Ignore.class))
- return methodProxy.invokeSuper(o, objects);
- // There is no pointcut
- if(! method.isAnnotationPresent(PointCut.class)) {
- return methodProxy.invokeSuper(o, objects);
- }else { // There is an entry point
- Advice advice=getAdvice(method);
- return doAdvice(o,objects,methodProxy,advice);
- }
- }
- // There are class annotations
- public static Object doController
- (Object o, Method method, Object[] objects, MethodProxy methodProxy, Advice advice) throws Throwable {
- // There are ignoring notes
- if(method.isAnnotationPresent(Ignore.class))
- return methodProxy.invokeSuper(o, objects);
- // There is an entry point
- if(method.isAnnotationPresent(PointCut.class)) {
- Advice advice2=getAdvice(method);
- return doAdvice(o,objects,methodProxy,advice2);
- } else { // There is no pointcut
- 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; /** * 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) public static Object doxyController (Object o) Method method, Object[] objects, MethodProxy MethodProxy) throws Throwable {/ / Ignore comments if (method. IsAnnotationPresent (Ignore. Class)) return methodProxy.invokeSuper(o, objects); // No pointcut 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, Advice Advice) throws Throwable {/ / Ignore comments if (method. IsAnnotationPresent (Ignore. Class)) return methodProxy. InvokeSuper (o, objects); / / have to start the if (method. IsAnnotationPresent (PointCut. Class)) {Advice advice2 = getAdvice (method); return doAdvice(o,objects,methodProxy,advice2); } else {/ / there is no point 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; }}Copy the code
Below are the concrete action execution classes
view plain
copy
?
- package mySpring.aop;
- import net.sf.cglib.proxy.MethodProxy;
- / * *
- * Created by 10033 on 2017/5/12.
- * Enforcement notice
- * /
- 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;
- }
- }
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; }}Copy the code
After executing AOP, we need to do the injection again, which is what the reinjection method of the auto-injection class above does.
Start the Spring is also very simple, as long as by Class. Class.forname (” mySpring. Autowired. The BeanFactory “); Just load the class. The downside of this, of course, is that it reduces flexibility; configuration files must follow strict specifications.
Here is the BeanFactory class:
view plain
copy
?
- 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”;
- // Initialize the IoC container
- static {
- AutomaticInjection.automaticInjection(KEY,map);
- ProxyFactory.makeProxyBean(map);
- // Generate the proxy and re-inject it
- 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);
- }
- }
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"; / / initialize the IoC container 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); }}Copy the code
I made a fool of myself, but I also calculated my wish to write a simple Spring. Many places need to be improved. Wang Daniu pointed out.
CGLIB’s subclass can’t get the attributes of its parent class. It took a long time to do that.
The above questions have been answered: