JDK动态代理

来源:互联网 发布:死是什么感觉 知乎 编辑:程序博客网 时间:2024/06/05 08:16
1、背景
   Spring AOP使用动态代理技术在运行期织入增强的代码,Spring AOP 的底层使用了两种代理模式一种是JDK的动态代理,另一种是基于CGLIB的动态代理。之所以需要两种代理机制,很大程度上是因为JDK本身只提供接口的代理,而不支持类的代理。
  
2、JDKProxy的代码
  1. package com.itheima.spring.jdkproxy;
  2. public interface PersonDao {
  3. public void savePerson();
  4. }
  1. package com.itheima.spring.jdkproxy;
  2. public class PersonDaoImpl implements PersonDao{
  3. @Override
  4. public void savePerson() {
  5. System.out.println("save person");
  6. }
  7. }
  1. package com.itheima.spring.jdkproxy;
  2. public class Transaction {
  3. public void beginTransaction(){
  4. System.out.println("begin transcation");
  5. }
  6. public void commit() {
  7. System.out.println("commit");
  8. }
  9. }
  1. package com.itheima.spring.jdkproxy;
  2. import java.lang.reflect.InvocationHandler;
  3. import java.lang.reflect.Method;
  4. /**
  5. * 拦截器:
  6. * 1、目标类导入进来
  7. * 2、事务导入进来
  8. * 3、invoke完成
  9. * ①开启事务
  10. * ②调用目标对象的方法
  11. * ③事务提交
  12. * @author xx
  13. *
  14. */
  15. public class MyInterceptor implements InvocationHandler{
  16. private Object target;//目标类
  17. private Transaction transaction;//事务,在这里代表一种功能,但如果要插入多个功能,就不能这么写了,看下一个案例,可以把他们加入在一个拦截器链表里
  18. public MyInterceptor(Object target, Transaction transaction) {
  19. super();
  20. this.target = target;
  21. this.transaction = transaction;
  22. }
  23. @Override
  24. public Object invoke(Object proxy, Method method, Object[] args)
  25. throws Throwable {
  26. String methodName = method.getName();
  27. if("savePerson".equals(methodName) || "updatePerson".equals(methodName)
  28. || "deletePerson".equals(methodName)){
  29. this.transaction.beginTransaction();//开启 事务
  30. method.invoke(target);//调用目标方法
  31. this.transaction.commit();//事务的提交
  32. }else{
  33. method.invoke(target);
  34. }
  35. return null;
  36. }
  37. }
  1. package com.itheima.spring.jdkproxy;
  2. import java.lang.reflect.Proxy;
  3. import org.junit.Test;
  4. /**
  5. * 1、拦截器的invoke方法是在是什么时候执行的?
  6. * 当在客户端,代理对象调用方法的时候,进入到了拦截器的invoke方法
  7. * 2、代理对象的方法体的内容是什么?
  8. * 拦截器的invoke方法的内容就是代理对象的方法的内容
  9. * 3、拦截器中的invoke方法的参数method是谁在什么时候传递过来的?
  10. * 代理对象调用方法的时候,进入了拦截器中的invoke方法,所以invoke
  11. * 方法中的参数method就是代理对象调用的方法
  12. * @author xx
  13. */
  14. public class JDKProxyTest {
  15. /**
  16. * 1、创建一个目标类
  17. * 2、创建一个事务
  18. * 3、创建一个拦截器
  19. * 4、动态生成一个代理对象
  20. */
  21. @Test
  22. public void test() {
  23. Object target = new PersonDaoImpl();
  24. Transaction transaction = new Transaction();
  25. MyInterceptor interceptor = new MyInterceptor(target,transaction);
  26. /*
  27. *1、目标类的加载器
  28. *2、目标类的所有的接口
  29. *3、拦截器
  30. */
  31. PersonDao personDao = (PersonDao) Proxy.newProxyInstance(target.getClass().getClassLoader(),
  32. target.getClass().getInterfaces(), interceptor);
  33. personDao.savePerson();
  34. }
  35. }

思考
动态代理是怎么生成的?
 jdk的动态代理不用写代理方法,它是由java虚拟机实现的,因为JDK生成的最终真正的代理类,它继承自Proxy并实现了我们定义的PersonDao接口,在实现PersonDao接口方法的内部,通过反射调用了InvocationHandlerImpl的invoke方法。
jdk动态代理比静态代理先进(不同)在哪?有什么优势?
①解决代码重用的问题②解耦,代码灵活性高,调用目标代码时,会在方法“运行时”动态的加入
以上的JdkProxy代码有什么缺点?
①拦截器中除了能调用目标对象的目标方法以外,功能是比较单一,这个例子只能处理事务(见下面代码)②拦截器中invoke方法的if语句(切入点)是不靠谱的,因为一旦方法多了要写很多(可用正则表达式)
3、另一种改进的JDK动态代理模式
  1. package com.itheima.spring.jdkproxy;
  2. /**
  3. * 给日志、事务等做了一个抽象,而这个抽象就是Interceptor
  4. * @author xx
  5. *
  6. */
  7. public interface Interceptor {
  8. /*
  9. * 除了目标对象的目标方法之外,其他任何的功能,比如事务、日志等都写在interceptor里面
  10. */
  11. public void interceptor();
  12. }
  1. package com.itheima.spring.jdkproxy;
  2. /**
  3. * Spring的实现比这复杂,用的责任链模式
  4. * @author xx
  5. *
  6. */
  7. public class Transaction implements Interceptor{
  8. @Override
  9. public void interceptor() {
  10. System.out.println("transaction");
  11. }
  12. }
  1. package com.itheima.spring.jdkproxy;
  2. import java.lang.reflect.InvocationHandler;
  3. import java.lang.reflect.Method;
  4. import java.util.List;
  5. /**
  6. * 拦截器:
  7. * 1、目标类导入进来
  8. * 2、事务导入进来
  9. * 3、invoke完成
  10. * ①开启事务
  11. * ②调用目标对象的方法
  12. * ③事务提交
  13. * @author xx
  14. *
  15. */
  16. public class MyInterceptor implements InvocationHandler{
  17. private Object target;//目标类
  18. List<Interceptor> interceptors;
  19. public MyInterceptor(Object target, List<Interceptor> interceptors) {
  20. super();
  21. this.target = target;
  22. this.interceptors = interceptors;
  23. }
  24. @Override
  25. public Object invoke(Object proxy, Method method, Object[] args)
  26. throws Throwable {
  27. String methodName = method.getName();
  28. if("savePerson".equals(methodName) || "updatePerson".equals(methodName)
  29. || "deletePerson".equals(methodName)){
  30. for(Interceptor interceptor: interceptors){
  31. interceptor.interceptor();
  32. }
  33. }
  34. method.invoke(target);
  35. return null;
  36. }
  37. }
  1. package com.itheima.spring.jdkproxy;
  2. import java.lang.reflect.Proxy;
  3. import java.util.ArrayList;
  4. import java.util.List;
  5. import org.junit.Test;
  6. /**
  7. * 1、拦截器的invoke方法是在是什么时候执行的?
  8. * 当在客户端,代理对象调用方法的时候,进入到了拦截器的invoke方法
  9. * 2、代理对象的方法体的内容是什么?
  10. * 拦截器的invoke方法的内容就是代理对象的方法的内容
  11. * 3、拦截器中的invoke方法的参数method是谁在什么时候传递过来的?
  12. * 代理对象调用方法的时候,进入了拦截器中的invoke方法,所以invoke
  13. * 方法中的参数method就是代理对象调用的方法
  14. * @author xx
  15. */
  16. public class JDKProxyTest {
  17. /**
  18. * 1、创建一个目标类
  19. * 2、创建一个事务
  20. * 3、创建一个拦截器
  21. * 4、动态生成一个代理对象
  22. */
  23. @Test
  24. public void test() {
  25. Object target = new PersonDaoImpl();
  26. Transaction transaction = new Transaction();
  27. List<Interceptor> interceptors = new ArrayList<Interceptor>();
  28. interceptors.add(transaction);
  29. MyInterceptor interceptor = new MyInterceptor(target,interceptors);
  30. /*
  31. *1、目标类的加载器
  32. *2、目标类的所有的接口
  33. *3、拦截器
  34. */
  35. PersonDao personDao = (PersonDao) Proxy.newProxyInstance(target.getClass().getClassLoader(),
  36. target.getClass().getInterfaces(), interceptor);
  37. personDao.savePerson();
  38. }
  39. }
思考
改进的代码解决了什么问题?
解决了之前代码的缺点一的问题
0 0
原创粉丝点击