逆水行舟 —— jdk动态代理和Cglib字节码增强
Posted msi-chen
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了逆水行舟 —— jdk动态代理和Cglib字节码增强相关的知识,希望对你有一定的参考价值。
利用拦截器加上反射机制生成一个实现代理接口的匿名类,在调用具体方法时,调用InvocationHandler来处理
JDK动态代理只需要JDK环境就可以进行代理,流程为:
-
实现InvocationHandler
-
使用Proxy.newProxyInstance产生代理对象
-
被代理的对象必须实现接口
具体列子如下:
public class UserServiceImpl implements UserService {
@Override
public void eat() {
System.out.println("---------吃饭");
}
@Override
public void wc() {
System.out.print("上茅房------>");
}
}
//切面类
public class MyAspect {
public void before(){
System.out.print("先洗手再");
}
public void after(){
System.out.print("后要洗手");
}
}
// 产生代理对象的工厂类
public class MyFactoryBean {
public static UserService getInstance(){
// target : 目标类
final UserService service = new UserServiceImpl();
// Aspect : 切面类
final MyAspect aspect = new MyAspect();
// Weaving : 织入,也就是产生代理的过程
UserService proxy = (UserService) Proxy.newProxyInstance(
MyFactoryBean.class.getClassLoader(),
new Class[]{UserService.class},
new InvocationHandler(){
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("eat")){
aspect.before();
}
Object invoke = method.invoke(service, args);
if (method.getName().equals("wc")){
aspect.after();
}
return invoke;
}
});
return proxy;
}
}
//测试方法
@Test
public void userTest(){
UserService userService = MyFactoryBean.getInstance(); //使用工厂类产出代理对象
userService.eat();
userService.wc();
}
效果如下:
-
通过加载对象类的class文件,修改其字节码生成子类的方式完成,不需要实现接口
-
但是需要第三方库:CGLIB类库的支持
public class MyProxy implements MethodInterceptor {
?
private Object personService;
public Object createProxy(Object obj){
this.personService = obj;
Enhancer e = new Enhancer();
e.setSuperclass(obj.getClass());
e.setCallback(this);
Object proxy = e.create();
return proxy; //返回代理对象
}
?
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object obj = null;
if ("eat".equals(method.getName())){
System.out.print("先洗手再----->");
}
?
obj = method.invoke(personService, objects);
if ("wc".equals(method.getName())){
System.out.print("---->之后要洗手");
}
return obj;
}
}
public class PeopleService {
public void eat(){
System.out.println("吃饭");
}
public void wc(){
System.out.print("上厕所");
}
}
@Test
public void Test1(){
MyProxy myProxy = new MyProxy();
PeopleService proxy =(PeopleService) myProxy.createProxy(new PeopleService());
proxy.eat();
效果如下:
关于在Spring的AOP中采用何种代理手段,我们不强加限制的话,会根据类是否有接口来区别对待
-
当一个类有接口的时候,就会选用JDK的动态代理
-
当一个类没有实现接口的时候,就会选用CGLIB代理的方式
-
JDK动态代理是针对实现了接口的类生成代理,不是针对类
-
CGLIB使用的是为被代理类生成一个子类,通过继承的方法覆盖并增强其方法,
但是因为是继承所以不能声明被代理类为final,无法被继承无法实现CGLIB代理
以上是关于逆水行舟 —— jdk动态代理和Cglib字节码增强的主要内容,如果未能解决你的问题,请参考以下文章