0 当动态代理遇到ioc (二)cglib 中的代理仍然不能切子函数,所以有本篇
1 此前已经解决cglib与asm冲突:当动态代理遇到ioc (三)cglib与asm jar包冲突
2 事务的try catch模型由exception扩叨叨throwable
3 抽象层
由于我们反射调用方法时,不再是method.invoke(origin object, args[]),所以抽象层要修改一下
protected Object invokeHasTransactional(SCEF_DB_TRANSACTIONAL scef_db_transactional, Method method, Object[] args, MethodProxy methodProxy, Object oCglib) throws Throwable { protected Object invokeNoTransactional(Method method, Object[] args, MethodProxy methodProxy, Object oCglib) throws Throwable {
Object returnValue = aopInvoke(method, args, methodProxy, oCglib);
jdk
protected Object aopInvoke(Method method, Object [] args, MethodProxy methodProxy, Object oCglib) throws Throwable { Object returnValue = method.invoke(target, args); return returnValue; }
if(scef_db_transactional != null) { res = invokeHasTransactional(scef_db_transactional, method, args, null, null); } else { res = invokeNoTransactional(method, args, null, null); }
cglib
@Override protected Object aopInvoke(Method method, Object[] args, MethodProxy methodProxy, Object oCglib) throws Throwable {
////////return method.invoke(target, args); return methodProxy.invokeSuper(oCglib, args); }
if (scef_db_transactional != null) { res = invokeHasTransactional(scef_db_transactional, null, objects, methodProxy, o); } else { res = invokeNoTransactional(null, objects, methodProxy, o); }
4 ioc反哺
解决了3形成了子函数切面后,报错:
首次处理 class sun.myproxybean.MyServiceImpl的ioc 处理 class sun.myproxybean.MyServiceImpl的myMapper1 处理 class sun.myproxybean.MyServiceImpl的myMapper2 处理 class sun.myproxybean.MyServiceImpl的myDao public void sun.myproxybean.MyServiceImpl.multi() @sun.myproxy.MY_TRANSACTIONAL() public final void sun.myproxybean.MyServiceImpl$$EnhancerByCGLIB$$5b3ba48d.multi() null public abstract void sun.myproxybean.MyService.multi() null Exception in thread "main" java.lang.RuntimeException: java.lang.NullPointerException at sun.myproxy.OdsTransactionProxyFactory.invokeHasTransactional(OdsTransactionProxyFactory.java:112) at sun.myproxy.OdsTransactionCglibFactory.intercept(OdsTransactionCglibFactory.java:65) at sun.myproxybean.MyServiceImpl$$EnhancerByCGLIB$$5b3ba48d.multi(<generated>) at sun.mybatis.Main.main(Main.java:58) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147) Caused by: java.lang.NullPointerException at sun.myproxybean.MyServiceImpl.multi(MyServiceImpl.java:29) at sun.myproxybean.MyServiceImpl$$EnhancerByCGLIB$$5b3ba48d.CGLIB$multi$0(<generated>) at sun.myproxybean.MyServiceImpl$$EnhancerByCGLIB$$5b3ba48d$$FastClassByCGLIB$$8fca1f76.invoke(<generated>) at net.sf.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228) at sun.myproxy.OdsTransactionCglibFactory.aopInvoke(OdsTransactionCglibFactory.java:73) at sun.myproxy.OdsTransactionProxyFactory.invokeHasTransactional(OdsTransactionProxyFactory.java:100) ... 8 more
显示ioc null了,故我们回顾一下IOC()中,反哺是反到原始对象
为了切子函数,不再使用method.invoke(origin object, args[])原对象,而是使用代理对象(子类对象)methodProxy.invokeSuper(oCglib, args);,因此反哺应在子类对象上进行
protected Object getTarget() {
return this.target;
}
@SuppressWarnings("squid:S3011")
protected void IOC() throws IllegalAccessException {
if (!alreadyIOC) {
Class clProxy = target.getClass();
Field[] fields = clProxy.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
if (!field.isAnnotationPresent(getBeanInjectAnnotationGuice()))
continue;
String name = field.getName();
Class fieldType = field.getType();
Object obj = getBeanFromFactoryGuice(fieldType);
if (obj != null)
field.set(getTarget(), obj);
}
alreadyIOC = true;
}
}
public class OdsTransactionCglibFactory extends OdsTransactionProxyFactory implements MethodInterceptor {
public OdsTransactionCglibFactory(Object target) {
super(target);
}
private Object targetCglib;
@Override
protected Object getTarget() {
return targetCglib;
}
@Override
public Object getProxyInstance() {
Enhancer en = new Enhancer();
en.setSuperclass(target.getClass());
en.setCallback(this);
Object oCglib = en.create();
this.targetCglib = oCglib;
return oCglib;
}
5 发现一个bug
@Inject
private MyDao myDao; // https://www.cnblogs.com/silyvin/p/14237270.html 第5点
// private MyDaoImpl myDao; // 导致myDao不是注入代理类对象,而是new MyDaoImpl,guice会自己newInstance一个
6 对于接口MyService中没有,仅在MyServiceImpl中有的子函数,处理“迎合某些人习惯将注解加到接口上”的时候去取接口的getMethod时报错
首次处理 class sun.myproxybean.MyServiceImpl的ioc 处理 class sun.myproxybean.MyServiceImpl$$EnhancerByCGLIB$$5b3ba48d的myMapper1 处理 class sun.myproxybean.MyServiceImpl$$EnhancerByCGLIB$$5b3ba48d的myMapper2 处理 class sun.myproxybean.MyServiceImpl$$EnhancerByCGLIB$$5b3ba48d的myDao public void sun.myproxybean.MyServiceImpl.multi() @sun.myproxy.MY_TRANSACTIONAL() public final void sun.myproxybean.MyServiceImpl$$EnhancerByCGLIB$$5b3ba48d.multi() null public abstract void sun.myproxybean.MyService.multi() null Exception in thread "main" java.lang.RuntimeException: java.lang.NoSuchMethodException: sun.myproxybean.MyService.subFunction() at sun.myproxy.OdsTransactionProxyFactory.invokeHasTransactional(OdsTransactionProxyFactory.java:117) at sun.myproxy.OdsTransactionCglibFactory.intercept(OdsTransactionCglibFactory.java:74) at sun.myproxybean.MyServiceImpl$$EnhancerByCGLIB$$5b3ba48d.multi(<generated>) at sun.mybatis.Main.main(Main.java:58) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147) Caused by: java.lang.NoSuchMethodException: sun.myproxybean.MyService.subFunction() at java.lang.Class.getMethod(Class.java:1786) at sun.myproxy.OdsTransactionCglibFactory.intercept(OdsTransactionCglibFactory.java:42) at sun.myproxybean.MyServiceImpl$$EnhancerByCGLIB$$5b3ba48d.subFunction(<generated>) at sun.myproxybean.MyServiceImpl.multi(MyServiceImpl.java:30) at sun.myproxybean.MyServiceImpl$$EnhancerByCGLIB$$5b3ba48d.CGLIB$multi$0(<generated>) at sun.myproxybean.MyServiceImpl$$EnhancerByCGLIB$$5b3ba48d$$FastClassByCGLIB$$8fca1f76.invoke(<generated>) at net.sf.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228) at sun.myproxy.OdsTransactionCglibFactory.aopInvoke(OdsTransactionCglibFactory.java:83) at sun.myproxy.OdsTransactionProxyFactory.invokeHasTransactional(OdsTransactionProxyFactory.java:105) ... 8 more
jdk动态代理的子函数没有办法切,被切到的一定是接口中有的方法,所以天然无此问题;而cglib会切到大概率没有事务注解的子函数,所以就暴露了这个问题
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
IOC();
// ewirweod
MY_TRANSACTIONAL scef_db_transactional = method.getAnnotation(MY_TRANSACTIONAL.class);
// 迎合有些人习惯将注解加到接口上
Class[] interfaces = target.getClass().getInterfaces();
if(scef_db_transactional == null) {
for(Class inter : interfaces) {
try {
Method classMethod = inter.getMethod(method.getName(), method.getParameterTypes());
scef_db_transactional = classMethod.getAnnotation(MY_TRANSACTIONAL.class);
} catch (NoSuchMethodException e1) {
;
} catch (Throwable e) {
e.printStackTrace();
}
break;
}
}
6.5 又引出了一个知识:
// MyServiceImpl&&CGLIB.method uewoqruwiiii { Method classMethod = o.getClass().getMethod(method.getName(), method.getParameterTypes()); System.out.println(classMethod); MY_TRANSACTIONAL my_transactional = classMethod.getAnnotation(MY_TRANSACTIONAL.class); System.out.println(my_transactional); }
这个地方居然也报错了
public class MyServiceImpl implements MyService { 。。。。。 /** * public o.getClass().getMethod 可以找到 * protected 不行 */ public void subFunction() { System.out.println(this); }
原因为:
getDeclaredMethod*()获取的是类自身声明的所有方法,包含public、protected和private方法。getMethod*()获取的是类的所有共有方法,这就包括自身的所有public方法,和从基类继承的、从接口实现的所有public方法。
anyway,所有getMethod方法都加上try catch throwable吧,以免影响主体进程
7 最终的日志
首次处理 class sun.myproxybean.MyServiceImpl的ioc 处理 class sun.myproxybean.MyServiceImpl$$EnhancerByCGLIB$$5b3ba48d的myMapper1 处理 class sun.myproxybean.MyServiceImpl$$EnhancerByCGLIB$$5b3ba48d的myMapper2 处理 class sun.myproxybean.MyServiceImpl$$EnhancerByCGLIB$$5b3ba48d的myDao public void sun.myproxybean.MyServiceImpl.multi() @sun.myproxy.MY_TRANSACTIONAL() public final void sun.myproxybean.MyServiceImpl$$EnhancerByCGLIB$$5b3ba48d.multi() null public abstract void sun.myproxybean.MyService.multi() null protected void sun.myproxybean.MyServiceImpl.subFunction() null Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary. 首次处理 class sun.myproxybean.MyDaoImpl的ioc 处理 class sun.myproxybean.MyDaoImpl$$EnhancerByCGLIB$$4b5f7f6c的myMapper2 public void sun.myproxybean.MyDaoImpl.multi() @sun.myproxy.MY_TRANSACTIONAL() public final void sun.myproxybean.MyDaoImpl$$EnhancerByCGLIB$$4b5f7f6c.multi() null public abstract void sun.myproxybean.MyDao.multi() null Exception in thread "main" java.lang.RuntimeException: java.lang.RuntimeException: java.lang.ArithmeticException: / by zero at sun.myproxy.OdsTransactionProxyFactory.invokeHasTransactional(OdsTransactionProxyFactory.java:117) at sun.myproxy.OdsTransactionCglibFactory.intercept(OdsTransactionCglibFactory.java:74) at sun.myproxybean.MyServiceImpl$$EnhancerByCGLIB$$5b3ba48d.multi(<generated>) at sun.mybatis.Main.main(Main.java:58) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147) Caused by: java.lang.RuntimeException: java.lang.ArithmeticException: / by zero at sun.myproxy.OdsTransactionProxyFactory.invokeHasTransactional(OdsTransactionProxyFactory.java:117) at sun.myproxy.OdsTransactionCglibFactory.intercept(OdsTransactionCglibFactory.java:74) at sun.myproxybean.MyDaoImpl$$EnhancerByCGLIB$$4b5f7f6c.multi(<generated>) at sun.myproxybean.MyServiceImpl.multi(MyServiceImpl.java:33) at sun.myproxybean.MyServiceImpl$$EnhancerByCGLIB$$5b3ba48d.CGLIB$multi$1(<generated>) at sun.myproxybean.MyServiceImpl$$EnhancerByCGLIB$$5b3ba48d$$FastClassByCGLIB$$8fca1f76.invoke(<generated>) at net.sf.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228) at sun.myproxy.OdsTransactionCglibFactory.aopInvoke(OdsTransactionCglibFactory.java:83) at sun.myproxy.OdsTransactionProxyFactory.invokeHasTransactional(OdsTransactionProxyFactory.java:105) ... 8 more Caused by: java.lang.ArithmeticException: / by zero at sun.myproxybean.MyDaoImpl.multi(MyDaoImpl.java:18) at sun.myproxybean.MyDaoImpl$$EnhancerByCGLIB$$4b5f7f6c.CGLIB$multi$0(<generated>) at sun.myproxybean.MyDaoImpl$$EnhancerByCGLIB$$4b5f7f6c$$FastClassByCGLIB$$e94f8239.invoke(<generated>) at net.sf.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228) at sun.myproxy.OdsTransactionCglibFactory.aopInvoke(OdsTransactionCglibFactory.java:83) at sun.myproxy.OdsTransactionProxyFactory.invokeHasTransactional(OdsTransactionProxyFactory.java:105) ... 16 more