博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring (AOP、静态代理、动态代理)
阅读量:255 次
发布时间:2019-03-01

本文共 6987 字,大约阅读时间需要 23 分钟。

1. AOP

 

1.1 AOP简介

 

  1. AOP Aspect Oriented Programing 面向切面编程

  2. AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码(性能监视、事务管理、安全检查、缓存)

  3. Spring中的Aop是纯Java来实现的,使用动态代理的方式增强代码

  4. Spring使用动态代理的机制是判断委托类是否实现了接口,如果实现了接口则使用jdk的动态代理,如果没有实现接口则使用cglib的动态代理

  5. AOP不是由Spring提出来的,是由AOP联盟定义的

 

1.2 AOP的专业术语

 

Joinpoint(连接点) :委托类中可以被增强的方法

Pointcut(切入点) :切点 ,要被增强的方法

Advice(通知/增强) :增强的代码

Target(目标对象) :委托对象

Weaving(织入) :增强应用切点的过程

Proxy(代理): 一个类被AOP织入增强后,就产生一个结果代理类

Aspect(切面): 切点通知的结合

 

1.3 Spring中的AOP实现

1.3.1 传统的Spring的AOP

一个切点和一个通知的组合

1.3.2 基于Aspectj的AOP

  1. AspectJ是一个基于Java语言的面向切面的AOP框架

  2. Spring2.0以后新增了对AspectJ切点表达式支持

  3. @AspectJ 是AspectJ1.5新增功能,通过JDK5注解技术,允许直接在Bean类中定义切面

  4. 新版本Spring框架,建议使用AspectJ方式来开发AOP

1.3.3 Aspectj的切点表达式

  • 语法:execution(表达式)

  • execution(<访问修饰符>?<返回类型><方法名>(<参数>)<异常>)

  • public * *(..) ---检索所有的public方法

  • execution(“* cn.uplooking.spring4.demo1.dao.*(..)”) ---只检索当前包

  • execution(“* cn.uplooking.spring4.demo1.dao..*(..)”) ---检索包及当前包的子包.

 

1.3.4 Aspect的增强类型

  • @Before 前置通知,相当于BeforeAdvice

@Before("execution(* com.uplooking.aop.UserDao.add*(..))")public void beforeAdvice() {System.out.println("前置通知....");}
  • @AfterReturning 后置通知,相当于AfterReturningAdvice

@AfterReturning(value = "execution(* com.uplooking.aop.UserDao.add*(..))", returning = "ret")public void afterReturningAdvice(String ret) {System.out.println("后置通知.." + ret);}
  • @Around 环绕通知,相当于MethodInterceptor

@Around("execution(* com.uplooking.aop.UserDao.add*(..))")public void arounrAdvice(ProceedingJoinPoint pjp) throws Throwable {System.out.println("环绕通知前..");pjp.proceed();System.out.println("环绕通知后..");}
  • @AfterThrowing抛出通知,相当于ThrowAdvice

@AfterThrowing("execution(* com.uplooking.aop.UserDao.add*(..))")public void throwAdvice() {System.out.println("异常通知....");}
  • @After 最终final通知,不管是否异常,该通知都会执行

@After(value = "execution(* com.uplooking.aop.UserDao.add*(..))")public void afterAdvice() {System.out.println("最终通知....");}

 

1.4 AOP的编程(AspectJ)

 

1.4.1 导入pom依赖

org.springframework
spring-context
4.3.12.RELEASE
org.springframework
spring-aspects
4.3.12.RELEASE
org.springframework
spring-test
4.3.12.RELEASE
junit
junit
4.12

 

引入约束

1.4.2(前置通知)

1.4.2.1 要被增强的类

package com.ma.aop;import org.springframework.stereotype.Repository;@Repositorypublic class UserDao {public void addUser(){System.out.println("添加用户...");}public void deleteUser(){System.out.println("删除用户...");}public void updateUser(){System.out.println("修改用户...");}public void selectUser(){System.out.println("查找用户...");}}

1.4.2.2 定义切面

package com.ma.aop;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.springframework.stereotype.Component;/*** 切面 = 切点(切点表达式)+ 通知 (方法)*/@Aspect@Componentpublic class MyAspect {@Before("execution(* com.ma.aop.UserDao.addUser())")public void beforeAdvice(){System.out.println("前置通知...");}}

1.4.2.3测试

import com.ma.aop.UserDao;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringRunner;@RunWith(SpringRunner.class)@ContextConfiguration("classpath:applicationContext.xml")public class TestUserDao {@Autowiredprivate UserDao userDao;@Testpublic void testAdd(){userDao.addUser();}}

 

1.4.2.5结果

 

1.4.3(后置通知)

 

1.4.3.1 要被增强的方法

package com.ma.aop;import org.springframework.stereotype.Repository;@Repositorypublic class UserDao {public void addUser(){System.out.println("添加用户...");}public String deleteUser(){System.out.println("删除用户...");return "已删除!";}public void updateUser(){System.out.println("修改用户...");}public void selectUser(){System.out.println("查找用户...");}}

1.4.3.2 定义切面

@AfterReturning(value = "execution(* com.ma.aop.UserDao.delete*())",returning = "ret")public void afterReturningAdvice(String ret){System.out.println("后置通知..."+"ret");}

注意:可以得到返回值。

 

1.4.3.3 测试

@Testpublic void testDelete(){userDao.deleteUser();}

 

1.4.3.4 结果

 

1.4.4(环绕通知)

 

1.4.4.1 要被增强的方法

package com.ma.aop;import org.springframework.stereotype.Repository;@Repositorypublic class UserDao {public void addUser(){System.out.println("添加用户...");}public String deleteUser(){System.out.println("删除用户...");return "已删除!";}public void updateUser(){System.out.println("修改用户...");}public void selectUser(){System.out.println("查找用户...");}}

1.4.4.2 定义切面

@Around("execution(* com.ma.aop.UserDao.update*())")public void afterReturningAdvice(){System.out.println("环绕通知...");}

注意:此时环绕通知可以阻止目标方法的执行,需要对目标方法进行放行。

@Around("execution(* com.ma.aop.UserDao.update*())")public void afterReturningAdvice(ProceedingJoinPoint pjp) throws Throwable {System.out.println("环绕通知前...");pjp.proceed();System.out.println("环绕通知后...");}

 

 

1.4.4.3 测试

@Testpublic void testUpdate(){userDao.updateUser();}

 

1.4.4.4 效果

 

1.4.5(异常通知)

 

1.4.5.1 要被增强的方法

package com.ma.aop;import org.springframework.stereotype.Repository;@Repositorypublic class UserDao {public void addUser(){System.out.println("添加用户...");}public String deleteUser(){System.out.println("删除用户...");return "已删除!";}public void updateUser(){System.out.println("修改用户...");}public void selectUser(){int i = 10/0;System.out.println("查找用户...");}}

1.4.5.2 定义切面

@AfterThrowing("execution(* com.ma.aop.UserDao.selectUser())")public void throwAdvice(){System.out.println("异常通知...");}

 

 

1.4.5.3 测试

@Testpublic void testSelect(){userDao.selectUser();}

 

1.4.5.4 效果

 

2.代理模式

不使用对象的真实操作,使用我们自己创建的代理对象来操作

 

2.1 静态代理

委托类和代理类共同实现同一个接口,在代理类中有委托类的对象。

1、公共接口

package com.ma.aop.agent;public interface WindWomen {String say();}

 

 

2、委托类

package com.ma.aop.agent;/*** 委托类*/public class Pan implements WindWomen {public String say() {return "晚上十点小河边见......";}}

3、代理类

package com.ma.aop.agent;public class Wang implements WindWomen {private WindWomen windWomen;//委托对象public Wang(WindWomen windWomen){/*** 在代理的构造器中持有一个委托的对象*/this.windWomen = windWomen;}public String say() {System.out.println("她找我让我给你带个话:");System.out.println(windWomen.say());System.out.println("记得啊!");return null;}}

 

4、测试类

package com.ma.aop.agent;public class AgentTest {public static void main(String[] args) {Pan pan = new Pan();Wang wang = new Wang(pan);wang.say();}}

5、效果

 

 

 

 

2.1动态代理

 

2.2.1 基于原生的JDK的动态代理

package com.ma.aop.agent;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class AgentTest {public static void main(String[] args) {final Pan pan = new Pan();WindWomen windWomen = (WindWomen) Proxy.newProxyInstance(AgentTest.class.getClassLoader(), Pan.class.getInterfaces(), new InvocationHandler() {public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("她找我让我给你带个话:");Object object = method.invoke(pan,args);System.out.println("记得啊!");return object;}});windWomen.say();}}

实现

 

 

 

效果

2.2.2 基于CGLIB的动态代理

因为原生的jdk的动态代理存在缺陷,代理类和委托类必须实现同一个接口

所以有个开源的动态代理框架出现了(CGLIB)

CGLIB不要求委托类必须实现接口:因为CGLIB底层是基于继承实现的

 

 

 

 

 

 

 

你可能感兴趣的文章