使用方法)"/>
SpringAOP —— 代理模式(记录使用方法)
前段时间在公司内网看到一篇关于AOP的文章,今天记录学习一下。
目录
什么是springAOP:
springAop原理:
静态代理:
动态代理:
SpringAOP上手使用:
SpringAOP的优势和缺点:
SpringAOP总结:
什么是springAOP:
首先介绍什么是springAop。官方介绍是面向切面编程,首先这是一种编程思想,基于java面向对象编程(OOP)的一种补充。面向对象变成思想是指万物皆对象。处理对象的过程叫方法,面向切面编程就是把方法看成一个整体,在不改变原有方法原有代码的情况下改变方法执行过程。
springAop原理:
这种神奇的效果是怎么实现的呢?这个要归功于23种设计模式的代理模式,要理解面向切面编程,必须要先理解代理模式。
代理模式理解很简单,比如你是彭于晏,但是你只专注于表演这个能力,只能拿到表演的钱,现在你特别出名,好多公司找你做代言,但你没空去洽谈,这个时候你就需要一个经纪人,经纪人会帮你接代言,帮你管理粉丝,帮你接戏,这样你的工作没有变,但是收入增加了。
这个过程中,彭于晏代表是原生对象。而全权代表彭于晏的经纪人被称为代理对象。当我需要彭于晏表演的时候,我去找经纪人,这个过程叫代理模式。
代理模式的两种常见的实现方式:静态代理,动态代理
静态代理:
先说静态代理:静态代理优点是:好理解、好观察;缺点是:难维护、死板、不适合在动态的项目需求中使用。
/*** 功能接口*/
public interface Function {// 人物技能public void sill();}
/*** 演员类*/
public class Actor implements Function {@Overridepublic void sill() {System.out.println("我是彭于晏,我会表演!");}}
public class Agent implements Function {private Function obj;public Agent(Function obj){this.obj = obj;}@Overridepublic void sill() {System.out.println("我是经纪人,我会接代言,管粉丝!(注:动态代理实现)");obj.sill();}
}
public static void main(String[] args) {// 静态代理测试Agent agent = new Agent(new Actor());agent.sill();}
这就是静态代理,目标对象和代理对象都需要实现自己的父类方法,一旦父类修改或新增了方法,需要在所有子类中一起变动,维护十分困难,所以一般在项目中很少使用静态代理。
动态代理:
再说动态代理:动态代理优点是:好使用、好维护;缺点是:不好理解、不好观测;
接口部分不变,演员(彭于晏)部分不变:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;/*** 动态代理实现*/
public class AgentProxy {private Object object;public AgentProxy(Object object) {this.object = object;}// 利用反射给目标对象生成代理对象public Object getProxyInstance() {return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("我是经纪人,我会接代言,管粉丝!(注:动态代理实现)");// 执行目标对象方法Object invoke = method.invoke(object, args);return invoke;}});}}
public static void main(String[] args) {// 动态代理测试Function fun = new Actor();AgentProxy agentProxy = new AgentProxy(new Actor());Function proxyInstance = (Function) agentProxy.getProxyInstance();proxyInstance.sill();}
所谓动态代理就是借用反射来创建代理对象,借用反射的特性来创建代理对象的好处就是通用性极高,这样就避免了上层接口改变,代理对象也要修改。
SpringAOP上手使用:
现在明白springAOP是干啥的,接下来展示springAOP在实际项目中的使用,SpringAOP已经实现了自动创建动态代理,我们只需要学会正确调用即可。
一、引入AOP依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>注:加入新依赖后要更新依赖。
二、使用切面注解@Aspect
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;@Component
@Aspect // 定义一个切面
public class AgentProxy {}
切面:定义当前类是切面类,需要给每一个满足切面条件的类生成不同的代理对象。
三、定义需要的通知
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;@Component
@Aspect // 定义一个切面
public class AgentProxy {// 声明前置通知@Before()public void doSkill() {System.out.println("我是经纪人,我会接代言,管粉丝!");}
通知:通知就是定义代理对象的代码要在什么时候生效。
以下是常用的通知类型,可以根据实际情况选用需要的通知:
注解 | 通知 |
---|---|
@Before | 通知方法会在目标方法调用之前执行 |
@After | 通知方法会在目标方法返回或异常后调用 |
@AfterReturning | 通知方法会在目标方法返回后调用 |
@AfterThrowing | 通知方法会在目标方法抛出异常后调用 |
@Around | 通知方法会在目标方法抛出异常后调用 |
四、填写需要的切面表达式
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;@Component
@Aspect // 定义一个切面
public class AgentProxy {/*** 切面表达式 * 调用返回值是void、Function这个类的sill()这个方法时就会生成对应的代理类* 切面表达式可以用通配符 **/@Before("execution(void com.example.musicserver.test.Function.sill())")public void doSkill() {System.out.println("我是经纪人,我会接代言,管粉丝!(AOP实现!)");}
切面表达式:定义了满足什么条件就去生成其代理类。
import com.example.musicserver.test.Function;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;@SpringBootApplication
public class MusicServerApplication {// 测试 AOP 前置通知public static void main(String[] args) {ConfigurableApplicationContext run = SpringApplication.run(MusicServerApplication.class, args);Function fun = (Function)run.getBean("actor");fun.sill();}}
AOP的环绕通知:
最后在说一下AOP的环绕通知,环绕通知,可以说是使用最频繁的通知方式,会将整个方法包围起来,可以再目标方法执行前和执行后执行自己的逻辑代码。
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;@Component
@Aspect // 定义一个切面
public class AgentProxy {/*** 环绕通知* 切面表达式:上面说到可以用通配符实现* 这里就是对com.example.musicserver.test路径所有方法进行拦截定义*/@Around("* com.example.musicserver.test.*.*()")public Object doSkill2(ProceedingJoinPoint pjp) {System.out.println("我是经纪人,正在接戏中。。。");Object proceed = null;try {// 这里需要我们执行拦截方法本身的代码(最后返回)proceed = pjp.proceed();} catch (Throwable throwable) {throwable.printStackTrace();}System.out.println("我是经纪人,演出结束,收钱!");return proceed;}}
ProceedingJoinPoint简单理解就是把原始方法封装起来,可以随时调用
环绕通知=前置逻辑+目标方法执行+后置逻辑执行,pjp的proceed方法就是用于目标方法的执行
注意:ProceedingJoinPoint.proceed() 返回的是目标方法执行后的返回值,一定要在环绕通知返回出去!!!
import com.example.musicserver.test.Function;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;@SpringBootApplication
public class MusicServerApplication {public static void main(String[] args) {// 环绕通知测试ConfigurableApplicationContext run = SpringApplication.run(MusicServerApplication.class, args);Function fun = (Function)run.getBean("actor");fun.sill();}}
SpringAOP的优势和缺点:
springAOP的优势很明显,在不改变原有代码的情况下,加入新的逻辑代码。
缺点是:逻辑不连贯,阅读体验不好。毕竟这是专门解决特定问题而出现的专用工具。
我们应该有一个意识,当我们需要批量给方法新增逻辑的时候,就能想到SpringAOP。
SpringAOP总结:
AOP是一种编程思想————面向切面编程
AOP实现原理————代理模式。 代理模式分为动态代理和静态代理。
SpringAOP使用步骤:1.导入依赖
2.使用切面注解
3.使用通知注解
4.使用切点申明表达式
java里面的概念很多,知识点也很多。很多概念和知识点是专为解决特定问题而诞生的,我们不可能记住全部知识点,但是在需要用到一个知识点的时候,要能想起什么知识点能解决这个问题,在不断的遇到问题,解决问题的过程中,我们对这些知识点的理解自然会更深刻,
更多推荐
SpringAOP —— 代理模式(记录使用方法)
发布评论