浅谈spring之jdk动态代理

学习来源于b站动力节点

JDK动态代理

​ 动态代理的实现方式常用的有两种:使用JDK的Proxy,与通过CGLIB生成代理。jdk的动态要求目标对象必须实现接口,这是java设计上的要求,使用jdk中的Proxy,Method,InvocationHanderl创建代理对象。接下来演示jdk动态代理实现的一个小业务。
案例:

目前该项目主要完成业务doSome方法或doOther方法。(模拟项目开发,用注解获取对象)

在service包下有SomeService接口(其中有doSome和doOther方法)。

package com.pingfan.bao05.service;
public interface SomeService {
    
    
    void doSome();
    void doOther();
}

service包下有impl包以及impl包下有实现SomeService接口的类SomeServiceImpl

package com.pingfan.bao05.service.impl;
import com.pingfan.bao05.service.SomeService;
import org.springframework.stereotype.Component;
@Component
public class SomeServiceImpl implements SomeService {
    
    
    @Override
    public void doSome() {
    
    
        System.out.println("执行业务方法doSome");
    }
    @Override
    public void doOther() {
    
    
        System.out.println("执行业务方法doOther");
    }
}

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <context:component-scan base-package="com.pingfan.bao05"/>
</beans>

测试:

@Test
public void test02(){
    
    
    String config="applicationContext.xml";
    ApplicationContext ac=new ClassPathXmlApplicationContext(config);
    SomeServiceImpl someService= (SomeServiceImpl) ac.getBean("someServiceImpl");
    someService.doSome();
}

该项目运行一段时间需要改造,对业务增加功能。比如增加记录时间的功能,和提交事务的功能。

对该功能可以在原有代码基础上实现:

对SomeServiceImpl进行修改

package com.pingfan.bao05.service.impl;

import com.pingfan.bao05.service.SomeService;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.Date;

@Component
public class SomeServiceImpl implements SomeService {
    
    
    @Override
    public void doSome() {
    
    
        System.out.println("执行方法的时间"+new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
        System.out.println("执行业务方法doSome");
        //提交事务
        System.out.println("方法执行完毕提交事务");
    }
    @Override
    public void doOther() {
    
    
        System.out.println("执行方法的时间"+new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
        System.out.println("执行业务方法doOther");
        System.out.println("方法执行完毕提交事务");
    }
}

测试:

@Test
public void test02(){
    
    
    String config="applicationContext.xml";
    ApplicationContext ac=new ClassPathXmlApplicationContext(config);
    SomeServiceImpl someService= (SomeServiceImpl) ac.getBean("someServiceImpl");
    someService.doSome();
    System.out.println("-----------");
    someService.doOther();
}

结果

执行方法的时间2021-01-16
执行业务方法doSome
方法执行完毕提交事务
-----------
执行方法的时间2021-01-16
执行业务方法doOther
方法执行完毕提交事务

至此功能添加成功,如果像这种类很多,在开发中添加起来很多次,而且都是重复的操作。接下来对该项目进行优化,使得逻辑清晰。

增加一个工具类。ServiceTools。(把非业务方法放到工具类中)

package com.pingfan.bao05.utiles;
import java.text.SimpleDateFormat;
import java.util.Date;
public class ServiceTools {
    
    
    public static void doLog(){
    
    
        System.out.println("非业务方法,执行方法的时间"+new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
    }
    public static void doTrans(){
    
    
        System.out.println("方法执行完毕提交事务");
    }
}

接下来使用jdk动态代理实现不改变原有代码为其增加打印日志和提交事务的功能。

实现步骤:

  1. 创建目标类,SomeServiceImpl类,给它的doSome,doOther增加日志和事务功能
  2. 创建InvocationHandler接口的实现类,在这个类实现给目标方法增加功能。
  3. 使用jdk中类Proxy,创建代理对象。实现创建对象的能力。

1我们已经完成,接下来实现2

MyIncationHandler.java

package com.pingfan.bao05.handler;

import com.pingfan.bao05.utiles.ServiceTools;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MyIncationHandler implements InvocationHandler {
    
    
    //目标对象
    private Object targect;//SomeServiceImpl类
    public MyIncationHandler(Object targect){
    
    
        this.targect=targect;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
        //通过代理对象执行方法时,会调用执行这个invoke
        //执行目标类的方法,通过Method类实现。
        Object res=null;
        ServiceTools.doLog();
        res=method.invoke(targect,args);//SomeServiceImpl.doOther或者doSome
        ServiceTools.doTrans();
        //目标方法执行结果
        return res;
    }
}

实现步骤3

@Test
public void test02(){
    
    
    String config="applicationContext.xml";
    ApplicationContext ac=new ClassPathXmlApplicationContext(config);
    //使用jdk的Proxy创建代理对象,创建目标对象。
    SomeServiceImpl targect= (SomeServiceImpl) ac.getBean("someServiceImpl");
    //创建InvocationHandler对象
    InvocationHandler handler=new MyIncationHandler(targect);
    //使用Proxy创建代理
    SomeService proxy=(SomeService) Proxy.newProxyInstance(
        targect.getClass().getClassLoader(),
        targect.getClass().getInterfaces(),handler);
    //通过代理执行方法,会调用handler中的invoke
    proxy.doSome();
}

结果

非业务方法,执行方法的时间2021-01-16
执行业务方法doSome
方法执行完毕提交事务

分析:首先执行proxy.doSome();他会执行proxy类中的handler类中的invake方法。而其中的method是通过jdk处理我们拿到的doSome,也就是Proxy.doSome,传递到了method,进而执行目标类的doSome方法,那我们就可以在此之前或者之后执行我们增加的功能。

现在又有个需求,我们只在执行doSome方法时,才执行日志和事务功能。

分析:通过获取到的方法名进行判断执行的方法,再进行增加功能。

对MyIncationHandler进行修改。

package com.pingfan.bao05.handler;

import com.pingfan.bao05.utiles.ServiceTools;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MyIncationHandler implements InvocationHandler {
    
    
    //目标对象
    private Object targect;//SomeServiceImpl类
    public MyIncationHandler(Object targect){
    
    
        this.targect=targect;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
        //通过代理对象执行方法时,会调用执行这个invoke
        //执行目标类的方法,通过Method类实现。
        Object res=null;
        if(method.getName().equals("doSome")){
    
    
            ServiceTools.doLog();
            res=method.invoke(targect,args);//SomeServiceImpl.doOther或者doSome
            ServiceTools.doTrans();
        }else{
    
    
            res=method.invoke(targect,args);//SomeServiceImpl.doOther或者doSome
        }
        //目标方法执行结果
        return res;
    }
}

测试:

@Test
public void test02(){
    
    
    String config="applicationContext.xml";
    ApplicationContext ac=new ClassPathXmlApplicationContext(config);
    //使用jdk的Proxy创建代理对象,创建目标对象。
    SomeServiceImpl targect= (SomeServiceImpl) ac.getBean("someServiceImpl");
    //创建InvocationHandler对象
    InvocationHandler handler=new MyIncationHandler(targect);
    //使用Proxy创建代理
    SomeService proxy=(SomeService) Proxy.newProxyInstance(
        targect.getClass().getClassLoader(),
        targect.getClass().getInterfaces(),handler);
    //通过代理执行方法,会调用handler中的invoke
    proxy.doSome();
    System.out.println("-----------");
    proxy.doOther();
}

结果:

非业务方法,执行方法的时间2021-01-16
执行业务方法doSome
方法执行完毕提交事务
-----------
执行业务方法doOther

总结:可以看到执行doSome方法时才会执行额外的功能。至此动态代理的实现完成,而我们接下来学习的aop他就是基于动态代理实现的,可以明显的发现,我们在不改变源代码的基础上增加了新的功能,体现了动态代理的解耦合。实现业务和非业务功能分离。

猜你喜欢

转载自blog.csdn.net/qq_43566782/article/details/112724909