Spring进阶-AOP注解开发

Spring进阶-AOP注解开发

目录

基于注解的 AOP 开发1 快速入门1.1 注解开发AOP制作步骤1.2 注解开发AOP注意事项1.3 创建目标接口和目标类(内部有切点)1.4 创建切面类1.5 将目标类和切面类的对象创建权交给 spring1.6 在切面类中使用注解配置织入关系1.7 在配置文类中开启组件扫描和 AOP 的自动代理1.8 测试代码1.9 测试结果

2 注解通知的类型2.1 AOP注解详解2.1.1 @Aspect2.2.2 @Pointcut2.2.3 @Before2.2.4 @After2.2.5 @AfterReturning2.2.6 @AfterThrowing2.2.7 @Around

3 企业开发经验3.1 AOP注解驱动

4 综合案例4.1 案例介绍4.2 案例分析4.3 案例制作步骤4.4 案例制作核心代码4.5 案例后续思考与设计

5 代理设计模式介绍5.1 静态代理和装饰者设计模式的区别5.1.1 相同点

不同点

静态代理动态代理技术JDK 的动态代理cglib 的动态代理

代理模式的选择

面试AOP是什么动态代理的分类在开发中经常的通知有哪些要想对某个方法进行增强有几种方式

基于注解的 AOP 开发

1 快速入门

1.1 注解开发AOP制作步骤

在XML格式基础上

导入坐标(伴随spring-context坐标导入已经依赖导入完成)开启AOP注解支持配置切面@Aspect定义专用的切入点方法,并配置切入点@Pointcut为通知方法配置通知类型及对应切入点@Before

1.2 注解开发AOP注意事项

1.切入点最终体现为一个方法,无参无返回值,无实际方法体内容,但不能是抽象方法

2.引用切入点时必须使用方法调用名称,方法后面的()不能省略

3.切面类中定义的切入点只能在当前类中使用,如果想引用其他类中定义的切入点使用“类名.方法名()”引用

4.可以在通知类型注解后添加参数,实现XML配置中的属性,例如after-returning后的returning属性

1.3 创建目标接口和目标类(内部有切点)

public interface TargetInterface {

public void method();

}

public class Target implements TargetInterface {

@Override

public void method() {

System.out.println("Target running....");

}

}

1.4 创建切面类

public class MyAspect {

//前置增强方法

public void before(){

System.out.println("前置代码增强.....");

}

}

1.5 将目标类和切面类的对象创建权交给 spring

@Component("target")

public class Target implements TargetInterface {

@Override

public void method() {

System.out.println("Target running....");

}

}

@Component("myAspect")

public class MyAspect {

public void before(){

System.out.println("前置代码增强.....");

}

}

1.6 在切面类中使用注解配置织入关系

@Component("myAspect")

@Aspect

public class MyAspect {

@Before("execution(* com.itheima.aop.*.*(..))")

public void before(){

System.out.println("前置代码增强.....");

}

}

或者把切点表达式抽取出来

@Component("myAspect")

@Aspect

public class MyAspect {

@Pointcut(""execution(* com.itheima.aop.*.*(..))"")

public void pt(){}

@Before("pt()")

public void before(){

System.out.println("前置代码增强.....");

}

}

1.7 在配置文类中开启组件扫描和 AOP 的自动代理

@Configuration

@ComponentScan("com.itheima")

@EnableAspectJAutoProxy

public class SpringConfig {

}

1.8 测试代码

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(classes=SpringConfig.class)

public class AopTest {

@Autowired

private TargetInterface target;

@Test

public void test1(){

target.method();

}

}

1.9 测试结果

2 注解通知的类型

通知的配置语法:@通知注解(“切点表达式”)

2.1 AOP注解详解

2.1.1 @Aspect

名称:@Aspect类型:注解位置:类定义上方作用:设置当前类为切面类格式: @Aspect public class AopAdvice { }说明:一个beans标签中可以配置多个aop:config标签

2.2.2 @Pointcut

名称:@Pointcut类型:注解位置:方法定义上方作用:使用当前方法名作为切入点引用名称格式: @Pointcut(“execution(* *(…))”) public void pt() { }说明:被修饰的方法忽略其业务功能,格式设定为无参无返回值的方法,方法体内空实现(非抽象)

2.2.3 @Before

名称:@Before类型:注解位置:方法定义上方作用:标注当前方法作为前置通知格式: @Before(“pt()”) public void before(){ }特殊参数:

2.2.4 @After

名称:@After类型:注解位置:方法定义上方作用:标注当前方法作为后置通知格式: @After(“pt()”) public void after(){ }特殊参数:

2.2.5 @AfterReturning

名称:@AfterReturning类型:注解位置:方法定义上方作用:标注当前方法作为返回后通知格式: @AfterReturning(value=“pt()”,returning = “ret”) public void afterReturning(Object ret) { }特殊参数:

returning :设定使用通知方法参数接收返回值的变量名

2.2.6 @AfterThrowing

名称:@AfterThrowing类型:注解位置:方法定义上方作用:标注当前方法作为异常后通知格式: @AfterThrowing(value=“pt()”,throwing = “t”) public void afterThrowing(Throwable t){ }特殊参数:

throwing :设定使用通知方法参数接收原始方法中抛出的异常对象名

2.2.7 @Around

名称:@Around类型:注解位置:方法定义上方作用:标注当前方法作为环绕通知格式: @Around(“pt()”) public Object around(ProceedingJoinPoint pjp) throws Throwable { Object ret = pjp.proceed(); return ret; }特殊参数:

AOP使用XML配置情况下,通知的执行顺序由配置顺序决定,在注解情况下由于不存在配置顺序的概念的概念,参照通知所配置的方法名字符串对应的编码值顺序,可以简单理解为字母排序

同一个通知类中,相同通知类型以方法名排序为准不同通知类中,以类名排序为准使用@Order注解通过变更bean的加载顺序改变通知的加载顺序

3 企业开发经验

通知方法名由3部分组成,分别是前缀、顺序编码、功能描述前缀为固定字符串,例如baidu、itheima等,无实际意义顺序编码为6位以内的整数,通常3位即可,不足位补0功能描述为该方法对应的实际通知功能,例如exception、strLenCheck

3.1 AOP注解驱动

名称:@EnableAspectJAutoProxy

类型:注解

位置:Spring注解配置类定义上方

作用:设置当前类开启AOP注解驱动的支持,加载AOP注解

格式:

@Configuration

@ComponentScan("com.itheima")

@EnableAspectJAutoProxy

public class SpringConfig {

}

4 综合案例

4.1 案例介绍

对项目进行业务层接口执行监控,测量业务层接口的执行效率

public interface AccountService {

void save(Account account);

void delete(Integer id);

void update(Account account);

List findAll();

Account findById(Integer id);

}

4.2 案例分析

测量接口执行效率:接口方法执行前后获取执行时间,求出执行时长

System.currentTimeMillis( ) 对项目进行监控:项目中所有接口方法,AOP思想,执行期动态织入代码

环绕通知proceed()方法执行前后获取系统时间

4.3 案例制作步骤

定义切入点(务必要绑定到接口上,而不是接口实现类上)制作AOP环绕通知,完成测量功能注解配置AOP开启注解驱动支持

4.4 案例制作核心代码

public class RunTimeMonitorAdvice {

//拦截所有的业务层接口中查询操作的执行

@Pointcut("execution(* com.itheima.service.*Service.find*(..))")

public void pt(){}

@Around("pt()")

public Object runtimeMonitor(ProceedingJoinPoint pjp) throws Throwable {

//获取执行签名信息

Signature signature = pjp.getSignature();

//通过签名获取执行类型(接口名)

String targetClass = signature.getDeclaringTypeName();

//通过签名获取执行操作名称(方法名)

String targetMethod = signature.getName();

//获取操作前系统时间beginTime

long beginTime = System.currentTimeMillis();

Object ret = pjp.proceed(pjp.getArgs());

//获取操作后系统时间endTime

long endTime = System.currentTimeMillis();

System.out.println(targetClass+" 中 "+targetMethod+" 运行时长 "+(endTime-beginTime)+"ms");

return ret;

}

}

4.5 案例后续思考与设计

测量真实性

开发测量是隔离性反复执行某个操作,是理想情况,上线测量差异过大上线测量服务器性能略低于单机开发测量上线测量基于缓存的性能查询要优于数据库查询测量上线测量接口的性能与最终对外提供的服务性能差异过大当外部条件发生变化(硬件),需要进行回归测试,例如数据库迁移 测量结果展示

测量结果无需每一个都展示,需要设定检测阈值阈值设定要根据业务进行区分,一个复杂的查询与简单的查询差异化很大阈值设定需要做独立的配置文件或通过图形工具配置(工具级别的开发)配合图形界面展示测量结果

5 代理设计模式介绍

5.1 静态代理和装饰者设计模式的区别

5.1.1 相同点

1)都要实现与目标类相同的业务接口

2)在俩个类中都要声明目标对象

3)都可以在不修改目标类的前提下增强目标方法

不同点

1)目的不同,装饰者,简单说,就是为了增强目标对象

静态代理的使用目的是为了保护和隐藏目标对象

2)对于目标对象的获取方式不同

装饰者中目标对象的获取,通过代参构造器传入,静态代理类中,是在无参构造器中直接创建。

静态代理

静态代理的思想:将被代理类作为代理类的成员,通过代理类调用被代理类的函数,并添加新的控制。包装类与被包装类实现同一接口,使得使用时的代码一致。

应用:已经有一个日志记录器LoggerSubject,需要对writeLog()函数的前后进行某些操作(如初始化、异常处理等),使用Proxy类间接调用LoggerSubject.writeLog()实现新控制操作的添加。

interface Logger {

void writeLog();

}

// 被代理类

class LoggerSubject implements Logger{

@Override

public void writeLog(){

System.out.println("writeLog by LoggerSubject");

}

}

// 代理类

class Proxy implements Logger{

Logger logger;

// 与装饰者模式的主要区别位置

// 代理模式一般要求和原来的类行为一致,因此构造函数不传入对象

Proxy(){

this.logger = new LoggerSubject();

}

@Override

public void writeLog(){

System.out.println("logger write before");

logger.writeLog();

System.out.println("logger write after");

}

}

public class StaticProxy {

public static void main(String []argvs){

Logger logger = new Proxy();

logger.writeLog();

}

}

动态代理技术

常用的动态代理技术

JDK 代理 : 基于接口的动态代理技术

cglib 代理:基于父类的动态代理技术

JDK 的动态代理

①目标类接口

public interface TargetInterface {

public void method();

}

②目标类

public class Target implements TargetInterface {

@Override

public void method() {

System.out.println("Target running....");

}

}

③动态代理代码

Target target = new Target(); //创建目标对象

//创建代理对象

TargetInterface proxy = (TargetInterface) Proxy.newProxyInstance(target.getClass()

.getClassLoader(),target.getClass().getInterfaces(),new InvocationHandler() {

@Override

public Object invoke(Object proxy, Method method, Object[] args)

throws Throwable {

System.out.println("前置增强代码...");

Object invoke = method.invoke(target, args);

System.out.println("后置增强代码...");

return invoke;

}

}

);

④ 调用代理对象的方法测试

// 测试,当调用接口的任何方法时,代理对象的代码都无序修改

proxy.method();

cglib 的动态代理

①目标类

public class Target {

public void method() {

System.out.println("Target running....");

}

}

②动态代理代码

Target target = new Target(); //创建目标对象

Enhancer enhancer = new Enhancer(); //创建增强器

enhancer.setSuperclass(Target.class); //设置父类

enhancer.setCallback(new MethodInterceptor() { //设置回调

@Override

public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

System.out.println("前置代码增强....");

Object invoke = method.invoke(target, objects);

System.out.println("后置代码增强....");

return invoke;

}

});

Target proxy = (Target) enhancer.create(); //创建代理对象

③调用代理对象的方法测试

//测试,当调用接口的任何方法时,代理对象的代码都无序修改

proxy.method();

代理模式的选择

Spirng可以通过配置的形式控制使用的代理形式,默认使用jdkproxy,通过配置可以修改为使用cglib

false表示使用默认JDK动态代理,true表示使用cglib动态代理

XML配置

注解驱动

//注解驱动

@EnableAspectJAutoProxy(proxyTargetClass = true)

面试

AOP是什么

面向切面编程,在不修改源代码的情况下对原有功能进行增强,底层是动态代理

动态代理的分类

分为jdk和cglib,jdk动态代理只能增强某个接口的子类对象,cglib接口和类的子类对象都可以增强,spring默认是jdk代理而spring boot是cglib

在开发中经常的通知有哪些

前置通知Before,后置通知After,环绕通知Around 这三个用的比较多

要想对某个方法进行增强有几种方式

1.继承

2.装饰者

3.动态代理

相关推荐

csgo透视指令是什么 csgo透视指令介绍
beat365最新版2022

csgo透视指令是什么 csgo透视指令介绍

📅 08-26 👁️ 2448
德国世界杯26人大名单
365bet游戏官方开户

德国世界杯26人大名单

📅 08-05 👁️ 5298
站姐是什么意思:明星应援站的管理者(发照片和行程等)
best365网页版登录官方网

站姐是什么意思:明星应援站的管理者(发照片和行程等)

📅 08-17 👁️ 1893
故椟的解释及意思
365bet游戏官方开户

故椟的解释及意思

📅 08-04 👁️ 4097
‎Jorte 日历
365bet游戏官方开户

‎Jorte 日历

📅 08-12 👁️ 6346
车载导航系统多少钱一个
365bet游戏官方开户

车载导航系统多少钱一个

📅 07-14 👁️ 6059