1. Spring AOP
-
代理模式
设计目的旨在为服务类与客户类之间插入其他功能,插入的功能对于调用者是透明的,起到伪装控制的作用。 如租房的例子:房客、中介、房东。 对应于代理模式中即:客户类、代理类 、委托类(被代理类)。 代理模式的两个设计原则: 1.代理类与委托类具有相似的行为(共同)。 2.代理类增强委托类的行为。 1.静态代理 三要素: 1.有共同的行为(结婚) - 接口 2.目标角色(新人) - 实现行为 3.代理角色(婚庆公司) - 实现行为 增强目标对象行为 特点: 1.目标角色固定。 2.在应用程序执行前就得到目标角色。 3.代理对象会增强目标对象的行为。 4.有可能存在多个代理 引起"类爆炸"(缺点)。 实现: 1.定义行为(共同) 定义接口。 2.目标对象(实现行为)。 3.代理对象(实现行为、增强目标对象的行为)。 4.通过代理对象实现目标对象的功能。 2.动态代理 动态代理类的字节码在程序运行时,由Java反射机制动态产生。 动态代理不仅简化了编程工作,而且提高了软件系统的可扩展性,因为反射机制可以生成任意类型的动态代理类。 特点: 1.目标对象不固定 。 2.在应用程序执行时动态创建目标对象 。 3.代理对象会增强目标对象的行为。 实现: 1.JDK动态代理(JDK动态代理的目标对象必须有接口实现) 1.newProxyInstance Proxy类: Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类,此类提供了如下操作方法: /*返回一个指定接口的代理类的实例方法调用分派到指定的调用处理程序。 (返回代理对象) loader:一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载 。 interfaces:一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口, 如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态), 这样我就能调用这组接口中的方法了 。 h:一个InvocationHandler接口,表示代理实例的调用处理程序实现的接口。每个代理实例都具有一个关联的调用处理程序。 对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的 invoke 方法(传入InvocationHandler接口的子类)。*/ public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 2.获取代理对象 public class JdkHandler implements InvocationHandler { //目标对象 private Object target; // 目标对象的类型不固定,创建时动态生成 //通过构造器将目标对象赋值 public JdkHandler(Object target) { this.target = target; } /*** 1、调用目标对象的方法(返回Object) * 2、增强目标对象的行为 * @param proxy 调用该方法的代理实例 * @param method 目标对象的方法 * @param args 目标对象的方法形参 * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //增强行为 System.out.println("==============方法前执行"); //调用目标对象的方法(返回Object) Object result = method.invoke(target,args); //增强行为 System.out.println("方法后执行=============="); return result; } /*** 得到代理对象 * @return */ public Object getProxy() { return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInt erfaces(),this); } } 3.通过代理对象实现目标对象的功能 //目标对象 You you = new You(); //获取代理对象 JdkHandler jdkHandler = new JdkHandler(you); Marry marry = (Marry) jdkHandler.getProxy(); //通过代理对象调用目标对象中的方法 marry.toMarry(); Java动态代理类中的invoke是怎么调用的: 在生成的动态代理类$Proxy0.class中,构造方法调用了父类Proxy.class的构造方法,给成员变量 invocationHandler赋值, $Proxy0.class的static模块中创建了被代理类的方法,调用相应方法时方法体中调用了父类中的成员变量InvocationHandler的invoke()方法。 2.CGLIB动态代理 cglib是针对类来实现代理的,它的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强, 但因为采用的是继承,所以不能对final修饰的类进行代理。 1.添加依赖 pom.xml: <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.2.2</version> </dependency> 2.定义类 实现MethodInterceptor接口 public class CglibInterceptor implements MethodInterceptor { //目标对象 private Object target; //通过构造器传入目标对象 public CglibInterceptor(Object target) { this.target = target; } /*** 获取代理对象 * @return */ public Object getProxy() { //通过Enhancer对象的create()方法可以生成一个类,用于生成代理对象 Enhancer enhancer = new Enhancer(); //设置父类 (将目标类作为其父类) enhancer.setSuperclass(target.getClass()); //设置拦截器 回调对象为本身对象 enhancer.setCallback(this); //生成一个代理类对象,并返回 return enhancer.create(); } /*** 拦截器 * 1、目标对象的方法调用 * 2、增强行为 * @param object 由CGLib动态生成的代理类实例 * @param method 实体类所调用的被代理的方法引用 * @param objects 参数值列表 * @param methodProxy 生成的代理类对方法的代理引用 * @return * @throws Throwable */ @Override public Object intercept(Object object, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { //增强行为 System.out.println("==============方法前执行"); //调用目标对象的方法(返回Object) Object result = methodProxy.invoke(target,objects); //增强行为 System.out.println("方法后执行=============="); return result; } } 3.调用方法 //目标对象 You you = new You(); CglibInterceptor cglibInterceptor = new CglibInterceptor(you); Marry marry = (Marry) cglibInterceptor.getProxy(); marry.toMarry(); User user = new User(); CglibInterceptor cglibInterceptor = new CglibInterceptor(user); User u = (User) cglibInterceptor.getProxy(); u.test(); JDK代理与CGLIB代理的区别: 1.JDK动态代理实现接口,Cglib动态代理继承思想。 2.JDK动态代理(目标对象存在接口时)执行效率高于Ciglib。 3.如果目标对象有接口实现,选择JDK代理,如果没有接口实现选择Cglib代理。
-
AOP
Aspect Oriented Programing面向切面编程,相比较oop面向对象编程来说,Aop关注的不再是程序代码中某个类,某些方法, 而aop考虑的更多的是一种面到面的切入,即层与层之间的一种切入,所以称之为切面。 作用: AOP主要应用于日志记录,性能统计,安全控制,事务处理等方面,实现公共功能性的重复使用。 特点: 1.降低模块与模块之间的耦合度,提高业务代码的聚合度。(高内聚低 耦合) 2.提高了代码的复用性。 3.提高系统的扩展性。(高版本兼容低版本) 4.可以在不影响原有的功能基础上添加新的功能。 底层实现: 动态代理(JDK + CGLIB) 基本概念: 1.Joinpoint(连接点) 被拦截到的每个点,spring中指被拦截到的每一个方法,spring aop一个连接点即代表一个方法的执行。 2.Pointcut(切入点) 对连接点进行拦截的定义(匹配规则定义 规定拦截哪些方法,对哪些方法进行处理),spring有专门的表达式语言定义。 3.Advice(通知) 拦截到每一个连接点即(每一个方法)后所要做的操作。 1.前置通知 (前置增强)— before() 执行方法前通知 。 2.返回通知(返回增强)— afterReturn 方法正常结束返回后的通知 3.异常抛出通知(异常抛出增强)— afetrThrow() 4.最终通知 — after 无论方法是否发生异常,均会执行该通知。 5.环绕通知 — around 包围一个连接点(join point)的通知,如方法调用。 这是最强大的一种通知类型。 环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它们自己的 返回值或抛出异常来结束执行。 4.Aspect(切面) 切入点与通知的结合,决定了切面的定义, 切入点定义了要拦截哪些类的哪些方法, 通知则定义了拦截过方法后要做什么, 切面则是横切关注点的抽象,与类相似,类是对物体特征的抽象,切面则是横切关注点抽象。 5.Target(目标对象) 被代理的目标对象。 6.Weave(织入) 将切面应用到目标对象并生成代理对象的这个过程即为织入。 7.Introduction(引入) 在不修改原有应用程序代码的情况下,在程序运行期为类动态添加方法或者字段的过程称为引入。
-
Spring AOP的实现
环境搭建: 坐标依赖引入: <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.9</version> </dependency> 添加spring.xml的配置: xmlns:aop="http://www.springframework.org/schema/aop" http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd 1.注解实现: 1.定义切面: @Component 将对象交给IOC容器去实例化 @Aspect 声明当前类是一个切面 切入点:匹配规则。规定什么方法被拦截、需要处理什么方法 定义切入点@Pointcut("匹配规则") Aop切入点表达式简介 1.执行任意公共方法:execution(public *(..)) 2.执行任意的set方法:execution(* set*(..)) 3.执行com.xxxx.service包下任意类的任意方法:execution(* com.xxxx.service.*.*(..)) 4.执行com.xxxx.service包以及子包下任意类的任意方法:execution(* com.xxxx.service..*.*(..)) 注意:表达式中的第一个* 代表的是方法的修饰范围 。 可选值:private、protected、public (* 表示所有范围) @Before(value = "方法名()") 声明前置通知,并将通知应用到定义的切入点上 目标类方法执行前,执行该通知 @AfterReturning(value = "方法名()") 声明返回通知,并将通知应用到定义的切入点上 目标类方法(无异常)执行后,执行该通知 @After(value = "方法名()") 声明最终通知,并将通知应用到定义的切入点上 目标类方法(无异常或有异常)执行后,执行该通知 @AfterThrowing(value = "方法名()") 声明异常通知,并将通知应用到定义的切入点上 目标类方法出现异常时,执行该通知 @Around(value = "方法名()") 声明环绕通知,并将通知应用到切入点上 方法执行前后 通过环绕通知定义相应处理 public Object around(ProceedingJoinPoint pjp) { Object object = null; object = pjp.proceed(); } 2.配置文件(spring.xml): <!--配置AOP代理--> <aop:aspectj-autoproxy/> 2.XML实现: 1.定义切面 @Component 将对象交给IOC容器去实例化 直接使用方法声明通知 public Object around(ProceedingJoinPoint pjp) { Object object = null; object = pjp.proceed(); } 2.配置文件(spring.xml): <!--aop相关配置--> <aop:config> <!--aop切面--> <aop:aspect ref="logCut02"> <!-- 定义aop 切入点 --> <aop:pointcut id="cut" expression="execution(* com.xxxx.service..*.* (..))"/> <!-- 配置前置通知 指定前置通知方法名 并引用切入点定义 --> <aop:before method="before" pointcut-ref="cut"/> <!-- 配置返回通知 指定返回通知方法名 并引用切入点定义 --> <aop:after-returning method="afterReturn" pointcut-ref="cut"/> <!-- 配置异常通知 指定异常通知方法名 并引用切入点定义 --> <aop:after-throwing method="afterThrow" throwing="e" pointcut- ref="cut"/> <!-- 配置最终通知 指定最终通知方法名 并引用切入点定义 --> <aop:after method="after" pointcut-ref="cut"/> <!-- 配置环绕通知 指定环绕通知方法名 并引用切入点定义 --> <aop:around method="around" pointcut-ref="cut"/> </aop:aspect> </aop:config>