Spring---AOP
面向切面编程(Aspect Oriennted Programing)
AOP概念
概述:面向切面编程是通过预编译和运行期间动态代理实现程序功能统一维护的一种技术。
简单来说,就是把程序重复的代码抽取出来,在需要执行的时候使用动态代理技术,在不修改源代码的基础上,对我们已有的方法进行增强。
为什么要使用AOP:利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各个部分之间的耦合降低,提高程序的重用性,同时提高了开发的效率。
作用:在程序运行期间,不修改源码对已有的方法增强
优势:1.减少重复代码;2.提高开发效率;3.维护方便。
实现方式:JDK动态代理和cglib动态代
其他相关概念:
横切关注点:影响应用多处的功能,可能横切多个对象
如权限、日志、事务。
切面(Aspect):横切关注点被编写为特殊的类,这些类称为切面。
如Log类,权限类
目标对象(Target Object):目标对象是被一个或者多个切面所通知的对象
通知(Advice):切面所做的工作(方法)。
- 前置通知:在目标对象的方法调用之前调用通知
- 后置通知:在目标对象的方法完成之后调用通知,无论方法执行成功与否
- 返回通知:在目标对象的方法执行成功之后调用通知
- 异常通知:在目标对象的方法抛出异常后进行通知
- 环绕通知:包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的方法
织入(Wearving):也叫切入,把切面连接到要切入的目标对象上,是形成代理方法的过程。
AOP的源码中用到了两种动态代理来实现拦截切入功能:jdk动态代理和cglib动态代理。
切点(Pointcut):通知(Advice)所要织入(Weaving)的具体位置。
连接点(Join Point):能够切入切面的点。连接点是一个虚拟的概念。
1.静态代理
优点:"业务类"只关注"业务逻辑",以保证重用性(代理的共有优点)
规模小:"代理对象"只受一种委托;如果"目标对象"很多,需一一代理,故不适用于大程序。
难扩展:若接口增一法,则实现类要改、代理类亦改。
2.cjlib动态代理
使用cglib开源包,将代理对象的class文件加载进来,利用字节码技术修改class文件的字节码生成子类,进而实现代理类。
<dependency> <groupId>cglib</groupId> <artifactId>cglib-nodep</artifactId> <version>3.2.2</version> </dependency>
3.jdk动态代理
代理类实现InvocationHandler接口,该接口中有个方法“invoke”,处理 "动态代理类" 的方法调用。
调用时,需要使用Proxy.newProxyInstance()方法创建代理类
代理使用时机:
Spring使用哪种方式创建代理对象?一般来说:
-
当被代理对象实现接口时,Spring使用JDK动态代理;
-
当被代理对象未实现接口时,Spring使用CGLIB对其进行代理。
注意:有些接口是不能使用JDK动态代理的,比如:Serializable
代码实现三种代理:
//卖家类(类似于工商局),实现卖家接口,才具备卖车资格 public interface Seller { void sell(); }
//汽车工厂(目标类)实现Seller接口, public class AutoFactory implements Seller{ public void sell() { System.out.println("卖新能源汽车"); }
}
/** * 静态代理类(4S店) * 目标:4S店使用静态代理,代理卖汽车厂家的车,实现收取买车前和买车后的管理费 */ public class FourShop implements Seller { private Seller realSeller; //实际卖车的人 public FourShop(Seller realSeller) { this.realSeller = realSeller; } public void sell() { System.out.println("4s店代理买车,收取管理费"); realSeller.sell(); System.out.println("4s店代理买车,收取返点"); } //测试 public static void main(String[] args) { Seller seller = new FourShop(new AutoFactory()); seller.sell(); } }
import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.Method; /** * Cglib动态代理: * 代理类一本作为一个拦截器使用,实现MethodInterceptor接口,重写intercept方法,在方法中使用Method的形参调用invoke()方法,将目标类对象作为参数传递
* Enhancer:代码增强类 */ public class CglibProxy implements MethodInterceptor { private Seller realSell; public CglibProxy(Seller realSell) { this.realSell = realSell; }
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("CglibProxy收费--before"); method.invoke(realSell); System.out.println("CglibProxy收费--after"); return null; }
//测试 public static void main(String[] args) { //Cglib动态代理,需用Enhancer增强类 Enhancer enhancer = new Enhancer(); //指定代理的类为汽车工厂 CglibProxy interceptor=new CglibProxy(new AutoFactory()); enhancer.setCallback(interceptor); //设置超类 enhancer.setSuperclass(Seller.class); //创建一个超类对象 Seller seller = (Seller)enhancer.create(); seller.sell(); } }
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * JDK动态代理,实现InvocationHandler接口和重写invoke方法 */ public class JDKProxy implements InvocationHandler { private Seller realSeller; public JDKProxy(Seller realSeller) { this.realSeller = realSeller; } // InvocationHandler(请求处理者) // invoke(...):核心方法——请求 // 集中处理 "动态代理类" 的所有方法调用 // 参数1:代理类的实例,类型如:class com.sun.proxy.$Proxy4 // 参数2:代理要执行的方法 // 参数3:方法的参数 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("JDK动态代理前"); method.invoke(realSeller); System.out.println("JDK动态代理后"); return null; } public static void main(String[] args) { Seller realSeller=new AutoFactory(); // - 参数1:ClassLoader loader ClassLoader classLoader = realSeller.getClass().getClassLoader(); // - 参数2:Class<?>[] interfaces【(被代理)接口数组(一个委托类可以实现多个接口)】 Class<?>[] interfaces = realSeller.getClass().getInterfaces(); // - 参数3:InvocationHandler jdkProxy JDKProxy jdkProxy = new JDKProxy(realSeller); Seller proxyInstance = (Seller)Proxy.newProxyInstance(classLoader, interfaces, jdkProxy); proxyInstance.sell(); } }
例:权限控制
在“查看工资”的功能中使用AOP,判断调用方是否有权限查看工资。
-
J1SalaryManager:薪资管理·接口
-
J2SalaryManagerImpl:薪资管理·实现类(目标对象)
-
J3Privilege:权限类(切面)
-
-
J5TestProxy:测试
/** * 薪资管理·接口 */ public interface J1SalaryManager { public void showSalary(); }
/** * 薪资管理·实现类(目标对象) */ public class J2SalaryManagerImpl implements J1SalaryManager { public J2SalaryManagerImpl() { System.out.println("【目标对象】" + this.getClass()); } public void showSalary() { System.out.println("【切点/连接点】showSalary()方法"); } }
/** * 权限类切面 */ public class J3Privilege { private String access;// 权限 public J3Privilege(String access) { System.out.println("【切面】" + this.getClass()); this.access = access; } public boolean judge() { System.out.println("【通知】权限判断:" + access); if ("admin".equals(access)) { return true; } return false; } }
/** * J4MyInterceptor:生成代理对象用 */ import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class J4MyInterceptor implements InvocationHandler { private J1SalaryManager target;// 目标对象 private J3Privilege privilege;// 切面(权限) public J4MyInterceptor(J1SalaryManager target, J3Privilege privilege) { this.target = target; this.privilege = privilege; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 验证权限 调用目标对象的目标方法 if (this.privilege.judge()) { method.invoke(target);// 目标方法 } else { System.out.println("没有权限查看工资"); } return null; } }
/** * JDK动态代理:测试类 */ import java.lang.reflect.Proxy; public class J5TestProxy { public static void main(String[] args) { J1SalaryManager target = new J2SalaryManagerImpl(); J3Privilege privilege = new J3Privilege("admin"); // 拦截器 J4MyInterceptor _itcpt = new J4MyInterceptor(target, privilege); // 代理 J1SalaryManager _proxy; _proxy = (J1SalaryManager) Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), _itcpt); System.out.println("【切入】生成代理对象:" + _proxy.getClass()); // 代理.调用 _proxy.showSalary(); } }