Java之Spring AOP入门到精通【IDEA版】

public static void main(String[] args) {

//1、获取容器

AnnotationConfigApplicationContext

ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);

//2、获取Bean对象

UserService userService = ac.getBean(“userService”, UserService.class);

//3、准备数据

User user = new User();

user.setId(“1”);

user.setUsername(“test”);

user.setNickname(“张三”);

//4、执行方法

userService.update(user);

}

}

  • 运行测试类(没有切入)

在这里插入图片描述

在这里插入图片描述

四、AOP常用注解分析


1、用于开启注解AOP支持

(1)@EnableAspectJAutoProxy
1)源码

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Import(AspectJAutoProxyRegistrar.class)

public @interface EnableAspectJAutoProxy {

/**

  • Indicate whether subclass‐based (CGLIB) proxies are to be created

as opposed

  • to standard Java interface‐based proxies. The default is {@code

false}.

*/

boolean proxyTargetClass() default false;

/**

  • Indicate that the proxy should be exposed by the AOP framework as

a {@code ThreadLocal}

  • for retrieval via the {@link

org.springframework.aop.framework.AopContext} class.

  • Off by default, i.e. no guarantees that {@code AopContext} access

will work.

  • @since 4.3.1

*/

boolean exposeProxy() default false;

}

2)说明

作用:

表示开启spring对注解aop的支持。

它有两个属性,分别是指定采用的代理方式和

是否暴露代理对象,通过AopContext可以进行访问。

从定义可以看得出,它引入AspectJAutoProxyRegister.class对象,该对象是基于注解@EnableAspectJAutoProxy注册一个AnnotationAwareAspectJAutoProxyCreator,该对象通过调用

AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

注册一个aop代理对象生成器。

关于AnnotationAwareAspectJAutoProxyCreator请参

属性:

proxyTargetClass:指定是否采用cglib进行代理。默认值是false,表示使用jdk的代理。

exposeProxy: 指定是否暴露代理对象,通过AopContext可以进行访问。

使用场景: 当我们注解驱动开发时,在需要使用aop实现某些功能的情况下,都需要用到此注解。

3)示例

在这里插入图片描述

package cn.itbluebox.config;

import org.springframework.context.annotation.ComponentScan;

import org.springframework.context.annotation.Configuration;

import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration

@ComponentScan(“cn.itbluebox”)

@EnableAspectJAutoProxy

public class SpringConfiguration {

}

2、用于配置切面的

(1)@Aspect
1)源码

/**

*/

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.TYPE)

public @interface Aspect {

/**

  • Per clause expression, defaults to singleton aspect

  • Valid values are “” (singleton), “perthis(…)”, etc

*/

public String value() default “”;

}

2)说明

作用:声明当前类是一个切面类。

属性:

value:默认我们的切面类应该为单例的。但是当切面类为一个多例类时,指定预处理的切入点表达式。用法是perthis(切入点表达式)。

它支持指定切入点表达式,或者是用@Pointcut修饰的方法名称(要求全限定方法名)

使用场景:此注解也是一个注解驱动开发aop的必备注解。

3)示例

@Component

@Scope(“prototype”)//注意:通常情况下我们的切面类是不需要多例的。

@Aspect(value=“execution(* cn.itbluebox.service.impl..(…))”)

public class LogUtil {

/**

  • 用于配置当前方法是一个前置通知

*/

@Before(“execution(* cn.itbluebox.service.impl..(…))”)

public void printLog(){

System.out.println(“执行打印日志的功能”);

}

}

3、用于配置切入点表达式的

(1)@Pointcut
1)源码

/**

*/

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.METHOD)

public @interface Pointcut {

/**

  • The pointcut expression

  • We allow “” as default for abstract pointcut

*/

String value() default “”;

/**

  • When compiling without debug info, or when interpreting pointcuts

at runtime,

  • the names of any arguments used in the pointcut are not available.

  • Under these circumstances only, it is necessary to provide the arg

names in

  • the annotation ‐ these MUST duplicate the names used in the

annotated method.

  • Format is a simple comma‐separated list.

*/

String argNames() default “”;

}

2)说明

作用:

此注解是用于指定切入点表达式的。

属性:

value:用于指定切入点表达式。

argNames:用于指定切入点表达式的参数。参数可以是execution中的,也可以是args中的。通常情况下不使用此属性也可以获得切入点方法参数。

使用场景:在实际开发中,当我们的多个通知需要执行,同时增强的规则确定的情况下,就可以把切入点表达式通用化。此注解就是代替xml中的<aop:pointcut>标签,实现切入点表达式的通用化。

3)示例

在这里插入图片描述

/*

Pointcut通用切入点表达式

execution允许

*/

@Pointcut(“execution(* cn.itbluebox.service.impl.UserServiceImpl.save(…))”)

private void pt1(){}

4、用于配置通知的

(1)@Before
1)源码

/**

*/

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.METHOD)

public @interface Before {

/**

  • The pointcut expression where to bind the advice

*/

String value();

/**

  • When compiling without debug info, or when interpreting pointcuts

at runtime,

  • the names of any arguments used in the advice declaration are not

available.

  • Under these circumstances only, it is necessary to provide the arg

names in

  • the annotation ‐ these MUST duplicate the names used in the

annotated method.

  • Format is a simple comma‐separated list.

*/

String argNames() default “”;

}

2)说明

作用:

被此注解修饰的方法为前置通知。前置通知的执行时间点是在切入点方法执行之

前。

属性:

value:用于指定切入点表达式。可以是表达式,也可以是表达式的引用。

argNames:用于指定切入点表达式参数的名称。它要求和切入点表达式中的参数名称

一致。通常不指定也可以获取切入点方法的参数内容。

使用场景:在实际开发中,我们需要对切入点方法执行之前进行增强, 此时就用到了前置通

知。

在通知(增强的方法)中需要获取切入点方法中的参数进行处理时,就要配合切入点表达

式参数来使用。

3)示例

/**

  • 前置通知

*/

@Before(value = “pt1(user)”,argNames = “user”)

public void beforeLog(User user){

System.out.println(“执行切入点方法前记录日志”+user);

}

(2)@AfterReturning
1)源码

/**

*/

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.METHOD)

public @interface AfterReturning {

/**

  • The pointcut expression where to bind the advice

*/

String value() default “”;

/**

  • The pointcut expression where to bind the advice, overrides

“value” when specified

*/

String pointcut() default “”;

/**

  • The name of the argument in the advice signature to bind the

returned value to

*/

String returning() default “”;

/**

  • When compiling without debug info, or when interpreting pointcuts

at runtime,

  • the names of any arguments used in the advice declaration are not

available.

  • Under these circumstances only, it is necessary to provide the arg

names in

  • the annotation ‐ these MUST duplicate the names used in the

annotated method.

  • Format is a simple comma‐separated list.

*/

String argNames() default “”;

}

2)说明

作用:

用于配置后置通知。

后置通知的执行是在切入点方法正常执行之后执行。

需要注意的是,由于基于注解的配置时,spring创建通知方法的拦截器链时,后置

通知在最终通知之后,所以会先执行@After注解修饰的方法。

属性:

value:用于指定切入点表达式,可以是表达式,也可以是表达式的引用。

pointcut:它的作用和value是一样的。

returning:指定切入点方法返回值的变量名称。它必须和切入点方法返回值名称一

致。

argNames:用于指定切入点表达式参数的名称。它要求和切入点表达式中的参数名称

一致。通常不指定也可以获取切入点方法的参数内容。

使用场景:此注解是用于配置后置增强切入点方法的。被此注解修饰方法会在切入点方法正常执行之后执行。

在我们实际开发中,像提交事务,记录访问日志,统计方法执行效率等等都可以利用后置通知实现。

在这里插入图片描述

User findById(String id);

在这里插入图片描述

public User findById(String id) {

System.out.println(“切入点方法开始执行。。。”);

User user = new User();

user.setId(id);

user.setUsername(“itbluebox”);

user.setNickname(“蓝盒子”);

return user;

}

  • 修改LogUtil

在这里插入图片描述

/*

后置通知

*/

@AfterReturning(value = “execution(* cn.itbluebox.service.impl.. (…))&&args(param)”,

returning = “user”)

public void afterReturningLog(String param,Object user){

System.out.println(“-----------------------”);

System.out.println(“正常执行切入点方法后记录日志,切入点方法的参数 是:”+param);

System.out.println(“正常执行切入点方法后记录日志,切入点方法的返回值 是:”+user);

System.out.println(“-----------------------”);

}

  • 修改测试类

在这里插入图片描述

package cn.itbluebox.test;

import cn.itbluebox.config.SpringConfiguration;

import cn.itbluebox.pojo.User;

import cn.itbluebox.service.UserService;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class SpringAOPTest {

public static void main(String[] args) {

//1、获取容器

AnnotationConfigApplicationContext

ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);

//2、获取Bean对象

UserService userService = ac.getBean(“userService”, UserService.class);

//3、准备数据

User user = new User();

user.setId(“1”);

user.setUsername(“test”);

user.setNickname(“张三”);

//4、执行方法

userService.findById(“1”);

}

}

在这里插入图片描述

在这里插入图片描述

(3)@AfterThrowing
1)源码

/**

*/

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.METHOD)

public @interface AfterThrowing {

/**

  • The pointcut expression where to bind the advice

*/

String value() default “”;

/**

  • The pointcut expression where to bind the advice, overrides

“value” when specified

*/

String pointcut() default “”;

/**

  • The name of the argument in the advice signature to bind the

thrown exception to

*/

String throwing() default “”;

/**

  • When compiling without debug info, or when interpreting pointcuts

at runtime,

  • the names of any arguments used in the advice declaration are not

available.

  • Under these circumstances only, it is necessary to provide the arg

names in

  • the annotation ‐ these MUST duplicate the names used in the

annotated method.

  • Format is a simple comma‐separated list.

*/

String argNames() default “”;

}

2)说明

作用:

用于配置异常通知。

属性:

value:用于指定切入点表达式,可以是表达式,也可以是表达式的引用。

pointcut:它的作用和value是一样的。

throwing:指定切入点方法执行产生异常时的异常对象变量名称。它必须和异常变量名称一致。

argNames:用于指定切入点表达式参数的名称。它要求和切入点表达式中的参数名称一致。

通常不指定也可以获取切入点方法的参数内容。

使用场景:用此注解修饰的方法执行时机是在切入点方法执行产生异常之后执行。

3)示例

在这里插入图片描述

package cn.itbluebox.service.impl;

import cn.itbluebox.pojo.User;

import cn.itbluebox.service.UserService;

import org.springframework.stereotype.Service;

@Service(“userService”)

public class UserServiceImpl implements UserService {

public void save(User user) {

System.out.println(“保存用户信息:”+user);

}

public void update(User user) {

System.out.println(“保存用户信息:”+user);

}

public User findById(String id) {

System.out.println(“切入点方法开始执行。。。”);

User user = new User();

user.setId(id);

user.setUsername(“itbluebox”);

user.setNickname(“蓝盒子”);

int i = 1/0;

return user;

}

}

在这里插入图片描述

在这里插入图片描述

(3)@After
1)源码

/**

*/

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.METHOD)

public @interface After {

/**

  • The pointcut expression where to bind the advice

*/

String value();

/**

  • When compiling without debug info, or when interpreting pointcuts

at runtime,

  • the names of any arguments used in the advice declaration are not

available.

  • Under these circumstances only, it is necessary to provide the arg

names in

  • the annotation ‐ these MUST duplicate the names used in the

annotated method.

  • Format is a simple comma‐separated list.

*/

String argNames() default “”;

}

2)说明

作用:

用于指定最终通知。

属性:

value:用于指定切入点表达式,可以是表达式,也可以是表达式的引用。

argNames:用于指定切入点表达式参数的名称。它要求和切入点表达式中的参数名称

一致。通常不指定也可以获取切入点方法的参数内容。

使用场景:最终通知的执行时机,是在切入点方法执行完成之后执行,无论切入点方法执行是

否产生异常最终通知都会执行。所以被此注解修饰的方法,通常都是做一些清理操作。

3)示例

/**

  • 最终通知

*/

@After(value = “execution(* cn.itbluebox.service.impl..(…))”)

public void afterLog(){

System.out.println(“无论切入点方法执行是否有异常都记录日志”);

}

(4)@Around
1)源码

/**

*/

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.METHOD)

public @interface Around {

/**

  • The pointcut expression where to bind the advice

*/

String value();

/**

  • When compiling without debug info, or when interpreting pointcuts

at runtime,

  • the names of any arguments used in the advice declaration are not

available.

  • Under these circumstances only, it is necessary to provide the arg

names in

  • the annotation ‐ these MUST duplicate the names used in the

annotated method.

  • Format is a simple comma‐separated list.

*/

String argNames() default “”;

}

2)说明

作用:用于指定环绕通知。

属性:

value:用于指定切入点表达式,可以是表达式,也可以是表达式的引用。

argNames:用于指定切入点表达式参数的名称。

它要求和切入点表达式中的参数名称一致。通常不指定也可以获取切入点方法的参数内容。

使用场景:环绕通知有别于前面介绍的四种通知类型。它不是指定增强方法执行时机的,而是

spring为我们提供的一种可以通过编码的方式手动控制增强方法何时执行的机制。

3)示例

/**

  • 环绕通知

*/

@Around(“execution(* com.itheima.service.impl..(…))”)

public Object arountPrintLog(ProceedingJoinPoint pjp){

//1.定义返回值

Object rtValue = null;

try{

//前置通知

System.out.println(“执行切入点方法前记录日志”);

//2.获取方法执行所需的参数

Object[] args = pjp.getArgs();

//3.执行切入点方法

rtValue = pjp.proceed(args);

//后置通知

System.out.println(“正常执行切入点方法后记录日志”);

}catch (Throwable t){

//异常通知

System.out.println(“执行切入点方法产生异常后记录日志”);

}finally {

//最终通知

System.out.println(“无论切入点方法执行是否有异常都记录日志”);

}

return rtValue;

}

五、用于扩展目标类的


1、@DeclareParents

(1)源码

/**

  • Declare parents mixin annotation

*/

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.FIELD)

public @interface DeclareParents {

/**

  • The target types expression

*/

String value();

/**

  • Optional class defining default implementation

  • of interface members (equivalent to defining

  • a set of interface member ITDs for the

  • public methods of the interface).

*/

Class defaultImpl() default DeclareParents.class;

// note ‐ a default of “null” is not allowed,

// hence the strange default given above.

}

(2)说明

作用:

用于给被增强的类提供新的方法。(实现新的接口)

属性:

value:用于指定目标类型的表达式。当在全限定类名后面跟上+时,表示当前类及其子类

defaultImpl:指定提供方法或者字段的默认实现类。

使用场景:当我们已经完成了一个项目的某个阶段开发,此时需要对已完成的某个类加入一些新的方法,我们首先想到的是写一个接口,然后让这些需要方法的类实现此接口,但是如果目标类非常复杂,牵一发而动全身,改动的话可能非常麻烦。

此时就可以使用此注解,然后建一个代理类,同时代理该类和目标类。

(3)示例

在这里插入图片描述

在这里插入图片描述

package cn.itbluebox.service;

import cn.itbluebox.pojo.User;

public interface ValidateService {

boolean checkUser(User user);

}

在这里插入图片描述

在这里插入图片描述

package cn.itbluebox.service.impl;

import cn.itbluebox.pojo.User;

import cn.itbluebox.service.ValidateService;

public class ValidateServiceImpl implements ValidateService {

@Override

public boolean checkUser(User user) {

if(user.getNickname().contains(“孙子”)){

return false;

}

return true;

}

}

  • 创建MyPointcut

在这里插入图片描述

在这里插入图片描述

package cn.itbluebox.pointcut;

import org.aspectj.lang.annotation.Pointcut;

public class MyPointcut {

/**

  • 用于定义通用的切入点表达式

*/

@Pointcut(value = “execution(* cn.itbluebox.service.impl..(…))”)

public void pointcut1(){}

}

  • AOP切面类的配置

在这里插入图片描述

package cn.itbluebox.utils;

import cn.itbluebox.pojo.User;

import cn.itbluebox.service.ValidateService;

import cn.itbluebox.service.impl.ValidateServiceImpl;

import org.aspectj.lang.ProceedingJoinPoint;

import org.aspectj.lang.annotation.*;

import org.springframework.stereotype.Component;

@Component

@Aspect

public class LogUtil {

@DeclareParents(value =

“cn.itbluebox.service.UserService+”, defaultImpl =

ValidateServiceImpl.class)

private ValidateService validateService;
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

为什么我不完全主张自学?
平台上的大牛基本上都有很多年的工作经验了,你有没有想过之前行业的门槛是什么样的,现在行业门槛是什么样的?以前企业对于程序员能力要求没有这么高,甚至十多年前你只要会写个“Hello World”,你都可以入门这个行业,所以以前要入门是完全可以入门的。
②现在也有一些优秀的年轻大牛,他们或许也是自学成才,但是他们一定是具备优秀的学习能力,优秀的自我管理能力(时间管理,静心坚持等方面)以及善于发现问题并总结问题。
如果说你认为你的目标十分明确,能做到第②点所说的几个点,以目前的市场来看,你才真正的适合去自学。

除此之外,对于绝大部分人来说,报班一定是最好的一种快速成长的方式。但是有个问题,现在市场上的培训机构质量参差不齐,如果你没有找准一个好的培训班,完全是浪费精力,时间以及金钱,这个需要自己去甄别选择。

我个人建议线上比线下的性价比更高,线下培训价格基本上没2W是下不来的,线上教育现在比较成熟了,此次疫情期间,学生基本上都感受过线上的学习模式。相比线下而言,线上的优势以我的了解主要是以下几个方面:
①价格:线上的价格基本上是线下的一半;
②老师:相对而言线上教育的师资力量比线下更强大也更加丰富,资源更好协调;
③时间:学习时间相对而言更自由,不用裸辞学习,适合边学边工作,降低生活压力;
④课程:从课程内容来说,确实要比线下讲的更加深入。

应该学哪些技术才能达到企业的要求?(下图总结)

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!
faultImpl =

ValidateServiceImpl.class)

private ValidateService validateService;
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。[外链图片转存中…(img-uuZbP2hI-1711984239434)]

[外链图片转存中…(img-jHQV30hS-1711984239435)]

[外链图片转存中…(img-f3tkm6RN-1711984239435)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

为什么我不完全主张自学?
平台上的大牛基本上都有很多年的工作经验了,你有没有想过之前行业的门槛是什么样的,现在行业门槛是什么样的?以前企业对于程序员能力要求没有这么高,甚至十多年前你只要会写个“Hello World”,你都可以入门这个行业,所以以前要入门是完全可以入门的。
②现在也有一些优秀的年轻大牛,他们或许也是自学成才,但是他们一定是具备优秀的学习能力,优秀的自我管理能力(时间管理,静心坚持等方面)以及善于发现问题并总结问题。
如果说你认为你的目标十分明确,能做到第②点所说的几个点,以目前的市场来看,你才真正的适合去自学。

除此之外,对于绝大部分人来说,报班一定是最好的一种快速成长的方式。但是有个问题,现在市场上的培训机构质量参差不齐,如果你没有找准一个好的培训班,完全是浪费精力,时间以及金钱,这个需要自己去甄别选择。

我个人建议线上比线下的性价比更高,线下培训价格基本上没2W是下不来的,线上教育现在比较成熟了,此次疫情期间,学生基本上都感受过线上的学习模式。相比线下而言,线上的优势以我的了解主要是以下几个方面:
①价格:线上的价格基本上是线下的一半;
②老师:相对而言线上教育的师资力量比线下更强大也更加丰富,资源更好协调;
③时间:学习时间相对而言更自由,不用裸辞学习,适合边学边工作,降低生活压力;
④课程:从课程内容来说,确实要比线下讲的更加深入。

应该学哪些技术才能达到企业的要求?(下图总结)

[外链图片转存中…(img-s0h2dhKc-1711984239436)]

[外链图片转存中…(img-v2Cibecw-1711984239437)]

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!

  • 27
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值