反射
1.反射机制
Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。
1.1如何理解反射
首先反射是具有动态性的
1.反射是Java中特有的一种技术
2.Java中自省特性的一种实现(对象运行时动态发现对象成员,属性,方法)
3.反射是实现Java动态编程的基石(例如:AOP)
1.2反射的应用起点
反射应用的起点,也可以理解为反射应用的入口,在Java中这个入口是字节码对象(Class对象).
获取方式如下:
1.类名.class
2.Class.forName(“包名.类名”) 这种事最常用的
3.实例对象.getClass()
总结: 任意一个类在同一个JVM内部,类对象是唯一的,此类对象会在第一次类加载时创建
1.3反射的应用场景
1.框架中对象的构建:
例如:mybatis中的resultType,resultMap,spring中的Bean
如下代码:之前学习过的分页查询 xml方式
mybatis中的resultType="cn.mry.mybatis.pojo.User"映射的结果封装到这个对象中,
利用反射方式获取User对象
${pageSize}同样也是利用反射原理获取类中的属性,底层是将参数封装了Map集合中
<select id="getUserList2" resultType="User">
select * from user
<where>
<if test="query != null and query !=''"> username like "%"#{query}"%"</if>
</where>
limlt ${(pageNum - 1) * pageSize},#{pageSize}
</select>
spring中的bean注解,spring管理的对象都叫做bean对象,并且这个对象的名字一般与方法名相同,也可以在注解中给对象起名字.可以直接从spring的容器Beans中获取
2.框架中的方法的调用:
例如:对象的set,get方法,spring MVC 控制层方法
Bean对象,也是根据反射创建对象的
@Bean//对象名默认为方法名
// @Bean("bCryptPasswordEncoder")bean对象名字为bCryptPasswordEncoder
public BCryptPasswordEncoder passwordEncoder (){
return new BCryptPasswordEncoder();
}
总之: 反射不能遇见未来,但是可以驾驭未来,通过反射可以更好构建一些编程框架,以实现通用变成,从而打到简单代码编写
2.反射起点创建对象
2.1验证:任意一个类在同一个JVM内部,类对象是唯一的,此类对象会在第一次类加载时创建
1.首先准备一个Configuration类.
2.以下为三种通过反射创建对象的方式:
3.用==判断对象是否为同一个对象
框架用的最多的就是这种方式
通过包路径+类名找到这个类,进而可以执行对象的创建,属性的赋值,方法的调用
Class<?> cls3 = Class.forName(“com.mry.reflect.Configuration”);
//首先准备一个类
class Configuration{
}
//测试
public class TestReflect01 {
public static void main(String[] args) throws Exception {
doClass01();
}
}
//1.反射起点
private static void doClass01() throws ClassNotFoundException{
//获取反射起点对象(字节码对象): 一个JVM最多有一份 类对象只有一份
//方式1
Configuration c1 = new Configuration();
Class<?> cls1 = c1.getClass();
//方式2
Class<?> cls2 = Configuration.class;
//方式3 框架中用的最多的就是这种方式
//框架用的最多的就是这种方式
//resultType = "com.mry.reflect.Configuration"
Class<?> cls3 = Class.forName("com.mry.reflect.Configuration");
System.out.println(cls1 == cls2);
System.out.println(cls2 == cls3);
//说明: 此对象在类加载时即可创建
}
2.2基于类对象创建无参实例对象
- 首先利用Class<?> cls3 = Class.forName(“com.mry.reflect.Configuration”);方式拿到这个对象,在通过这个对象构建类的实例对象
- 系统底层通过cls3.getDeclaredConstructor();方式获取无参构造函数,通过 con.newInstance();无参构造函数对象,构建类的无参实例对象
- 调用doInstance01(),就可以获取到实例对象了.但是这个前提是Configuration类中必须要有无参构造函数,如果没有无参构造函数的话会报错:com.mry.reflect.Configuration.()
class Configuration{
// private String path;
public Configuration(){ }
// public Configuration(String path){
this.path = path;
}
}
//测试
public class TestReflect01 {
public static void main(String[] args) throws Exception {
doInstance01();
}
}
//2.基于类对象创建实例 无参
private static void doInstance01() throws Exception{
//利用这种方式,构建类的实例对象
//通过反射构建对象时,需要类里有无参构造函数
//如果类中没有无参构造函数的话,会报错com.mry.reflect.Configuration.<init>()
Class<?> cls3 = Class.forName("com.mry.reflect.Configuration");
//获取无参构造方法的对象 系统底层通过这种方式获取,这是默认的无参构造函数
Constructor<?> con = cls3.getDeclaredConstructor();
//获取类中的私有方法 也就是暴力反射
con.setAccessible(true);
//构建类的实例对象 获取的对象都是Object类型的
Object obj = con.newInstance();
System.out.println(obj);
}
2.3基于类对象创建含参实例对象
- 利用cls3.getDeclaredConstructor(String.class)获取含参的构造函数
- 创建对象时,就需要传参数Object obj = con.newInstance(“classpath:cfg.xml”);
class Configuration{
private String path;
public Configuration(){ }
public Configuration(String path){
this.path = path;
}
}
//测试
public class TestReflect01 {
public static void main(String[] args) throws Exception {
doInstance02();
}
}
//3.基于类对象创建实例 含参
private static void doInstance02() throws Exception{
//利用这种方式,构建类的实例对象
Class<?> cls3 = Class.forName("com.mry.reflect.Configuration");
//获取含参构造方法的对象 系统底层通过这种方式获取对象
Constructor<?> con = cls3.getDeclaredConstructor(String.class);
//获取类中的私有方法 也就是暴力反射
con.setAccessible(true);
//构建类的实例对象 获取的对象都是Object类型的
Object obj = con.newInstance("classpath:cfg.xml");
System.out.println(obj);
}
2.4基于类对象创建实例 包含@Component注解时创建对象 调用无参/含参
如果创建对象时需要判断是否有注解修饰时,有注解修饰就创建对象,没有注解修饰就不创建这个对象
- 自定义注解Component,该注解可以描述类,并且在运行时有效
- 首先判断类是否被Component注解修饰boolean flag = cls3.isAnnotationPresent(Component.class);
- 假如需要把对象注入到容器中,可以给对象起个名字,也可以默认(默认名是类的首字母小写)
- 以config作为key,实例对象作为value,存储到Map集合中
利用con.newInstance(“classpath:cfg.xml”)获取实例对象也就是Map中的value,利用cls3.getAnnotation(Component.class)方式,拿到注解.利用comp.value()获取属性,也就是Map中的key - map.put(beanName, obj); beanName=config obj=实例对象
这就是模拟spring中获取对象的原理
@Retention(RetentionPolicy.RUNTIME)//运行时有效
@Target(ElementType.TYPE)//可以描述的类型 类中有效
@interface Component{
String value() default "";
}//注解组件
//有这个注解才允许构建对象
@Component("config")
class Configuration{
private String path;
public Configuration(){ }
public Configuration(String path){
this.path = path;
}
public void setPath(String path) {
this.path = path;
}
public String getPath() {
return path;
}
@Override
public String toString() {
return "Configuration{" +
"path='" + path + '\'' +
'}';
}
}
//测试
public class TestReflect01 {
public static void main(String[] args) throws Exception {
doInstance03();
}
}
//4.基于类对象创建实例 包含@Component注解时创建对象 调用无参/含参
private static void doInstance03() throws Exception{
//利用这种方式,构建类的实例对象
// @Component修饰这个类,spring扫描时会将带有注解的扫描进内存
Class<?> cls3 = Class.forName("com.mry.reflect.Configuration");
//判断是否有Component这个注解 方法同理
boolean flag = cls3.isAnnotationPresent(Component.class);
if (!flag)return;
//获取含参构造方法的对象 系统底层通过这种方式获取对象
Constructor<?> con = cls3.getDeclaredConstructor(String.class);
//获取类中的私有方法 也就是暴力反射
con.setAccessible(true);
// Value 构建类的实例对象 获取的对象都是Object类型的
Object obj = con.newInstance("classpath:cfg.xml");
//将对象以K,V方式存储到Spring容器中
Map<String,Object> map = new HashMap<>();
//拿到注解
Component comp = cls3.getAnnotation(Component.class);
//拿到注解后 通过注解.value()获取属性 作为key
String beanName = comp.value();
//模拟spring中的原理 将对象存到map容器中
map.put(beanName, obj);
//spring中就是以K-V方式存储对象
System.out.println(map);
}
2.5基于类对象创建实例 访问属性,给属性赋值
- 想要获取属性,首先还是要获取实例对象Class.forName(“com.mry.reflect.Configuration”);
- 可以利用cls3.getDeclaredField(“path”);获取属性
- 想为属性赋值,首先还是要先创建对象的con.newInstance();
- 获取到的属性中有set方法可以为属性赋值f1.set(obj, “classpath:cfg.xml”);
- 但是需要注意一点,属性是私有的,所以需要暴力反射,设置属性可以访问f1.setAccessible(true);
@Retention(RetentionPolicy.RUNTIME)//运行时有效
@Target(ElementType.TYPE)//可以描述的类型 类中有效
@interface Component{ //想要把对象存储到容器中,可以给对象起个名字,系统底层也可以是类名首字母小写
String value() default "";//定义
}//注解组件
//类对象
@Component("config")
class Configuration{
private String path;
public Configuration(){ }
public Configuration(String path){
this.path = path;
}
public void setPath(String path) {
this.path = path;
}
public String getPath() {
return path;
}
@Override
public String toString() {
return "Configuration{" +
"path='" + path + '\'' +
'}';
}
}
//测试
public class TestReflect01 {
public static void main(String[] args) throws Exception {
doField01();
}
}
public static void doField01() throws Exception{
//在框架中不知道写的是什么类,所以通过这种方式获取对象
Class<?> cls3 = Class.forName("com.mry.reflect.Configuration");
//获取对象中的属性
Field f1 = cls3.getDeclaredField("path");
//想为属性赋值一定是要有对象的情况下才能给对象的属性赋值
System.out.println(f1);
//获取无参构造方法的对象 系统底层通过这种方式获取对象
Constructor<?> con = cls3.getDeclaredConstructor();
//获取类中的私有方法 也就是暴力反射
con.setAccessible(true);
//构建类的实例对象 获取的对象都是Object类型的
Object obj = con.newInstance();
//获取类中的私有属性 暴力反射
f1.setAccessible(true);
//属性是私有的 要给目标对象赋值,不能直接赋值 需要加con.setAccessible(true)
// Class com.mry.reflect.TestReflect01 can not access a member of class com.mry.reflect.Configuration with modifiers "private"
f1.set(obj, "classpath:cfg.xml");
}
2.6基于类对象创建实例 调用类中某个对象的方法
- 对于框架来说,框架不知道属性具体的类型,所以不能直接通过obj直接调用,所以只能通过放射方式调用
- 与属性类似,利用反射cls3.getDeclaredMethod(“setPath”, String.class);调用set方法给属性赋值
- 调用到set方法后利用method.invoke(obj,“classpath:cfg.xml”);给属性赋值
@Retention(RetentionPolicy.RUNTIME)//运行时有效
@Target(ElementType.TYPE)//可以描述的类型 类中有效
@interface Component{ //想要把对象存储到容器中,可以给对象起个名字,系统底层也可以是类名首字母小写
String value() default "";//定义
}//注解组件
//类对象
@Component("config")
class Configuration{
private String path;
public Configuration(){ }
public Configuration(String path){
this.path = path;
}
public void setPath(String path) {
this.path = path;
}
public String getPath() {
return path;
}
@Override
public String toString() {
return "Configuration{" +
"path='" + path + '\'' +
'}';
}
}
//测试
public class TestReflect01 {
public static void main(String[] args) throws Exception {
doMethod01();
}
}
public static void doMethod01() throws Exception{
//在框架中不知道写的是什么类,所以通过这种方式获取对象
Class<?> cls3 = Class.forName("com.mry.reflect.Configuration");
//获取无参构造方法的对象 系统底层通过这种方式获取对象
Constructor<?> con = cls3.getDeclaredConstructor();
//获取类中的私有方法 也就是暴力反射
con.setAccessible(true);
//构建类的实例对象 获取的对象都是Object类型的
Object obj = con.newInstance();
//调用对象的方法
Method method = cls3.getDeclaredMethod("setPath", String.class);
//通过反射执行方法
method.invoke(obj,"classpath:cfg.xml");
System.out.println(obj);
Method method1 = cls3.getDeclaredMethod("getPath");
method1.invoke(obj);
method.invoke(obj);
System.out.println(obj);
}