反射是什么?
RTTI是类型在编译器就知道了,但是如果类型在编译期不可知,那么就需要使用反射来获取运行时得类型信息。Class和java.lang.reflect类库为反射提供了支持。使用反射时,需要先检查这个对象,看它属于哪个类,然后加载这个类的class对象,那个类的class文件对于jvm来说必须是可获取的,可以在本地机器,也可以是从网络取得。
class对象
java class文件加载过程:jvm把描述类的数据从class文件加载(loading)到内存(java方法区)中,中间对数据进行校验(verification)、转换解析(resolution)和初始化(initialization),最终形成可以被jvm直接使用的Java类,这就是class文件的加载。
获取class对象的三种方式及区别
Class.forName()
类.class
对象.getClass()
类.class不会自动初始化该class对象,而Class.forName()会初始化该class对象
类.class不会抛出异常,而Class.forName()会抛出异常。
instanceof和isInstance区别
类型转换前先做检查
A instanceof B A.class.isInstance(对象实例); instanceof比较硬编码,需要知道确切的类,而isInstance只要传递实例对象即可,更加的灵活。
Instanceof有严格的限制,只能将其与命名类型进行比较,而不能与Class对象比较。
实战
通过java反射仿造spring加载bean的过程
项目的目录结构如下:
首先pom文件导入依赖的jar包,junit和slf4j
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
<!-- <scope>test</scope> -->
</dependency>
在resources文件夹下,新建两个properties文件,bean.properties和log4j.properties bean.properties用来配置我们需要加载的类,内容如下:
spring.beanname=com.study.Battle.reflect.Student
log4j.properties文件是配置日志输出的地址,本例输出到控制台。内容如下:
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
然后新建两个实体类,people和student,student继承people
public interface People {
void init();
String getName();
}
Student.java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Student implements People {
public static Logger LOGGER = LoggerFactory.getLogger(Student.class);
private String name;
public String getName() {
return name;
}
public void init() {
// TODO Auto-generated method stub
LOGGER.info("Student init......");
name = "init";
}
}
新建一个PropertyUtil.java工具类,用来读取properties文件,来获取bean的name
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PropertyUtil {
private static Logger LOGGER = LoggerFactory.getLogger(PropertyUtil.class);
public static List<String> getBeanName(String path) {
List<String> list = new ArrayList<String>();
Properties properties = new Properties();
// 使用ClassLoader加载properties配置文件生成对应的输入流
InputStream in = PropertyUtil.class.getClassLoader().getResourceAsStream(path);
// 使用properties对象加载输入流
try {
properties.load(in);
// 获取key对应的value值
list.add(properties.getProperty("spring.beanname"));
} catch (IOException e) {
LOGGER.info("文件路径错误,找不到文件");
}
return list;
}
}
新建一个工厂类,PropertyBeanFactory.java来实现从properties文件读取并加载bean
public interface BeanFactory {
Object getBean(String beanName);
}
// 继承BeanFactory
public class PropertyBeanFactory implements BeanFactory {
private static Logger LOGGER = LoggerFactory.getLogger(PropertyBeanFactory.class);
// bean的容器
public static Map<String, Object> beans = new HashMap<String, Object>();
// 从properties文件中加载bean
public static void loadBean() {
List<String> beanNames = PropertyUtil.getBeanName("bean.properties");
String name = null;
try {
for (String beanName : beanNames) {
if (beans.get(beanName) != null)
continue;
name = beanName;
Class<?> beanClass = Class.forName(beanName);
Object object = beanClass.newInstance();
if (People.class.isInstance(object)) {
((People) object).init();
;
}
beans.put(getSimpleName(beanName), object);
}
} catch (ClassNotFoundException e) {
LOGGER.info("找不到类" + name);
} catch (Exception e) {
}
}
public static String getSimpleName(String beanName) {
if (beanName == null) {
return null;
} else {
int index = beanName.lastIndexOf(".");
if (index < beanName.length()) {
return beanName.substring(index + 1);
} else {
return null;
}
}
}
static {
loadBean();
}
public Object getBean(String beanName) {
// TODO Auto-generated method stub
return beans.get(beanName);
}
}
一切准备就绪,可以获取从容器中获取bean了。
public class ReflectDemo {
public static void main(String[] args) {
BeanFactory beanFactory = new PropertyBeanFactory();
People people = (People) beanFactory.getBean("Student");
System.out.println(people.getName());
}
}
输出结果如下:
INFO [main] - Student init......
init