《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

终端运行以上代码

相关推荐
敲代码的嘎仔29 分钟前
LeetCode面试HOT100—— 206. 反转链表
java·数据结构·学习·算法·leetcode·链表·面试
froginwe1130 分钟前
CSS Text(文本)详解
开发语言
雨中飘荡的记忆30 分钟前
设计模式之适配器模式详解
java·设计模式·适配器模式
繁华似锦respect32 分钟前
C++ 自定义 String 类
服务器·开发语言·c++·哈希算法·visual studio
客梦33 分钟前
数据结构-图结构
java·数据结构·笔记
世界尽头与你34 分钟前
CVE-2020-1938_ Apache Tomcat AJP 文件读取与包含漏洞
java·网络安全·渗透测试·tomcat·apache
n***44334 分钟前
Java进阶:IO大全
java·开发语言·python
Seven9735 分钟前
剑指offer-45、扑克牌顺⼦
java
程序员欣宸36 分钟前
LangChain4j实战之一:准备工作
java·ai编程