动态代理有种实现方式:一个是jdk提供的,一个是cglib提供的。
动态代理三要素:proxy代理类、InvocationHandler接口、Invoke方法。
1. TransactionManager.java
package proxy.jdk;
import org.springframework.stereotype.Component;
@Component
public class TransactionManager {
public void begin(){
System.out.println("事务开启");
}
public void commit(){
System.out.println("事务提交");
}
public void rollback(){
System.out.println("事务回滚");
}
}
2. UserDao.java
package proxy.jdk.dao;
public interface UserDao {
public void addUser();
public void updateUser();
}
3. UserDaoImpl.java
package proxy.jdk.dao;
import org.springframework.stereotype.Repository;
@Repository(value="userDao")
public class UserDaoImpl implements UserDao{
public void addUser() {
System.out.println("新增用户");
}
public void updateUser() {
System.out.println("修改用户");
}
}
4. UserService.java
package proxy.jdk.service;
public interface UserService {
public void addUser();
public void updateUser();
}
5. UserServiceImpl.java
package proxy.jdk.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import proxy.jdk.dao.UserDao;
@Service(value="userService")
public class UserServiceImpl implements UserService{
@Autowired
private UserDao userDao;
public void addUser() {
userDao.addUser();
}
public void updateUser() {
userDao.updateUser();
}
}
6. DynamicProxy.java
步骤:
- 写一个获取代理方法,把被代理对象和事务对象作为参数传入
- 通过反射创建实例 Proxy.newProxyInstance,参数:类加载器、接口
- 匿名内部类new InvocationHandler,参数:代理对象、方法,参数
- 回调 result = method.invoke,参数:目标对象、参数
package proxy.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class DynamicProxy {
// 创建代理对象,传入真实对象和事务对象
public static Object getProxy(final Object target, final TransactionManager tx) {
Object proxy = Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
// 采用匿名内部类方式,参数必须final
new InvocationHandler() {
// 代理对象,真实对象的方法,真实对象的参数
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
Long beginTime = System.currentTimeMillis();
try {
tx.begin();
// 通过反射调用真实对象的方法
result = method.invoke(target, args);
tx.commit();
} catch (Exception e) {
tx.rollback(); // 异常后返回null
}
Long endTime = System.currentTimeMillis();
System.out.println("执行时间:" + (endTime - beginTime) + " 毫秒");
// 必须设置返回值,否则下面过程就无法获取到执行结果
return result;
}
});
return proxy;
}
}
7. applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
">
<context:component-scan base-package="proxy.jdk.dao,proxy.jdk.service.UserService,proxy.jdk.service,proxy.jdk"/>
</beans>
8. 测试类
package proxy.jdk;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import proxy.jdk.service.UserService;
public class TestSpring {
@Test
public void test(){
//启动spring容器
ApplicationContext context = new ClassPathXmlApplicationContext("proxy/jdk/applicationContext.xml");
UserService target = (UserService) context.getBean("userService");
TransactionManager tx = (TransactionManager) context.getBean("transactionManager");
UserService proxy = (UserService)DynamicProxy.getProxy(target, tx);
System.out.println(proxy.getClass());
proxy.addUser();
}
}
9. 执行结果
class com.sun.proxy.$Proxy7 //代理对象
事务开启
新增用户
事务提交
执行时间:0 毫秒 //没有真正的业务所以执行很快
Proxy代理类
Proxy是JDK中用来创建代理对象的类,其中使用最多的是newProxyInstance方法来创建代理对象。
public static Object newProxyInstance(
ClassLoader loader, //由哪个类加载器加载生成代理对象
Classs<?>[] interfaces, //接口对象数组,必须传入目标对象的接口
InvocationHandler h //表示一个invocationHandler对象,当代理对象执行方法时,会调用改InvocatonHandler对象
) throws IllegalArgumentException
注意:由于实现类没有接口,会导致执行报错!如:PersonServiceImpl proxyImpl = (PersonServiceImpl)DynamicProxy.getProxy(impl, tx);
java.lang.ClassCastException: com.sun.proxy.$Proxy8 cannot be cast to proxy.jdk.service.PersonServiceImpl
at proxy.jdk.TestSpring.test(TestSpring.java:27)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)