AOP
AOP(Aspect Orient Programming),一般称为面向切面编程,作为面向对象的一种补充,用于处理系统中分布于各个模块的横切关注点,比如事务管理、日志、缓存等等。AOP实现的关键在于AOP框架自动创建的AOP代理,AOP代理主要分为静态代理和动态代理,静态代理的代表为AspectJ;而动态代理则以Spring AOP为代表,包括JDK动态代理和CGLIB动态代理。静态代理是编译期实现,动态代理是运行期实现,可想而知前者拥有更好的性能。
静态代理是编译阶段生成AOP代理类,也就是说生成的字节码就织入了增强后的AOP对象;动态代理则不会修改字节码,而是在内存中临时生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。
Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理。JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK动态代理的核心是InvocationHandler接口和Proxy类。
如果目标类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类的子类,注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的,诸如private的方法也是不可以作为切面的。
静态代理
静态代理不常使用,不做介绍,我们聊一下AspectJ
简单地说, AspectJ和 Spring AOP 有不同的目标。Spring aop 旨在提供一个跨 Spring IoC 的简单的 aop 实现, 以解决程序员面临的最常见问题。它不打算作为一个完整的 AOP 解决方案 —— 它只能应用于由 Spring 容器管理的 bean。而AspectJ 是原始的 aop 技术, 目的是提供完整的 aop 解决方案。它更健壮, 但也比 Spring AOP 复杂得多。还值得注意的是, AspectJ 可以在所有域对象中应用。
动态代理
分为JDK动态代理与CGLib动态代理
JDK动态代理
利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生成一个实现代理接口的匿名类,再调用具体方法前调用InvokeHandler来处理。
CGLiB动态代理
利用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
何时使用JDK还是CGLiB
- 1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP。
- 2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP。
- 3、如果目标对象没有实现了接口,必须采用CGLIB库,Spring会自动在JDK动态代理和CGLIB之间转换。
如何强制使用CGLIB实现AOP?
- 1、添加CGLIB库(aspectjrt-xxx.jar、aspectjweaver-xxx.jar、cglib-nodep-xxx.jar)
- 2、在Spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class="true"/>
JDK动态代理和CGLIB字节码生成的区别,为什么jdk动态代理必须基于接口?
根本原因是通过 JDK 动态代理生成的类已经继承了 Proxy 类,所以无法再使用继承的方式去对类实现代理。
- 1、JDK 动态代理本质上是实现了被代理对象的接口,而 Cglib 本质上是继承了被代理对象,覆盖其中的方法。
- 2、JDK 动态代理只能对实现了接口的类生成代理,Cglib 则没有这个限制。但是 Cglib 因为使用继承实现,所以 Cglib 无法代理被 final 修饰的方法或类。
- 3、在调用代理方法上,JDK 是通过反射机制调用,Cglib是通过字节码处理框架ASM,将代理对象类的class文件加载进来,通过修改其字节码生成子类。
JDK动态代理和CGLIB效率对比
JDK1.7 之前,由于使用了 FastClass 机制,Cglib 在执行效率上比 JDK 快,但是随着 JDK 动态代理的不断优化,从 JDK 1.7 开始,JDK 动态代理已经明显比 Cglib 更快了。
代码验证
项目整体结构
OrderService接口
package com.yj.aop.anno.service;
public interface OrderService {
public abstract int addOrder();
public abstract int updateOrder();
public abstract int deleteOrder();
}
OrderServiceImpl类
package com.yj.aop.anno.service;
import org.springframework.stereotype.Service;
import com.yj.aop.anno.MyPointCut;
@Service("orderService")
public class OrderServiceImpl implements OrderService {
@Override
@MyPointCut
public int addOrder() {
System.out.println("addOrder");
return 1;
}
@Override
@MyPointCut
public int updateOrder() {
System.out.println("updateOrder");
int i = 1 / 0;
return 1;
}
@Override
@MyPointCut
public int deleteOrder() {
System.out.println("deleteOrder");
return 1;
}
}
UserService类
package com.yj.aop.anno.service;
import org.springframework.stereotype.Service;
import com.yj.aop.anno.MyPointCut;
@Service
public /*final*/ class UserService {
@MyPointCut
public int addUser() {
System.out.println("addUser");
return 1;
}
@MyPointCut
public int updateUser() {
System.out.println("updateUser");
int i = 1 / 0;
return 1;
}
@MyPointCut
public int deleteUser() {
System.out.println("deleteUser");
return 1;
}
}
Init类
package com.yj.aop.anno;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import com.yj.aop.anno.service.OrderServiceImpl;
import com.yj.aop.anno.service.UserService;
import com.yj.util.AopTargetUtils;
import com.yj.util.SpringContextHolder;
/*
* 主要是为了输出Service的代理类型是JDK动态代理还是CGLIb代理
* */
@Component
public class Init implements CommandLineRunner{
@Override
public void run(String... args) throws Exception {
ApplicationContext ctx=SpringContextHolder.getApplicationContext();
OrderServiceImpl orderService=(OrderServiceImpl)AopTargetUtils.getTarget(ctx.getBean("orderService"));
UserService userService=(UserService)AopTargetUtils.getTarget(ctx.getBean("userService"));
}
}
MyAspect类
package com.yj.aop.anno;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
//定义切面
@Component
@Aspect
public class MyAspect {
//定义各种通知
@Before(value = "myPointCut()")
public void before(JoinPoint joinPoint) {
System.out.println("before : " + joinPoint.getSignature().getName());
}
@AfterReturning(value = "myPointCut()", returning = "ret")
public void afterReturning(JoinPoint joinPoint, Object ret) {
System.out.println("afterReturning: " + joinPoint.getSignature().getName() + " ,返回值-->" + ret);
}
@Around(value = "myPointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("前");
Object obj = joinPoint.proceed();
System.out.println("后");
return obj;
}
@AfterThrowing(value = "myPointCut()", throwing = "e")
public void afterThrowing(JoinPoint joinPoint, Throwable e) {
System.out.println("抛出异常通知 : " + e.getMessage());
}
@After(value = "myPointCut()")
public void after(JoinPoint joinPoint) {
System.out.println("after");
}
//定义切入点
/*
* 对包下的service进行拦截 第一个”*“符号,表示返回值的类型任意;
* com.sample.service.impl AOP所切的服务的包名,即我们的业务部分
* 包名后面的”..“ 表示当前包及子包 第二个”*“ 表示类名,*即所有类。
* .*(..) 表示任何方法名,括号表示参数,两个点表示任何参数类型
*/
@Pointcut("execution(* com.yj.aop.anno.service..*.*(..))")
private void myPointCut() {
}
/**
* 对注解进行拦截
*/
/**
* @Pointcut("execution(@com.yj.aop.anno.MyPointCut * *..*.*(..))") private
* void myPointCut() { }
*/
}
MyPointCut注解
package com.yj.aop.anno;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.CLASS)
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
public @interface MyPointCut {
}
OrderService接口
package com.yj.aop.xml.service;
public interface OrderService {
public abstract int addOrder();
public abstract int updateOrder();
public abstract int deleteOrder();
}
OrderServiceImpl类
package com.yj.aop.xml.service;
public class OrderServiceImpl implements OrderService {
@Override
public int addOrder() {
System.out.println("addOrder");
return 1;
}
@Override
public int updateOrder() {
System.out.println("updateOrder");
int i = 1 / 0;
return 1;
}
@Override
public int deleteOrder() {
System.out.println("deleteOrder");
return 1;
}
}
UserService类
package com.yj.aop.xml.service;
public /*final*/ class UserService{
public int addUser() {
System.out.println("addUser");
return 1;
}
public int updateUser() {
System.out.println("updateUser");
int i = 1/ 0;
return 1;
}
public int deleteUser() {
System.out.println("deleteUser");
return 1;
}
}
MyAspect
package com.yj.aop.xml;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class MyAspect {
public void before(JoinPoint joinPoint) {
System.out.println("before: " + joinPoint.getSignature().getName());
}
public void afterReturning(JoinPoint joinPoint, Object ret) {
System.out.println("afterReturning: " + joinPoint.getSignature().getName() + " ,返回值-->" + ret);
}
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("around前");
Object obj = joinPoint.proceed();
System.out.println("around后");
return obj;
}
public void afterThrowing(JoinPoint joinPoint, Throwable e) {
System.out.println("afterThrowing : " + e.getMessage());
}
public void after(JoinPoint joinPoint) {
System.out.println("after: " + joinPoint.getSignature().getName());
}
}
TestController类
package com.yj.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.yj.aop.anno.service.OrderService;
import com.yj.aop.anno.service.UserService;
@RestController
public class TestController {
@Autowired
private UserService userService;
@Autowired
private OrderService orderService;
@RequestMapping("/addUser")
public void addUser() {
userService.addUser();
}
@RequestMapping("/updateUser")
public void updateUser() {
userService.updateUser();
}
@RequestMapping("/deleteUser")
public void deleteUser() {
userService.deleteUser();
}
@RequestMapping("/addOrder")
public void addOrder() {
orderService.addOrder();
}
@RequestMapping("/updateOrder")
public void updateOrder() {
orderService.updateOrder();
}
@RequestMapping("/deleteOrder")
public void deleteOrder() {
orderService.deleteOrder();
}
}
AopTargetUtils类
package com.yj.util;
import java.lang.reflect.Field;
import org.springframework.aop.framework.AdvisedSupport;
import org.springframework.aop.framework.AopProxy;
import org.springframework.aop.support.AopUtils;
public class AopTargetUtils {
/**
* 获取 目标对象
*
* @param proxy
* 代理对象
* @return
* @throws Exception
*/
public static Object getTarget(Object proxy) throws Exception {
System.out.println("proxy.getClass():"+proxy.getClass());
if (!AopUtils.isAopProxy(proxy)) {
System.out.println("不是代理对象");
return proxy;// 不是代理对象
}
if (AopUtils.isJdkDynamicProxy(proxy)) {
return getJdkDynamicProxyTargetObject(proxy);
} else { // cglib
return getCglibProxyTargetObject(proxy);
}
}
private static Object getCglibProxyTargetObject(Object proxy) throws Exception {
System.out.println("当前代理模式:cglib代理");
Field h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0");
h.setAccessible(true);
Object dynamicAdvisedInterceptor = h.get(proxy);
Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised");
advised.setAccessible(true);
Object target = ((AdvisedSupport) advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget();
return target;
}
private static Object getJdkDynamicProxyTargetObject(Object proxy) throws Exception {
System.out.println("当前代理模式:jdk动态代理");
Field h = proxy.getClass().getSuperclass().getDeclaredField("h");
h.setAccessible(true);
AopProxy aopProxy = (AopProxy) h.get(proxy);
Field advised = aopProxy.getClass().getDeclaredField("advised");
advised.setAccessible(true);
Object target = ((AdvisedSupport) advised.get(aopProxy)).getTargetSource().getTarget();
return target;
}
}
SpringContextHolder类
package com.yj.util;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class SpringContextHolder implements ApplicationContextAware {
private static ApplicationContext context;
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static ApplicationContext getApplicationContext() {
return context;
}
}
Application类
package com.yj;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}
application.properties
#spring.aop.proxy-target-class=true
Beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userService" class="com.yj.aop.xml.service.UserService" />
<bean id="orderServiceImpl" class="com.yj.aop.xml.service.OrderServiceImpl" />
<bean id="myAspect" class="com.yj.aop.xml.MyAspect" />
<aop:aspectj-autoproxy proxy-target-class="true" />
<aop:config>
<aop:pointcut expression="execution(* com.yj.aop.xml.service..*.*(..))"
id="myPointCut" />
<aop:aspect ref="myAspect">
<aop:before method="before" pointcut-ref="myPointCut" />
<aop:after-returning method="afterReturning"
pointcut-ref="myPointCut" returning="ret" />
<aop:around method="around" pointcut-ref="myPointCut" />
<aop:after-throwing method="afterThrowing"
pointcut-ref="myPointCut" throwing="e" />
<aop:after method="after" pointcut-ref="myPointCut" />
</aop:aspect>
</aop:config>
</beans>
TestXmlAop类
package com.yj.aop;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.yj.aop.xml.service.OrderService;
import com.yj.aop.xml.service.OrderServiceImpl;
import com.yj.aop.xml.service.UserService;
import com.yj.util.AopTargetUtils;
public class TestXmlAop {
UserService userService;
OrderService orderService;
@Before
public void init() throws Exception {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("Beans.xml");
userService=(UserService) AopTargetUtils.getTarget(ctx.getBean("userService"));
orderService=(OrderServiceImpl) AopTargetUtils.getTarget(ctx.getBean("orderServiceImpl"));
userService=(UserService) ctx.getBean("userService");
orderService=(OrderService) ctx.getBean("orderServiceImpl");
}
@Test
public void addUser() {
userService.addUser();
}
@Test
public void updateUser() {
userService.updateUser();
}
@Test
public void deleteUser() {
userService.deleteUser();
}
@Test
public void addOrder() {
orderService.addOrder();
}
@Test
public void updateOrder() {
orderService.updateOrder();
}
@Test
public void deleteOrder() {
orderService.deleteOrder();
}
}
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.yj</groupId>
<artifactId>Aop</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Aop</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
<relativePath />
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
</dependencies>
</project>
验证
1.首先验证采用注解方式的AOP
①application.properties内的不设置spring.aop.proxy-target-class,或者设置spring.aop.proxy-target-class=false的情况下
启动Application,由于自动执行Init类,控制台显示
proxy.getClass():class com.sun.proxy.$Proxy62
当前代理模式:jdk动态代理
proxy.getClass():class com.yj.aop.anno.service.UserService$$EnhancerBySpringCGLIB$$a8af1850
当前代理模式:cglib代理
可以看到OrderService类由于是 接口/实现类的模式,所以采用的是默认的JDK动态代理模式
UserService由于不是 接口/实现类的模式,所以采用的是CGLib动态代理模式
②application.properties内的设置spring.aop.proxy-target-class=true,启动Application,控制台显示
proxy.getClass():class com.yj.aop.anno.service.OrderServiceImpl$$EnhancerBySpringCGLIB$$cad3ca7a
当前代理模式:cglib代理
proxy.getClass():class com.yj.aop.anno.service.UserService$$EnhancerBySpringCGLIB$$8d40acb5
当前代理模式:cglib代理
可以看到spring.aop.proxy-target-class=true的情况下,无论是不是 接口/实现类的模式,采用的都是CGLib动态代理模式
③无论是哪种代理模式,项目启动后,随便访问TestController中的一个路由,查看代理的效果
我们访问
http://127.0.0.1:8080/addOrder
控制台显示
前
before : addOrder
addOrder
后
after
afterReturning: addOrder ,返回值-->1
可以验证,无论是采用的是哪种代理模式,最终实现的效果都是一样的
2.验证采用xml方式的AOP
①Beans.xml内的不设置<aop:aspectj-autoproxy proxy-target-class /> 标签,或者设置<aop:aspectj-autoproxy proxy-target-class="false" />的情况下
执行TestXmlAop的Junit类的updateUser方法,控制台显示
proxy.getClass():class com.yj.aop.xml.service.UserService$$EnhancerBySpringCGLIB$$33697d31
当前代理模式:cglib代理
proxy.getClass():class com.sun.proxy.$Proxy7
当前代理模式:jdk动态代理
可以看到OrderService类由于是 接口/实现类的模式,所以采用的是默认的JDK动态代理模式
UserService由于不是 接口/实现类的模式,所以采用的是默认的CGLib动态代理模式
②Beans.xml内的设置<aop:aspectj-autoproxy proxy-target-class="true" />,在此执行Junit方法,控制台显示
proxy.getClass():class com.yj.aop.xml.service.UserService$$EnhancerBySpringCGLIB$$33697d31
当前代理模式:cglib代理
proxy.getClass():class com.yj.aop.xml.service.OrderServiceImpl$$EnhancerBySpringCGLIB$$94429df0
当前代理模式:cglib代理
可以看到<aop:aspectj-autoproxy proxy-target-class="true" />的情况下,无论是不是 接口/实现类的模式,采用的都是CGLib动态代理模式
③无论是采用的是哪种代理模式,最终实现的效果都是一样的
before: addUser
around前
addUser
after: addUser
around后
afterReturning: addUser ,返回值-->1