在Java中反射和动态代理机制很强大,通过反射机制在运行时获取信息。用于实现IOC。
代理是Java基本的设计模式,提供向对象插入额外的或不同的操作。Java的动态代理能动态的创建代理对象,以及动态的调用代理方法。用于实现AOP。
一、Java反射机制
1、作用
(1)在运行时判断任意一个对象所属的类;
(2)在运行时获取类的对象;
(3)在运行时获得类所具有的成员变量和方法等;
2、Reflection类:
package com.makocn.reflection;
import java.lang.reflect.Method;
import java.util.Hashtable;
public class Reflection {
public static void main(String[] args) throws Exception {
Reflection reflection = new Reflection();
reflection.getNameTest();
System.out.println("*****************************************");
reflection.getMethodTest();
}
public void getNameTest() throws Exception {
String name = "马可中国";
Class cls = name.getClass();
System.out.println("String类名: " + cls.getName());
Class superBtnClass = cls.getSuperclass();
System.out.println("String的父类名: " + superBtnClass.getName());
Class clsTest = Class.forName("java.util.Date");
System.out.println("clsTest name: " + clsTest.getName());
}
public void getMethodTest() throws Exception {
Class cls = Class.forName("com.makocn.reflection.Reflection");
Class ptypes[] = new Class[2];
ptypes[0] = Class.forName("java.lang.String");
ptypes[1] = Class.forName("java.util.Hashtable");
Method method = cls.getMethod("testMethod", ptypes);
Object args[] = new Object[2];
args[0] = "oh, my dear!";
Hashtable<String, String> ht = new Hashtable<String, String>();
ht.put("name", "马可中国");
args[1] = ht;
String returnStr = (String) method.invoke(new Reflection(), args);
System.out.println("returnStr= " + returnStr);
}
public String testMethod(String str, Hashtable ht) throws Exception {
String returnStr = "返回值";
System.out.println("测试testMethod()方法调用");
System.out.println("str= " + str);
System.out.println("名字= " + (String) ht.get("name"));
System.out.println("结束testMethod()方法调用");
return returnStr;
}
}
3、运行结果:
String类名: java.lang.String
String的父类名: java.lang.Object
clsTest name: java.util.Date
*****************************************
测试testMethod()方法调用
str= oh, my dear!
名字= 马可中国
结束testMethod()方法调用
returnStr= 返回值
二、Java耦合例子
1、Chinese类:
package com.makocn.reflection;
public class Chinese{
public void sayHello(String name) {
String helloWorld = "你好," + name;
System.out.println(helloWorld);
}
}
2、American类:
package com.makocn.reflection;
public class American{
public void sayHello(String name) {
String helloWorld = "Hello," + name;
System.out.println(helloWorld);
}
}
3、HelloWorld类:
package com.makocn.reflection;
public class HelloWorld {
public static void main(String[] args) {
Chinese chinese = new Chinese();
chinese.sayHello("马可中国");
American american = new American();
american.sayHello("makocn");
}
}
从HelloWorld可以看到,该类与Chinese.java类和American.java类都存在强耦合关系。
4、运行结果:
你好,马可中国
Hello,makocn
三、工厂模式解耦例子
1、Human接口:
package com.makocn.reflection;
public interface Human {
public void sayHello(String name);
}
2、Chinese类:
package com.makocn.reflection;
public class Chinese implements Human{
public void sayHello(String name) {
String helloWorld = "你好," + name;
System.out.println(helloWorld);
}
}
3、American类:
package com.makocn.reflection;
public class American implements Human{
public void sayHello(String name) {
String helloWorld = "Hello," + name;
System.out.println(helloWorld);
}
}
4、HumanFactory工厂类:
package com.makocn.reflection;
public class HumanFactory {
public Human getHuman(String type) {
if ("chinese".equals(type)) {
return new Chinese();
} else {
return new American();
}
}
}
5、HelloWorld类:
package com.makocn.reflection;
public class HelloWorld {
public static void main(String[] args) {
HumanFactory factory = new HumanFactory();
Human human1 = factory.getHuman("chinese");
human1.sayHello("马可中国");
Human human2 = factory.getHuman("american");
human2.sayHello("makocn");
}
}
6、运行结果:
你好,马可中国
Hello,makocn
利用工厂HumanFactory,我们不再与具体的实现类Chinese和American存在耦合关系,而只是与接口类Human存在耦合关系,具体对象的获取只是通过传入字符串来获取,降低了类与类之间的耦合性。但如果我们要换具体类的实现时,则我们还需修改工厂类中的字符串。
四、IOC实现
IOC(Inverse of Control)翻译为控制反转,也称DI(Dependence Injection)依赖注入。实现好莱坞原则:不用你主动来找我,我会通知你。
通过IOC可以将实现类、参数信息等配置在其对应的配置文件中,当需要更改实现类或参数信息时,只需要修改配置文件即可,进一步降低了类与类之间的耦合。同时还可以对某对象所需要的其它对象进行注入。
Spring的IOC的实现原理就是Java反射机制, 同时Spring还充当了工厂角色,Spring的工厂类读取配置文件、利用反射机制注入对象等。程序可通过bean的名称获取对应的对象。
1、BeanFactory工厂类
package com.makocn.reflection;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
public class BeanFactory {
private Map<String, Object> beanMap = new HashMap<String, Object>();
public void init(String xml) {
try {
// 读取指定的配置文件
SAXReader reader = new SAXReader();
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// 从class目录下获取指定的xml文件
InputStream ins = classLoader.getResourceAsStream(xml);
Document doc = reader.read(ins);
Element root = doc.getRootElement();
Element foo;
// 遍历bean
for (Iterator i = root.elementIterator("bean"); i.hasNext();) {
foo = (Element) i.next();
// 获取bean的属性id和class
Attribute id = foo.attribute("id");
Attribute cls = foo.attribute("class");
// 利用Java反射机制,通过class的名称获取Class对象
Class bean = Class.forName(cls.getText());
// 获取对应class的信息
java.beans.BeanInfo info = java.beans.Introspector.getBeanInfo(bean);
// 获取其属性描述
java.beans.PropertyDescriptor pd[] = info.getPropertyDescriptors();
// 设置值的方法
Method mSet = null;
// 创建一个对象
Object obj = bean.newInstance();
// 遍历该bean的property属性
for (Iterator ite = foo.elementIterator("property"); ite.hasNext();) {
Element foo2 = (Element) ite.next();
// 获取该property的name属性
Attribute name = foo2.attribute("name");
String value = null;
// 获取该property的子元素value的值
for (Iterator ite1 = foo2.elementIterator("value"); ite1.hasNext();) {
Element node = (Element) ite1.next();
value = node.getText();
break;
}
for (int k = 0; k < pd.length; k++) {
if (pd[k].getName().equalsIgnoreCase(name.getText())) {
mSet = pd[k].getWriteMethod();
// 利用Java的反射极致调用对象的某个set方法,并将值设置进去
mSet.invoke(obj, value);
}
}
}
// 将对象放入beanMap中,其中key为id值,value为对象
beanMap.put(id.getText(), obj);
}
} catch (Exception e) {
System.out.println(e.toString());
}
}
public Object getBean(String beanName) {
Object obj = beanMap.get(beanName);
return obj;
}
public static void main(String[] args) {
BeanFactory factory = new BeanFactory();
factory.init("config.xml");
JavaBean javaBean = (JavaBean) factory.getBean("javaBean");
System.out.println("userName=" + javaBean.getUserName());
System.out.println("password=" + javaBean.getPassword());
}
}
2、JavaBean类:
package com.makocn.reflection;
public class JavaBean {
private String userName;
private String password;
public String getPassword() {
return password;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public void setPassword(String password) {
this.password = password;
}
}
3、config.xml配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="javaBean" class="com.makocn.reflection.JavaBean">
<property name="userName">
<value>马可中国</value>
</property>
<property name="password">
<value>1357908642abc</value>
</property>
</bean>
</beans>
4、运行结果:
userName=马可中国
password=1357908642abc
虽然在main()方法中没有对属性赋值,但属性值已经被注入。
在BeanFactory类中:
Class bean = Class.forName(cls.getText());通过类名来获取对应的类;
mSet.invoke(obj, value);通过invoke方法来调用特定对象的特定方法;
实现的原理都是基于Java的反射机制。
这只是对IOC的一个简单演示,Spring要复杂得多;如一个bean引用另一个bean,可有多个配置文件,通过多种方式载入配置文件等。