反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。
JDK的java.lang.reflect包提供了反射支持。
程序运行期间,Java会给每个对象都维护一个运行时类型标识。该对象保存着该类的信息。
三种获取Class类的方法:
java.lang.reflect包下有三个类Field、Method、Constructor分别描述了域、方法、构造器。可以获取类的所有信息。
Class类的getFields、getMethods、getConstructors方法会返回响应的的信息。
两个简单例子
下面实现了两个相同的操作,不同的是在反射中我们可以事先没有得到该类,通过反射的方法去new 实例,并调用方法。
代理类在程序运行时完成创建的代理方式被成为动态代理。
动态代理相对于静态代理来说,耦合性更低。像静态代理如果需要代理100个接口,就得写100个代理类,而动态代理只需要一个。
接口以及被代理对象
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代理了被代理类。
代理的目的是构造一个和被代理对象有相同行为的对象,所以不一定需要通过持有的方式来实现,也可以通过继承,并重写父类方法来实现。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(); }
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); } }
AOP(Aspect-OrientedProgramming,面向方面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。在OOP中允许定义从上到下的关系,但是对横向关系就无能为力了,AOP就提供了横向的处理能力。比如说日志,权限校验等功能会横切在代码中,这会导致代码不能复用。
spring 通过动态代理实现了AOP。