反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。
JDK的java.lang.reflect包提供了反射支持。
1.Class类
程序运行期间,Java会给每个对象都维护一个运行时类型标识。该对象保存着该类的信息。
三种获取Class类的方法:
- object.getClass()
- Class.forName(类的全路径名)
- Employee.class
2.反射
java.lang.reflect包下有三个类Field、Method、Constructor分别描述了域、方法、构造器。可以获取类的所有信息。
Class类的getFields、getMethods、getConstructors方法会返回响应的的信息。
两个简单例子
下面实现了两个相同的操作,不同的是在反射中我们可以事先没有得到该类,通过反射的方法去new 实例,并调用方法。
3.动态代理
代理类在程序运行时完成创建的代理方式被成为动态代理。
动态代理相对于静态代理来说,耦合性更低。像静态代理如果需要代理100个接口,就得写100个代理类,而动态代理只需要一个。
3.1 JDK动态代理
接口以及被代理对象
public interface House {
void sell();
}
public class Bieshu implements House{
@Override
public void sell() {
System.out.println("卖了大别墅~");
}
}
InvocationHandler
public class HouseHandler implements InvocationHandler {
private Object obj;
public HouseHandler(Object obj){
super();
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] objects) throws Throwable {
System.out.println("出售前~");
method.invoke(obj);
System.out.println("出售后~");
return null;
}
}
main方法
public static void main(String[] args) {
Bieshu bieshu = new Bieshu();
HouseHandler houseHandler = new HouseHandler(bieshu);
Class<?> bieshuClass = bieshu.getClass();
House proxyInstance = (House) Proxy.newProxyInstance(bieshuClass.getClassLoader(), bieshuClass.getInterfaces(), houseHandler);
proxyInstance.sell();
}
--------------------------------
出售前~
卖了大别墅~
出售后~
所以总的来说JDK代理就是两层代理,Proxy代理了InvocationHandler,而InvocationHandler代理了被代理类。
3.2 CGLib代理
代理的目的是构造一个和被代理对象有相同行为的对象,所以不一定需要通过持有的方式来实现,也可以通过继承,并重写父类方法来实现。CGLib就是如此。
被代理类
public class HelloService {
public HelloService() {
System.out.println("HelloService构造");
}
/**
* 该方法不能被子类覆盖,Cglib是无法代理final修饰的方法的
*/
final public String sayOthers(String name) {
System.out.println("HelloService:sayOthers>>" + name);
return null;
}
public void sayHello() {
System.out.println("HelloService:sayHello");
}
}
MethodInterceptor
public class CglibProxy implements MethodInterceptor {
@Override
public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
// 这里增强
System.out.println("收钱");
return arg3.invokeSuper(arg0, arg2);
}
}
main
public static void main(String[] args) {
// 通过CGLIB动态代理获取代理对象的过程
Enhancer enhancer = new Enhancer();
// 设置enhancer对象的父类
enhancer.setSuperclass(HelloService.class);
// 设置enhancer的回调对象
enhancer.setCallback(new MyMethodInterceptor());
// 创建代理对象
HelloService proxy= (HelloService)enhancer.create();
// 通过代理对象调用目标方法
proxy.sayHello();
}
3.3 两者的区别
- JDK动态代理是面向接口的
- CGLib动态代理是通过字节码底层继承来实现的。
- spring中当被代理类是实现类就会使用JDK代理,否则则用CGLib
- CGLib创建动态代理对象在运行中比JDK代理的快很多,所以使用单例时比较适合。
- CGLib创建动态代理对象在创建中比JDK代理的慢很多
- 目前随着JDK的版本提高,JDK动态代理的效率已经比CGLib高了。
public class ReflectTest {
public static class BasketBall{
private int price;
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
}
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// 1.正常调用
BasketBall basketBall = new BasketBall();
basketBall.setPrice(10);
// 2.反射调用
System.out.println(basketBall.getClass().getName());
String name = "reflect.ReflectTest$BasketBall";
Class classBasketBall = Class.forName(name);
Method method = classBasketBall.getMethod("setPrice", int.class);
Constructor constructor = classBasketBall.getConstructor();
Object instance = constructor.newInstance();
method.invoke(instance,10);
}
}
3.4 AOP
AOP(Aspect-OrientedProgramming,面向方面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。在OOP中允许定义从上到下的关系,但是对横向关系就无能为力了,AOP就提供了横向的处理能力。比如说日志,权限校验等功能会横切在代码中,这会导致代码不能复用。
spring 通过动态代理实现了AOP。