IOC容器-实现原理反射

释放双眼,带上耳机,听听看~!

在上一篇中我们通过spring的IOC容器,成功实例化了我们指定的对象,在这一篇中我们将简单了解一下spring底层到底是怎么实现上述功能呢。也就是Java中反射技术。由于反射技术是Java API中提供的功能,所以在这里我们不在做过多的介绍,而是通过测试用例,来了解一下反射技术的具体应用。

下面我们将用反射技术,来实现上一篇中IOC容器实现的功能。

  • 属性注入
public class UserDetail {

    public void print(String user) {
        System.out.println(user);
    }
}
public class UserInfo {

    private UserDetail userDetail;
    private UserDetailAble userDetailAble;

    public UserInfo() {
    }

    public UserInfo(UserDetail userDetail) {
        this.userDetail = userDetail;
    }

    public void setUserDetail(UserDetail userDetail) {
        this.userDetail = userDetail;
    }

    public void setUserDetailAble(UserDetailAble userDetailAble) {
        this.userDetailAble = userDetailAble;
    }

    public void print(String msg) {
        userDetail.print(msg);
    }

    public void print() {
        userDetailAble.print();
    }

}
public class ReflectTest {
    public static UserInfo instanceUserInfo(String classPath) throws Exception {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        Class clazz = classLoader.loadClass(classPath);

        Constructor constructor = clazz.getDeclaredConstructor((Class[]) null);
        UserInfo userInfo = (UserInfo) constructor.newInstance();

        Method setUserDetail = clazz.getMethod("setUserDetail", UserDetail.class);
        setUserDetail.invoke(userInfo, new UserDetail());

        return userInfo;
    }

    public static void main(String[] args) throws Exception {
        UserInfo userInfo = instanceUserInfo("com.jilinwula.spring.UserInfo");
        userInfo.print("属性注入");
    }
}
属性注入
  • 构造函数注入
public class ReflectTest {
    public static UserInfo instanceUserInfo(String classPath) throws Exception {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        Class clazz = classLoader.loadClass(classPath);

        Constructor constructor = clazz.getDeclaredConstructor(UserDetail.class);
        UserInfo userInfo = (UserInfo) constructor.newInstance(new UserDetail());

        return userInfo;
    }

    public static void main(String[] args) throws Exception {
        UserInfo userInfo = instanceUserInfo("com.jilinwula.spring.UserInfo");
        userInfo.print("构造函数注入");
    }
}
构造函数注入
  • 接口注入
public class ReflectTest {
    public static UserInfo instanceUserInfo(String classPath) throws Exception {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        Class clazz = classLoader.loadClass(classPath);

        Constructor constructor = clazz.getDeclaredConstructor((Class[]) null);
        UserInfo userInfo = (UserInfo) constructor.newInstance();

        Method setUserDetailAble = clazz.getDeclaredMethod("setUserDetailAble", UserDetailAble.class);
        setUserDetailAble.invoke(userInfo, new UserDetailAbleImpl());

        return userInfo;
    }

    public static void main(String[] args) throws Exception {
        UserInfo userInfo = instanceUserInfo("com.jilinwula.spring.UserInfo");
        userInfo.print();
    }
}
接口注入

上面的代码基本实现了我们上一篇中IOC容器的功能,但是在用Java反射技术实例化对象时,如果方法是私用的也就是private,那么我们通过上述的方式调用则会出现问题,请看下面测试用例。

public class UserInfo {

    private UserDetail userDetail;
    private UserDetailAble userDetailAble;

    public UserInfo() {
    }

    public UserInfo(UserDetail userDetail) {
        this.userDetail = userDetail;
    }

    public void setUserDetail(UserDetail userDetail) {
        this.userDetail = userDetail;
    }

    private void setUserDetailAble(UserDetailAble userDetailAble) {
        this.userDetailAble = userDetailAble;
    }

    public void print(String msg) {
        userDetail.print(msg);
    }

    public void print() {
        userDetailAble.print();
    }

}

我们已经将setUserDetailAble的方法设置成了私用方法。然后我们直接执行上一个已经通过的接口注入的测试用例。看一下执行结果。

Exception in thread "main" java.lang.IllegalAccessException: Class com.jilinwula.spring.ReflectTest can not access a member of class com.jilinwula.spring.UserInfo with modifiers "private"
	at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
	at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
	at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)
	at java.lang.reflect.Method.invoke(Method.java:491)
	at com.jilinwula.spring.ReflectTest.instanceUserInfo(ReflectTest.java:19)
	at com.jilinwula.spring.ReflectTest.main(ReflectTest.java:25)

我们看虽然我们测试用例什么都没有改,但上述结果还是出现了异常。这是为什么呢?这是因为在使用Java反射时如果要访问private类型的方法或字段必须要取消Java语言的语法检查。也就是添加额外的代码,还是可以调用私用的方法和属性的,在这一点上与我们正常开发时还是有区别的,因为Java语言规定私用方法只能在本类中调用,但是如果用Java中反射技术是可以打破这个限制的。下面我们看怎么调用私用的方法。

public class ReflectTest {
    public static UserInfo instanceUserInfo(String classPath) throws Exception {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        Class clazz = classLoader.loadClass(classPath);

        Constructor constructor = clazz.getDeclaredConstructor((Class[]) null);
        UserInfo userInfo = (UserInfo) constructor.newInstance();

        Method setUserDetailAble = clazz.getDeclaredMethod("setUserDetailAble", UserDetailAble.class);
        setUserDetailAble.setAccessible(true);
        setUserDetailAble.invoke(userInfo, new UserDetailAbleImpl());

        return userInfo;
    }

    public static void main(String[] args) throws Exception {
        UserInfo userInfo = instanceUserInfo("com.jilinwula.spring.UserInfo");
        userInfo.print();
    }
}
0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧