我们通常通过代理来实现aop,spring中常常用JDK代理,因为JDK代理要实现接口,灵活性相较于CGLib较低,本文讲解一下cglib代理对象如何实现aop。
首先aop是在原对象调用方法时,在调用前后加一些业务,比如打印日志,权限之类的,然而在不改变原类代码的情况下就要给原方法加业务是实现不了的,如果实现了,我们的代码就毫无安全可言了。但我们还是需要这种需求,所以提出了代理。
先介绍一下cglib代理,它是以原类为父类,派生出来的代理类,通过拦截接口完成对方法的拦截。
首先我们要知道代理对象和原对象是父子关系,且调用代理的方法不会调用原来方法的。证明如下。
public class ProxyFactory {
@SuppressWarnings("unchecked")
public static <T> T GetProxyByClass(Class<?> klass) throws InstantiationException, IllegalAccessException {
Object object = klass.newInstance();
Enhancer enhancer = new Enhancer();
//所代理的对象
enhancer.setSuperclass(klass);
//方法拦截器
MethodInterceptor methodInterceptor = new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] paramters, MethodProxy arg3) throws Throwable {
System.out.println(method.getName()+"方法调用前");
//Object result = method.invoke(object, paramters);
System.out.println(method.getName()+"方法调用后");
return null;
}
};
enhancer.setCallback(methodInterceptor);
return (T)enhancer.create();
}
}
这段的意思就是拦截器调用方法时,会调用intercept方法,解释一下这四个参数,obj是指向要生成代理对象的指针,method就是代理调用了哪个方法,paramters是参数。比如说我们用代理对象调用了父类的一个方法,那么这个method就是父类方法(注意!父类的方法,不是代理的方法);
public class StudentModel {
public String stuId;
public String name;
public String password;
public StudentModel() {
}
public String getStuId() {
return stuId;
}
public void setStuId(String stuId) {
System.out.println("setStuId被调用");
this.stuId = stuId;
}
这是一个简单的java类
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
Object proxy = ProxyFactory.GetProxyByClass(StudentModel.class);
//System.out.println(proxy);
((StudentModel) proxy).setStuId("446");
测试:
我们发现调用代理的父类方法并没有调用其本身啊,得出结论,代理转型到父类对象后调用的方法不是原对象的方法。因为我的原对象方法是要输出一句话的,这里没有。我们在看一下代理对象的属性变化了没?
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
Object proxy = ProxyFactory.GetProxyByClass(StudentModel.class);
//System.out.println(proxy);
((StudentModel) proxy).setStuId("446");
System.out.println(((StudentModel) proxy).getStuId());
是null啊,这说明代理和原对象之间好像没联系啊,方法不同,属性也不同,唯一相通的就是我么可以在intercepter方法里头调用原对象方法啊,那我们就试一下。
public class ProxyFactory {
@SuppressWarnings("unchecked")
public static <T> T GetProxyByClass(Class<?> klass) throws InstantiationException, IllegalAccessException {
Object object = klass.newInstance();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(klass);
MethodInterceptor methodInterceptor = new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] paramters, MethodProxy arg3) throws Throwable {
System.out.println(method.getName()+"方法调用前");
Object result = method.invoke(object, paramters);
System.out.println(method.getName()+"方法调用后");
return result;
}
};
enhancer.setCallback(methodInterceptor);
return (T)enhancer.create();
}
}
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
Object proxy = ProxyFactory.GetProxyByClass(StudentModel.class);
//System.out.println(proxy);
((StudentModel) proxy).setStuId("446");
System.out.println(((StudentModel) proxy).getStuId());
原方法是执行了,但我们要知道,那是在intercepter里头用method,invke()才调用的啊。终于清楚了,代理就是个用来调用原方法的对象,仅仅起到当代理对象调用原对象方法时,把原方法的method和paramters一起传递到intercepter方法里头去了,原方法是否调用还要看程序大大们写不写调用语句呀。
如果
private static <T> T MakeCGlProxy(Class<?> klass,Object object) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(klass);
MethodInterceptor methodInterceptor =new MethodInterceptor() {
//方法拦截器
@Override
public Object intercept(Object obj, Method method, Object[] paramterlist, MethodProxy arg3) throws Throwable {
//添加拦截器
return doInvoke(klass, object,method ,paramterlist);
}
};
enhancer.setCallback(methodInterceptor);
return (T) enhancer.create();
}
中的intercepter方法返回null的话,那么 (T) enhancer.create();就会是null;
intercepter方法返回null的话,那么 (T) enhancer.create();就会是null;
intercepter方法返回null的话,那么 (T) enhancer.create();就会是null;
intercepter方法返回null的话,那么 (T) enhancer.create();就会是null;
intercepter方法返回null的话,那么 (T) enhancer.create();就会是null;
为啥呢?CGlib依赖于方法,如果你的方法返回null,那么cglib也就失去了作用,jdk开发人员就让它为null了,一样是推测,小编无法证明
有人可能要问了,代理时子类调用方法不是没有覆盖父类方法所以才调用了父类方法嘛?答:如果你能覆盖父类方法,也就是给这个动态对象加一个方法,请告诉我,我也在愁这块呢,不胜感激。
最后絮叨两句,CGLIB代理是原对象派生类,不信可以在测试类上加final,肯定报错,我试过了,代理对象里头是有原对象作为组合的,也就是有原对象的空间,操作原对象只能在intercepter里通过原对象的方法来操作,但小编还证明不了,这只是猜测,希望有大佬看到可以指点迷经。谢谢您的阅读,再会。