• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

千里之行,始于足下

  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

Java反射和代理

反射

什么是反射

Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是JVM得到类对象之后,再通过类对象进行反编译,从而获取对象的各种信息。

获取类的3种方法

  • 1、Class.forName("全类名"):将字节吗文件加载进内存,返回Class对象,多用于配指文件,将类名定义在配置文件中,便于利用java的反射机制生成类对象,加载类。
//加载一个用户实体类UserBean
Class c1 = Class.forName("com.test.UserBean");
  • 2、类名.class:通过类名的属性class获取,多用于传递参数。
Class c2 = UserBean.class;
  • 3、对象.getClass():多用于对象获取字节码的方式。
UserBean user = new UserBean();
Class c3 = user.getClass();

有了类就可以通过反射的方式来创建对象、操作属性和操作方法

通过类创建对象

  • 1、使用newInstance()方法。
Object obj = stuClass.newInstance();
Student stu = (Student) obj;
  • 2、反射获取构造函数,使用构造函数的方式创建对象。
Constructor<?> constructor = stuClass.getConstructor(int.class, String.class);
Object obj2 = constructor.newInstance(12, "23");
Student stu2 = (Student) obj2;

通过类获取构造器

方法 用途
getConstructor(Class<?>... parameterTypes) 获取与参数类型匹配的public类型的构造方法
getConstructors() 获取所有的public类型的构造方法
getDeclaredConstructor(Class...<?> parameterTypes) 获取与参数类型匹配的所有构造方法
getDeclaredConstructors() 获取所有的构造方法

通过类操作属性

方法 用途
getField(String name) 获取某个public类型的属性
getFields() 获取所有的public类型的属性
getDeclaredField(String name) 获取某个属性对象
getDeclaredFields() 获取所有的属性对象
// 获取类
Class<Student> stuClass = (Class<Student>) Class.forName("data.Student");
// 通过类实例化对象,对象属性都是空的 
final Student student = stuClass.newInstance();
// 获取对象public修饰的属性
final Field score = stuClass.getField("score");
// 给属性赋值
score.set(student, 12);

// 获取对象private修饰的属性并赋值
final Field age = stuClass.getDeclaredField("age");
age.setAccessible(true);
age.set(student, 12);

通过类操作方法

方法 用途
getMethod(String name, Class...<?> parameterTypes) 获取与参数类型匹配的public类型的方法
getMethods() 获取所有的public类型的方法(包含继承的方法
getDeclaredMethod(String name, Class...<?> parameterTypes) 获取与参数类型匹配的所有方法
getDeclaredMethods() 获取所有的方法

获取到函数后,我们可以使用invoke(Object obj, Object... args)来调用对象的函数,并同时给其进行传参。使用getMethods()来调用公有方法:

Class<Student> stuClass = (Class<Student>) Class.forName("data.Student");
final Student student = stuClass.newInstance();
final Method study = stuClass.getMethod("study");
study.invoke(student);

反射的使用

反射最大的作用就在于我们可以不在编译时知道某个对象的类型,而在运行时得到。同时我们只需要得到我们想得到的类的名字即可(如果不在一个包,必须写完整的名字包括包名)。

/**
	成员变量:field对象(因为可能会有多个成员变量,一般用数组存储)
	构造方法:Constructor Constructor[]
	成员方法:Method Method[]
*/
public class Main {
	public static void main(String[] args) throws Exception{
		//返回A的构造方法
		Constructor c = A.class.getConstructor();
		//返回A类的所有为public 声明的构造方法
		Constructor[] cons = A.class.getConstructors();
		//返回A类所有的构造方法,包括private
		Constructor[] cons2 = A.class.getDeclaredConstructors();
		//返回A类的第一个public 方法
		Method m = A.class.getMethod("say");
		//执行
		m.invoke(A.class.newInstance(), null);
		//返回A类所有的public 方法
		Method[] ms = A.class.getMethods();
		//返回A类所有的方法,包括private
		Method[] allMs = A.class.getDeclaredMethods();
		//返回A类的public字段
		Field field = A.class.getField("i");
		System.out.println(field.get(A.class.newInstance()));
		//返回A类的static 字段
		System.out.println(field.get(null));
	}
}
 
class A{
	public int i = 1;
	public static int b = 2;
	public A(){
		System.out.println("无参构造");
	}
	private A(String s){
		System.out.println("有参构造"+s);
	}
	
	public void say(){
		System.out.println("say");
	}
}

代理

静态代理

静态代理需要先定义接口,被代理对象与代理对象一起实现相同的接口,代理类持有目标类的引用,然后通过调用相同的方法来调用目标对象的方法
举个栗子:

接口类 CarInterfaces.class,定义一组接口

interface CarInterfaces {
    void run();
}

目标类 Car.class

public class Car implements CarInterfaces {
    @Override
    public void run() {
        System.out.println("Car run()");
    }
}

代理类 CarSystemProxy.class

public class CarSystemProxy implements CarInterfaces {
    Car car;

    @Override
    public void run() {
        System.out.println("before car run");
        if (car == null) {
            car = new Car();
        }
        car.run();
        System.out.println("after car run");
    }
}

当目标类接口增多后,代理类要代理新增的接口,如果按照静态代理的模式,代理类中要同步新增接口,这时候就显得比较麻烦

动态代理

动态代理与静态代理的区别主要在:

  • 静态代理在编译时就已经实现
  • 动态代理是在运行时动态生成的,在运行时动态生成类字节码,并加载到JVM中

两种常见的动态代理方式:JDK原生动态代理和CGLIB动态代理

JDK动态代理

JDK动态代理:Java自动生成代理类class对象,保存在内存中,通过反射获取代理类的构造方法,进而创建代理类的实例对象

public class RenterInvocationHandler<T> implements InvocationHandler{
	//被代理类的对象
	private T target;
	
	public RenterInvocationHandler(T target){
		this.target = target;
	}

	/**
        * proxy:代表动态代理对象
        * method:代表正在执行的方法
        * args:代表调用目标方法时传入的实参
        */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		//代理过程中插入其他操作
		System.out.println("租客和中介交流");
		Object result = method.invoke(target, args);
		return result;
	}
}

例:
1、业务接口和实现类如下

public interface UserService {
    void login();
}
public class UserServiceImpl implements UserService {
    @Override
    public void login() {
        System.out.println("login success!");
    }
}

2、调用处理器如下

public class UserInvocationHandler implements InvocationHandler {
    // 目标对象
    private Object tarket;

    public UserInvocationHandler() {

    }
    public UserInvocationHandler(Object tarket) {
        this.tarket = tarket;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 调用目标对象的方法
        method.invoke(tarket, null);
        // 增强功能
        System.out.println("login time:" + new Date());
        return null;
    }
}

3、测试

public static void main(String[] args) {
      // 创建目标对象
      UserServiceImpl userService = new UserServiceImpl();
      // 创建调用处理器
      UserInvocationHandler handler = new UserInvocationHandler(userService);
      // 创建代理对象
      UserService proxy = (UserService) Proxy.newProxyInstance(UserServiceImpl.class.getClassLoader(),
              UserServiceImpl.class.getInterfaces(),
              handler);
      proxy.login();
  }

CGLIB动态代理

CGLIB动态代理:也叫子类代理,通过在内存中构建一个子类,在子类用方法拦截的方式拦截所有父类方法的调用,然后加入自己的操作。因为使用的是继承,不能代理final类

public class ProxyFactory<T> implements MethodInterceptor {

	private T target;

	public ProxyFactory(T target) {
		this.target = target;
	}

	// 创建代理对象
	public Object getProxyInstance() {
		// 1.cglib工具类
		Enhancer en = new Enhancer();
		// 2.设置父类
		en.setSuperclass(this.target.getClass());
		// 3.设置回调函数
		en.setCallback(this);
		return en.create();
	}

        //拦截方法
	@Override
	public Object intercept(Object obj, Method method, Object[] args,
			MethodProxy methodProxy) throws Throwable {
		System.out.println("开始事务...");

		// 执行目标对象的方法
		Object result = method.invoke(target, args);

		System.out.println("提交事务...");
		return result;
	}
}

posted on 2022-11-10 21:16  我隔壁是老王  阅读(180)  评论(0)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3