参考资料:
IOC:控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。
AOP:Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
(以上是度娘给出的说法,可以看到这样的说法很不容易让人理解,过于官方化,下面是我的理解)

IOC:我们平时在写Java程序时,总要通过new的方式来产生一个对象,对象的生死存亡是与我们直接挂钩的,我们需要它时,就new一个,不需要他时就通过GC帮我们回收;控制反转的意思就是将对象的生死权交给第三方,由第三方来生成和销毁对象,而我们只在需要时从第三方手中取获取,其余不管,这样,对象的控制权就在第三方手里,我们只有使用权!这就是所谓的控制反转!
在Spring中,提供了 文件配置和注解的方式来向第三方表明我们需要第三方帮我们创建什么对象,Spring就是这个第三方!它负责通过 文件的解析或者包扫描的方式,找到我们给出的映射关系,利用反射机制,在其内部帮我们“new”出对象,再存储起来,供我们使用!

AOP :就是动态代理的体现,在Spring中就是利用JDKProxy或者CGLibProxy技术,对方法进行拦截!
比如说有一个叫做fun()的方法,我们在其执行前后对其拦截:

\"\" 

就像这样,fun看成是纵向的线,那么就相当于用平面把这条线截断了!

 

有了上面的铺垫,我们可以大概知道,Sping的IOC和AOP可以帮我们创建并管理对象,可以对对象的方法进行拦截,那么这两个技术合在一起,就可以达到自动帮我们创建、管理、并对对象进行拦截!

下面先给出我简化的SpringIOC容器框图:
模拟的IOC框图

\"\"

这是我简化后的IOC框图,实际上的SpringIOC是非常庞大的,里面包含了许多接口,以及继承关系,它把要处理的事务区分的非常细腻,将问题由大化小,层层递减,把面向接口,高内聚低耦合体现的淋漓尽致。
Spring提供了注解和 方式的注入,所以后面会有两个分支,分别处理注解和 文件的配置!

BeanFactory:
在别的地方说法是一个最底层容器,其实不要被其“误导”,在我这它仅仅只是一个接口,只提供了最基础的一些方法,而方法的具体实现就需要真正的高级容器了!代码如下:

 1 public interface BeanFactory {
 2     String FACTORY_BEAN_PREFIX = "&";
 3       getBean(String var1) throws BeansException;
 4     <T> T getBean(String var1, Class<T> var2) throws BeansException;
 5     <T> T getBean(Class<T> var1) throws BeansException;
 6       getBean(String var1,  ... var2) throws BeansException;
 7     <T> T getBean(Class<T> var1,  ... var2) throws BeansException;
 8     boolean containsBean(String var1);
 9     boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;
10     boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;
11     boolean isTypeMatch(String var1, Class<?> var2) throws NoSuchBeanDefinitionException;
12     Class<?> getType(String var1) throws NoSuchBeanDefinitionException;
13     String[] getAliases(String var1);
14 }

(这里我直接挪用了Spring的源码,由于是模拟实现,所以后面只实现了其getBean的方法)

 

ApplicationContext:
在别的地方的说法是一个高级容器,其实,它还是一个接口,只不过在源码中其继承了许多接口(核心还是BeanFactory),是一个集大成者,提供了远比BeanFactory更多的功能。但目前所要实现的核心暂时用不上它,所以暂时留一个空接口吧...

1 public interface ApplicationContext extends BeanFactory {
2     // TODO
3 }

说到这里,就不能往下继续了,因为在上面我们看到的,所谓的“容器”,仅仅是定义了接口,完全不能装东西啊,还有,所谓的容器里又要装什么?这里就要引入Bean!

Bean:
其实就是IOC容器里存放的东西!前面我说过,Spring会根据我们给出的映射关系,帮我们创建对象并存储起来,那么是否这个对象就是Bean?是!但也不是!如果说Spring仅仅只是帮我们管理对象,那么它的功能也太单一了,那么,现在不得不再次提到前面说过的AOP!
前面说到Spring中的AOP使用了JDKProxy和CGLibProxy这两种代理技术,这两种技术的目的都是产生代理对象,对方法进行拦截。那么,是否这个代理对象就是我们的Bean?不完全是!Bean其实是原对象+代理对象!先不急,看到后面就会明白!

下面介绍两种动态代理技术:
JDKProxy
使用JDK代理,其所代理的类,必须要实现某一个接口:

 1 @SuppressWarnings("unchecked")
 2 public <E> E getJDKProxy(Class<?> klass,   tag ) {
 3     return (E) Proxy.newProxyInstance(klass.getClassLoader(), 
 4             klass.getInterfaces(),
 5             new InvocationHandler() {
 6                 @Override
 7                 public   invoke(  proxy, Method method,  [] args) 
 8                         throws Throwable {
 9                     // TODO 置前拦截,可对参数args进行判断
10                       result = null;
11                     try {
12                         result = method.invoke(tag , args);
13                     } catch (Exception e) {
14                         // TODO 对异常进行拦截
15                         throw e;
16                     }
17                     // TODO 置后拦截,可对方法返回值进行修改
18                     return result;
19                 }
20             });
21 }

使用JDK代理的话就不得不传入一个原始对象,所以如果不考虑CGLib代理,那么Bean就是原始对象+代理对象!

 

CGLibProxy:
使用CGLib代理,是让被代理的类作为代理对象的父类,故原类不能被final修饰,也不能对final修饰的方法拦截!

以下是网上绝大多数人给出的用法:

 1 @SuppressWarnings("unchecked")
 2 public <E> E getCGLibProxy(Class<?> klass) {
 3     Enhancer enhancer = new Enhancer();
 4     enhancer.setSuperclass(klass); // 从这里可以明显看到,让被代理的类作为了代理对象的父类
 5     enhancer.setCallback(new MethodInterceptor() {
 6         @Override
 7         public   intercept(  proxy , Method method,  [] args, MethodProxy methodProxy) 
 8                 throws Throwable {
 9             // TODO 置前拦截,可对参数args进行判断
10               result = null;
11             try {
12                 result = methodProxy.invokeSuper(proxy , args);
13             } catch (Exception e) {
14                 // TODO 对异常进行拦截
15                 throw e;
16             }
17             // TODO 置后拦截,可对方法返回值进行修改
18             return result;
19         }
20     });
21     return (E) enhancer.create();
22 }

这种方式是没错,但是不适用于后面要做的,至于原因,后面分析到了就会明白!
所以使用如下方式:

 1 @SuppressWarnings("unchecked")
 2 public <E> E getCGLibProxy(Class<?> klass,   tag ) {
 3     Enhancer enhancer = new Enhancer();
 4     enhancer.setSuperclass(klass);
 5     enhancer.setCallback(new MethodInterceptor() {
 6         @Override
 7         public   intercept(  proxy , Method method,  [] args, MethodProxy methodProxy) 
 8                 throws Throwable {
 9             // TODO 置前拦截,可对参数args进行判断
10               result = null;
11             try {
12                 result = method.invoke(tag , args);
13             } catch (Exception e) {
14                 // TODO 对异常进行拦截
15                 throw e;
16             }
17             // TODO 置后拦截,可对方法返回值进行修改
18             return result;
19         }
20     });
21     return (E) enhancer.create();
22 }

由于是模拟实现,后面就全部采用CGLib代理!
可以看到以上面这种方式进行CGLib代理就需要原始对象,那么前面说到的Bean就必须是原对象+代理对象!
当然我知道以invokeSuper那种方式是不需要原始对象,但是原因不是因为Bean,还在后面!

综上,Bean的定义如下:

1 public interface BeanElement {
2     <E> E getProxy();
3       get ();
4     boolean isDI(); // 用于以后判断是否完成注入
5 }

在这里我把BeanElement定义为了一个接口,后面会产生两个分支,会产生两种不同处理方式的Bean,用接口更灵活,耦合也低!

现在知道了Bean到底是什么,我们就可以往下继续进行:


AbstractApplicationContext:
ApplicationContext的具体实现类,但它是一个抽象类,只能实现部分功能,往后在它的基础上还要分化成两支,那么,把所有的Bean存在这里面再合适不过了!

 1 public abstract class AbstractApplicationContext implements ApplicationContext {
 2     protected static final Map<String, String> beanNameMap;  // key : id      value : className
 3     protected static final Map<String, BeanElement> beanMap; // key : className value : Bean
 4     protected AopFactory aopFactory; // Aop工厂,生产代理对象
 5     
 6     static {
 7         beanNameMap = new HashMap<>();
 8         beanMap = new HashMap<>();
 9     }
10     
11     protected AbstractApplicationContext() {
12         aopFactory = new AopFactory();
13         // 设置切面
14         aopFactory.setAdvice(
15                 (new AdviceHander())
16                 .setIntercepterFactory(new IntercepterLoader()));
17     }
18     
19     protected void add(String id, String className, BeanElement bean) throws BeansException {
20         if (beanMap.containsKey(className)) {
21             // TODO 可以抛异常!
22             return;
23         }
24         beanMap.put(className, bean);
25         if (id.length() <= 0) return;
26         if (beanNameMap.containsKey(id)) {
27             throw new BeansException("bean:" + id + "已定义!");
28         }
29         beanNameMap.put(id, className);
30     }
31 }

其中的aopFactory是代理工厂,负责生产代理,会在后面给出,先不急。
可以看到,AbstractApplicationContext这个类持有了两张静态的map,第一组是用来存取Bean的别名(id),第二组用来存放真正的Bean,这就是我们真正意义上的容器,由于其map都是static修饰的,在类加载的时候就存在了,所以往后有别的类继承它时,这两个map是共享的!只增加了一个add方法,只要是继承自它的子类,都会通过这种方式添加Bean!并且这个类是protected的,不对外提供使用!

 

我们先进行左边的分支,用注解的方式完成IOC
这里说的注解都是自定义注解,属于RUNTIME,就必须通过反射机制来获取,反射机制就要得到类或者类名称,那么就先得到符合要求的类,这里就要用到包扫描,我在前面的博客中有介绍过:【Java】包、jar包的扫描
首先是对类的注解:
@Component

1 @Retention(RetentionPolicy.RUNTIME)
2 @Target(ElementType.TYPE)
3 public @interface Component {
4     String id() default "";
5 }

其中的id相当于类名称的别名,具有唯一性,如果不设置则不处理!通过这个注解我们就可以判断哪个类是需要进行操作的,就应该自动地为这个类生成一个对象和代理对象,将其添加到beanMap中,就是bean的标志!
如果说用过Spring的 配置,这其实就相当于一个bean标签:

1 <bean id="XXX" class="XXX">
2     ......
3 </bean>

注解中的id就相当于id属性,我们是通过反射机制得到注解的,当然能得到类名称,那就相当于有了class属性!

但是仅仅有这个注解是完全不够的,我们只能通过反射机制产生一个对象,但它的成员都没赋值,仅仅是一具躯壳,所以就需要对成员添加注解,将我们需要的值注入进来;当然也可以给方法添加注解,通过setter方法给成员赋值,Spring就是使用的这种方式!
这是对成员的注解:
@Autowired

1 @Retention(RetentionPolicy.RUNTIME)
2 @Target({ElementType.FIELD, ElementType.METHOD, ElementType.CONSTRUCTOR})
3 public @interface Autowired {
4     boolean requisite() default true;
5 }

这里直接使用的spring源码,在源码中,这个注解可以成员、方法以及构造方法添加。其中的requisite是是否必须注入,这是Spring提供的更为细腻的操作,我的实现暂时不考虑它。

如果说这个注解是给成员添加的,那么标志着它需要赋值!使用这个注解的前提是它本身是一个复杂类型,不是基本类型,它的赋值,是将我们beanMap中的符合要求的Bean注入进去!至于基本类型后面有别的解决办法。
用Component 和Autowired注解其实就相当于如下 的配置:

1 <bean id="XXX" class="XXX">
2     <property name="XXX" ref="XXX">
3 </bean>

我们还是通过反射机制得到的Autowired注解,那么一定可以得到成员名称,和成员类型,成员名称就相当于name属性,通过成员类型就可以得到类型名称,就相当于ref属性!

如果说是给构造方法添加,那么就规定了我们在反射机制执行时需要调用哪个构造方法,相当于如下:

1 <bean id="XXX" class="XXX">
2     <constructor-arg index="0" ref="XXX">
3     <constructor-arg index="1" ref="XXX">
4 </bean>

对于构造方法的处理我觉得使用注解的方式比 配置要更好,注解可以直接定位到某一个构造方法,但是 文件的方式就需要遍历比较,找出符合要求的,而且关于这个符合要求这一说还有更为复杂的问题,我会在后面用 的方式详细介绍!

还有一种就是对方法添加,实际上就是提供给setter方法使用的,通过执行setter方法给成员赋值,可能看到这里会觉得这样做是不是多次一举,其实不是,因为这样做会避免我后面会谈到的循环依赖的问题!所以给方法添加,和对成员添加等效:

1 <bean id="XXX" class="XXX">
2     <property name="XXX" ref="XXX">
3 </bean>


上面我说过使用Autowired 是不处理基本类型的,它是将bean注入的,基本类型完全没有必要作为bean,那么,我们就可以给出一个注解,直接赋值:

1 @Retention(RetentionPolicy.RUNTIME)
2 @Target({ElementType.FIELD, ElementType.PARAMETER})
3 public @interface Value {
4     String value();
5 }

其中value就是我们要注入的值,但是它是String类型的,可能需要强制类型转换
演示如下:

 1 @Component
 2 public class TestA {
 3     @Autowired
 4     private TestB testB;
 5     @Value(value="直接赋值")
 6     private String member;
 7     
 8     public TestA() {
 9     }
10 
11 }
12 
13 @Component(id = "testB")
14 public class TestB {
15     private int a;
16     private TestA testA;
17     
18     @Autowired
19     public TestB(@Value(value="10")int a, TestA testA) {
20         this.a = a;
21         this.testA = testA;
22     }
23     
24 }

就相当于:

1 <bean class="xxx.xxx.TestA">
2     <property name="testB" ref="xxx.xxx.TestB">
3     <property name="member" value="直接赋值">
4 </bean>
5 <bean id="testB" class="xxx.xxx.TestB">
6     <constructor-arg index="0" value="10"></constructor-arg>
7     <constructor-arg index="1" ref="xxx.xxx.TestA"></constructor-arg>
8 </bean>

为了简单处理,Autowired注解我在后面就只处理成员的。

有了上面的两个注解是否够呢?当然不够。仔细想一想,如果说我们需要Spring帮我们创建的对象,其对应的类又被打成了jar包,那么我们完全没有办法对已经形成jar包的代码添加注解;又或者说是我们需要创建的对象不是通过反射机制就能产生的,它是一个工厂对象,需要走工厂模式那一套来创建,上面的两个注解就远远不能满足我们的要求了!

 

@Bean

1 @Retention(RetentionPolicy.RUNTIME)
2 @Target(ElementType.METHOD)
3 public @interface Bean {
4     String id() default "";
5 }

可以看出这是对方法的注解,因为我们可以通过反射机制执行方法,将方法的返回值作为Bean的原始对象,再产生一个代理对象,这样就能解决上面的所说的问题!

 1 @Component
 2 public class Action {
 3     
 4     @Bean(id="getDocumentBuilderFactory")
 5     public DocumentBuilderFactory getDocumnet() throws Exception {
 6         return DocumentBuilderFactory.newInstance();
 7     }
 8     
 9     @Bean
10     public DocumentBuilder getDocumentBuilder(DocumentBuilderFactory factory) throws Exception {
11         return factory.newDocumentBuilder();
12     }
13     
14 }

就相当于:

1 <bean class="xxx.xxx.Action "></bean>
2 <bean class="javax. .parsers.DocumentBuilderFactory" id="getDocumentBuilderFactory" 
3     factory-method="newInstance"></bean>
4 <bean class="javax. .parsers.DocumentBuilderFactory" 
5     factory-bean="getDocumentBuilderFactory" factory-method="newDocumentBuilder">
6 </bean>

有了这些注解,我们就能对包进行扫描,可以继续向下进行!

AnnotationContext:
这个类继承自AbstractApplicationContext,它也是protected的,不对外提供,我用它来进行有关注解的扫描和解析,但它的功能有限,不能完成全部的注入,这会涉及到注入的顺序,以及注入之间的依赖关系:

  1 /**
  2  * 执行包扫描
  3  * 将符合要求的结果添加到父类AbstractApplicationContext的beanMap中
  4  * 只处理@Component和@Bean注解
  5  * @Autowired注解留给下一级处理
  6  */
  7 public class AnnotationContext extends AbstractApplicationContext {
  8     // method缓冲区,保存暂时不能执行的方法,其中的MethodBuffer用来封装method,以及invoke时所需要的内容
  9     private List<MethodBuffer> methodList; 
 10 
 11     protected AnnotationContext() {
 12     }
 13     
 14     protected AnnotationContext(String packageName) {
 15         scanPackage(packageName);
 16     }
 17     
 18     /**
 19      * 通过aopFactory产生代理对象,将代理对象和原始对象封装成bean添加到父类的map中
 20      */
 21     private void addBean(Class<?> klass,    , String id, String className) {
 22         // aopFactory是其父类AbstractApplicationContext的成员,原来产生代理对象
 23           proxy = aopFactory.creatCGLibProxy(klass,  );
 24         // AnnoationBean是BeanElement接口的一个实现类
 25         AnnotationBean bean = new AnnotationBean( , proxy);
 26         // 父类AbstractApplicationContext的add方法
 27         add(id, className, bean);
 28     }
 29     
 30     protected void scanPackage(String packageName) {
 31         new PackageScanner() {
 32             @Override
 33             public void dealClass(Class<?> klass) {
 34                 if (!klass.isAnnotationPresent(Component.class)) return;
 35                 Component component = klass.getAnnotation(Component.class);
 36                 String className = klass.getName();
 37                 String name = component.id();
 38                 try {
 39                     // 这里简单起见,不考虑构造方法的重载,默认执行无参构造 
 40                         = klass.newInstance();
 41                     // 产生BeanElement,加入到beanMap中
 42                     addBean(klass,  , name, className);
 43                     // 处理带有@Bean注解的方法
 44                     dealMethod(klass,  );
 45                 } catch (Exception e) {
 46                     // TODO 
 47                     e.printStackTrace();
 48                 }
 49             }
 50         }.scanPackage(packageName);
 51         if (methodList == null) return;
 52         // 执行缓存的所有方法
 53         for (MethodBuffer methodBuffer : methodList) {
 54             // 获得方法执行所需要的东西
 55             String id = methodBuffer.getId();
 56             Class<?> returnClass = methodBuffer.getReturnClass();
 57             Method method = methodBuffer.getMethod();
 58                 = methodBuffer.get ();
 59             Parameter[] parameters = methodBuffer.getParameters();
 60             
 61             try {
 62                 dealMultiParaMethod(returnClass, method,  , parameters, id);
 63             } catch (Exception e) {
 64                 // TODO 
 65                 e.printStackTrace();
 66             } 
 67         }
 68         methodList.clear();
 69     }
 70     
 71     private void dealMultiParaMethod(Class<?> returnClass, Method method, 
 72                , Parameter[] parameters, String id) 
 73                     throws BeansException, IllegalAccessException, IllegalArgumentException, 
 74                     InvocationTargetException, ValueOnlyPrimitiveType {
 75         int count = parameters.length;
 76          [] result = new  [count];
 77         
 78         for (int index = 0; index < count; index++) {
 79             Parameter para = parameters[index];
 80             // 判断参数是否带有@Value注解
 81             if (para.isAnnotationPresent(Value.class)) { 
 82                 Class<?> type = para.getType();
 83                 // 判断@Value注解标识的参数是否是基本类型(八大类型和String)
 84                 if (!type.isPrimitive() && !type.equals(String.class)) {
 85                     throw new ValueOnlyPrimitiveType("Value只能用基本类型!");
 86                 }
 87                 // TypeConversion是我自己的一个工具类,用于将字符串转换成基本类型!
 88                 result[index] = TypeConversion.getValue(para.getAnnotation(Value.class).value(), 
 89                                             para.getType().getSimpleName());
 90             } else {
 91                 // 如果不是基本类型,那么就需要从beanMap中获取符合要求的bean
 92                 result[index] = getBean(para.getType());
 93             }
 94         }
 95         // 执行方法,获得方法返回值
 96           return  = method.invoke( , result);
 97         // 为方法执行结果创建bean,添加到beanMap中
 98         addBean(returnClass, return  , id, returnClass.getName());
 99     }
100     
101     /**
102      * 遍历所有方法,处理带有@Bean注解的方法
103      */
104     private void dealMethod(Class<?> klass,    ) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
105         Method[] methods = klass.getDeclaredMethods();
106         for (Method method : methods) {
107             if (!method.isAnnotationPresent(Bean.class)) continue; 
108             
109             Class<?> returnType = method.getReturnType();
110             if (returnType.equals(void.class)) {
111                 // TODO 如果没有返回值,那么根本没有必要做
112                 return;
113             }
114             String id= method.getAnnotation(Bean.class).id();
115             Parameter[] parameters = method.getParameters();
116             // 判断方法是否有参数,没有参数,直接执行,添加Bean
117             // 有参数就先缓存起来,等所有都扫描完成后再执行
118             if (parameters.length <= 0) {
119                   return  = method.invoke( );
120                 addBean(returnType, return , id, returnType.getName());
121             } else {
122                 (methodList = methodList == null ? new ArrayList<>() : methodList)
123                     .add(new MethodBuffer(returnType,  , method, parameters, id));
124             }
125         }
126     }
127     
128 }

这个类只负责扫描包,为带有@Component注解的类通过反射机制生成对象,再通过代理工厂将其加工成代理对象,然后封装再AnnotationBean中作为bean,将其添加到BeanMap中!
其次还处理了带有@Bean注解的的方法,如果说是方法带有参数,那么就像将这个方法的执行延后,等所有东西都扫描完成后再执行;而对于无参的方法,则可以直接执行,并为执行结果产生的对象创建代理,生成AnnotationBean,添加进beanMap中。至于@Autowired注解这个类暂不处理,留给下一级处理!
AnnotationBean 类如下:

 1 /**
 2  * 注解分支中BeanElement的具体实现类
 3  */
 4 public class AnnotationBean implements BeanElement {
 5     private boolean DI; // 判断是否完成注入
 6     private    ; // 原始对象
 7     private   proxy; // 代理对象
 8     


					
				
收藏 打印
您的足迹: