学习来源于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动态代理实现不改变原有代码为其增加打印日志和提交事务的功能。
实现步骤:
- 创建目标类,SomeServiceImpl类,给它的doSome,doOther增加日志和事务功能
- 创建InvocationHandler接口的实现类,在这个类实现给目标方法增加功能。
- 使用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他就是基于动态代理实现的,可以明显的发现,我们在不改变源代码的基础上增加了新的功能,体现了动态代理的解耦合。实现业务和非业务功能分离。