一、前情提要
1.目的
主要是实现一遍
静态代理
和动态代理的
简易代码
加深对Mybatis中的代理模式的理解
2.代理模式概念
- 代理模式的最大特点就是,
核心功能是由被代理对象自己去执行的
,代理对象只是增加了代理功能,在被代理对象的核心功能的基础上进一步封装,以实现更强大的功能 - 代理模式的编程风格是
面向接口编程
: 成员变量、方法入参、初始化构造器的参数、调用核心功能的方法等,都是通过接口 - 代理分为
静态代理
和动态代理
静态代理
可以自由切换被代理对象,实现某个被代理功能的强化,在这一点上比较灵活,但是对被代理功能的强化和封装方面是固定的,无法实现动态强化被代理的功能- 如果业务功能增加了,那么代理对象对被代理对象功能的封装也需要重新编写,在这一点上非常局限,由此引出了动态代理
动态代理
分为JDK动态代理
和GCLib动态代理
- JDK动态代理使用的是JDK提供的Proxy工具类,基于反射,实现动态代理
- 特点是代理对象不需要实现业务功能接口,就能够去代理其他对象,但只能使用被代理对象实现了接口中的业务功能,被代理对象自己的方法无法调用
- GCLib动态代理使用的是字节码框架ASM,对字节码文件进行代理
- 又称为
子类代理
,通过动态的在内存中构建子类对象,重写父类的方法进行代理功能的强化 - 如果被代理对象没有实现接口,则只能通过CGLib动态代理实现功能强化
- 特点是代理对象不需要实现业务功能接口,就能够去代理其他对象,并且可以代理目标所有的功能方法
- 被代理对象的类不能是final,否则报错
- 目标对象的方法不能是final/static修饰,那么方法不会被拦截。静态没有多态,final不能修改
- Cglib采用底层的字节码技术,在子类中采用方法拦截的技术,拦截父类指定方法的调用,并顺势植入代理功能的代码
- 又称为
二、项目相关
- JDK动态代理使用的是JDK提供的Proxy工具类,基于反射,实现动态代理
1.项目目录的层次结构
2.项目相关的依赖:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.kbkwwk.mybatis</groupId>
<artifactId>dynamic-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- CGLib动态代理功能的依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.18</version>
</dependency>
</dependencies>
</project>
3.代理功能实现的接口:Service.java
package cn.kbkwwk.mybatis.service.api;
/**
* ClassName: Service
* Package: cn.kbkwwk.mybatis.service.api
* Description:
*
* @Date: 2022-05-30 3:06 星期一
* @Author: Kevin
*/
public interface Service {
void sing();
void show();
}
4.被代理的目标一:SuperStarLiu.java
注意:不论是静态代理还是动态代理,被代理的目标都需要去实现核心功能接口
package cn.kbkwwk.mybatis.service.impl;
import cn.kbkwwk.mybatis.service.api.Service;
/**
* ClassName: SuperStarLiu
* Package: cn.kbkwwk.mybatis.dynamic
* Description:
*
* @Date: 2022-05-30 3:03 星期一
* @Author: Kevin
*/
public class SuperStarLiu implements Service {
@Override
public void sing() {
System.out.println("我是刘德华,我在唱歌...");
}
@Override
public void show() {
System.out.println("我是刘德华,我在表演...");
}
}
5.被代理的目标二:SuperStarZhou.java
注意:不论是静态代理还是动态代理,被代理的目标都需要去实现核心功能接口
package cn.kbkwwk.mybatis.service.impl;
import cn.kbkwwk.mybatis.service.api.Service;
/**
* ClassName: SuperStarZhou
* Package: cn.kbkwwk.mybatis.dynamic
* Description:
*
* @Date: 2022-05-30 3:03 星期一
* @Author: Kevin
*/
public class SuperStarZhou implements Service {
@Override
public void sing() {
System.out.println("我是周杰伦,我在唱歌...");
}
@Override
public void show() {
System.out.println("我是周杰伦,我在表演...");
}
/**
* 非接口方法,即被代理对象自身的方法
*/
public void dance() {
System.out.println("我是周杰伦,我在跳舞...");
}
}
6.静态代理对象的类:StaticAgent.java
注意:静态代理对象需要实现与被代理对象一样的接口,通过接口的引用,在被代理对象核心功能的基础上进一步封装强化功能,再去调用,实现功能的强化
优点是:切换被代理对象容易,但是当被代理对象的功能需要拓展,则代理对象的代码也需要同步修改,在这一点上比较局限,由此引申出动态代理
package cn.kbkwwk.mybatis.dynamic;
import cn.kbkwwk.mybatis.service.api.Service;
/**
* ClassName: StaticAgent
* Package: cn.kbkwwk.mybatis.dynamic
* Description:
*
* @Date: 2022-05-30 2:52 星期一
* @Author: Kevin
*/
public class StaticAgent implements Service{
// 被代理的接口
private Service target;
public StaticAgent() {
}
public StaticAgent(Service target) {
this.target = target;
}
public Service getTarget() {
return target;
}
public void setTarget(Service target) {
this.target = target;
}
@Override
public void sing() {
System.out.println("我是静态代理对象");
System.out.println("我在做前置的协调工作");
// 核心功能还是由接口引用被代理对象,被代理对象去执行核心功能
target.sing();
System.out.println("我在做收尾的协调工作");
}
@Override
public void show() {
System.out.println("我是静态代理对象");
System.out.println("我在做前置的协调工作");
// 核心功能还是由接口引用被代理对象,被代理对象去执行核心功能
target.show();
System.out.println("我在做收尾的协调工作");
}
}
7.JDK动态代理对象的类:JDKDynamicAgentFactory.java
JDK动态代理,不需要实现功能接口。用Proxy工具类,指定被代理的对象,在创建代理实例时,获取被代理对象调用的方法,进行封装强化。
优点是:实现了动态代理,当代理对象功能拓展后,不需要再修改代码
缺点是:实现被代理功能强化,是通过接口
去引用根据被代理对象创建的实例,调用方法实现的,因此,只能代理那些被代理对象实现了接口的功能,无法代理被代理对象自己的一些方法(非接口中的方法)
package cn.kbkwwk.mybatis.dynamic;
import cn.kbkwwk.mybatis.service.api.Service;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* ClassName: JDKDynamicAgentFactory
* Package: cn.kbkwwk.mybatis.dynamic
* Description:
*
* @Date: 2022-05-30 11:30 星期一
* @Author: Kevin
*/
public class JDKDynamicAgentFactory {
// 被代理的接口
private Service target;
public JDKDynamicAgentFactory() {
}
public JDKDynamicAgentFactory(Service target) {
this.target = target;
}
public Service getTarget() {
return target;
}
public void setTarget(Service target) {
this.target = target;
}
/**
* 获取JDK代理对象
*/
public Object getJDKProxyAgent(){
Class<? extends Service> clazz = target.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("我是JDK动态代理对象");
System.out.println("我在做前置的协调工作");
// Object proxy为被代理的对象本身,如果这里填proxy,会递归
Object methodValue = method.invoke(target,args);
System.out.println("我在做收尾的协调工作");
return methodValue;
}
});
}
}
8.CGLib动态代理对象的类:CGLibDynamicAgentFactory.java
CGBLib动态代理,引入了spring-core依赖,里面包含了需要的cglib-jar
又称为子类代理
,可以简单大致理解为继承重写父类方法,以此实现功能的强化
相比于JDK动态代理,cglib动态代理需要导入依赖
优点是:可以代理被代理对象的所有非final/static方法
package cn.kbkwwk.mybatis.dynamic;
import cn.kbkwwk.mybatis.service.api.Service;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* ClassName: CGLibDynamicAgentFactory
* Package: cn.kbkwwk.mybatis.dynamic
* Description:
* 需要引入spring-core依赖
*
* @Date: 2022-05-30 12:10 星期一
* @Author: Kevin
*/
public class CGLibDynamicAgentFactory implements MethodInterceptor {
private Service target;
public CGLibDynamicAgentFactory(Service target) {
this.target = target;
}
public Service getTarget() {
return target;
}
public void setTarget(Service target) {
this.target = target;
}
public CGLibDynamicAgentFactory() {
}
/**
* 拦截并重写父类的方法,强化代理功能
*
* @param proxy
* @param method
* @param args
* @param methodProxy
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("我是JDK动态代理对象");
System.out.println("我在做前置的协调工作");
// Object proxy为被代理的对象本身,如果这里填proxy,会递归
Object methodValue = method.invoke(target, args);
System.out.println("我在做收尾的协调工作");
return methodValue;
}
/**
* 创建并返回子类代理对象
*
* @return
*/
public Object getCGLibDynamicAgent() {
// 使用工具类
Enhancer en = new Enhancer();
// 设置父类
en.setSuperclass(target.getClass());
// 设置回调函数,调用的是intercept拦截并强化后的方法
en.setCallback(this);
// 创建并返回子类代理对象
return en.create();
}
}
9.测试类:TestStaticStaticAgent.java
package cn.kbkwwk.mybatis;
import cn.kbkwwk.mybatis.dynamic.CGLibDynamicAgentFactory;
import cn.kbkwwk.mybatis.dynamic.JDKDynamicAgentFactory;
import cn.kbkwwk.mybatis.dynamic.StaticAgent;
import cn.kbkwwk.mybatis.service.api.Service;
import cn.kbkwwk.mybatis.service.impl.SuperStarLiu;
import cn.kbkwwk.mybatis.service.impl.SuperStarZhou;
import org.junit.Test;
/**
* ClassName: TestStaticStaticAgent
* Package: cn.kbkwwk.mybatis
* Description:
*
* @Date: 2022-05-30 3:04 星期一
* @Author: Kevin
*/
public class TestStaticStaticAgent {
@Test
public void testStaticAgent(){
System.out.println("测试一:");
// 创建接口的实现类对象,即被代理对象
Service liu = new SuperStarLiu();
// 因为代理对象实现了Service接口,所以new出来的代理对象不需要再强转类型
Service agent = new StaticAgent(liu);
// 调用代理对象封装后的代理功能
agent.sing();
Service zhou = new SuperStarZhou();
// 当需要被代理的对象换了,不再需要修改代理功能封装的方法,只需要将被代理对象传给代理对象
Service agent1 = new StaticAgent(zhou);
agent1.sing();
}
@Test
public void testStaticAgent1(){
System.out.println("测试二:");
// 接口类型引用实现的子类对象,只能调用实现了接口中指定的方法,子类自己的方法(没有在接口中声明),无法通过接口引用调用
// 当代理对象不使用接口类型引用时,功能会更加强大
StaticAgent agent = new StaticAgent();
agent.setTarget(new SuperStarLiu());
agent.sing();
agent.setTarget(new SuperStarZhou());
agent.show();
}
@Test
public void testJDKDynamicAgent(){
System.out.println("测试三:");
// 创建代理对象工厂
JDKDynamicAgentFactory agentFactory = new JDKDynamicAgentFactory();
// 创建被代理对象
SuperStarLiu liu = new SuperStarLiu();
// 设置需要代理的对象
agentFactory.setTarget(liu);
// 用工厂对象创建创建代理对象
// 注意返回的是Object类型,因此如果要使用被代理对象的方法,只需要强转成实现的接口类型,就可以使用被代理对象中的功能
Service jdkProxyAgent = (Service)agentFactory.getJDKProxyAgent();
jdkProxyAgent.sing();
SuperStarZhou zhou = new SuperStarZhou();
agentFactory.setTarget(zhou);
Service jdkProxyAgent1 = (Service) agentFactory.getJDKProxyAgent();
jdkProxyAgent1.show();
}
@Test
public void testCGLibDynamicAgent(){
System.out.println("测试四:");
// 创建CGLib代理工程类
CGLibDynamicAgentFactory cgLibDynamicAgentFactory = new CGLibDynamicAgentFactory();
// 设置需要被代理的对象
cgLibDynamicAgentFactory.setTarget(new SuperStarLiu());
// 根据工厂对象获取CGLib代理对象,并强转为被代理对象的类类型
SuperStarLiu agent = (SuperStarLiu) cgLibDynamicAgentFactory.getCGLibDynamicAgent();
agent.show();
cgLibDynamicAgentFactory.setTarget(new SuperStarZhou());
SuperStarZhou agent1 = (SuperStarZhou) cgLibDynamicAgentFactory.getCGLibDynamicAgent();
agent1.show();
//
agent1.dance();
}
}
10.运行测试程序的结果
测试一:
测试一:
我是静态代理对象
我在做前置的协调工作
我是刘德华,我在唱歌...
我在做收尾的协调工作
--------------------------------
我是静态代理对象
我在做前置的协调工作
我是周杰伦,我在唱歌...
我在做收尾的协调工作
--------------------------------
测试二:
测试二:
我是静态代理对象
我在做前置的协调工作
我是刘德华,我在唱歌...
我在做收尾的协调工作
--------------------------------
我是静态代理对象
我在做前置的协调工作
我是周杰伦,我在表演...
我在做收尾的协调工作
--------------------------------
测试三:
测试三:
我是JDK动态代理对象
我在做前置的协调工作
我是刘德华,我在唱歌...
我在做收尾的协调工作
--------------------------------
我是JDK动态代理对象
我在做前置的协调工作
我是周杰伦,我在表演...
我在做收尾的协调工作
--------------------------------
测试四:
测试四:
我是CGLib动态代理对象
我在做前置的协调工作
我是刘德华,我在表演...
我在做收尾的协调工作
--------------------------------
我是CGLib动态代理对象
我在做前置的协调工作
我是周杰伦,我在表演...
我在做收尾的协调工作
--------------------------------
我是CGLib动态代理对象
我在做前置的协调工作
我是周杰伦,我在跳舞...
我在做收尾的协调工作
--------------------------------