❤️‍Spring从入门到大神--AOP从入门到精通

Posted 一只楠喃

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了❤️‍Spring从入门到大神--AOP从入门到精通相关的知识,希望对你有一定的参考价值。


1、AOP介绍

AOP:全称是Aspect Oriented Programming即:面向切面编程。
面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。

简单的说它就是把我们程序重复的代码抽取出来,在需要执行的时候,使用代理的技术,在不修改原来代码的基础上,对已有方法进行增强。

2、 入门案例

2.1、需求:

User类中有方法eat . 现在在不修改eat方法的前提下,对eat进行增强. 增强的内容是,在执行eat之前,先执行 沐浴更衣 这个动作.

2.2、实现思路:

1.搭建基本的测试 可以在测试类中执行eat方法
2.创建切面类,指明对eat方法进行增强;

目标类:Dog

@Component
public class Dog {
public void eat() {
System.out.println(" aop dog 正在吃…");
}
}

配置类:

@Configuration
@ComponentScan(basePackages = {“com.czxy.demo15_aop.domain”,“com.czxy.demo15_aop.aop”})
public class Demo15Configuration {
}

测试类

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = Demo15Configuration.class)
public class TestDemo15 {
@Resource
private Dog dog;
@Test
public void testDemo15() {
dog.eat();
}
}

修改配置类,开启AOP编程

@EnableAspectJAutoProxy //开启AOP

  • 编写切面类 MyAspect,对目标类的方法进行增强

@Component //将当前类,添加到spring容器中
@Aspect //切面编程注解
public class MyAspect {
// 切入点表达式: 方法签名
// @注解(“execution(返回值类型 包名.类名.方法名(参数类型))”)
@Before(“execution(void com.czxy.demo15_aop.domain.Dog.eat())”)
public void init() {
System.out.println(" 15 初始化");
}
}

3、AOP作用和优势

  • 作用:
    1.在程序运行期间,不修改源码对已有方法进行增强。

  • 优势:
    1.减少重复代码
    2.提高开发效率
    3.维护方便

4、 入门案例2

4.1、需求

使用AOP 对UserService接口的两个方法进行增强. 在方法执行之前,开启事务,在方法执行之后关闭事务.
目标类:

@Repository
public class UserDao {
public void insertUser() {
System.out.println(“添加”);
}
public void updateUser() {
System.out.println(“更新”);
}
}

配置类

@Configuration
@ComponentScan(basePackages = {“com.czxy.demo16_aop”})
@EnableAspectJAutoProxy //开启AOP
public class Demo16Configuration {
}

测试类

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = Demo16Configuration.class)
public class TestDemo16 {
@Resource
private UserDao userDao;
@Test
public void testDemo15() {
userDao.insertUser();
System.out.println("----------");
userDao.updateUser();
}
}

切面类

@Component
@Aspect
public class MyAspect {
@Before(“execution(void com.czxy.demo16_aop.dao.UserDao.*User())”)
public void start() {
System.out.println(“开启事务”);
}
@After(“execution(void com.czxy.demo16_aop.dao.UserDao.*User())”)
public void commit() {
System.out.println(“提交事务”);
}
}

5、AOP实现方法

Spring AOP 主要通过2种代理技术来实现:JDK动态代理、CGLIB动态代理

  • JDK动态代理:用于对接口+实现类情况进行代理。

@EnableAspectJAutoProxy(proxyTargetClass = false)

  • CGLIB:用于对仅有类情况进行代理

@EnableAspectJAutoProxy(proxyTargetClass = true)

经过某实践者测试Jdk动态代理的性能强于CGLIB,所以Spring会默认使用JDK动态代理。

6、相关AOP术语

Target(目标对象):
代理的目标对象。
例如:UserServiceImpl
Joinpoint(连接点):
程序的某个特定位置,比如类开始初始化前,初始化后,方法调用前后,抛出异常前后等。
例如:eat方法调用前后
Pointcut(切入点):
基于连接点,链接点描述的可以增强的点,如果真的在这个点增强了,那这个点就叫做切入点。
例如:eat方法调用之前如果进行了增强,那这个点就叫做切入点
Advice(通知/增强):
需要添加的那段增强功能的逻辑代码,
通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。
例如:要在eat之前添加 wash,之后添加clean ,那wash和clean就是增强
Aspect(切面):
是切入点和通知的结合。
例如:MyAspect类
Proxy(代理):
一个类被AOP织入增强后,就产生一个结果代理类。
织入:
增强添加到目标类的具体链接点上的过程。

7、相关注解

7.1、切入点表达式

execution:
匹配方法的执行(常用)
execution(表达式)
表达式语法:execution([修饰符] 返回值类型 包名.类名.方法名(参数))
写法说明:
全匹配方式:
public void com.itheima.service.impl.CustomerServiceImpl.saveCustomer()
访问修饰符可以省略
void com.itheima.service.impl.CustomerServiceImpl.saveCustomer()
返回值可以使用号,表示任意返回值
* com.itheima.service.impl.CustomerServiceImpl.saveCustomer()
包名可以使用
号,表示任意包,但是有几级包,需要写几个*
* ....CustomerServiceImpl.saveCustomer()
使用…来表示当前包,及其子包
* com…CustomerServiceImpl.saveCustomer()
类名可以使用号,表示任意类
* com…
.saveCustomer()
方法名可以使用号,表示任意方法
* com…
.()
参数列表可以使用
,表示参数可以是任意数据类型,但是必须有参数
* com….()
参数列表可以使用…表示有无参数均可,有参数可以是任意类型
* com…
.(…)
全通配方式:
* .
(…)

7.2、通知方法

  • 方式1:没有参数形式

@Before(“execution(public void com.czxy.demo01.User.eat())”)
public void bf01(){
System.out.println("洗手 ");
}

  • 方式2:获得参数JoinPoint,从而获得目标类,目标方法等信息

import org.aspectj.lang.JoinPoint;

@Before(“execution(public void com.czxy.demo01.User.eat())”)
public void bf01(JoinPoint jp){
System.out.println("洗手 ");
System.out.println(“目标类:”+jp.getTarget());//获取目标类
System.out.println(“切入点:”+jp.getSignature());//获取切入点

执行结果

  • 方式3:环绕通知获得参数ProceedingJoinPoint,对目标方法的执行进行控制。

@Around(“execution(public void com.czxy.demo01.User.eat())”)
public void ar(ProceedingJoinPoint jp) throws Throwable {
System.out.println("洗手 ");
jp.proceed();//执行 eat方法
System.out.println(“擦嘴”);
}

执行效果:

7.3、抽取公共切入点

使用@PointCut可以将公共的切入点进行抽取,一般都声明在私有方法上。
在通知注解使用,通过方法名引用。

@Pointcut(“execution(* com.czxy.service….(…))”)
private void myPointcut(){
}
@Before(“myPointcut()”)
public void bf(JoinPoint joinPoint){
System.out.println(“前置…” + joinPoint.getTarget());
System.out.println(“前置…” + joinPoint.getSignature().getName());
}
@AfterReturning(“myPointcut()”)
public void af(){
System.out.println(“后置…”);
}

8、完整通知演示

8.1、AOP编程

  • 编写需要对目标类,增量的类和方法(可以复制)

@Component
@Aspect
public class MyAspect {
@Before(“execution(* com.czxy.service….(…))”)
public void bf(){
System.out.println(“开始”);
}
@AfterReturning(“execution(* com.czxy.service….(…))”)
public void af(){
System.out.println(“结束”);
}
}

  • 修改配置类

@ComponentScan(basePackages={“com.czxy”})
@EnableAspectJAutoProxy
public class SpringConfigruation {
}

  • 测试

8.2、目标接口和类

  • 接口

public interface UserService {
public void saveUser();
public String updateUser();
}

  • 实现类

@Service
public class UserServiceImpl implements UserService {
@Override
public void saveUser() {
System.out.println(“save”);
}
@Override
public String updateUser() {
System.out.println(“update”);
return “abc”;
}
}

8.3、配置类

@ComponentScan(basePackages={“com.czxy”})
@EnableAspectJAutoProxy
public class SpringConfigruation {
}

8.4、切面类

// 切面类也要交给IOC
@Component
// 声明当前类是切面类
@Aspect
public class PersonAspect {
// 抽取公共切入点
@Pointcut(“execution(public String com.czxy.domain.Person.eat())”)
public void pc1(){
}
@Before(“pc1()”)
public void b01(){
System.out.println("洗手手 ");
}
// 环绕通知 在 eat前后都会增强
@Around(“pc1()”)
public Object ar(ProceedingJoinPoint pjp) throws Throwable {
System.out.println(“洗手”);
// 让目标方法 执行 也就是 eat执行
Object obj= pjp.proceed();
System.out.println(“擦嘴”);
// 环绕通知完, 把 目标方法的返回值 返回。 这样 @AfterReturning才能捕获到结果
return obj;
}
@After(“pc1()”)
public void a01(){
System.out.println(“盘子舔干净”);
}
@AfterReturning(value = “pc1()”,returning = “rel”)
public void a02(JoinPoint joinPoint , String rel){
System.out.println("嘿嘿嘿 "+rel);
}
@AfterThrowing(value=“pc1()”,throwing=“ex”)
public void at(JoinPoint joinPoint ,Throwable ex){
System.out.println(“异常:” + ex.getMessage());
}
}

8.5、测试类

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes={SpringConfigruation.class})
public class UserDaoTest {
@Resource
private UserService userService;
@Test
public void testFindAll() throws SQLException{
userService.saveUser();
int i = 1/0;
userService.updateUser();
}
}

以上是关于❤️‍Spring从入门到大神--AOP从入门到精通的主要内容,如果未能解决你的问题,请参考以下文章

❤️‍Spring从入门到大神--Spring整合MyBatis

❤️‍Spring全家桶从入门到大神--spring mvc 参数绑定

❤️‍Spring全家桶从入门到大神-- spring mvc 拦截器

❤️‍Spring从入门到大神--事务管理(搞笑漫画讲解!简单易懂!!)

❤️‍Spring全家桶从入门到大神--mvc 常见知识点

❤️‍Spring全家桶从入门到大神--SpringMVC之SSM整合(用户-增删改查!)