Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的法的功能称为java语言的反射机制。
反射机制:具有操作字节码文件的能力
- java.lang.Class
:保留一个类的全部信息 - java.util.reflect.Constructor:保存了一个类当中的构造方法的所有信息
- java.util.reflect.Method:保存了一个类当中的方法的所有信息
- java.util.reflect.Field:保存了一个类当中的成员属性的所有信息
动态代理
动态代理简单来说就是在程序执行过程中,创建代理对象,通过代理对象执行方法,给目标类的方法增加额外的功能,也叫做功能增强。
JDK动态代理的实现步骤
- 首先我们需要有一个目标类,在目标类的基础上通过动态代理实现功能增强
- 创建InvocationHandler接口的实现类,在这个类中实现invoke方法,在invoke方法中实现给目标类的方法增强功能
- 通过JDK中的Proxy创建代理,通过代理调用目标类中的方法,实现功能增强
JDK动态代理的代码实现
- 创建一个接口
public interface SomeService {
void doSome();
void doOther();
}
- 目标类实现这个接口:
public class SomeServiceImpl implements SomeService {
@Override
public void doSome() {
System.out.println("执行doSome");
}
@Override
public void doOther() {
System.out.println("执行doOther");
}
}
- 创建一个工具类,里面是增强的功能:
public class ServiceTools {
public static void doLog(){
System.out.println("方法的执行时间:" + new Date());
}
public static void doTrans(){
System.out.println("方法执行完毕,提交事务");
}
}
- 创建InvocationHandler接口的实现类
public class MyInvocationHandler implements InvocationHandler {
//目标对象
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
//通过代理对象执行方法时,会调用执行这个invoke()方法
/**
*
* @param proxy 代理类实例
* @param method 被代理的方法
* @param args 方法的参数数组
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("执行MyInvocationHandler中的invoke方法");
//打印被调用的目标类中的方法名
System.out.println(method.getName());
Object res = null;
//实现的增强的功能
ServiceTools.doLog();
//执行目标类方法,通过Method实现
res = method.invoke(target, args);
//增强的功能
ServiceTools.doTrans();
//返回目标方法的执行结果
return res;
}
}
- 通过Proxy创建代理
public static void main(String[] args) {
//创建目标对象
SomeService target = new SomeServiceImpl();
//创建InvocationHandler对象
InvocationHandler handler = new MyInvocationHandler(target);
//使用Proxy创建代理
SomeService proxy = (SomeService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
handler
);
//通过代理执行方法,会调用handle中的invoke()方法
proxy.doSome();
}
通用类:
public class JdkProxy implements InvocationHandler {
private Object target;
public JdkProxy(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long starTime = System.nanoTime();
Object result = method.invoke(this.target, args);
long endTime = System.nanoTime();
System.out.println(this.target.getClass() + ".m1()方法耗时(纳秒):" + (endTime - starTime));
return result;
}
/**
* 用来创建targetInterface接口的代理对象
*
* @param target 需要被代理的对象
* @param targetInterface 被代理的接口
* @param <T>
* @return
*/
public static <T> T createProxy(Object target, Class<T> targetInterface) {
if (!targetInterface.isInterface()) {
throw new IllegalStateException("targetInterface必须是接口类型!");
} else if (!targetInterface.isAssignableFrom(target.getClass())) {
throw new IllegalStateException("target必须是targetInterface接口的实现类!");
}
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new JdkProxy(target));
}
public static <T> T createProxy(Object target) {
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new JdkProxy(target));
}
}
使用:
IService proxy = JdkProxy.createProxy(new SomeServiceImpl(), SomeService.class);
IService proxy1 = JdkProxy.createProxy(new SomeServiceImpl());
总结一下动态代理实现过程:
- 通过getProxyClass0()生成代理类。
- 通过Proxy.newProxyInstance()生成代理类的实例对象,创建对象时传入InvocationHandler类型的实例。
- 调用新实例的方法,即此例中的m1(),m2(),即原InvocationHandler类中的invoke()方法。
动态代理的作用:
- 在目标类源代码不改变的情况下,增加目标类的功能
- 当涉及到多个方法都需要增加某些同样的功能时,使用动态代理可以减少代码的重复
- 可以使目标类专注于业务逻辑代码
- 实现解耦合,让业务功能和日志、事务和非事务功能分离
为什么jdk动态代理必须实现接口:
//反编译出来看,继承了Proxy类,所以没法再继承了,java是单继承
String path = "logs/$Proxy0.class";
byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0",
IServiceImpl.class.getInterfaces());
FileOutputStream out = new FileOutputStream(path);
out.write(classFile);
out.flush();
out.close();
cglib
public static void main(String[] args) {
//使用Enhancer来给某个类创建代理类,步骤
//1.创建Enhancer对象
Enhancer enhancer = new Enhancer();
//2.通过setSuperclass来设置父类型,即需要给哪个类创建代理类
enhancer.setSuperclass(Service1.class);
/*
3.设置回调,需实现org.springframework.cglib.proxy.Callback接口,
此处我们使用的是org.springframework.cglib.proxy.MethodInterceptor,
也是一个接口,实现了Callback接口,
当调用代理对象的任何方法的时候,都会被MethodInterceptor接口的invoke方法处理
*/
/**
* 此处
* 1. MethodInterceptor 拦截所有方法
* 2. 拦截所有方法并返回自定义的值(FixedValue)
* 3. 直接放行,不做任何操作(NoOp.INSTANCE)
* 4. 不同的方法使用不同的拦截器(CallbackFilter)
* */
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
/**
* 代理对象方法拦截器
* @param o 代理对象
* @param method 被代理的类的方法,即Service1中的方法
* @param objects 调用方法传递的参数
* @param methodProxy 方法代理对象
* @return
* @throws Throwable
*/
System.out.println("调用方法:" + method);
//可以调用MethodProxy的invokeSuper调用被代理类的方法
Object result = methodProxy.invokeSuper(o, objects);
return result;
}
});
//4.获取代理对象,调用enhancer.create方法获取代理对象,这个方法返回的是Object类型的,所以需要强转一下
Service1 proxy = (Service1) enhancer.create();
//5.调用代理对象的方法
proxy.m1();
proxy.m2();
Service1 proxy1 = CglibProxy.createProxy(new Service1());
proxy1.m2();
proxy1.m1();
}
通用方法
public class CglibProxy implements MethodInterceptor {
//目标对象
private Object target;
public CglibProxy(Object target) {
this.target = target;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
long starTime = System.nanoTime();
//调用被代理对象(即target)的方法,获取结果
Object result = method.invoke(target, objects); //@1
long endTime = System.nanoTime();
System.out.println(method + ",耗时(纳秒):" + (endTime - starTime));
return result;
}
/**
* 创建任意类的代理对象
*
* @param target
* @param <T>
* @return
*/
public static <T> T createProxy(T target) {
CglibProxy cglibProxy = new CglibProxy(target);
Enhancer enhancer = new Enhancer();
enhancer.setCallback(cglibProxy);
enhancer.setSuperclass(target.getClass());
return (T) enhancer.create();
}
}
Service1
public class Service1 {
public void m1() {
System.out.println("m1");
}
public void m2() {
System.out.println("m2");
}
}