Java高级编程:反射机制

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对象

  1. 对象的getClass()方法;
  2. 类的.class(最安全/性能最好)属性;
  3. 运用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()方法(类必须有一个无参数的构造器;类的构造器的访问权限需要足够。)

  • 有参的构造方法构建对象时

    1. 根据全类名获取对应的Class对象
    2. 调用指定参数结构的构造器,生成Constructor的实例
    3. .通过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类完成。步骤:

  1. 通过Class类的getMethod(String name,Class…parameterTypes)方法取得一个Method对象,并设置此方法操作时所需要的参数类型。
  2. 之后使用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 通过反射调用类中的指定属性

  1. 在反射机制中,可以直接通过Field类操作类中的属性,通过Field类提供的set()和get()方法就可以完成设置和取得属性内容的操作。
    public Field getField(String name) 返回此Class对象表示的类或接口的指定的public的Field。
    public Field getDeclaredField(String name)返回此Class对象表示的类或接口的指定的Field。

  2. 在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保存成功

注入成功

发布了40 篇原创文章 · 获赞 9 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/aawmx123/article/details/87802091