1. 程式人生 > >逆水行舟 —— jdk動態代理和Cglib字節碼增強

逆水行舟 —— jdk動態代理和Cglib字節碼增強

類庫 sys factor src 就是 利用 override inf pub

JDK動態代理

利用攔截器加上反射機制生成一個實現代理接口的匿名類,在調用具體方法時,調用InvocationHandler來處理

JDK動態代理只需要JDK環境就可以進行代理,流程為:

  1. 實現InvocationHandler

  2. 使用Proxy.newProxyInstance產生代理對象

  3. 被代理的對象必須實現接口

具體列子如下:

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();
}

效果如下:

技術分享圖片

CGLIB動態代理

  1. 通過加載對象類的class文件,修改其字節碼生成子類的方式完成,不需要實現接口

  2. 但是需要第三方庫: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中采用何種代理手段,我們不強加限制的話,會根據類是否有接口來區別對待

  1. 當一個類有接口的時候,就會選用JDK的動態代理

  2. 當一個類沒有實現接口的時候,就會選用CGLIB代理的方式

兩種代理方式的本質:

  1. JDK動態代理是針對實現了接口的類生成代理,不是針對類

  2. CGLIB使用的是為被代理類生成一個子類,通過繼承的方法覆蓋並增強其方法,

    但是因為是繼承所以不能聲明被代理類為final,無法被繼承無法實現CGLIB代理

逆水行舟 —— jdk動態代理和Cglib字節碼增強