到处都是用的动态代理,这次是认真的
静态代理:事先已经知道了要代理什么,而且编译时代理类就已经生成
动态代理:事先不知道要代理什么,运行的时候才能确定,实现InvocationHandler接口,调用Proxy的newProxyInstance()方法通过反射动态地创建代理类
下图就是创建代理类的流程
先还是一段简单的InvocationHandler的动态代理例子,然后再根据执行流程来分析
定义一个interface,也就是真实被代理的类实现的接口
package com.lihuia.invoke.service;
/**
* Copyright (C), 2018-2019
* FileName: HelloService
* Author: lihui
* Date: 2019/11/20
*/
public interface HelloService {
void sayHello();
}
实现类,被代理的真实对象就这里来的
package com.lihuia.invoke.impl;
import com.lihuia.invoke.service.HelloService;
import lombok.extern.slf4j.Slf4j;
/**
* Copyright (C), 2018-2019
* FileName: HelloImpl
* Author: lihui
* Date: 2019/11/20
*/
@Slf4j
public class HelloImpl implements HelloService {
@Override
public void sayHello() {
log.info("Hello World!");
}
}
注入一堆Bean
package com.lihuia.invoke.config;
import com.lihuia.invoke.handler.HelloHandler;
import com.lihuia.invoke.impl.HelloImpl;
import com.lihuia.invoke.service.HelloService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Copyright (C), 2018-2019
* FileName: HelloConfig
* Author: lihui
* Date: 2019/11/20
*/
@Configuration
public class HelloConfig {
@Bean
public HelloService helloService() {
return new HelloImpl();
}
@Bean
public HelloHandler helloHandler() {
return new HelloHandler(helloService());
}
}
测试类,通过代理类调用真实类的sayHello()方法
package com.lihuia.invoke;
import java.lang.reflect.Proxy;
import javax.annotation.Resource;
import com.lihuia.invoke.config.HelloConfig;
import com.lihuia.invoke.handler.HelloHandler;
import com.lihuia.invoke.service.HelloService;
import lombok.extern.slf4j.Slf4j;
import org.testng.annotations.Test;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
/**
* Copyright (C), 2018-2019
* FileName: HelloTest
* Author: lihui
* Date: 2019/11/20
*/
@ContextConfiguration(classes = {HelloConfig.class})
@Slf4j
public class HelloTest extends AbstractTestNGSpringContextTests {
@Resource
private HelloService helloService;
@Resource
private HelloHandler helloHandler;
@Test
public void helloProxyTest() {
HelloService helloProxy = (HelloService) Proxy.newProxyInstance(
helloHandler.getClass().getClassLoader(),
helloService.getClass().getInterfaces(),
helloHandler
);
helloProxy.sayHello();
}
}
Proxy.newProxyInstance(),这个静态方法就是通过反射生成一个代理对象,关键是三个参数
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
源码里的注释如下:
* @param loader the class loader to define the proxy class
* @param interfaces the list of interfaces for the proxy class
* to implement
* @param h the invocation handler to dispatch method invocations to
* @return a proxy instance with the specified invocation handler of a
* proxy class that is defined by the specified class loader
* and that implements the specified interfaces
详细说明如下:
ClassLoader loader:代理类的加载器,也就是HelloHandler的类加载器
Class<?>[] interfaces:代理类实现的接口,可以有多个,也就是上面的HelloService
InvocationHandler h:当动态代理对象调用了方法的时候,就会关联上一个InvocationHandler对象,并最终又它来调用
这里可以打个断点调试一下,的确是在代理对象调用方法的时候,会调用对应的Handler的invoke方法
Handler类如下
package com.lihuia.invoke.handler;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import lombok.extern.slf4j.Slf4j;
/**
* Copyright (C), 2018-2019
* FileName: HelloHandler
* Author: lihui
* Date: 2019/11/20
*/
@Slf4j
public class HelloHandler implements InvocationHandler {
private Object target;
public HelloHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
method.invoke(target, args);
return null;
}
}
通过构造方法传入一个真实的被代理对象,然后通过反射来调用方法,invoke方法的三个参数
Object proxy:代理对象
Method method:代理对象调用的方法
Object[] args:方法的参数
这里可以修改一下调用方法,加一个参数,可以得到参数的传递如下
invoke()方法return的就是调用方法的返回值
但是可以发现一点,invoke的第一个参数proxy没用到,而且它的类型很奇怪com.sun.proxy.$Proxy0,那是因为在创建代理对象的时候是在运行的时候动态生成的对象,至于说proxy会不会用到,Stack Overflow上有一个例子可以参考下:
所以JDK动态代理的流程就是调用Proxy类的newProxyInstance方法通过反射创建一个代理类对象,调用代理类目标接口方法时,会自动转发到对应的Handler处理器里的invoke()方法,来完成method的执行
最后还是想再写下Java的反射,因为Java本身是一个编译性语言,而反射这种动态运行过程中做一些动作号称逼格很高,但是理解起来并不很GET得到,下面通过一个小例子来看看反射的优势
接口
public interface UserService {
void Working();
}
实现类Teacher
public class Teacher implements UserService {
@Override
public void Working() {
System.out.println("Teacher Working");
}
}
实现类Doctor
public class Doctor implements UserService {
@Override
public void Working() {
System.out.println("Doctor Working");
}
}
工厂类
public class UserFactory {
public static UserService getInstance(String userType) {
UserService userService = null;
if ("Teacher".equals(userType)) {
userService = new Teacher();
} else if ("Doctor".equals(userType)) {
userService = new Doctor();
} else {
System.out.println(userType);
}
return userService;
}
}
Main方法
public class UserTest {
public static void main(String[] args) {
UserService userService = UserFactory.getInstance("Teacher");
userService.Working();
}
}
假如我们要持续新增各种职业的人,因为各行各业的人都要辛苦Working来赚钱,那么正常做法就要在UserFactory里getInstance方法新增新的职业分支,就会越来越难维护,代码还很冗长
下面来看看反射的做法
工厂类
public class UserFactory {
public static UserService getInstance(String userType) {
UserService userService = null;
try {
userService = (UserService)Class.forName(userType).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return userService;
}
}
Main方法
public class UserTest {
public static void main(String[] args) {
UserService userService = UserFactory.getInstance("com.lihuia.reflect.Teacher");
userService.Working();
}
}
这样,你如果要新增一个职业类,那么只要getInstance方法里传入该类的路径,都可以通过反射在运行中动态地创建对象,不需要修改代码
OVER