
1、动态代理的几种方式
Java主要有两种代理,JDK和Cglib动态代理。先看JDK代理实例如下:
JDK创建代理有一个限制,即它只能为接口创建代理实例。举个例子如下:
public interface Advice { void beforeMethod(); void afterMethod(); } public class TimeAdvice implements Advice { long startTime; long endTime; public void beforeMethod() { startTime = System.nanoTime(); // 获取开始时间 System.out.println("开始计算程序运行时间" + startTime); } public void afterMethod() { endTime = System.nanoTime(); // 获取结束时间 System.out.println("计算程序运行时间: " + (endTime - startTime) + "ns"); } }
public interface SalaryInterface { public void doSalary(); } public class Salary implements SalaryInterface{ public void doSalary() { System.out.println("进行薪资计算的逻辑处理"); } }
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /* * 每一个代理实例都必须指定一个调用处理器,代理对象调用方法时, * 该方法会指派到调用处理器的invoke()中去。代理的方法封装成 * invoke中的method对象,其中的参数封装成Object[]. */ public class MyProxy implements InvocationHandler{ private Object obj; // 希望被代理的对象 private Advice advice; // 绑定代理对象 public Object bind(Object obj, Advice advice) { this.obj = obj; this.advice = advice; return Proxy.newProxyInstance( obj.getClass().getClassLoader(), // 类加载器 obj.getClass().getInterfaces(), // 创建目标类所需要使用的一组接口 this // 一个实现InvocationHandler的实例,用来整合横切与业务逻辑 ); } /* * 实现代理 * method为方法名,args为代理实例某一方法的入参数组,而obj为所属的实例对象 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; try { advice.beforeMethod(); result = method.invoke(obj, args); advice.afterMethod(); } catch (Exception e){ e.printStackTrace(); } return result; } }
public class Bootstrap { public static void main(String[] args) { Advice advice = new TimeAdvice(); SalaryInterface p = new Salary(); // Proxy利用InvocationHandler动态创建一个符合某一接口的实例,生成目标类的代理对象 MyProxy proxy = new MyProxy(); SalaryInterface y = (SalaryInterface)proxy.bind(p, advice); y.doSalary(); // 相当于调用proxy.invoke(proxy, "doSalary, null); } }
开始计算程序运行时间43041762316001 进行薪资计算的逻辑处理 计算程序运行时间: 882610ns
CGLib采用非常底层的字节码技术,可以为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,并顺势织入横切逻辑。
CGGlib创建的代理对象要比JDK的性能高很多,但是创建时所花费的时间却比JDK动态代理要多。所以对于singleton的代理对象或者具有实例池的代码,由于无须频繁创建代码对象,用CGLib比较合适。也就是生命周期长的实例用CGLib比较合适。
/** * 使用cglib动态代理 */ public class Cglib implements MethodInterceptor { private Object target; long startTime; long endTime; /** * 创建代理对象 */ public Object getInstance(Object target) { this.target = target; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(this.target.getClass()); // 设置需要创建子类的类 enhancer.setCallback(this); // 回调方法 return enhancer.create(); // 通过字节码技术动态创建子类实例 } @Override // 回调方法 ,拦截所有的父类方法调用 public Object intercept(Object obj, Method method, Object[] args,MethodProxy proxy) throws Throwable { startTime = System.nanoTime(); // 获取开始时间 System.out.println("开始计算程序运行时间" + startTime); Object result = proxy.invokeSuper(obj, args); // 通过代码类调用父类中的方法 endTime = System.nanoTime(); // 获取结束时间 System.out.println("计算程序运行时间: " + (endTime - startTime) + "ns"); return result; } }
/* * 采用非常底层的字节码技术,可以为一个类创建子类,并在子类中采用方法拦截的技术拦截 * 所有父类方法的调用,并顺势织入横切逻辑 * * singletom的代理对象或者具有实例池的代理,因为无须频繁创建代理对象,比较适合用CGLib动态代理技术 * * 由于CGLib采用动态创建子类的方式生成代理对象,所以不能对目标类中的final,private等方法进行代理 */ public class TestCglib { public static void main(String[] args) { Cglib cglib=new Cglib(); Salary salary=(Salary)cglib.getInstance(new Salary()); salary.doSalary(); } }
2、Spring AOP(Aspect Oriented Programming面向切面编程)与IoC(Inverse of Control控制反转)的实现
Spring AOP主要就是通过动态代理来实现,而Ioc是通过反射来实现,将创建对象和对象之间的依赖管理交给IoC容器来做,完成对象之间的解耦。
举个反射的例子,如下:
public class Car { private String brand; private String color; private int maxSpeed; public Car() { System.out.println("init car!!"); } public Car(String brand, String color, int maxSpeed) { this.brand = brand; this.color = color; this.maxSpeed = maxSpeed; } public void introduce() { System.out.println("brand:" + brand + ";color:" + color + ";maxSpeed:" + maxSpeed); } public String getBrand() { return brand; } public void setBrand(String brand) { this.brand = brand; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } public int getMaxSpeed() { return maxSpeed; } public void setMaxSpeed(int maxSpeed) { this.maxSpeed = maxSpeed; } }
public class ReflectTest { public static Car initByDefaultConst() throws Throwable { // loadClass()方法必须使用全额限定名 ClassLoader loader = Thread.currentThread().getContextClassLoader(); // 每一个类在JVM中都拥有一个对应的java.lang.Class对象,用来描述类结构信息 // Class clazz = loader.loadClass("reflect.Car"); Class clazz = Class.forName("reflect.Car"); Constructor cons = clazz.getDeclaredConstructor((Class[]) null); Car car = (Car) cons.newInstance(); Method setBrand = clazz.getMethod("setBrand", String.class); setBrand.invoke(car, "红旗CA72"); Method setColor = clazz.getMethod("setColor", String.class); setColor.invoke(car, "黑色"); Method setMaxSpeed = clazz.getMethod("setMaxSpeed", int.class); setMaxSpeed.invoke(car, 200); return car; } public static Car initByParamConst() throws Throwable { ClassLoader loader = Thread.currentThread().getContextClassLoader(); Class clazz = loader.loadClass("reflect.Car"); Constructor cons = clazz.getDeclaredConstructor(new Class[] { String.class, String.class, int.class }); Car car = (Car) cons.newInstance(new Object[] { "吉利TOPMIX", "绿色", 120 }); return car; } public static void main(String[] args) throws Throwable { Car car1 = initByDefaultConst(); Car car2 = initByParamConst(); car1.introduce(); car2.introduce(); } }
init car!! brand:红旗CA72;color:黑色;maxSpeed:200 brand:吉利TOPMIX;color:绿色;maxSpeed:120
每一个类在JVM中都拥有一个对应的java.lang.Class对象,它提供了类结构信息的描述。数组、枚举、注解甚至void都有对应的Class对象。可以从Class对象中获取构造函数、成员变量、方法等类元素的反射对象,并以编程的方式通过这些反射对目标类对象进行操作。
3、Spring的事务实现原理