Spring之Aop

发布时间 2023-06-05 09:13:35作者: YxinHaaa

代理模式

代理模式的两个设计原则:

  1. 代理类 与 委托类 具有相似的行为(共同)

  2. 代理类增强委托类的行为

代理的三要素

a、有共同的行为 - 接口

b、目标角色 - 实现行为

c、代理角色 - 实现行为 增强目标对象行为

静态代理

特点

1、目标角色固定

2、在应用程序执行前就得到目标角色

3、代理对象会增强目标对象的行为

4、有可能存在多个代理 引起"类爆炸"(缺点)

示例

接口

package com.yxh.service;

public interface CrudService {
    void insert();
    void delete();
    void update();
    void selectAll();
}

委托类

package com.yxh.service;

public class CrudServiceImpl implements CrudService{
    @Override
    public void insert() {
        System.out.println("增加");
    }

    @Override
    public void delete() {
        System.out.println("删除");
    }

    @Override
    public void update() {
        System.out.println("修改");

    }

    @Override
    public void selectAll() {
        System.out.println("查询所有");
    }
}

静态代理

package com.yxh.service;

public class CrudServiceImplProxy implements CrudService{
   private CrudServiceImpl csipl =  new CrudServiceImpl();
    @Override
    public void insert() {
        System.out.println("开启事务");
        csipl.insert();
        System.out.println("提交事务");
    }

    @Override
    public void delete() {
        System.out.println("开启事务");
        csipl.delete();
        System.out.println("提交事务");
    }

    @Override
    public void update() {
        System.out.println("开启事务");
        csipl.update();
        System.out.println("提交事务");
    }

    @Override
    public void selectAll() {
        csipl.selectAll();
    }
}

动态代理

相比于静态代理,动态代理在创建代理对象上更加的灵活,动态代理类的字节码在程序运行时,由Java反射机制动态产生。它会根据需要,通过反射机制在程序运行期,动态的为目标对象创建代理对象,无需程序员手动编写它的源代码。动态代理不仅简化了编程工作,而且提高了软件系统的可扩展性,因为反射机制可以生成任意类型的动态代理类。代理的行为可以代理多个方法,即满足生产需要的同时又达到代码通用的目的。

动态代理的两种实现方式:

  1. JDK 动态代理

  2. CGLIB动态代理

特点

  1. 目标对象不固定
  2. 在应用程序执行时动态创建目标对象
  3. 代理对象会增强目标对象的行为

示例:

package com.yxh.service;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class GetCrudServiceImplProxy {
    public static CrudService getProxyClass(){
        CrudService cs = (CrudService) Proxy.newProxyInstance(GetCrudServiceImplProxy.class.getClassLoader(), new Class[]{CrudService.class}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                CrudServiceImpl crudService = new CrudServiceImpl();
                String name = method.getName();
                if (name.contains("selectAll")) {
                    method.invoke(crudService);
                }else {
                    System.out.println("事务开始");
                    method.invoke(crudService);
                    System.out.println("事务提交");
                }
                return null;

            }
        });
        return cs;
    }
}

SpringAop

AOP简介和作用

  • AOP(Aspect Oriented Programming)面向切面编程,一种编程范式,指导开发者如何组织程序结构。
    • OOP(Object Oriented Programming)面向对象编程。
  • 作用:简单的说就是在不改变方法源代码的基础上对方法进行功能增强。
  • Spring理念:无入侵式/无侵入式

image-20230602165039248

  • 连接点(JoinPoint):正在执行的方法,例如:update()、delete()、select()等都是连接点。
  • 切入点(Pointcut):进行功能增强了的方法,例如:update()、delete()方法,select()方法没有被增强所以不是切入点,但是是连接点。
    • 在SpringAOP中,一个切入点可以只描述一个具体方法,也可以匹配多个方法
      • 一个具体方法:com.tyhxzy.dao包下的BookDao接口中的无形参无返回值的save方法
      • 匹配多个方法:所有的save方法,所有的get开头的方法,所有以Dao结尾的接口中的任意方法,所有带有一个参数的方法
  • 通知(Advice):在切入点前后执行的操作,也就是增强的共性功能
    • 在SpringAOP中,功能最终以方法的形式呈现
  • 通知类:通知方法所在的类叫做通知类
  • 切面(Aspect):描述通知与切入点的对应关系,也就是哪些通知方法对应哪些切入点方法。

AOP的真正目的

AOP真正目的是:你写代码的时候,只需考虑主流程,而不用考虑那些不重要的,但又必须要写的其它相同的代码,这些其它的相同代码所在的类就是切面类。

主要应用场景:

1.事务处理

2.日志记录

3.用户权限

AOP入门案例

配置文件实现

【第一步】导入aop相关坐标

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.tyhxzy</groupId>
    <artifactId>spring005_0602</artifactId>
    <version>1.0-SNAPSHOT</version>


    <dependencies>
        <!--junit的依赖-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <!--spring核心依赖,会将spring-aop传递进来-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.10.RELEASE</version>
        </dependency>
        <!--切入点表达式依赖,目的是找到切入点方法,也就是找到要增强的方法-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.4</version>
        </dependency>

        <!-- spring整合junit包 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.2.10.RELEASE</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

接口

package com.yxh.service;

public interface CrudService {
    void add();
    void selectAll();
    void update();
    void delete();
}

实现类

package com.yxh.service;

public class CrudServiceImpl implements CrudService{
    @Override
    public void add() {
        System.out.println("add");
    }

    @Override
    public void selectAll() {
        System.out.println("selectAll");
    }

    @Override
    public void update() {
        System.out.println("update");
    }

    @Override
    public void delete() {
        System.out.println("delete");
    }
}

通知类

package com.yxh.util;

public class TrancationAdvice {
    public void start(){
        System.out.println("start advice---");
    }

    public void end(){
        System.out.println("end advice---");
    }
}


spring配置配置文件applicationContent.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
">

    <bean id="cs" class="com.yxh.service.CrudServiceImpl">
    </bean>

    <bean id="ta" class="com.yxh.util.TrancationAdvice">
    </bean>

    <aop:config>
        <aop:pointcut id="ptc" expression="execution(void com.yxh.service.CrudServiceImpl.*())"/>
        <aop:aspect id="aspc" ref="ta">
            <aop:before method="start" pointcut-ref="ptc"/>
            <aop:after method="end" pointcut-ref="ptc"/>
        </aop:aspect>
    </aop:config>
</beans>

测试类

package com.yxh;

import com.yxh.service.CrudService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@ContextConfiguration(locations = "classpath:applicationContent.xml")
public class AopTest {
    @Autowired
    private CrudService crudService;
    @Test
    public void testAop(){
        crudService.delete();
    }
}