首页 > 其他分享 >SpringAOP

SpringAOP

时间:2023-06-09 10:57:50浏览次数:42  
标签:void class pointcut SpringAOP new foo public

一、proxy增强

1、基于JDK

java自带的代理功能,只能针对接口,目标类与代理类为平级关系

public class JDKProxy{
	interface Foo{
		void foo();
	}
	
	static class Target implements Foo{
		public void foo(){
			System.out.println("target foo");
		}
	}
	
	public static void main(String[] param){
	
	Target target = new Target();
	
		ClassLoader loader = JDKProxy.class.getClassLoader();
		Foo proxy = (Foo)Proxy.newProxyInstance(loader, new Class[]{Foo.class}, new InvocationHandler(){
			@Override
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
				System.out.println("before...");
				Object result = method.invoke(target, args);
				System.out.println("after...");
				
				//返回目标方法执行的结果
				return result;
			}
		});
		
		proxy.foo();
	}
}

2、基于Cglib

目标类与代理类是父子关系,所以目标类不应该为final,而代理方法代理目标方法是基于方法重写,所以目标方法也不能为final

public class CglibProxy{
	static class Target{
		public void foo(){
			System.out.println("target foo");
		}
	}
	
	public static void main(String[] param){
		Target target = new Target();
		
		Target proxy = (Target) Enhancer.create(Target.class, new MethodInterceptor(){
			@Override
			public Object intercept(Object proxyObject, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable{
				System.our.println("before...);
				
				//1、通过反射调用方法(针对每一个方法产生一个代理对象,前16次通过反射,从第17次开始使用对象直接调用)
				Object result = method.invoke(target, args);
				
				//内部没有用反射,实际上,既然intercept的参数已经包括了一个方法的完整签名,又有实现类,所以已经具备了通过实例对象直接调用方法的条件,methodProxy.invoke就是做了这样的一个实现,它内部通过FastClass为每一个方法生成一个方法代理,并且将目标类中的每个Method和实例中的方法一一对应起来,所以在intercept中执行此语句的时候,在内部可以直接用实例调用方法,没有反射,所以效率更高,且这种方式只为每一个目标类生成两个代理对象,较反射调用产生的对象更少,Spring中就是用的这个方式
				//Object result = methodProxy.invoke(target, args);
				
				//与methodProxy.invoke方法的原理基本一致,只是不需要目标实例
				//Object result = methodProxy.invokeSuper(proxyObject, args);
				
				System.our.println("after...);
				
				return result;	//返回方法执行结果
			}
		});
		
		proxy.foo();
	}
}

二、ajc增强

可以使用Aspectj的ajc编译器,通过在编译期修改目标类的class文件,直接将增强代码写入目标方法,这个方法和Spring容器无关,可以突破proxy方式的一些限制,比如可以对静态方法增强
修改pom.xml,安装插件

<build>
	<plugins>
		<plugin>
			<groupId>org.codehaus.mojo</groupId>
			<artifactId>aspectj-maven-plugin</artifactId>
			<version>1.14.8</version>
			<configuration>
				<compilianceLevel>1.8</compilianceLevel>
				<source>8</source>
				<target>8</target>
				<showWeaveInfo>true</showWeaveInfo>
				<verbose>true</verbose>
				<Xlint>ignore</Xlint>
				<encoding>UTF-8</encoding>
			</configuration>
			<executions>
				<execution>
					<goals>
						<goal>compile</goal>
						<goal>test-compile</goal>
					</goals>
				</execution>
			</executions>
		</plugin>
	</plugins>
</build>

三、agent增强

待补充

四、Aspect应用

@Aspect
@Component
public class MyAspect {
	//1、方法规则式拦截,直接编写增强逻辑,符合execution表达式的所有方法都会被拦截并织入代码
	@Before("execution(* com.example.aop.DemoMethodService.*(..))")
	public void before(JoinPoint joinPoint) {
		MethodSignature signature = (MethodSignature)joinPoint.getSignature();
		Method method = signature.getMethod();
		System.out.println("方法规则式拦截," + method.getName());
	}
	
	//2.1、定义一个切点,匹配所有被标注了@Action注解的方法,此规则适用于目标方法上有注解的情况
	@Pointcut("@annotation(com.example.aop.Action)")
	public void pointcut_1(){}
	
	//2.2、定义一个切点,根据方法签名来匹配方法
	@Pointcut("execution(* com.example.aop.DemoMethodService.*(..))")
	public void pointcut_2(){}
	
	//2.2、为符合条件的Pointcut编写增强逻辑
	//可以使用布尔运算 (&&、||、!)等用来丰富匹配条件,如@After("pointcut_2() && !pointcut_1()") 匹配符合pointcut_2方法签名,但是没有被标注@Action注解的方法
	@After("pointcut_1()")
	public void afterAdd(JoinPoint joinPoint) {
		MethodSignature signature = (MethodSignature)joinPoint.getSignature();
		Method method = signature.getMethod();
		Action action = method.getAnnotation(Action.class);
		System.out.println("after*******************注解式拦截: " + action.name());
	}
}

五、Aspect底层

aspect属于高级切面,spring在解析时,会把Aspect高级切面转化成advisor低级切面,原理就是扫描被@Aspect注解的类,拿到被@Pointcut标注的方法,进一步获取@Pointcut的值,使用如下例所示的AspectJExpressionPointcut创建切点,...,最后进行增强

pubic class MyAdvice{
	public static void main(String[] args){
		//切点
		AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
		pointcut.setExpression("execution(* foo())");	//只有foo方法会被增强,其底层实际是使用pointcut.matches()来判断方法是否符合要求
		
		//通知
		//org.aopalliance.intercept.MethodInterceptor
		MethodInterceptor advice = new MethodInterceptor(){
			@Override
			public Object invoke(MethodInvocation invocation) throws Throwable{
				System.out.println("before...");
				Object result = invocation.proceed();
				System.out.println("after...");
				
				return result;
			}
		}
		
		//切面
		DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
		
		//创建代理
		Target1 target = new Target1();
		ProxyFactory factory = new ProxyFactory();
		factory.setTarget(target);
		factory.addAdvisor(advisor);
		factory.setInterfaces(target.getClass().getInterfaces());	//告诉代理工厂,创建的代理要实现哪些接口
		I1 proxy = (I1)factory.getProxy();	//Spring自动选择使用jdk代理或cglib代理
		
		//调用方法
		proxy.foo();
		proxy.bar();
	}
	
	interface I1{
		void foo();
		void bar();
	}
	
	static class Target1 implements I1{
		public void foo(){System.out.println("target1 foo")}
		public void bar(){System.out.println("target1 bar")}
	}
	
	static class Target2{
		public void foo(){System.out.println("target2 foo")}
		public void bar(){System.out.println("target2 bar")}
	}
}

高级切面转低级切面

AspectInstanceFactory factory = new AspectInstanceFactory(new MyAspect());
//遍历高级切面的方法,收集所有解析好的低级切面
List<Advisor> advisorList = new ArrayList<>();
for(Method method : MyAspect.class.getDeclaredMethods()){
	//解析被@Before注解的方法,转化成低级切面
	if(method.isAnnotationPresent(Before.class)){
		//解析切点
		String expression = method.getAnnotation(Before.class).value();
		AspectJExpressionPoinntcut pointcut = new AspectJExpressionPointcut();
		pointcut.setExpression(expression);
		//通知类,与@Before注解对应,使用AspectJMethodBeforeAdvice
		AspectJMethodBeforeAdvice advice = new AspectJMethodBeforeAdvice(method, pointcut, factory);
		//切面
		Advisor advisor = new DefaultPoitcutAdvisor(pointcut, advice);
		
		advisorList.add(advisor);
	}
}

AspectJMethodBeforeAdvice类似的通知还有:

  • AspectJAroundAdvice(环绕通知)
  • AspectJAfterReturningAdvice
  • AspectJAfterThrowingAdvice(环绕通知)
  • AspectJAfterAdvice(环绕通知)

注意:这些通知最后执行时又会被统一转化为环绕通知(MethodIntercept接口,已经是环绕通知的不必再次转换),先按优先级从高到底调用前置通知,前置通知执行完后,按优先级从低到高反序执行后置通知,(一层一层嵌套,和过滤器类似,在Aspect切面类上可以使用@Order指定优先级,值越小,优先级越高,最后生成的环绕通知层级越靠外),实际就是递归

通知统一转换为环绕通知

Target target = new Target();
ProxyFactory proxyFactory = new ProxyFactory();
proxy.setTarget(target);
proxy.addAdvisors(advisorList);

//统一转换为环绕通知
List<Object> methodInterceptorList = proxyFactory.getInterceptorAndDynamicInterceptionAdvicer(Target.class.getMethod("foo"), Target.class);

六、Spring选择代理

  • ProxyFactory.proxyTargetClass = false,而且目标实现了接口,则使用jdk代理
  • ProxyFactory.proxyTargetClass = false,但是目标没有实现接口,则使用cglib代理
  • ProxyFactory.proxyTargetClass = true,不管目标有没有实现接口,都使用cglib代理

标签:void,class,pointcut,SpringAOP,new,foo,public
From: https://www.cnblogs.com/tanmujin/p/17440803.html

相关文章

  • springAOP
    一,AOP1,面向切面编程AspectOrientedProgramming2,编程思想的发展路程①Logicjava:java逻辑编程②OOP:面向对象编程③OIP:interface面向接口编程④面向配置文件编程以上的思想,都是逐步升级的概念⑤AOP在OOP的基础上,增强了OOP的功能3,实现方式①基于配置(xml......
  • 对SpringIOC和SpringAOP的理解
    SpringIOC和SpringAOP是Spring的两个核心组件。SpringIOC:SpringIOC是一个管理bean的容器,能够帮我们管理bean的整个生命周期,在没有SpringIOC的时候,我们需要自己手动的管理bean以及bean的依赖关系,这样会增加耦合,而有了SpringIOC,它能帮我们管理bean以及bean的依赖关系,使得代码解耦。......
  • SpringAOP精简版
    AOP简介概念:AOP是一种编程范式作用:做无入侵式增强程序功能Spring是如何实现AOP的?1.导坐标2.在Spring核心配置类上添加开启SpringAOP驱动注解3.定义通知类,@Component,@Aspect4.添加切入点,@PointCut5.制作通知,@Before等SpringAOP执行流程1.启动Spring容器2.读取切......
  • SpringAOP【Web后端开发进阶】
    AOP(思想):面向切面编程思想的实现:动态代理 动态代理的2种实现方式:1、基于接口的JDK动态代理2、基于子类的CGLIB动态代理 AOP思想的作用:1、在不改变原程序代码的前提下,对方法功能增强2、像添加插件一样,任意插拔。(程序更加灵活)......
  • SpringIOC和SpringAOP
    作为一个Spring使用者条件:拥有深入的Spring框架知识和开发经验,能够熟练地运用Spring框架来构建复杂的应用程序。了解Spring框架的核心概念和设计思想,如控制反转(IoC)、依赖注入(DI)、面向切面编程(AOP)等,并能灵活运用这些概念来解决实际问题。熟悉Spring框架中各个模块的功能和用法,如......
  • 14.SpringAOP 编程
    SpringAOP编程课程目标代理设计模式Spring的环境搭建SpringIOC与AOPSpring事物与传播行为一、代理模式1.1概述代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理访问目标对象。这样好处:可以在目标对象实现的基础上,增强额外的功能操作。(扩......
  • 开发日志02-解决`response`和SpringAop层相关冲突报错问题
    解决一个Bug在昨晚的开发中遇到了一个非常令人头疼的Bugjava.lang.IllegalStateException:getOutputStream()hasalreadybeencalledforthisresponse报错信息如下......
  • 浅谈SpringAOP功能源码执行逻辑
    如题,该篇博文主要是对Spring初始化后AOP部分代码执行流程的分析,仅仅只是粗略的讲解整个执行流程,喜欢细的小伙伴请结合其他资料进行学习。在看本文之前请一定要有动态代理的......
  • SpringAop是使用JDK代理还是使用CGLIB代理实现
    先说结论:在spring-aop的默认逻辑中,aop默认优先使用JDK代理,前提是目标对象是基于接口的实现类。源码如下:入口在AbstractAdvisingBeanPostProcessor.postProcessAfterInitia......
  • 【Logback+Spring-Aop】实现全面生态化的全链路日志追踪系统服务插件「SpringAOP 整合
    承接前文针对于上一篇【Logback+Spring-Aop】实现全面生态化的全链路日志追踪系统服务插件「Logback-MDC篇」的功能开发指南之后,相信你对于Sl4fj以及Log4j整个生态体系的功......