Java動態代理機制詳解(JDK動態代理與CGLIB動態代理區別)

代理是一種常用的設計模式,其目的就是爲其他對象提供一個代理以控制對某個對象的訪問。代理類負責爲委託類預處理消息,過濾消息並轉發消息,以及進行消息被委託類執行後的後續處理。在講述動態代理前,我們先通過一個例子瞭解一下什麼是靜態代理,這裏以事務控制爲例。

1.靜態代理

1.1 pom.xml文件配置

<properties>
    <!-- Spring -->
    <spring.version>4.1.3.RELEASE</spring.version>
</properties>
<dependencies>
    <!-- Spring Begin -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <!-- Spring End -->
</dependencies>

1.2 業務接口代碼

public interface UserService {
    /**
     * 
     * 功能描述: <br>
     * 〈保存用戶〉
     *
     * @since [1.0.0](可選)
     */
    public void saveUser();

    /**
     * 
     * 功能描述: <br>
     * 〈更新用戶〉
     *
     * @since [1.0.0](可選)
     */
    public void updateUser();
}

1.3 業務實現類代碼

@Service("userService")
public class UserServiceImpl implements UserService {
    @Override
    public void saveUser() {
        System.out.println("userService saveUser()...");
    }

    @Override
    public void updateUser() {
        System.out.println("userService updateUser()...");
    }
}

1.4 事務管理類代碼

@Service("transactionManager")
public class TransactionManager {
    public void beginTransaction() {
        System.out.println("開啓事務....");
    }

    public void commint() {
        System.out.println("提交事務....");
    }

    public void rollback() {
        System.out.println("回滾事務....");
    }
}

1.5 代理類代碼

@Service("userServiceProxy")
public class UserServiceProxy implements UserService {
    @Resource(name = "userService")
    private UserService userService;

    @Resource(name = "transactionManager")
    private TransactionManager transactionManager;

    @Override
    public void saveUser() {
        try {
            transactionManager.beginTransaction();
            userService.saveUser();
            transactionManager.commint();
        } catch (Exception e) {
            transactionManager.rollback();
        }
    }

    @Override
    public void updateUser() {
        try {
            transactionManager.beginTransaction();
            userService.updateUser();
            transactionManager.commint();
        } catch (Exception e) {
            transactionManager.rollback();
        }
    }
}

1.6 測試類代碼

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:applicationContext.xml" })
public class ProxyTest {
    @Resource(name = "userServiceProxy")
    private UserService userService;

    @Test
    public void testStaticProxy() {
        userService.saveUser();
    }
}

1.7 測試結果

開啓事務....
userService saveUser()...
提交事務....

從上面的代碼,可以看出靜態代理給我們帶來的一系列問題,如在代理對象中包含了真實對象的引用,如果我們需要爲不同的業務進行代理,就需要爲每個業務都創建一個代理對象,甚是麻煩,所以引出了動態代理。

2.JDK動態代理

2.1 業務類接口

public interface UserService {
    /**
     * 
     * 功能描述: <br>
     * 〈保存用戶〉
     *
     * @since [1.0.0](可選)
     */
    public void saveUser();

    /**
     * 
     * 功能描述: <br>
     * 〈更新用戶〉
     *
     * @since [1.0.0](可選)
     */
    public void updateUser();
}

2.2 業務實現類

@Service("userService")
public class UserServiceImpl implements UserService {
    @Override
    public void saveUser() {
        System.out.println("userService saveUser()...");
    }

    @Override
    public void updateUser() {
        System.out.println("userService updateUser()...");
    }
}

2.3 事務管理類代碼

@Service("transactionManager")
public class TransactionManager {
    public void beginTransaction() {
        System.out.println("開啓事務....");
    }

    public void commint() {
        System.out.println("提交事務....");
    }

    public void rollback() {
        System.out.println("回滾事務....");
    }
}

2.4 代理類代碼

@Service("transactionManagerProxy")
public class TransactionManagerProxy implements InvocationHandler {
    private Object target;

    @Resource(name = "transactionManager")
    private TransactionManager transactionManager;

    public Object createProxyObject(Object object) {
        this.target = object;
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        transactionManager.beginTransaction();
        Object object = null;
        try {
            object = method.invoke(target, args);
            transactionManager.commint();
        } catch (Exception e) {
            transactionManager.rollback();
        }
        return object;
    }
}

2.5 測試類代碼

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:applicationContext.xml" })
public class ProxyTest {
    @Resource(name = "transactionManagerProxy")
    private TransactionManagerProxy transactionManagerProxy ;

    @Test
    public void testStaticProxy() {
        UserService userService = (UserService) transactionManagerProxy.createProxyObject(new UserServiceImpl());
        userService.saveUser();
    }
}

2.6 測試結果

開啓事務....
userService saveUser()...
提交事務....

但是,JDK的動態代理依靠接口實現,如果有些類並沒有實現接口,則不能使用JDK代理,這就要使用cglib動態代理了。

3.CGLIB動態代理

3.1 業務接口類

public interface UserService {
    /**
     * 
     * 功能描述: <br>
     * 〈保存用戶〉
     *
     * @since [1.0.0](可選)
     */
    public void saveUser();

    /**
     * 
     * 功能描述: <br>
     * 〈更新用戶〉
     *
     * @since [1.0.0](可選)
     */
    public void updateUser();
}

3.2 業務實現類

@Service("userService")
public class UserServiceImpl implements UserService {
    @Override
    public void saveUser() {
        System.out.println("userService saveUser()...");
    }

    @Override
    public void updateUser() {
        System.out.println("userService updateUser()...");
    }
}

3.3 事務管理類

@Service("transactionManager")
public class TransactionManager {
    public void beginTransaction() {
        System.out.println("開啓事務....");
    }

    public void commint() {
        System.out.println("提交事務....");
    }

    public void rollback() {
        System.out.println("回滾事務....");
    }
}

3.4 事務代理類

public class TransactionManagerProxy implements org.springframework.cglib.proxy.InvocationHandler {
    private Object target;
    private TransactionManager transactionManager;

    public TransactionManagerProxy(Object target, TransactionManager transactionManager) {
        this.target = target;
        this.transactionManager = transactionManager;
    }

    // 創建代理對象
    public Object getProxyInstance() {
        Enhancer enhancer = new Enhancer();
        enhancer.setClassLoader(this.getClass().getClassLoader());
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        transactionManager.beginTransaction();
        Object object = null;
        try {
            object = method.invoke(target, args);
            transactionManager.commint();
        } catch (Exception e) {
            transactionManager.rollback();
        }
        return object;
    }
}

3.5 測試類

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:applicationContext.xml" })
public class ProxyTest {
    @Resource(name = "transactionManager")
    private TransactionManager transactionManager;

    @Test
    public void testStaticProxy() {
        UserServiceImpl userServiceImpl = (UserServiceImpl) new TransactionManagerProxy(new UserServiceImpl(), transactionManager).getProxyInstance();
        userServiceImpl.saveUser();
    }
}

3.6 測試結果

開啓事務....
userService saveUser()...
提交事務....

1.CGLIB可以生成目標類的子類,並重寫父類非final修飾符的方法。
2.要求類不能是final的,要攔截的方法要是非final,非static,非private的
3.動態代理的最小單位是類(所有類中的方法都會被處理)

注:若目標對象實現了若干接口,Spring就會使用JDK動態代理,若目標對象沒有實現任何接口,Spring就使用CGLIB庫生成目標對象的子類。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章