控制反转IOC

来源:互联网 发布:今天经济数据 编辑:程序博客网 时间:2024/05/29 08:39

控制反转IOC

什么是IOC

        IOC(Inversionof Control),意为控制反转,不是什么技术,而是一种设计思想。Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你对象的内部直接控制。

        关键问题:

        谁控制谁,控制什么:传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象,而Ioc是有专门一个容器来创建这些对象,即由容器来控制对象的创建;谁控制谁?当然是Ioc容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。

         为何是反转,哪些方面反转了:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。

        下面举个例子说明说明是IOC:

        假设我们要设计一个Girl和一个Boy类,其中Girl有kiss方法,即Girl想要Kiss一个Boy。那么,我们的问题是,Girl如何能够认识这个Boy?

        青梅竹马:Girl从小就知道自己的Boy。

public class Girl {    void kiss(){Boy boy = newBoy();    }}

        然而从开始就创建的Boy缺点就是无法在更换。并且要负责Boy的整个生命周期。如果我们的Girl想要换一个怎么办?

        亲友介绍:由中间人负责提供Boy来见面。

public class Girl {   void kiss() {     Boy boy = BoyFactory.createBoy();     } }

        亲友介绍,固然是好。如果不满意,尽管另外换一个好了。但是,亲友BoyFactory经常是以Singleton的形式出现,不然就是,存在于Globals,无处不在,无处不能。实在是太繁琐了一点,不够灵活。

        父母包办:一切交给父母。

public class Girl {    void kiss(Boy boy) {     // kiss boy    boy.kiss();   } }

        Well,这是对Girl最好的方法,只要想办法贿赂了Girl的父母,并把Boy交给他。这就是IOC,将对象的创建和获取提取到外部。由外部容器提供需要的组件。


IOC能做什么

        IoC不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。

        其实IoC对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在IoC/DI思想中,应用程序就变成被动的了,被动的等待IoC容器来创建并注入它所需要的资源了。


IOC和DI

        DI—DependencyInjection,即“依赖注入”:组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。

        理解DI的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”:

    1. 谁依赖于谁:当然是应用程序依赖于IoC容器;
    2. 为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源;
    3. 谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象;
    4. 注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。

        IoC和DI由什么关系呢?其实它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以2004年大师级人物Martin Fowler又给出了一个新的名字:“依赖注入”,相对IoC 而言,“依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”。


Spring中如何使用

        旧版本的Spring中,要想获取对象,必须先在applicationContext.xml中进行配置,我们在Spring中是这样获取对象的:

public static void main(String[] args) {   ApplicationContext context = new FileSystemXmlApplicationContext("applicationContext.xml");   Lol lol = (Lol) context.getBean("lol");   lol.gank(); }

        在 applicationContext.xml 配置文件中是这样的:

<bean id="lol" class="com.biezhi.test.Lol"><property name="name" value="剑圣" />   </bean>  

        Lol 类是这样的:

public class Lol {private String name;public Lol() {}public void gank(){System.out.println(this.name + "在gank!!");}public String getName() {return name;}public void setName(String name) {this.name = name;}}

        由于每次都要配置Xml显得很麻烦,所以Spring有了更高级的用法,在3.0版本之后有了基于Annotation的注入实现:首先还是要在 xml 中配置启用注解方式:

<context:annotation-config/> 

        这样就能使用注解驱动依赖注入了,下面是一个使用场景:

public class Lol {@Autowiredprivate DuangService duangService ;public void buyDuang(String name, int money) {duangService.buy(name, money);}}@Service("duangService")public class DuangService {public void buy(String name, int money){if(money > 0){System.out.println(name + "买了" + money + "毛钱的特效,装逼成功!");} else{System.out.println(name + "没钱还想装逼,真是匪夷所思");}}}


类装载器ClassLoader

类装载器工作机制

        类装载器就是寻找类的字节码文件并构造出类在JVM内部表示对象的组件。在Java中,类装载器把一个类装入JVM中,要经过以下步骤:

1. 装载:查找和导入Class文件;

2. 链接:执行校验、准备和解析步骤,其中解析步骤是可以选择的:

a) 校验:检查载入Class文件数据的正确性;

b) 准备:给类的静态变量分配存储空间;

c) 解析:将符号引用转成直接引用;

d) 初始化:对类的静态变量、静态代码块执行初始化工作。

        类装载工作由ClassLoader及其子类负责,ClassLoader是一个重要的Java运行时系统组件,它负责在运行时查找和装入Class字节码文件。JVM在运行时会产生三个ClassLoader:根装载器、ExtClassLoader(扩展类装载器)和AppClassLoader(系统类装载器)。其中,根装载器不是ClassLoader的子类,它使用C++编写,因此我们在Java中看不到它,根装载器负责装载JRE的核心类库,如JRE目标下的rt.jar、charsets.jar等。ExtClassLoader和AppClassLoader都是ClassLoader的子类。其中ExtClassLoader负责装载JRE扩展目录ext中的JAR类包;AppClassLoader负责装载Classpath路径下的类包。

        这三个类装载器之间存在父子层级关系,即根装载器是ExtClassLoader的父装载器,ExtClassLoader是AppClassLoader的父装载器。默认情况下,使用AppClassLoader装载应用程序的类,我们可以做一个实验:

package com.zxt.test;public class ClassLoaderTest {public static void main(String[] args) {ClassLoader loader = Thread.currentThread().getContextClassLoader();System.out.println("current loader = " + loader);System.out.println("parent loader = " + loader.getParent());System.out.println("grandparent loader = " + loader.getParent().getParent());}}

        运行以上代码,在控制台上将打出以下信息:

current loader =sun.misc.Launcher$AppClassLoader@73d16e93parent loader = sun.misc.Launcher$ExtClassLoader@15db9742// 根装载器在Java中访问不到,所以返回nullgrandparent loader = null

        通过以上的输出信息,我们知道当前的ClassLoader是AppClassLoader,父ClassLoader是ExtClassLoader,祖父ClassLoader是根类装载器,因为在Java中无法获得它的句柄,所以仅返回null。

        JVM装载类时使用“全盘负责委托机制”,“全盘负责”是指当一个ClassLoader装载一个类的时,除非显式地使用另一个ClassLoader,否则该类所依赖及引用的类也由这个ClassLoader载入;“委托机制”是指先委托父装载器寻找目标类,只有在找不到的情况下才从自己的类路径中查找并装载目标类。这一点是从安全角度考虑的,试想如果有人编写了一个恶意的基础类(如java.lang.String)并装载到JVM中将会引起多么可怕的后果。但是由于有了“全盘负责委托机制”,java.lang.String永远是由根装载器来装载的,这样就避免了上述事件的发生。


ClassLoader重要方法

        在Java中,ClassLoader是一个抽象类,位于java.lang包中。下面对该类的一些重要接口方法进行介绍:

        1.Class loadClass(Stringname) name参数指定类装载器需要装载类的名字,必须使用全限定类名,如com.baobaotao. beans.Car。该方法有一个重载方法loadClass(String name ,booleanresolve),resolve参数告诉类装载器是否需要解析该类。在初始化类之前,应考虑进行类解析的工作,但并不是所有的类都需要解析,如果JVM只需要知道该类是否存在或找出该类的超类,那么就不需要进行解析。

        2.Class defineClass(Stringname, byte[] b, int off, int len) 将类文件的字节数组转换成JVM内部的java.lang.Class对象。字节数组可以从本地文件系统、远程网络获取。name为字节数组对应的全限定类名。

        3.ClassfindSystemClass(String name) 从本地文件系统载入Class文件,如果本地文件系统不存在该Class文件,将抛出ClassNotFoundException异常。该方法是JVM默认使用的装载机制。

        4.ClassfindLoadedClass(String name)调用该方法来查看ClassLoader是否已装入某个类。如果已装入,那么返回java.lang.Class对象,否则返回null。如果强行装载已存在的类,将会抛出链接错误。

        5.ClassLoader getParent() 获取类装载器的父装载器,除根装载器外,所有的类装载器都有且仅有一个父装载器,ExtClassLoader的父装载器是根装载器,因为根装载器非Java编写,所以无法获得,将返回null。

        除JVM默认的三个ClassLoader以外,可以编写自己的第三方类装载器,以实现一些特殊的需求。类文件被装载并解析后,在JVM内将拥有一个对应的java.lang.Class类描述对象,该类的实例都拥有指向这个类描述对象的引用,而类描述对象又拥有指向关联ClassLoader的引用,如图所示:


        每一个类在JVM中都拥有一个对应的java.lang.Class对象,它提供了类结构信息的描述。数组、枚举、注解以及基本Java类型(如int、double等),甚至void都拥有对应的Class对象。Class没有public的构造方法。Class对象是在装载类时由JVM通过调用类装载器中的defineClass()方法自动构造的。


Java反射机制

        Class反射对象描述类语义结构,可以从Class对象中获取构造函数、成员变量、方法类等类元素的反射对象,并以编程的方式通过这些反射对象对目标类对象进行操作。这些反射对象类在java.reflect包中定义,下面是最主要的三个反射类:

        Constructor:类的构造函数反射类,通过Class#getConstructors()方法可以获得类的所有构造函数反射对象数组。在JDK5.0中,还可以通过getConstructor(Class...parameterTypes)获取拥有特定入参的构造函数反射对象。Constructor的一个主要方法是newInstance(Object[] initargs),通过该方法可以创建一个对象类的实例,相当于new关键字。在JDK5.0中该方法演化为更为灵活的形式:newInstance (Object... initargs)。

        Method:类方法的反射类,通过Class#getDeclaredMethods()方法可以获取类的所有方法反射类对象数组Method[]。在JDK5.0中可以通过getDeclaredMethod(String name, Class... parameterTypes)获取特定签名的方法,name为方法名;Class...为方法入参类型列表。Method最主要的方法是invoke(Object obj,Object[] args),obj表示操作的目标对象;args为方法入参,下面的例子演示了这个反射类的使用方法。在JDK 5.0中,该方法的形式调整为invoke(Object obj, Object... args)。此外,Method还有很多用于获取类方法更多信息的方法:

1.Class getReturnType():获取方法的返回值类型;

2.Class[]getParameterTypes():获取方法的入参类型数组;

3.Class[]getExceptionTypes():获取方法的异常类型数组;

4. Annotation[][]getParameterAnnotations():获取方法的注解信息,JDK 5.0中的新方法;

        Field:类的成员变量的反射类,通过Class#getDeclaredFields()方法可以获取类的成员变量反射对象数组,通过Class#getDeclaredField(Stringname)则可获取某个特定名称的成员变量反射对象。Field类最主要的方法是set(Object obj, Object value),obj表示操作的目标对象,通过value为目标对象的成员变量设置值。如果成员变量为基础类型,用户可以使用Field类中提供的带类型名的值设置方法,如setBoolean(Object obj,boolean value)、setInt(Object obj, intvalue)等。

        此外,Java还为包提供了Package反射类,在JDK5.0中还为注解提供了AnnotatedElement反射类。总之,Java的反射体系保证了可以通过程序化的方式访问目标类中所有的元素。


设计自己的IOC

        设计一个简单的IOC容器要可以存储对象,还要有注解注入的功能即可。

        Java语言允许通过程序化的方式间接对Class进行操作,Class文件由类装载器装载后,在JVM中将形成一份描述Class结构的元信息对象,通过该元信息对象可以获知Class的结构信息:如构造函数、属性和方法等。Java允许用户借由这个Class相关的元信息对象间接调用Class对象的功能,这就为使用程序化方式操作Class对象开辟了途径。

        下面是Java反射机制的简单的例子。

Hero.java

package com.zxt.model;/* * A LOL gram Hero damoin */public class Hero {// Hero nameprivate String name;// Outfit of the heroprivate String outfit;public Hero() {}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getOutfit() {return outfit;}public void setOutfit(String outfit) {this.outfit = outfit;}public void say() {System.out.println(this.name + "购买了" + this.outfit);}}

ReflecTest.java

package com.zxt.test;import java.lang.reflect.Constructor;import java.lang.reflect.Method;import com.zxt.model.Hero;public class ReflectTest {public static void main(String[] args) throws Exception {/* * 1.ͨ通过类装载器获取Hero类对象 *  * 获取当前线程的ClassLoader,然后通过指定的全限定类"com.zxt.model.Hero"装载Hero类对应的反射实例 */ClassLoader loader = Thread.currentThread().getContextClassLoader();Class<?> clazz = loader.loadClass("com.zxt.model.Hero");/* * 2.获取类的默认构造器对象并通过它实例化Hero *  * 通过Hero的反射类对象获取Hero的构造函数对象cons,通过构造函数对象的newInstrance()方法实例化Hero对象, * 其效果等同于new Hero() */Constructor<?> cons = clazz.getDeclaredConstructor((Class[]) null);Hero hero = (Hero) cons.newInstance();/* * 3.ͨ通过反射方法设置属性 *  * 通过Hero的反射类对象的getMethod(String methodName,Class * paramClass)获取属性的Setter方法对象, 第一个参数是目标Class的方法名;第二个参数是方法入参的对象类型。 * 获取方法反射对象后,即可通过invoke(Object obj,Object param)方法调用目标类的方法, * 该方法的第一个参数是操作的目标类对象实例;第二个参数是目标方法的入参。 */Method Name = clazz.getMethod("setName", String.class);Name.invoke(hero, "小鱼人");Method Outfit = clazz.getMethod("setOutfit", String.class);Outfit.invoke(hero, "爆裂魔杖");// 4.运行方法hero.say();}}

        对于private或protected的成员变量和方法,只要JVM的安全机制允许,也可以通过反射进行调用。

PrivateHero.java

package com.zxt.model;public class PrivateHero {// Hero nameprivate String name;// Outfit of the heroprivate String outfit;public void say() {System.out.println(this.name + "购买了" + this.outfit);}}

ReflectPrivateTest.java

package com.zxt.test;import java.lang.reflect.Field;import com.zxt.model.PrivateHero;public class ReflectPrivateTest {public static void main(String[] args) throws Exception {/* * 1.ͨ通过类装载器获取Hero类对象 *  * 获取当前线程的ClassLoader,然后通过指定的全限定类"com.zxt.model.PrivateHero"装载PrivateHero类对应的反射实例 */ClassLoader loader = Thread.currentThread().getContextClassLoader();Class<?> clazz = loader.loadClass("com.zxt.model.PrivateHero");PrivateHero hero = (PrivateHero) clazz.newInstance();/* * 2.ͨ通过反射方法设置属性 *  * 对于private或protected的成员变量和方法,只要JVM的安全机制允许,也可以通过反射进行调用 */Field name = clazz.getDeclaredField("name");// 取消Java语言访问检查以访问private变量name.setAccessible(true);name.set(hero, "小鱼人");Field outfit = clazz.getDeclaredField("outfit");outfit.setAccessible(true);outfit.set(hero, "爆裂魔杖");// 3.运行方法hero.say();}}

        了解了Java里面反射机制及类装载器之后,就可以开始实现自己的IOC了,首先设计接口,一个IOC容器中最核心的当属容器接口。

        容器要有存储和移除一个对象的能力,其次可以含括更多的获取和注册对象的方法。

Container.java

package com.ioc;import java.util.Set;// IOC容器public interface Container {// 初始化装配public void initWrited();// 返回所有bean对象名称public Set<String> getBeanNames();// 注册Bean// 注册一个Bean到容器中public Object registerBean(Object bean);// 注册一个Class到容器中public Object registerBean(Class<?> clazz);// 注册一个带名称的Bean到容器中public Object registerBean(String name, Object bean);// 获取Bean// 根据Class获取Beanpublic <T> T getBean(Class<T> clazz);// 根据名称获取Beanpublic <T> T getBeanByName(String name);// 删除Bean// 删除个Beanpublic void remove(Class<?> clazz);// 根据名称删除一个Beanpublic void removeByName(String name);}

       接下来是一个实现了该接口的简单容器类:

SampleContainer.java

package com.ioc;import java.lang.reflect.Field;import java.util.Iterator;import java.util.Map;import java.util.Map.Entry;import java.util.Set;import java.util.concurrent.ConcurrentHashMap;import com.ioc.annotation.AutoWrited;import com.ioc.util.ReflectUtil;/* * 容器的简单实现 */@SuppressWarnings("unchecked")public class SampleContainer implements Container {/* * 保存所有的bena对象,格式为com.ioc.xxx:@52x2xa */private Map<String, Object> beans;/* * 存储bena和name的关系,格式为xxx:com.ioc.xxx * 一般我们程序中使用的类名就是xxx,不会加包前缀 * xxx:com.ioc.xxx形式叫做全限定类名, * 也就是beanKeys的name:可以是普通类名也可以是全限定类名,而value值都是全限定类名 */private Map<String, String> beanKeys;public SampleContainer() {this.beans = new ConcurrentHashMap<String, Object>();this.beanKeys = new ConcurrentHashMap<String, String>();}@Overridepublic void initWrited() {Iterator<Entry<String, Object>> it = beans.entrySet().iterator();while (it.hasNext()) {Map.Entry<String, Object> entry = (Map.Entry<String, Object>) it.next();Object object = entry.getValue();injection(object);}}public void injection(Object object) {// 所有字段try {Field[] fields = object.getClass().getDeclaredFields();for (Field field : fields) {// 需要注入的字段AutoWrited autoWrited = field.getAnnotation(AutoWrited.class);if (null != autoWrited) {// 要注入的字段Object autoWritedField = null;String name = autoWrited.name();if (!name.equals("")) {String className = beanKeys.get(name);if (null != className && !className.equals("")) {autoWritedField = beans.get(className);}if (null == autoWritedField) {throw new RuntimeException("Unable to load" + name);}} else {if (autoWrited.value() == Class.class) {autoWritedField = recursiveAssembly(field.getType());} else {// 指定配置的类autoWritedField = this.getBean(autoWrited.getClass());if (null == autoWritedField) {autoWritedField = recursiveAssembly(autoWrited.value());}}}if (null == autoWritedField) {throw new RuntimeException("Unable to load" + field.getType().getCanonicalName());}boolean accessible = field.isAccessible();field.setAccessible(true);field.set(object, autoWritedField);field.setAccessible(accessible);}}} catch (SecurityException ex) {ex.printStackTrace();} catch (IllegalArgumentException ex) {ex.printStackTrace();} catch (IllegalAccessException ex) {ex.printStackTrace();}}private Object recursiveAssembly(Class<?> clazz) {if (null != clazz) {return this.registerBean(clazz);}return null;}@Overridepublic Set<String> getBeanNames() {return beanKeys.keySet();}@Overridepublic Object registerBean(Object bean) {String name = bean.getClass().getName();beanKeys.put(name, name);beans.put(name, bean);return bean;}@Overridepublic Object registerBean(Class<?> clazz) {String name = clazz.getName();beanKeys.put(name, name);Object bean = ReflectUtil.newInstance(clazz);beans.put(name, bean);return bean;}@Overridepublic Object registerBean(String name, Object bean) {String className = bean.getClass().getName();beanKeys.put(name, className);beans.put(className, bean);return bean;}@Overridepublic <T> T getBean(Class<T> clazz) {String name = clazz.getName();Object object = beans.get(name);if (null != object) {return (T) object;}return null;}@Overridepublic <T> T getBeanByName(String name) {String className = beanKeys.get(name);Object object = beans.get(className);if (null != object) {return (T) object;}return null;}@Overridepublic void remove(Class<?> clazz) {String className = clazz.getName();if(null != className && !className.equals("")) {beanKeys.remove(className);beans.remove(className);}}@Overridepublic void removeByName(String name) {String className = beanKeys.get(name);if(null != className && ! className.equals("")) {beanKeys.remove(name);beans.remove(className);}}}

        这里将所有Bean的名称存储在 beanKeys 这个map中,将所有的对象存储在 beans 中,用 beanKeys 维护名称和对象的关系。

        在装配的时候步骤如下:

1. 判断是否使用了自定义命名的对象(是:根据name查找bean)。

2. 判断是否使用了Class类型Bean(是:根据Class查找Bean,如果查找不到则创建一个无参构造函数的Bean)

        程序用到的工具类代码:

ReflectUtil.java

package com.ioc.util;import java.io.File;import java.io.IOException;import java.lang.reflect.Method;import java.net.JarURLConnection;import java.net.URISyntaxException;import java.net.URL;import java.util.ArrayList;import java.util.Enumeration;import java.util.List;import java.util.jar.JarEntry;import java.util.jar.JarFile;public abstract class ReflectUtil {// ------------新建对象实例------------// @param 对象名称public static Object newInstance(String className) {Object object = null;try {Class<?> clazz = Class.forName(className);object = clazz.newInstance();} catch (Exception ex) {ex.printStackTrace();}return object;}// @param clazz class对象public static Object newInstance(Class<?> clazz) {Object object = null;try {object = clazz.newInstance();} catch (InstantiationException ex) {ex.printStackTrace();} catch (IllegalAccessException ex) {ex.printStackTrace();}return object;}// -------------------------------------// 类型转换@SuppressWarnings("unchecked")public static <T> T cast(Object value, Class<T> type) {if (value != null && !type.isAssignableFrom(value.getClass())) {if (is(type, int.class, Integer.class)) {value = Integer.parseInt(String.valueOf(value));} else if (is(type, long.class, Long.class)) {value = Long.parseLong(String.valueOf(value));} else if (is(type, float.class, Float.class)) {value = Float.parseFloat(String.valueOf(value));} else if (is(type, double.class, Double.class)) {value = Double.parseDouble(String.valueOf(value));} else if (is(type, boolean.class, Boolean.class)) {value = Boolean.parseBoolean(String.valueOf(value));} else if (is(type, String.class)) {value = String.valueOf(value);}}return (T) value;}// 某对象是否是指定对象类型列表中一个private static boolean is(Object object, Object... objects) {if (object != null && objects != null) {for (Object mb : objects) {if (object.equals(mb)) {return true;}}}return false;}// -----------------------------------// 查找方法public static Method getMethodByName(Object classOrBean, String methodName) {Method method = null;if (classOrBean != null) {Class<?> clazz = null;if (classOrBean instanceof Class<?>) {clazz = (Class<?>) classOrBean;} else {clazz = classOrBean.getClass();}for (Method met : clazz.getMethods()) {if (met.getName().equals(methodName)) {method = met;break;}}}return method;}public static Method getMethodByName(Class<?> clazz, String methodName) {Method method = null;for (Method met : clazz.getMethods()) {if (met.getName().equals(methodName)) {method = met;break;}}return method;}// ------------------------------------------// 扫描包下面所有的类public static List<String> scanPackageClass(String rootPackageName) {List<String> classNames = new ArrayList<String>();try {ClassLoader loader = Thread.currentThread().getContextClassLoader();URL url = loader.getResource(rootPackageName);String protocol = url.getProtocol();if ("file".equals(protocol)) {File[] files = new File(url.toURI()).listFiles();for (File f : files) {scanPackageClassInFile(rootPackageName, f, classNames);}} else if ("jar".equals(protocol)) {JarFile jar = ((JarURLConnection) url.openConnection()).getJarFile();scanPackClassInJar(rootPackageName, jar, classNames);}} catch (URISyntaxException ex) {ex.printStackTrace();} catch (IOException ex) {ex.printStackTrace();}return classNames;}// 扫描文件夹下所有class文件private static void scanPackageClassInFile(String rootPackageName, File rootFile, List<String> classNames) {String absFileName = rootPackageName + "." + rootFile.getName();if (rootFile.isFile() && absFileName.endsWith(".class")) {classNames.add(absFileName.substring(0, absFileName.length() - 6));} else if (rootFile.isDirectory()) {String tmPackageName = rootPackageName + "." + rootFile.getName();for (File f : rootFile.listFiles()) {scanPackageClassInFile(tmPackageName, f, classNames);}}}// 扫描jar里面的类private static void scanPackClassInJar(String packageDirName, JarFile jar, List<String> classNames) {Enumeration<JarEntry> entries = jar.entries();while (entries.hasMoreElements()) {JarEntry entry = entries.nextElement();String name = entry.getName().replace("/", ".");if (name.startsWith(packageDirName) && name.endsWith(".class")) {classNames.add(name.substring(0, name.length() - 6));}}}}

AutoWrited.java

package com.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 AutoWrited {// 要注入的类类型Class<?> value() default Class.class;// bean的名称String name() default "";}

测试该IOC容器

FaceService.java

package com.ioc.test;public class FaceService {public void buy(String name, int money) {System.out.println(name + "买了" + money + "毛钱特效,装逼成功!");}} 

LoL.java

package com.ioc.test;import com.ioc.annotation.AutoWrited;public class Lol {@AutoWritedprivate FaceService faceService;public void work() {faceService.buy("剑圣", 5);}public FaceService getFaceService() {return faceService;}}

LoL2.java

package com.ioc.test;import com.ioc.annotation.AutoWrited;public class Lol2 {@AutoWrited(value = FaceService.class)private FaceService faceService;public void work() {faceService.buy("德玛", 5);}public FaceService getFaceService() {return faceService;}}

LoL3.java

package com.ioc.test;import com.ioc.annotation.AutoWrited;public class Lol3 {@AutoWrited(name = "face")private FaceService faceService;public void work() {faceService.buy("瞎子", 5);}public FaceService getFaceService() {return faceService;}}

IocTest.java

package com.ioc.test;import com.ioc.Container;import com.ioc.SampleContainer;public class IocTest {private static Container container = new SampleContainer();public static void baseTest(){container.registerBean(Lol.class);// 初始化注入container.initWrited();Lol lol = container.getBean(Lol.class);lol.work();}public static void iocClassTest(){container.registerBean(Lol2.class);// 初始化注入container.initWrited();Lol2 lol = container.getBean(Lol2.class);lol.work();}public static void iocNameTest(){container.registerBean("face", new FaceService());container.registerBean(Lol3.class);// 初始化注入container.initWrited();Lol3 lol = container.getBean(Lol3.class);lol.work();}public static void main(String[] args) {baseTest();iocClassTest();iocNameTest();}}

最后程序输出的结果是:

剑圣买了5毛钱特效,装逼成功!德玛买了5毛钱特效,装逼成功!瞎子买了5毛钱特效,装逼成功!

0 0
原创粉丝点击