11.Spring-AOP的原理

本文最后更新于2023.11.30-17:47,某些文章具有时效性,若有错误或已失效,请在下方留言或联系涛哥

AOP

Aspect Oriented Programming:面向切面编程

OOP

Object Oriented Programming:面向对象编程

面向切面编程:基于OOP基础之上的编程思想,OOP面向的主要对象是类,而AOP面向的主要对象是切面,在日志处理,安全管理,事务管理,权限管理等方面有非常重要的作用。

将跟主要业务没有关系的公共功能代码,不改之前代码的前提下 增强到之前写好的方法的指定位置,这种编程思想叫做AOP

AOP的主要实现是 代理模式(Proxy Pattern)

什么是代理模式?

对其他对象提供一种代理以控制对这个对象的访问

两种代理

静态代理:

需要一个公共接口,被代理类 和代理类 实现同一个接口,同时 代理类

代理类中 把被代理类 作为构造方法的参数 传入。

缺点:

1)代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。

2)代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。如上的代码是只为UserManager类的访问提供了代理,但是如果还要为其他类如Department类提供代理的话,就需要我们再次添加代理Department的代理类。

步骤:

a.创建被代理类

package com.edu.dao.impl;
​
import com.edu.dao.UserDao;
​
import java.text.SimpleDateFormat;
import java.util.Date;
​
public class UserDaoImplProxy implements UserDao {
    private UserDaoImpl userDaoImpl;
​
    public UserDaoImplProxy(UserDaoImpl userDaoImpl) {
        this.userDaoImpl = userDaoImpl;
    }
​
    Date date = new Date();
​
    public void selectUser() {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("YYYY-MM-dd HH:mm ms");
        System.out.println(simpleDateFormat.format(date));
        userDaoImpl.selectUser();
    }
​
    public void addUser() {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("YYYY-MM-dd HH:mm ms");
        System.out.println(simpleDateFormat.format(date));
        userDaoImpl.addUser();
    }
​
    public void updateUser() {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("YYYY-MM-dd HH:mm ms");
        System.out.println(simpleDateFormat.format(date));
        userDaoImpl.updateUser();
    }
​
    public void deleteUser() {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("YYYY-MM-dd HH:mm ms");
        System.out.println(simpleDateFormat.format(date));
        userDaoImpl.deleteUser();
    }
}

b.通过 代理对象的有参构造

UserDao proxy = new UserDaoImplProxy(new UserDaoImpl());
proxy.addUser();
proxy.deleteUser();
proxy.selectUser();
proxy.updateUser();

动态代理:

在程序运行时运用反射机制动态创建而成

根据如上的介绍,你会发现每个代理类只能为一个接口服务,这样程序开发中必然会产生许多的代理类

所以我们就会想办法可以通过一个代理类完成全部的代理功能,那么我们就需要用动态代理

动态代理是在运行时,通过反射机制实现动态代理,并且能够代理各种类型的对象

动态代理的两种方式

1.jdk提供的 Proxy类(jdk动态代理)

步骤:

a. 创建 要被代理的对象

b.通过 Proxy.newProxyInstance(arg0,arg1,arg2) 获取 代理对象 并进行强制转换

arg0 被代理对象的 类类加载器

arg1 代理对象 要实现的接口

arg2 一个 InvocationHandler 用于在代理对象执行方法时进行增强(可以使用 匿名内部类 InvocationHandler 的实现 或者是 λ表达式)

示例

        UserService us = new UserServiceImpl();
        UserService proxy = Proxy.newProxyInstance(us.getClass.getClassLoader(),new Class[]{UserService.class},new InvocationHandler(){
            // 重写 接口方法 invoke
            //该方法的三个参数 proxy 代表代理对象,method 代表要执行的方法 args代表方法的参数
            // 返回值Object 代表  方法执行的返回值
            public Object invoke(Object proxy,Method method,Object[] args){
                object result = null;
                //method在执行 invoke时,第一个参数为被代理对象,第二个参数是 args。
                method.invoke(us,args);
                return result;
            }
        })  

方法1

 final ProductDaoImpl productDaoImpl = new ProductDaoImpl();
        ProductDao o = (ProductDao) Proxy.newProxyInstance(productDaoImpl.getClass().getClassLoader(), new Class[]{ProductDao.class}, new InvocationHandler(){
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Random random=new Random();
                float floatNumber = random.nextFloat();
                System.out.println(floatNumber);
                if (floatNumber>0.5){
                    return method.invoke(productDaoImpl,args);
                }else {
                    return null;
                }
            }
        });
        o.addProduct();
        o.deleteProduct();
        o.selectProduct();
        o.updateProduct();

 

方法2

1,创建被代理对象

package com.edu.dao.impl;
​
import com.edu.dao.ProductDao;
​
public class ProductDaoImpl implements ProductDao {
    public void selectProduct() {
        System.out.println("执行了selectProduct的方法");
    }
​
    public void addProduct() {
        System.out.println("执行了addProduct的方法");
    }
​
    public void updateProduct() {
        System.out.println("执行了updateProduct的方法");
    }
​
    public void deleteProduct() {
        System.out.println("执行了deleteProduct的方法");
    }
}

2,创建InvocationHandler

package com.edu.handler;
​
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Random;
​
public class Handler implements InvocationHandler {
    private Object target;
​
    public Handler(Object o) {
        this.target = o;
    }
​
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Random random=new Random();
        float floatNumber = random.nextFloat();
        System.out.println(floatNumber);
        if (floatNumber>0.5){
            return method.invoke(target,args);
        }else {
            return null;
        }
​
    }
}

3,代理使用

 ProductDaoImpl productDaoImpl = new ProductDaoImpl();
 ProductDao o = (ProductDao) Proxy.newProxyInstance(productDaoImpl.getClass().getClassLoader(), new Class[]{ProductDao.class}, new Handler(productDaoImpl));
        o.addProduct();
        o.deleteProduct();
        o.selectProduct();
        o.updateProduct();

2.cglib动态代理

步骤:

a.创建要被代理的对象:定义被代理类

b.定义Interceptor 实现MethodIntenceptor(用于增强方法)

*public Object **intercept**(Object object, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable;*
  • 参数Object object是被代理对象,不会出现死循环的问题。
  • 参数java.lang.reflect.Method method是java.lang.reflect.Method类型的被拦截方法。

  • 参数Object[] args是被被拦截方法的参数。

  • 参数MethodProxy proxy是CGLIB提供的MethodProxy 类型的被拦截方法。

c.创建方法拦截器

d.通过 Enhancer.create()

//父类类型 方法拦截器

方法1

final AdminDaoImpl adminDaoImpl = new AdminDaoImpl();
        AdminDao o = (AdminDao)Enhancer.create(AdminDao.class, new MethodInterceptor() {
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                long start =System.currentTimeMillis();
                Object res = methodProxy.invoke(adminDaoImpl, objects);
                long end =System.currentTimeMillis();
                System.out.println("执行了"+method.getName()+"方法耗时:"+(end-start));
                return res;
            }
        });
        o.addAdmin();
        o.deleteAdmin();
        o.selectAdmin();
        o.updateAdmin();
    }

方法2

代理对象

package com.edu.dao.impl;
​
import com.edu.dao.AdminDao;
​
public class AdminDaoImpl implements AdminDao {
    public void selectAdmin() throws InterruptedException {
//        Thread.sleep(2000);
        System.out.println("执行了selectAdmin的方法");
    }
​
    public void addAdmin() throws InterruptedException {
//        Thread.sleep(2000);
        System.out.println("执行了addAdmin的方法");
    }
​
    public void updateAdmin() throws InterruptedException {
//        Thread.sleep(2000);
        System.out.println("执行了updateAdmin的方法");
    }
​
    public void deleteAdmin() throws InterruptedException {
//        Thread.sleep(2000);
        System.out.println("执行了deleteAdmin的方法");
    }
}

方法拦截器

package com.edu.ProxyInterceptor;
​
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
​
import java.lang.reflect.Method;
​
public class Interceptor implements MethodInterceptor {
    private Object target;
​
    public Interceptor(Object o) {
        this.target = o;
    }
​
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        long start =System.currentTimeMillis();
        Object res = methodProxy.invoke(target, objects);
        long end =System.currentTimeMillis();
        System.out.println("执行了"+method.getName()+"方法耗时:"+(end-start));
        return res;
    }
}

使用代理

AdminDaoImpl adminDaoImpl = new AdminDaoImpl();
AdminDao o = (AdminDao)Enhancer.create(AdminDao.class, new Interceptor(adminDaoImpl));
o.addAdmin();
o.deleteAdmin();
o.selectAdmin();
o.updateAdmin();
  • JDK动态代理 要求被代理类 必须实现接口
  • cglib不要求被代理类实现接口,但是不能是被final修饰的类

  • java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。

  • cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

Spring的动态代理

1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP

2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP

3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换

如何强制使用CGLIB实现AOP?

(1)添加CGLIB库,SPRING_HOME/cglib/*.jar

(2)在spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class="true"/>

JDK动态代理和CGLIB字节码生成的区别?

(1)JDK动态代理只能对实现了接口的类生成代理,而不能针对类

(2)CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法  因为是继承,所以该类或方法最好不要声明成final

任务

1。创建UserDao接口 2ProductDao 接口 3AdminDao 接口

2。每个接口 有一个实现类 实现基本的增删该查功能。

3。 要求 UserDao接口的方法 使用 静态代理进行增强,记录 每次方法 执行开始的时间。

4。ProductDao 用JDK 动态代理(Proxy)进行增强,里面的 查询方法 添加方法

每次运行 产生一个随机数 随机数大与0.5的时候 ,该方法才能被执行,否则返回null

5。AdminDao 使用CGLIB 动态代理进行增强,记录每一个方法 执行耗时。

download
来源:百度网盘 | 提取码:7l9e

阅读剩余
THE END