JDK动态代理
public class Order { private Object orderInfo; //订单创建时间进行按年分库 private Long createTime; private String id; public Object getOrderInfo() { return orderInfo; } public void setOrderInfo(Object orderInfo) { this.orderInfo = orderInfo; } public Long getCreateTime() { return createTime; } public void setCreateTime(Long createTime) { this.createTime = createTime; } public String getId() { return id; } public void setId(String id) { this.id = id; } }
public class OrderDao { public int insert(Order order){ System.out.println("OrderDao创建Order成功!"); return 1; } }
public interface IOrderService { int createOrder(Order order); }
public class OrderService implements IOrderService { private OrderDao orderDao; public OrderService(){ //如果使用Spring应该是自动注入的 //我们为了使用方便,在构造方法中将orderDao直接初始化了 orderDao = new OrderDao(); } public int createOrder(Order order) { System.out.println("OrderService调用orderDao创建订单"); return orderDao.insert(order); } }
public class OrderServiceDynamicProxy implements InvocationHandler { private SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy"); Object proxyObj; public Object getInstance(Object proxyObj) { this.proxyObj = proxyObj; Class<?> clazz = proxyObj.getClass(); return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { before(args[0]); Object object = method.invoke(proxyObj,args); after(); return object; } private void after() { System.out.println("Proxy after method"); //还原成默认的数据源 DynamicDataSourceEntity.restore(); } //target 应该是订单对象Order private void before(Object target) { try { //进行数据源的切换 System.out.println("Proxy before method"); //约定优于配置 Long time = (Long) target.getClass().getMethod("getCreateTime").invoke(target); Integer dbRouter = Integer.valueOf(yearFormat.format(new Date(time))); System.out.println("静态代理类自动分配到【DB_" + dbRouter + "】数据源处理数据"); DynamicDataSourceEntity.set(dbRouter); }catch (Exception e){ e.printStackTrace(); } } }
public static void main(String[] args) { try { Order order = new Order(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd"); Date date = sdf.parse("2019/04/05"); order.setCreateTime(date.getTime()); IOrderService orderService = (IOrderService)new OrderServiceDynamicProxy().getInstance(new OrderService()); orderService.createOrder(order); }catch (Exception e){ e.printStackTrace(); } }
CGLib动态代理容易踩的坑:
1 无法代理final修饰的方法
CGLib和JDK动态代理对比:
1 JDK动态代理是实现了被代理对象的接口,CGLib是继承了被代理对象
2 JDK和CGLib都是在运行期生成字节码,JDK是直接写Class字节码,CGLib使用ASM框架写Class字节码,Cglib代理实现更复杂,生成代理类比JDK效率低。
3 JDK调用代理方法,是通过反射机制调用,CGLib是通过FastClass机制直接调用方法,CGLib执行效率更高
优点:
1 代理模式能将代理对象与真实被调用的目标对象分离
2 一定程度上降低了系统的耦合程度,易于扩展
3 代理可以起到保护目标对象的作用
4 增强目标对象的职责
缺点:
1 代理模式会造成系统设计中类的数目增加
2 在客户端和目标对象之间增加了一个代理对象,会造成请求处理速度变慢
3 增加了系统的复杂度
Spring中的代理选择原则:
1 当Bean有实现接口时,Spring就会用JDK的动态代理
2 当Bean没有实现接口时,Spring选择CGLib
3 Spring可以通过配置强制使用CGLib,只需在Spring的配置文件中加入如下代码: <aop:aspectj-autoproxy proxy-target-class="true"/>
public class Customer { public void findLove(){ System.out.println("儿子要求:肤白貌美大长腿"); } }
public class CGlibMeipo implements MethodInterceptor { public Object getInstance(Class<?> clazz) throws Exception{ //相当于Proxy,代理的工具类 Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(clazz); enhancer.setCallback(this); return enhancer.create(); } public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { before(); Object obj = methodProxy.invokeSuper(o,objects); after(); return obj; } private void before(){ System.out.println("我是媒婆,我要给你找对象,现在已经确认你的需求"); System.out.println("开始物色"); } private void after(){ System.out.println("OK的话,准备办事"); } }
public static void main(String[] args) { try { //JDK是采用读取接口的信息 //CGLib覆盖父类方法 //目的:都是生成一个新的类,去实现增强代码逻辑的功能 //JDK Proxy 对于用户而言,必须要有一个接口实现,目标类相对来说复杂 //CGLib 可以代理任意一个普通的类,没有任何要求 //CGLib 生成代理逻辑更复杂,效率,调用效率更高,生成一个包含了所有的逻辑的FastClass,不再需要反射调用 //JDK Proxy生成代理的逻辑简单,执行效率相对要低,每次都要反射动态调用 //CGLib 有个坑,CGLib不能代理final的方法 System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E://cp_classes"); Customer obj = (Customer) new CGlibMeipo().getInstance(Customer.class); System.out.println(obj); obj.findLove(); } catch (Exception e) { e.printStackTrace(); } }