回调方法是指一个方法被传递到另一个方法中,在其完成之后被调用。在Java中,回调方法经常被用于GUI编程、事件处理、网络通信等领域。其特点如下:
1.回调方法是一种异步的方式,即在方法被调用的时候并不会立即执行,而是等待某个特定的条件满足后才会执行。
2.回调方法可以接收参数,并且可以返回一个值。
3.回调方法必须要实现一个指定的接口或抽象类,以保证其可以被调用。
4.回调方法可以被使用在多线程环境下,实现多线程之间的通信。
5.回调方法减少了代码的耦合性,使得代码更加灵活,易于维护和扩展。
6.回调方法可以在不同的类之间进行传递,实现跨类调用的功能。
7.回调方法可以实现事件处理等异步操作。
回调方法的使用可以分为以下几步:
1.定义接口或抽象类:定义一个接口或抽象类,并在其中声明需要回调的方法。
2.实现接口或抽象类:编写实现接口或抽象类的类,同时实现需要回调的方法。
3.传递回调对象:将实现了接口或抽象类的类的对象传递给需要使用回调的方法中。
4.调用回调方法:在需要时,调用回调方法。
具体使用步骤如下:
1.定于接口或抽象类
public interface Callback {
void callbackMethod(int result);
}
2.实现接口或抽象类
public class CallbackImpl implements Callback {
@Override
public void callbackMethod(int result) {
System.out.println("Result received from callback method:" + result);
}
}
3.传递回调对象
public class CallbackUser {
private Callback callback;
public void setCallback(Callback callback) {
this.callback = callback;
}
public void performOperation(int num1, int num2) {
int result = num1 + num2;
callback.callbackMethod(result);
}
}
4.调用回调方法
public class Main {
public static void main(String[] args) {
CallbackUser callbackUser = new CallbackUser();
Callback callback = new CallbackImpl();
callbackUser.setCallback(callback);
callbackUser.performOperation(10, 20);
}
}
这样,当performOperation
方法被调用时,会先对num1
和num2
进行运算,然后将结果传递给callbackMethod
方法。在CallbackImpl
类中,callbackMethod
方法接收到处理好的结果,并处理它。因此,可以使用回调方法来实现异步操作和事件处理等功能。
public class DataHandler {
public void getData(DataCallback<String> callback) {
new Thread(() -> {
try {
System.out.println("从远程数据库获取数据");
Thread.sleep(5000);
String a = "一大堆数据";
System.out.println("获取完了");
callback.ok(a);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}).start();
}
}
public interface DataCallback<T> {
void ok(T data);
}
public class Demo01 {
public static void main(String[] args) {
DataHandler handler = new DataHandler();
handler.getData(new DataCallback<String>() {
@Override
public void ok(String data) {
System.out.println("获取的data" + data);
}
});
System.out.println(111);
System.out.println(222);
System.out.println(333);
}
}
DataHandler
类定义了一个getData
方法,该方法中开启了一个新线程,模拟从远程数据库获取数据,并将获取到的数据传递给回调函数进行处理。注意,回调对象被作为参数传递到方法中。该回调对象需要实现DataCallback
接口。
DataCallback
接口定义了一个ok方法
,这个方法中用于处理从远程数据库获取的数据。可以通过实现DataCallback
接口中的ok方法
,传入DataHandler
类的getData方法
中,这样当数据获取完毕后,就会自动调用DataCallback
的ok方法
。
在Demo01类
中,创建DataHandler对象
,然后调用getData方法
,将DataCallback
的实现对象传递进去。在getData方法
中,新开启了一个线程获取数据,等待5秒后,打印获取完毕的提示,然后调用回调对象的ok方法
处理获取到的数据。
在Main方法
中,还有三个System.out.println
语句,可以看出,在回调方法被调用之前,main线程
不会等待,会先执行下一行语句。但是回调方法调用后,会重新回到主线程中继续执行。这也是回调方法的一个优点:可以异步地执行后续操作,而不会阻塞主线程。
动态代理是一种在运行时动态生成代理类的机制。代理类继承了被代理类的接口,并且拥有相同的方法名和参数。
在 Java 中,动态代理可以通过 java.lang.reflect.Proxy
类和 java.lang.reflect.InvocationHandler
接口来实现。具体流程如下:
创建一个实现 InvocationHandler 接口的类,该类中实现了 invoke 方法,该方法会在代理类的每个方法被调用时被执行。
使用 Proxy 类的 newProxyInstance()
方法创建一个代理类实例,该方法需要传入三个参数:类加载器、代理类要实现的接口以及代理类对应的 InvocationHandler 实例。
通过代理类实例调用目标方法时,代理类会将该方法的调用转发给 InvocationHandler 实例中的 invoke 方法,而 InvocationHandler 实例中的 invoke 方法则可以进行一些特定的操作,如添加日志、安全控制等。
在Java中,生成动态代理对象需要以下步骤:
创建一个实现InvocationHandler接口
的类,该类实现invoke方法
,代表代理对象方法被调用时需要执行的操作。
使用Proxy类
的newProxyInstance方法
创建代理实例,该方法需要传入以下三个参数:
类加载器(ClassLoader)
:用于加载代理类的类加载器。
代理类要实现的接口(Class[])
:代理类需要去实现的接口数组。
InvocationHandler
:代理类方法被调用的处理器。
InvocationHandler
实例中的invoke方法
,而InvocationHandler
中的invoke方法
,可以在方法执行前后,进行需要的操作处理。下面是一个简单的例子,演示如何生成动态代理对象:
public interface MyInterface {
public void sayHello();
}
public class MyInvocationHandler implements InvocationHandler {
private Object target; // 被代理的对象
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before sayHello..."); // 在方法执行前,添加处理逻辑
Object result = method.invoke(target, args); // 调用目标方法
System.out.println("after sayHello..."); // 在方法执行后,添加处理逻辑
return result;
}
}
public class Test {
public static void main(String[] args) {
MyInterface obj = new MyInterfaceImpl();
MyInvocationHandler handler = new MyInvocationHandler(obj);
MyInterface proxyObj = (MyInterface) Proxy.newProxyInstance(obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(), handler);
proxyObj.sayHello(); // 调用代理对象的方法
}
}
在以上示例代码中,我们定义了一个接口 MyInterface
,并且有一个实现该接口的实现类 MyInterfaceImpl
。然后我们创建了一个 MyInvocationHandler
类,该类实现 InvocationHandler
接口,重写 invoke 方法。在invoke方法中添加了方法执行前后的处理逻辑。最后通过调用 Proxy.newProxyInstance
方法生成了 proxyObj
代理对象,通过代理对象调用sayHello方法,就会执行invoke方法中的逻辑。
动态代理常用于 AOP 编程,下面通过一个简单的案例来讲解其应用。
假设我们有一个 Logger 类,用于在程序中输出日志信息。我们希望在每次调用某些方法时都自动打印出日志,这时候就可以使用动态代理来实现。
首先,我们需要创建一个 InvocationHandler 接口的实现类,用于处理代理类方法的调用。代码如下:
public class LoggerInvocationHandler implements InvocationHandler {
private Object target;
public LoggerInvocationHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Start " + method.getName() + " method: " + Arrays.toString(args));
Object result = method.invoke(target, args);
System.out.println("End " + method.getName() + " method. Result: " + result);
return result;
}
}
在此实现类中,我们定义了一个构造方法,用于传入被代理对象(即 Logger 类的实例)。在 invoke 方法中,我们先打印出方法名和参数,然后再通过反射调用被代理对象相应的方法。最后,我们再次打印出方法名和返回值,以表示方法调用结束。
接下来,我们需要创建代理类。这可以通过 Proxy 类的静态方法 newProxyInstance 来实现。代码如下:
// 创建 Logger 类的实例
Logger logger = new Logger();
// 创建 InvocationHandler 的实例
LoggerInvocationHandler handler = new LoggerInvocationHandler(logger);
// 创建动态代理对象
ILogger proxy = (ILogger) Proxy.newProxyInstance(
logger.getClass().getClassLoader(),
logger.getClass().getInterfaces(),
handler
);
在创建代理对象时,我们需要传入三个参数:类加载器、目标对象实现的接口、以及 InvocationHandler 的实例。注意,我们需要将代理对象强制转换为接口类型,以便后续操作。
最后,我们可以通过代理对象调用方法,同时在控制台中输出相应的信息。代码如下:
proxy.info("This is a test message");
proxy.warn("Warning: This is just a test message");
在执行上述代码时,我们可以看到控制台打印出相应的日志信息,证明动态代理已经生效。此时,我们就成功地通过动态代理实现了在指定方法前后自动打印日志的需求。