Spring之AOP

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

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猎户星座。

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值