《JavaEE企业级应用开发教程(Spring+Spring MVC+Mybatis)》3.2 动态代理

(1)创建一个名为chapter03的Web项目,导入Spring框架所需JAR包到项目的lib目录中,并发布到类路径下

pom.xml

XML 复制代码
<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/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.itheima</groupId>
  <artifactId>chapter03</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>chapter03 Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>4.3.6.RELEASE</version>
    </dependency>
  </dependencies>
  <build>
    <finalName>chapter03</finalName>
  </build>
</project>

(2)在src目录下,创建一个com.itheima.jdk包,在该包下创建接口UserDao,并在该接口中编写添加和删除的方法

UserDao.java

java 复制代码
package com.itheima.jdk;

public interface UserDao {
    public void addUser();
    public void deleteUser();
}

(3)在com.itheima.jdk包中,创建UserDao接口的实现类UserDaoImpl,分别实现接口中的方法,并在每个方法中添加一条输出语句。本案例中会将实现类UserDaoImpl作为目标类,对其中的方法进行增强处理。

UserDaoImpl.java

java 复制代码
package com.itheima.jdk;

public class UserDaoImpl implements UserDao {
    public void addUser() {
        System.out.println("添加用户");
    }
    public void deleteUser() {
        System.out.println("删除用户");
    }
}

(4)在src目录下,创建一个com.itheima.aspect包,并在该包下创建切面类MyAspect,在该类中定义一个模拟权限检查的方法和一个模拟记录日志的方法,这两个方法就表示切面中的通知。

MyAspect.java

java 复制代码
package com.itheima.aspect;

public class MyAspect {
    public void check_Permissions() {
        System.out.println("模拟权限检查...");
    }
    public void log() {
        System.out.println("模拟日志记录...");
    }
}

(5)在com.itheima.jdk包下,创建代理类JdkProxy,该类需要实现InvocationHandler接口,并编写代理方法。在代理方法中,需要通过Proxy类实现动态代理。

JdkProxy.java

java 复制代码
package com.itheima.jdk;



import com.itheima.aspect.MyAspect;

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

public class JdkProxy implements InvocationHandler {
    private UserDao userDao;
    public Object createProxy(UserDao userDao) { // 创建代理方法
        this.userDao = userDao;
        ClassLoader classLoader = JdkProxy.class.getClassLoader(); //类加载器
        Class[] clazz = userDao.getClass().getInterfaces(); //被代理对象的所有接口
        return Proxy.newProxyInstance(classLoader, clazz, this); //使用代理类,进行增强,返回的是代理后的对象
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
    //proxy:被代理后的对象
    //method:将要被执行的方法
    //args:执行方法时需要的参数
        MyAspect myAspect = new MyAspect(); //声明切面
        myAspect.check_Permissions(); //前增强
        Object obj = method.invoke(userDao, args); //在目标类上调用方法
        myAspect.log(); //后增强
        return obj;
    }
}

(6)在com.itheima.jdk包中,创建测试类JdkTest。在该类中的main()方法中创建代理对象和目标对象,然后从代理对象中获得对目标对象userDao增强后的对象,最后调用该对象中的添加和删除方法

JdkTest.java

java 复制代码
package com.itheima.jdk;

public class JdkTest {
    public static void main(String[] args) {
        JdkProxy jdkProxy = new JdkProxy(); // 创建代理对象
        UserDao userDao= new UserDaoImpl(); // 创建目标对象
        UserDao userDao1 = (UserDao) jdkProxy.createProxy(userDao); //从代理对象获得增强的目标对象

        userDao1.addUser();
        userDao1.deleteUser();
    }
}

执行程序后,控制台的输出结果如图

通过一个案例来演示CGLIB代理的实现过程,具体步骤如下。

(1)在src目录下,创建一个com.itheima.cglib包,在包中创建一个目标类UserDao, UserDao不需要实现任何接口,只需定义一个添加用户的方法和一个删除用户的方法,如文件3-6所示。

UserDao.java

java 复制代码
package com.itheima.cglib;

public class UserDao { //目标类
    public void addUser() {
        System.out.println("添加用户");
    }
    public void deleteUser() {
        System.out.println("删除用户");
    }
}

(2)在com.itheima.cglib包中,创建代理类CglibProxy,该代理类需要实现MethodInterceptor接口,并实现接口中的intercept()方法。

CglibProxy.java

java 复制代码
package com.itheima.cglib;

import com.itheima.aspect.MyAspect;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;


import java.lang.reflect.Method;

public class CglibProxy implements MethodInterceptor {

    public  Object createProxy(Object target){
        Enhancer enhancer = new Enhancer(); // 创建一个动态类对象,Enhancer 是 cglib 的核心类
        enhancer.setSuperclass(target.getClass()); //确定需要增强的类,设置其父类
        enhancer.setCallback(this); //设置回调
        return enhancer.create(); //创建代理对象
    }

    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        //proxy 根据指定父类生成的代理对象
        //method 拦截的方法
        //args 拦截方法的参数数组
        //methodProxy 方法的代理对象,执行父类的方法
        MyAspect myAspect = new MyAspect(); //创建切面类对象
        myAspect.check_Permissions(); //前增强
        Object obj = methodProxy.invokeSuper(proxy, args); //在目标类上调用方法
        myAspect.log(); //后增强
        return obj;
    }
}

(3)在com.itheima.cglib包中,创建测试类CglibTest。在该类的main()方法中首先创建代理对象和目标对象,然后从代理对象中获得增强后的目标对象,最后调用对象的添加和删除方法。

CglibTest.java

java 复制代码
package com.itheima.cglib;

public class CgibTest {
    public static void main(String[] args) {
        CglibProxy cglibProxy = new CglibProxy(); //创建代理对象
        UserDao userDao = new UserDao(); //创建目标对象
        UserDao userDao1 = (UserDao)cglibProxy.createProxy(userDao); //获取增强后的目标对象

        userDao1.addUser();
        userDao1.deleteUser();
    }
}

执行程序后,控制台的输出结果如图

直接运行会报错

pom.xml

XML 复制代码
<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/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.itheima</groupId>
  <artifactId>chapter03</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>chapter03 Maven Webapp</name>
  <url>http://maven.apache.org</url>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>4.3.6.RELEASE</version>
    </dependency>

    
  </dependencies>
  <build>
    <finalName>chapter03</finalName>
    <plugins>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>exec-maven-plugin</artifactId>
        <version>3.6.2</version>
        <configuration>
          <mainClass>com.itheima.cglib.CgibTest</mainClass>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
java 复制代码
cd /Users/主机名/Project/javaee/chapter03 && CP=$(cat classpath.txt) && java --add-opens java.base/java.lang=ALL-UNNAMED -cp "target/classes:$CP" com.itheima.cglib.CgibTest

在chapter03的终端窗口运行上述代码

对ProxyFactoryBean类有了初步的了解后,接下来通过一个典型的环绕通知案例,来演示Spring使用ProxyFactoryBean创建AOP代理的过程,具体步骤如下。

(1)在核心JAR包的基础上,再向chapter03项目的lib目录中导入AOP的JAR包spring-aop-4.3.6.RELEASE.jar和aopalliance-1.0.jar

修改 pom.xml

XML 复制代码
<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/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.itheima</groupId>
  <artifactId>chapter03</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>chapter03 Maven Webapp</name>
  <url>http://maven.apache.org</url>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>4.3.6.RELEASE</version>
    </dependency>

    <!-- 添加AOP相关依赖 -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>4.3.6.RELEASE</version>
    </dependency>

    <dependency>
      <groupId>aopalliance</groupId>
      <artifactId>aopalliance</artifactId>
      <version>1.0</version>
    </dependency>
  </dependencies>
  <build>
    <finalName>chapter03</finalName>
    <plugins>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>exec-maven-plugin</artifactId>
        <version>3.6.2</version>
        <configuration>
          <mainClass>com.itheima.cglib.CgibTest</mainClass>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

(2)在src目录下,创建一个com.itheima.factorybean包,在该包中创建切面类MyAspect。由于实现环绕通知需要实现org.aopalliance.intercept. MethodInterceptor接口,所以MyAspect类需要实现该接口,并实现接口中的invoke()方法,来执行目标方法。

MyAspect.java

java 复制代码
package com.itheima.factorybean;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class MyAspect implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation mi) throws Throwable {
        check_Permissions();
        Object obj = mi.proceed();
        log();
        return obj;
    }

    public void check_Permissions(){
        System.out.println("模拟检查权限...");
    }

    public void log(){
        System.out.println("模拟记录日志...");
    }
}

(3)在com.itheima.factorybean包中,创建配置文件applicationContext.xml,并指定代理对象

在 maven 项目中,在 resources 下创建applicationContext.xml

applicationContext.xml

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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
    <!-- 目标类-->
    <bean id="userDao" class="com.itheima.jdk.UserDaoImpl" />
    <!--切面类-->
    <bean id="myAspect" class="com.itheima.factorybean.MyAspect" />
    <!--代理对象-->
    <bean id="userDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="proxyInterfaces" value="com.itheima.jdk.UserDao" /> <!--指定代理接口-->
        <property name="target" ref="userDao" /> <!--指定目标对象-->
        <property name="interceptorNames" value="myAspect" /><!--指定切面类-->
        <property name="proxyTargetClass" value="true"/><!--指定代理方式-->
    </bean>
</beans>

(4)在com.itheima.factorybean包中,创建测试类ProxyFactoryBeanTest,在类中通过Spring容器获取代理对象的实例,并执行目标方法。

ProxyFactoryBeanTest.java

java 复制代码
package com.itheima.factorybean;

import com.itheima.jdk.UserDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ProxyFactoryBeanTest {
    public static void main(String args[]){
        String xmlPath = "applicationContext.xml";
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
        UserDao userDao = (UserDao) applicationContext.getBean("userDaoProxy");

        userDao.addUser();
        userDao.deleteUser();
    }
}

执行程序后,控制台的输出结果如图

java 复制代码
 cd /Users/主机名/Project/javaee/chapter03 && export CP=$(cat classpath.txt) && java --add-opens java.base/java.lang=ALL-UNNAMED -cp "target/classes:$CP" com.itheima.factorybean.ProxyFactoryBeanTest

终端运行以上代码

相关推荐
Scott9999HH8 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
腻害兔8 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
码智社9 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海9 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖10 小时前
JDK 26 新特性详解
java·开发语言
马优晨11 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区12 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大13 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai13 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
维天说13 小时前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json