IOC容器-实现原理反射

/ Spring / 没有评论 / 348浏览

在上一篇中我们通过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();
}
}
接口注入