AOP是一种面向切面编程思想,也是面向对象设计(OOP)的一种延伸。
在Spring实现AOP有两种实现方式,一种是采用JDK动态代理实现,另外一种就是采用CGLIB代理实现,Spring是如何实现的在上篇已经讲到了Spring Bean的生命周期以及IOC源码解析
AOP可以做日志记录,或者事务回滚,在Spring的事务使用就是通过AOP进行事务的回滚的
JDK动态代理
这个是属于JDK提供的一种代理方式,需提供接口才能使用,主要用的类有两个:1、Proxy:这个主要是生成接口代理对象;2、InvocationHandler:反射射包下的一个接口,Proxy生成的代理接口对象,调用接口方法就会走InvocationHandler实现类的invoke() 方法
使用示例:
//创建一个接口
public class UserDao {String getUserInfo();
}
//实现一个InvocationHandler接口的实现类
public class MyInvocationHandler implements InvocationHandler {//需实现的方法//proxy 代表当前对象自己,建议不要使用,如果使用的话会反复的调用自己,而调用自己会反复走当前invoke方法,容易出现栈溢出;//method 指调用的方法Method;//args 调用方法的参数;@Overridepublic Object invoke(Object proxy, Method method, Object[] args){//....return null;}//获取接口代理对象public <T> T getProxy(CLass<T> cls){//第一个参数为类加载器,这个可以默认使用当前的AppClassLoader,即使用getClassLoader()方法接口//需创建代理对象的接口class数组,//invocationHandler的实现类对象,这里调用的是当前对象,调用的时候就会走当前对象的invoke方法return (E) Proxy.newProxyInstance(cls.getClassLoader(), new Class<?>[] {cls},this);}}public static void main(Stirng[] args){UserDao dao = new MyInvocationHandler().getProxy(UserDao.class);//这样就能获取到了UserDao接口的代理对象了
}
在Spring当中是采用 JdkDynamicAopProxy 这个类实现的,可以去源码里看一下这个类继承了InvocationHandler
CGLIB代理
这个代理就比较厉害了,是通过ASM修改 .class字节码进行实现的,并且不需要接口,具体的组成结构可以去看一下大佬写的这篇CGLIB(Code Generation Library)
使用示例:
/*** CGLIB 代理*/Enhancer enhancer = new Enhancer();enhancer.setSuperclass(SpringTest06Service.class);enhancer.setCallback(new MethodInterceptor() {public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {System.out.println(Thread.currentThread().getName()+"run start...");Object result = arg3.invokeSuper(arg0, arg2);System.out.println(Thread.currentThread().getName()+"result:"+result);System.out.println(Thread.currentThread().getName()+"run end...");return result;}});service = (SpringTest06Service) enhancer.create();System.out.println(service);String testAop = service.testAop("aaaaaaaa", "bbbbbbbbbbb");System.out.println(testAop);
在Spring里面实现的类是 ObjenesisCglibAopProxy
上面就是两种代理的实现方式,但是在Spring里面是怎么使用的呢
Spring使用AOP
需引入Spring相关的AOP的包
<dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>5.1.12.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>5.1.12.RELEASE</version></dependency>
在配置类上需开启AOP进行方法拦截,@EnableAspectJAutoProxy 启动AOP代理,这个注解有两个参数
1、proxyTargetClass:这个是指定使用JDK动态代理拦截(false)还是使用CGLIB进行拦截(true),默认为false,
2、exposeProxy:表示是否能够让AopContext访问,默认为false就是不能访问。
源码是怎么解析可以自己去看,这个注解通过使用@Import注入了一个SmartInstantiationAwareBeanPostProcessor的实现类,可以自己调试查看,这里就不具体讲了,有空就在聊聊吧
最后配置相对应的切面拦截类
@Aspect
@Component
public class AopConfig {private static final Log log = LogFactory.getLog(AopConfig.class);//切点,需指定到具体修饰符,具体方法名以及具体参数类型,这里表示所有修饰符,com.test.SpringCoreTest.test06.service这个包下的所有类的所有方法并且参数为两个的类型为String的方法进行拦截@Pointcut("execution(* com.test.SpringCoreTest.test06.service.*.*(String,String))")public void pointCut() {}//执行前,位于Around环绕方法前的后面执行@Before(value = "pointCut()")public void before() {System.out.println("方法执行前 @Before ....");}//执行后,位于Around环绕方法后的后面执行@After(value = "pointCut()")public void after() {System.out.println("方法执行后 @After ....");}//即将返回执行@AfterReturning(value = "pointCut()")public void afterReturning() {System.out.println("方法执行后返回 @AfterReturning ....");}//抛出异常执行@AfterThrowing(value="pointCut()",throwing="e")public void afterThrowing(Exception e) {System.out.println("方法执行异常后返回 @AfterThrowing ....");System.out.println(e);}//环绕方法,能够拦截执行参数,并且能够修改@Around(value = "pointCut()")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {System.out.println("around方法执行前@Around ....");Object obj = joinPoint.proceed();System.out.println("around方法执行后@Around ....");return obj;}}
配置好后,调用拦截的方法就可以触发AOP拦截了。