Spring学习之AOP的动态代理

        AOP的全称是Aspect-Oriented Programming,即面向切面编程(也称面向方面编程)。它是面向对象编程(OOP)的一种补充,目前已成为一种比较成熟的编程方式。
        在学习使用AOP之前,我们先了解一下AOP的专业术语。见下表:

术语解释
Aspect(切面)封装的用于横向插入系统功能(如事务、日志等)的类。该类要被Spring容器识别为切面,需要在配置文件通过<bean>元素指定。
Joinpoint(连接点)在程序执行过程中的某个阶段点。在AOP中,连接点就是指方法的调用。
Pointcut(切入点)切面与程序流程的交叉点,即那些需要处理的连接点,如下图所示。
Advice(通知 / 增强处理)AOP框架在特定的切入点执行的增强处理,即在定义好的切入点处所要执行的程序代码。
Target Object(目标对象)指所有被通知的对象,也称为被增强对象。
Proxy(代理)将通知应用到目标对象之后,被动态创建的对象。
Weaving(织入)将切面代码插入到目标对象上,从而生成代理对象的过程。

                
                                                                切面、连接点和切入点

        Spring AOP使用了两种代理机制:一种是基于JDK的动态代理,一种是基于CGLIB的动态代理。
        1.JDK动态代理

         JDK动态代理是通过java.lang.reflect.Proxy 类来实现的,我们可以调用Proxy类的newProxyInstance()方法来创建代理对象。
        我们通过案例来演示Spring中JDK动态代理的实现过程。
           (1).打开Eclipse,在SpringTest项目的src目录下,创建一个com.example.jdk包,包中创建用户管理接口UserDao接口。

package com.example.jdk;

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

           (2).打开Eclipse,在SpringTest项目的src目录下,在com.example.jdk包,创建UserDao接口的实现类UserDaoImpl类。

package com.example.jdk;

public class UserDaoImpl implements UserDao{

 @Override
 public void addUser() {
  // TODO Auto-generated method stub
  System.out.println("添加用户");
 }
 
 @Override
 public void deleteUser() {
  // TODO Auto-generated method stub
  System.out.println("删除用户");
 }
 
}

           (3).打开Eclipse,在SpringTest项目的src目录下,在com.example.jdk包,创建切面类MyAspect类。

package com.example.jdk;
public class MyAspect {
 public void startAffair(){
  System.out.println("开始事务");
 }
 public void stopAffair(){
  System.out.println("停止事务");
 }
}

           (4).打开Eclipse,在SpringTest项目的src目录下,在com.example.jdk包,创建代理类JdkProxy类,该类需要实现InvocationHandler接口,并编写代理方法。在代理方法中,需要通过Proxy类实现动态代理类。
           JdkProxy实现了接口中invoke()方法,动态代理类所调用的方法都会由该方法处理。newProxyInstance()方法中包含3个参数,第1个参数是当前类的类加载器,第2个参数表示的是被代理对象实现的所有接口,第3个参数this代表的是代理类JdkProxy本身。

package com.example.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.text.SimpleDateFormat;
import java.util.Date;

public class JdkProxy implements InvocationHandler{
 private UserDao userDao;
 //创建代理方法
 public Object createProxy(UserDao userDao){
  this.userDao = userDao;
  //类加载器
  ClassLoader classLoader = JdkProxy.class.getClassLoader();
  //被代理对象实现所有接口
  Class[] classes = userDao.getClass().getInterfaces();
  //使用代理类进行增强,返回代理后的对象
  return Proxy.newProxyInstance(classLoader, classes, this);
 }
 
  /*
   *proxy 被代理后的对象
   *method 将要被执行的方法信息(反射)
   *args 执行方法时需要的参数
   */
 @Override
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  // TODO Auto-generated method stub
  MyAspect myAspect = new MyAspect();
  Date date = new Date();
  SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
  System.out.println(f.format(date));
  myAspect.startAffair();
  Object object = method.invoke(userDao, args);
  myAspect.stopAffair();
  System.out.println(f.format(date));
  return object;
 }
 
}

           (5).打开Eclipse,在SpringTest项目的src目录下,在com.example.jdk包,创建测试类JdkTest类。

package com.example.jdk;

public class JdkTest {
 public static void main(String[] args) {
  //创建代理对象
  JdkProxy jdkProxy = new JdkProxy();
  //创建目标对象
  UserDao userDao = new UserDaoImpl();
  //从代理对象中获取增强后的目标对象
  UserDao user = (UserDao) jdkProxy.createProxy(userDao);
  user.addUser();
  System.out.println("--------------------");
  user.delUser();
 }
}

           运行结果:
在这里插入图片描述

        2.CGLIB代理
        通过上面的学习可知,JDK的动态代理用起来非常简单,但它有局限,使用动态代理的对象必须实现一个或多个接口。如果想代理没有实现接口的类,那么可以使用CGLIB代理。

        CGLIB(Code Generation Library)是一个高性能开源的代码生成包,它采用非常底层的字节码技术,对指定的目标类生成一个子类,并对子类进行增强。在Spring的核心包中已经集成了CGLIB所需要的包,不需要另外导入JAR包。
        我们通过案例来演示CGLIB代理的实现过程。

           (1).打开Eclipse,在SpringTest项目的src目录下,创建一个com.example.cglib包,包中创建User类,不需要实现任何接口。

package com.example.cglib;

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

           (2).打开Eclipse,在SpringTest项目的src目录下,在com.example.cglib包,创建切面类MyAspect类。

package com.example.cglib;

public class MyAspect {
 public void startAffair(){
  System.out.println("开始事务");
 }
 public void stopAffair(){
  System.out.println("停止事务");
 }
}

           (3).打开Eclipse,在SpringTest项目的src目录下,在com.example.cglib包,创建代理类CglibProxy类,该类需要实现MethodInterceptor接口,并实现接口中的intercept()方法。

package com.example.cglib;

import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;

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

public class CglibProxy implements MethodInterceptor{
 //代理方法
 public Object createProxy(Object target){
  //创建动态类对象
  Enhancer enhancer = new Enhancer();
  //确定需要增强的类,设置其父类
  enhancer.setSuperclass(target.getClass());
  //添加回调函数
  enhancer.setCallback(this);
  //返回创建的代理类
  return enhancer.create();
 } 
 
 /*
  * arg0: CGLIB根据指定父类生成的代理的对象
  * arg1: 拦截的方法
  * arg2:拦截方法的参数数组
  * arg3: 方法的代理类,用于执行父类的方法 
  */
 @Override
 public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
  //创建切面类对象
  MyAspect myAspect = new MyAspect();
  Date date = new Date();
  SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
  System.out.println(f.format(date));
  myAspect.startAffair();
  Object object = arg3.invokeSuper(arg0, arg2);
  myAspect.stopAffair();
  System.out.println(f.format(date));
  return object;
 }
 
}

           (4).打开Eclipse,在SpringTest项目的src目录下,在com.example.cglib包,创建测试类CglibTest类。

package com.example.cglib;

public class CglibTest {
 public static void main(String[] args) {
  //创建代理对象
  CglibProxy cglibProxy = new CglibProxy();
  //创建目标对象
  UserDao userDao = new UserDao();
  //获取增强后的目标对象
  UserDao user = (UserDao) cglibProxy.createProxy(userDao);
  user.addUser();
  System.out.println("--------------------");
  user.delUser();
 }
}

           运行结果:

在这里插入图片描述

        通过以上的学习,我们对Spring中的两种代理模式有了一定的了解。Spring中的AOP代理默认是使用JDK动态代理的方式,而使用ProxyFactoryBean是创建AOP代理的最基本方式。

        3.基于代理类的AOP实现
        首先,我们需要了解一下Spring的通知类型, Spring按照通知在目标类方法的连接点位置,可以分为5种类型。
        3.1 Spring的通知类型
        org.springframework.aop.MethodBeforeAdvice(前置通知)
        在目标方法执行前实施增强,可以应用于权限管理等功能。
        org.springframework.aop.AfterReturningAdvice(后置通知)
        在目标方法执行后实施增强,可以应用于关闭流、上传文件、删除临时文件等功能。
        org.aopalliance.intercept.MethodInterceptor(环绕通知)
        在目标方法执行前后实施增强,可以应用于日志、事务管理等功能。
        org.springframework.aop.ThrowsAdvice(异常抛出通知)
        在方法抛出异常后实施增强,可以应用于处理异常记录日志等功能。
        org.springframework.aop.IntroductionInterceptor(引介通知)
        在目标类中添加一些新的方法和属性,可以应用于修改老版本程序。

        3.2 ProxyFactoryBean

        ProxyFactoryBean是FactoryBean接口的实现类,FactoryBean负责实例化Bean,ProxyFactoryBean负责为其他Bean创建代理实例。

属性名称描述
target代理的目标对象。
proxyInterfaces代理要实现的接口。
proxyTargetClass是否对类进行代理(而不是对接口进行代理)。设置为true时,使用cglib代理。
interceptorNames需要织入目标的Advice。
singleton :返回的代理对象是否为单实例,默认为true(单实例)。
optimize 当设置为true时 ,强制使用cglib代理。

        3.3 代理类的AOP实现

        我们通过一个典型的环绕通知案例,演示使用ProxyFactoryBean创建AOP代理的过程。

           (1).打开Eclipse,在SpringTest项目的src目录下,创建一个com.example.proxyfactorybean包,包中创建切面类MyAspect类。

package com.example.proxyfactorybean;

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

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

public class MyAspect implements MethodInterceptor{
 public void startAffair(){
  System.out.println("开始事务");
 }
 public void stopAffair(){
  System.out.println("停止事务");
 }
 
 @Override
 public Object invoke(MethodInvocation arg0) throws Throwable {
  Date date = new Date();
  SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
  System.out.println(f.format(date));
  startAffair();
  Object object = arg0.proceed();
  stopAffair();
  System.out.println(f.format(date));
  return object;
 }
 
}

           (2).打开Eclipse,在SpringTest项目的src目录的com.example.proxyfactorybean包,创建Spring的配置文件applicationContext.xml。
           目标类直接使用JDK动态代理中的UserDaoImpl类,不再此包中编写。

<?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.xsd"> 
 
 <!-- 目标类  -->
 <bean id="userDao" class="com.example.jdk.UserDaoImpl"></bean>
 <!-- 切面类  -->
 <bean id="myAspect" class="com.example.proxyfactorybean.MyAspect"></bean>
 <!-- 代理对象  -->
 <bean id="MyProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
  <!-- 代理实现的接口  -->
  <property name="proxyInterfaces" value="com.example.jdk.UserDao"></property>
  <!-- 指定目标对象  -->
  <property name="target" ref="userDao"></property>
  <!-- 指定切面,植入环绕通知 -->
  <property name="interceptorNames" value="myAspect"></property>
  <!-- 指定代理方式 -->
  <property name="proxyTargetClass" value="true"></property>
 </bean>
</beans>

           (5).打开Eclipse,在SpringTest项目的src目录下,在com.example.proxyfactorybean包,创建测试类ProxyFactoryBeanTest类。

package com.example.proxyfactorybean;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.example.jdk.UserDao;

public class ProxyFactoryBeanTest {
 public static void main(String[] args) {
  ApplicationContext ac =
    new ClassPathXmlApplicationContext("com/example/proxyfactorybean/applicationContext.xml");
  UserDao userDao = (UserDao) ac.getBean("MyProxy");
  userDao.addUser();
  System.out.println("---------------------");
  userDao.delUser();
 }
}

           运行结果:

在这里插入图片描述
其他的通知可以修改相应的代码来实现,这里不再实现,大家自行修改。
这就是Spring中AOP的动态代理,若有错漏,欢迎指正,希望大家一起学习进步!!!!
如果转载以及CV操作,请务必注明出处,谢谢!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值