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
作者:涛哥
链接:https://ltbk.net/back/spring_family/spring/article/405.html
文章版权归作者所有,未经允许请勿转载。