1.动态语言
动态语言,是指程序在运行时可以改变其结构:新的函数可以被引进,已有的函数可以被删除等在结构上的变化。比如众所周知的ECMAScript(JavaScript)便是一个动态语言。除此之外如Ruby、Python等也都属于动态语言,而C、C++等语言则不属于动态语言。(引自: 百度百科)
var execString = "alert(Math.floor(Math.random()*10));";
eval(execString);
2.反射机制
- 反射指的是可以于运行时加载、探知、使用编译期间完全未知的类
- 程序在运行状态中,可以动态加载一个只有名称的类,对于任意一个已加载的类,都能够知道这个类的所有属性和方法,对于任意对象都能够调用它的任一方法和属性
Class c = Class.forName("com.wmx.test.User");
- 加载完类之后,在堆内存中,就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息,我们可以通过这个对象看到类的结构,这个对象就像一面镜子,透过这个镜子看到类的结构,所以我们形象的称之为:反射
3.Java反射机制提供的功能
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时调用任意一个对象的成员变量和方法
- 生成动态代理
4.Java反射API
反射API用来生成在当前JAVA虚拟机中的类、接口或者对象的信息。
- Class类:反射的核心类,可以获取类的属性,方法等内容信息。
- Field类:Java.lang.reflect.表示类的属性,可以获取和设置类的中属性值。
- Method类:Java.lang.reflect。表示类的方法,它可以用来获取类中方法的信息或者执行方法
- Construcor类:Java.lang.reflect。表示类的构造方法。
5.Class类
5.1 什么是Class类
在面向对象的世界里,万物皆对象。类是对象,类是java.lang.Class类的实例对象。另外class类只有java虚拟机才能new出来。任何一个类都是Class 类的实例对象。
5.2 如何获取Class对象
- 对象的getClass()方法;
- 类的.class(最安全/性能最好)属性;
- 运用Class.forName(String className)动态加载类,className需要是类的全限定名(最常用).
String str = new String();
Class<? extends String> class2 = str.getClass();
Class<? extends String> class1 = String.class;
Class<?> forName = Class.forName("java.lang.String");
5.3 有了Class对象能做什么
5.3.1 创建对象
-
调用Class对象的newInstance()方法(类必须有一个无参数的构造器;类的构造器的访问权限需要足够。)
-
有参的构造方法构建对象时
- 根据全类名获取对应的Class对象
- 调用指定参数结构的构造器,生成Constructor的实例
- .通过Constructor的实例创建对应类的对象,并初始化类属性
package com.blog;
public class Person {
private String name;
private Integer age;
public Person(){
}
public Person(String name,Integer age){
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
通过Class生成Person的对象
import java.lang.reflect.Constructor;
public class Test {
public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName("com.blog.Person");
Person person1 = (Person)clazz.newInstance();
Constructor<?> constroctor = Class.forName("com.blog.Person").getDeclaredConstructor(String.class,Integer.class);
Person person2 = (Person)constroctor.newInstance("wmx",20);
System.out.println(person1);
System.out.println(person2);
}
}
执行结果
Person [name=null, age=null]
Person [name=wmx, age=20]
5.3.2 通过反射调用类的结构
Class类提供了大量的实例方法来获取该Class对象所对应的详细信息,Class类大致包含如下方法,其中每个方法都包含多个重载版本,因此我们只是做简单的介绍,详细请参考JDK文档
获取内容 | 方法签名 |
---|---|
构造器 | Constructor getConstructor(Class<?>… parameterTypes) |
包含的方法 | Method getMethod(String name, Class<?>… parameterTypes) |
包含的属性 | Field getField(String name) |
包含的Annotation | < A extends Annotation > A getAnnotation(Class< A> annotationClass) |
所实现的接口 | Class<?>[] getInterfaces() |
修饰符 | int getModifiers() |
所在包 | Package getPackage() |
全路径类名 | String getName() |
类名 | String getSimpleName() |
5.4 常用操作
5.4.1 通过反射调用类中的指定方法
通过反射,调用类中的方法,通过Method类完成。步骤:
- 通过Class类的getMethod(String name,Class…parameterTypes)方法取得一个Method对象,并设置此方法操作时所需要的参数类型。
- 之后使用Object invoke(Object obj, Object[] args)进行调用,并向方法中传递要设置的obj对象的参数信息。
public class Test {
public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName("com.blog.Person");
Person person1 = (Person)clazz.newInstance();
Constructor<?> constroctor = Class.forName("com.blog.Person").getDeclaredConstructor(String.class,Integer.class);
Person person2 = (Person)constroctor.newInstance("wmx",20);
//利用反射调用方法
Method method = clazz.getMethod("setName", String.class);
method.invoke(person1, "wmx1");
System.out.println(person1);
System.out.println(person2);
}
}
执行结果:
Person [name=wmx1, age=null]
Person [name=wmx, age=20]
说明:方法Object invoke(Object obj, Object … args)
1. Object 对应原方法的返回值,若原方法无返回值,此时返回null
2. 若原方法若为静态方法,此时形参Object obj可为null
3. 若原方法形参列表为空,则Object[] args为null
4. 若原方法声明为private,则需要在调用此invoke()方法前,显式调用方法对象的setAccessible(true)方法,将可访问private的方法。
5.4.2 通过反射调用类中的指定属性
-
在反射机制中,可以直接通过Field类操作类中的属性,通过Field类提供的set()和get()方法就可以完成设置和取得属性内容的操作。
public Field getField(String name) 返回此Class对象表示的类或接口的指定的public的Field。
public Field getDeclaredField(String name)返回此Class对象表示的类或接口的指定的Field。 -
在Field中:
public Object get(Object obj) 取得指定对象obj上此Field的属性内容
public void set(Object obj,Object value) 设置指定对象obj上此Field的属性内容
public class Test {
public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName("com.blog.Person");
Person person1 = (Person)clazz.newInstance();
//利用反射调用方法
Method method = clazz.getMethod("setName", String.class);
method.invoke(person1, "wmx1");
//利用反射获取属性,并赋值
Field ageField = clazz.getDeclaredField("age");
ageField.setAccessible(true);
ageField.set(person1, 10);
System.out.println(person1);
}
}
执行结果:
Person [name=wmx1, age=10]
说明:在类中属性都设置为private的前提下,在使用set()和get()方法时,首先要使用Field类中的setAccessible(true)方法将需要操作的属性设置为可以被外部访问。否则会报下面异常
Exception in thread “main” java.lang.IllegalAccessException: Class com.blog.Test can not access a member of class com.blog.Person with modifiers “private”
at sun.reflect.Reflection.ensureMemberAccess(Unknown Source)
at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(Unknown Source)
at java.lang.reflect.AccessibleObject.checkAccess(Unknown Source)
at java.lang.reflect.Field.set(Unknown Source)
at com.blog.Test.main(Test.java:18)
6.反射的经典应用(Spring自动注入)
下面我们使用反射模拟一下Spring的自动注入
项目的目录结构如下图:
实体类:
@Data
public class User {
private String username;
private String password;
}
dao层:
public interface UserDao {
public void save(User user);
}
dao实现层
public class UserDaoImpl implements UserDao{
public void save(User user) {
System.out.println(user.getUsername()+"保存成功");
}
}
service层:
public interface UserService {
public void save(User user);
}
service实现层
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void save(User user) {
userDao.save(user);
}
public UserDao getUserDao() {
return userDao;
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
bean.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="u" class="com.wmx.dao.impl.UserDaoImpl"></bean>
<bean id="userService" class="com.wmx.service.impl.UserServiceImpl">
<property name="userDao" bean="u"></property>
</bean>
</beans>
根据bean.xml自动给userService注入userDao的实例
BeanFactory:
public interface BeanFactory {
public Object getBean(String id);
}
ClassPathXMLApplicationContext:
public class ClassPathXMLApplicationContext implements BeanFactory{
private Map<String , Object> beans = new HashMap<String, Object>();
public ClassPathXMLApplicationContext() throws Exception{
SAXReader reader = new SAXReader();
InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("bean.xml");
Document doc = reader.read(resourceAsStream);
Element root=doc.getRootElement();
System.out.println(root.getName());
Iterator<Element> iterator = root.elementIterator();
while(iterator.hasNext()) {
Element element = iterator.next();//获取子元素
String id= element.attributeValue("id");
String clazz= element.attributeValue("class");
//使用反射生成实例对象
Object o = Class.forName(clazz).newInstance();
//将bean放入组件
beans.put(id, o);
List elements = element.elements();
for(int i=0;i<elements.size();i++) {
Element e = (Element)elements.get(i);
String name = e.attributeValue("name");
String bean = e.attributeValue("bean");
Object beanObject = beans.get(bean);
String methodName = "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
Class<?> interfaces = beanObject.getClass().getInterfaces()[0];
Method m = o.getClass().getMethod(methodName, beanObject.getClass().getInterfaces()[0]);
//使用反射调用setter方法,注入元素
m.invoke(o, beanObject);
}
}
}
public Object getBean(String id) {
return beans.get(id);
}
}
测试:
public class UserServiceImplTest {
@Test
public void test() throws Exception{
ClassPathXMLApplicationContext factory = new ClassPathXMLApplicationContext();
UserService bean = (UserService)factory.getBean("userService");
User user = new User();
user.setUsername("admin");
bean.save(user);
}
}
执行结果:
admin保存成功
注入成功