Loading...

Java核心(四)——动态代理

  iwehdio的博客园:https://www.cnblogs.com/iwehdio/

学习自:

1、静态代理

  • 代理是一种模式,提供了对目标对象的间接访问方式,即通过代理访问目标对象。如此便于在目标实现的基础上增加额外的功能操作,前拦截,后拦截等,以满足自身的业务需求。
  • 需要在业务代码前后打印日志,如果直接添加的话:
    1. 直接修改源程序,不符合开闭原则。应该对扩展开放,对修改关闭
    2. 如果有几十个、上百个方法,修改量太大
    3. 存在重复代码(都是在核心代码前后打印日志)
    4. 日志打印硬编码在类中,不利于后期维护
  • 静态代理的实现:
    • 编写一个代理类,实现与目标对象相同的接口,并在内部维护一个目标对象的引用。
    • 通过构造器塞入目标对象,在代理对象中调用目标对象的同名方法,并添加前拦截,后拦截等所需的业务功能。
    • 静态代理只解决了上面四个缺点中的第一点。
    • 如果需要对多个类进行改造,就需要对每个类都实现一个代理。
    • 我们其实想要的并不是代理类,而是代理对象!那么,能否让JVM根据接口自动生成代理对象呢?

2、动态代理

  • 思路:通过获得被代理对象的接口的Class对象,获取被代理对象的方法信息。

  • 代理Class其实就是附有构造器的接口Class,一样的类结构信息,却能创建实例。

    image-20210120203829657

  • java.lang.reflect.Proxy.getProxyClass():返回代理类的Class对象。

  • 也就说,只要传入目标类实现的接口的Class对象,getProxyClass()方法即可返回代理Class对象,而不用实际编写代理类。

image-20210120204035393

image-20210120204331951

  • 动态代理使用流程:

    image-20210120204606915

    • 每次调用代理对象的方法都会调用invoke(),且invoke()的返回值就是代理方法的返回值。

    image-20210120204707762

  • 动态代理底层调用逻辑:

    • 静态代理:往代理对象的构造器传入目标对象,然后代理对象调用目标对象的同名方法。
    • 动态代理:constructor反射创建代理对象时,需要传入InvocationHandler。

    image-20210120205030879

    • 引入InvocationHandler的好处是:

      • JVM创建代理对象时不必考虑方法实现,只要造一个空壳的代理对象
      • 后期代理对象想要什么样的方法实现,我写在invocationHandler对象的invoke()方法里送进来便是
    • invocationHandler的作用,像是把“方法”和“方法体”分离。

    • 代理对象中有个成员变量invocationHandler,每一个方法里只有一句话:handler.invoke()。所以调任何一个代理方法,最终都会跑去调用invoke()方法。

    • invoke()方法是代理对象和目标对象的桥梁:

      image-20210120205350139

  • 但是我们真正想要的结果是:调用代理对象的方法时,去调用目标对象的方法。

  • 所以,接下来努力的方向就是:设法在invoke()方法得到目标对象,并调用目标对象的同名方法。

  • invoke()方法的形参:

    • Object proxy:很遗憾,是代理对象本身,而不是目标对象(不要调用,会无限递归)
    • Method method:本次被调用的代理对象的方法
    • Obeject[] args:本次被调用的代理对象的方法参数
  • 这样传入目标对象即可:

    public class ProxyTest {
    	public static void main(String[] args) throws Throwable {
    		CalculatorImpl target = new CalculatorImpl();
    		Calculator calculatorProxy = (Calculator) getProxy(target);
    		calculatorProxy.add(1, 2);
    		calculatorProxy.subtract(2, 1);
    	}
    
    	private static Object getProxy(final Object target) throws Exception {
    		Object proxy = Proxy.newProxyInstance(
    				target.getClass().getClassLoader(),/*类加载器*/
    				target.getClass().getInterfaces(),/*让代理对象和目标对象实现相同接口*/
    				new InvocationHandler(){/*代理对象的方法最终都会被JVM导向它的invoke方法*/
    					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    						System.out.println(method.getName() + "方法开始执行...");
    						Object result = method.invoke(target, args);
    						System.out.println(result);
    						System.out.println(method.getName() + "方法执行结束...");
    						return result;
    					}
    				}
    		);
    		return proxy;
    	}
    }
    

3、AOP

  • 自己写了一个UserController,以及UserServiceImpl implements UserService,并且在UserController中注入Service层对象:

    @Autowired
    private UserService userService;
    

    那么,这个userService一定是我们写UserServiceImpl的实例吗?

    • 给UserServiceImpl加了@Transactional 注解,那么注入的就不是我们写UserServiceImpl,而是其的动态代理。
    • 要用动态代理完成事务管理,还需要编写一个通知类,并把通知对象传入代理对象,通知负责事务的开启和提交,并在代理对象内部调用目标对象同名方法完成业务功能。

    image-20210122182814925

    • Spring为了实现事务,也编写了一个通知类,TransactionManager。利用动态代理创建代理对象时,Spring会把transactionManager织入代理对象,然后将代理对象注入。
  • 不用AOP时,交叉业务直接写在方法内部的前后,用了AOP交叉业务写在方法调用前后。这与AOP的底层实现方式有关:动态代理其实就是代理对象调用目标对象的同名方法,并在调用前后加增强代码。不过这两种最终运行效果是一样的。

  • AOP的实现:

    • 需要一个通知类(TransactionManager)执行事务,一个代理工厂帮助生成代理对象,然后利用动态代理将事务代码织入代理对象的各个方法中。

    • 加了个@MyTransactional后,代理工厂给我返回一个代理对象:

      image-20210122183331475

    • 代理对象方法 = 事务 + 目标对象方法。

      image-20210122183349373

    • 事务操作,必须使用同一个Connection对象。如何保证?

      • 第一次从数据源获取Connection对象并开启事务后,将它存入当前线程的ThreadLocal中,等到了DAO层,还是从ThreadLocal中取。

      image-20210122183530618

  • AOP事务的实现:

    • ConnectionUtils工具类

      public class ConnectionUtils {
      
          private ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
      
          private static BasicDataSource dataSource = new BasicDataSource();
      
          //静态代码块,设置连接数据库的参数
          static{
              dataSource.setDriverClassName("com.mysql.jdbc.Driver");
              dataSource.setUrl("jdbc:mysql://localhost:3306/test");
              dataSource.setUsername("root");
              dataSource.setPassword("123456");
          }
      
      
          /**
           * 获取当前线程上的连接
           * @return
           */
          public Connection getThreadConnection() {
              try{
                  //1.先从ThreadLocal上获取
                  Connection conn = tl.get();
                  //2.判断当前线程上是否有连接
                  if (conn == null) {
                      //3.从数据源中获取一个连接,并且存入ThreadLocal中
                      conn = dataSource.getConnection();
                      tl.set(conn);
                  }
                  //4.返回当前线程上的连接
                  return conn;
              }catch (Exception e){
                  throw new RuntimeException(e);
              }
          }
      
          /**
           * 把连接和线程解绑
           */
          public void removeConnection(){
              tl.remove();
          }
      }
      
    • AOP通知(事务管理器)

      /**
       * 和事务管理相关的工具类,它包含了,开启事务,提交事务,回滚事务和释放连接
       */
      public class TransactionManager {
      
          private ConnectionUtils connectionUtils;
      
          public void setConnectionUtils(ConnectionUtils connectionUtils) {
              this.connectionUtils = connectionUtils;
          }
      
          /**
           * 开启事务
           */
          public  void beginTransaction(){
              try {
                  connectionUtils.getThreadConnection().setAutoCommit(false);
              }catch (Exception e){
                  e.printStackTrace();
              }
          }
      
          /**
           * 提交事务
           */
          public  void commit(){
              try {
                  connectionUtils.getThreadConnection().commit();
              }catch (Exception e){
                  e.printStackTrace();
              }
          }
      
          /**
           * 回滚事务
           */
          public  void rollback(){
              try {
                  connectionUtils.getThreadConnection().rollback();
              }catch (Exception e){
                  e.printStackTrace();
              }
          }
      
      
          /**
           * 释放连接
           */
          public  void release(){
              try {
                  connectionUtils.getThreadConnection().close();//还回连接池中
                  connectionUtils.removeConnection();
              }catch (Exception e){
                  e.printStackTrace();
              }
          }
      }
      
    • 自定义注解

      @Target({ElementType.TYPE, ElementType.METHOD})
      @Retention(RetentionPolicy.RUNTIME)
      public @interface MyTransactional {
      }
      
    • Service

      public interface UserService {
      	void getUser();
      }
      
       
      public class UserServiceImpl implements UserService {
      	@Override
      	public void getUser() {
      		System.out.println("service执行...");
      	}
      }
      
    • 实例工厂

      public class BeanFactory {
      
      	public Object getBean(String name) throws Exception {
      		//得到目标类的Class对象
      		Class<?> clazz = Class.forName(name);
      		//得到目标对象
      		Object bean = clazz.newInstance();
      		//得到目标类上的@MyTransactional注解
      		MyTransactional myTransactional = clazz.getAnnotation(MyTransactional.class);
      		//如果打了@MyTransactional注解,返回代理对象,否则返回目标对象
      		if (null != myTransactional) {
      			ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
      			TransactionManager txManager = new TransactionManager();
      			txManager.setConnectionUtils(new ConnectionUtils());
      			//装配通知和目标对象
      			proxyFactoryBean.setTxManager(txManager);
      			proxyFactoryBean.setTarget(bean);
      			Object proxyBean = proxyFactoryBean.getProxy();
      			//返回代理对象
      			return proxyBean;
      		}
      		//返回目标对象
      		return bean;
      	}
      }
      
    • 代理工厂

      public class ProxyFactoryBean {
      	//通知
      	private TransactionManager txManager;
      	//目标对象
      	private Object target;
      
      	public void setTxManager(TransactionManager txManager) {
      		this.txManager = txManager;
      	}
      
      	public void setTarget(Object target) {
      		this.target = target;
      	}
      
      	//传入目标对象target,为它装配好通知,返回代理对象
      	public Object getProxy() {
      		Object proxy = Proxy.newProxyInstance(
      				target.getClass().getClassLoader(),/*1.类加载器*/
      				target.getClass().getInterfaces(), /*2.目标对象实现的接口*/
      				new InvocationHandler() {/*3.InvocationHandler*/
      					@Override
      					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      						try {
      							//1.开启事务
      							txManager.beginTransaction();
      							//2.执行操作
      							Object retVal = method.invoke(target, args);
      							//3.提交事务
      							txManager.commit();
      							//4.返回结果
      							return retVal;
      						} catch (Exception e) {
      							//5.回滚事务
      							txManager.rollback();
      							throw new RuntimeException(e);
      						} finally {
      							//6.释放连接
      							txManager.release();
      						}
      
      					}
      				}
      		);
      		return proxy;
      	}
      
      }
      

iwehdio的博客园:https://www.cnblogs.com/iwehdio/
posted @ 2021-01-22 19:24  iwehdio  阅读(123)  评论(0编辑  收藏  举报