逆水行舟 —— jdk動態代理和Cglib字節碼增強
阿新 • • 發佈:2019-05-02
類庫 sys factor src 就是 利用 override inf pub
利用攔截器加上反射機制生成一個實現代理接口的匿名類,在調用具體方法時,調用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字節碼增強