十、SpringAOP相关术语以及入门案例

一、相关术语

1、Joinpoint(连接点)
Join point: a point during the execution of a program, such as the execution of a method or the handling of an exception. In Spring AOP, a join point always represents a method execution.
通俗的说就是被代理对象中可以被增强的方法(在 spring 中,这些点指的是方法,因为 spring 只支持方法类型的 连接点。)
2、Pointcut(切入点)
Pointcut: a predicate that matches join points. Advice is associated with a pointcut expression and runs at any join point matched by the pointcut (for example, the execution of a method with a certain name). The concept of join points as matched by pointcut expressions is central to AOP, and Spring uses the AspectJ pointcut expression language by default.
在代理对象中被增强的连接点,就叫切入点,切入点都是连接点,连接点不一定是切入点,例如:

/**
 * @auther Mr.Liao
 *
 * 连接点:连接业务与增强方法中的点,可以通过增强把事物的支持加到方法中来
 * 切入点:被增强的连接点(方法)
 */
public interface IAccountService {

    /**
     * 更新
     */
    void updateAccount(Account account);

    /**
     * 转账功能
     * @param sourceName    转出账户名称
     * @param targetName    转入账户名称
     * @param money         转账金额
     */
    void transfer(String sourceName,String targetName,Float money);

    void test();//它只是连接点,但不是切入点,因为没有被增强
}
public class BeanFactory {
    private IAccountService accountService;
    private TransactionManager txManager;

    public void setTxManager(TransactionManager txManager) {
        this.txManager = txManager;
    }
    public final void setAccountService(IAccountService accountService) {
        this.accountService = accountService;
    }
    /**
     * 获取Service代理对象
     * @return
     */
    public IAccountService getAccountService() {
        return (IAccountService) Proxy.newProxyInstance(accountService.getClass().getClassLoader(),
                accountService.getClass().getInterfaces(),
                new InvocationHandler() {
                    /**
                     * 添加事务的支持
                     * @param proxy
                     * @param method
                     * @param args
                     * @return
                     * @throws Throwable
                     * method.invoke()方法参数:1、被代理对象,2、被代理对象方法的参数
                     */
                    // 整个的invoke方法在执行就是环绕通知
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // test方法没有事物的支持
                        if("test".equals(method.getName())){
                            return method.invoke(accountService,args);
                        }
                        Object rtValue;
                        try {
                            //1.开启事务
                            txManager.beginTransaction(); // 前置通知
                            //2.执行操作
                            rtValue = method.invoke(accountService, args);// 在环绕通知中有明确的切入点方法调用
                            //3.提交事务
                            txManager.commit(); // 后置通知
                            //4.返回结果
                            return rtValue;
                        } catch (Exception e) {
                            //5.回滚操作
                            txManager.rollback(); // 异常通知
                            throw new RuntimeException(e);
                        } finally {
                            //6.释放连接
                            txManager.release(); // 最终通知
                        }
                    }
                });
    }
}

对于上面的test()方法,在创建代理对象的时候进行了判断,并没有得到增强,所以不是切入点。

if("test".equals(method.getName())){
  	return method.invoke(accountService,args);
}

3、Advice(通知/增强)
Advice: action taken by an aspect at a particular join point. Different types of advice include “around”, “before” and “after” advice. Many AOP frameworks, including Spring, model an advice as an interceptor, maintaining a chain of interceptors around the join point.
通俗来讲就是在切入点方法执行的时候要对其增强的方法,比如上述代码中对事物控制的几个方法都是通知,其中在切入点方法执行之前执行的是前置通知,在切入点方法之后执行的是后置通知,在catch()中执行的是异常通知finally{}里面执行的是最终通知环绕通知就是整个invoke()方法的执行。
4、Target(目标对象)
Target object: object being advised by one or more aspects. Also referred to as the advised object. Since Spring AOP is implemented using runtime proxies, this object will always be a proxied object.
代理的目标对象,即AccountServiceImpl
5、Weaving(织入)
Weaving: linking aspects with other application types or objects to create an advised object. This can be done at compile time (using the AspectJ compiler, for example), load time, or at runtime. Spring AOP, like other pure Java AOP frameworks, performs weaving at runtime.
是指把增强应用到目标对象来创建新的代理对象的过程,spring 采用动态代理织入。
6、Proxy(代理)
AOP proxy: an object created by the AOP framework in order to implement the aspect contracts (advise method executions and so on). In the Spring Framework, an AOP proxy will be a JDK dynamic proxy or a CGLIB proxy.
一个类被 AOP 织入增强后,就产生一个结果代理类。
7、Aspect(切面)
Aspect: a modularization of a concern that cuts across multiple classes. Transaction management is a good example of a crosscutting concern in enterprise Java applications. In Spring AOP, aspects are implemented using regular classes (the schema-based approach) or regular classes annotated with the @Aspect annotation (the @AspectJ style).
建立的切入点方法和通知方法之间的对应关系,哪个连接要被增强,要被哪些通知方法增强,以及通知方法增强要执行的时机,配合起来就是一个抽象的切面(假装懂了。。。)

二、入门案例

模拟数据库操作,对操作纪录日志

public interface IAccountService {

    /**
     * 模拟保存账户
     */
   void saveAccount();

    /**
     * 模拟更新账户
     */
   void updateAccount(int i);

    /**
     * 删除账户
     */
   int deleteAccount();
}
public class AccountServiceImpl implements IAccountService{

    public void saveAccount() {
        System.out.println("执行了保存");
    }
    public void updateAccount(int i) {
        System.out.println("执行了更新"+i);

    }
    public int deleteAccount() {
        System.out.println("执行了删除");
        return 0;
    }
}

纪录日志的工具类(定义通知方法)

public class Logger {
    /**
     * 用于打印日志:计划让其在切入点方法执行之前执行(切入点方法就是业务层方法)
     */
    public  void printLog(){
        System.out.println("Logger类中的printLog方法开始记录日志了。。。");
    }
}
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.8.7</version>
    </dependency>
</dependencies>

bean.xml配置文件

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

    <!-- 配置spring的IOC,把service对象配置进来-->
    <bean id="accountService" class="com.liaoxiang.service.impl.AccountServiceImpl"></bean>

    <!-- 配置Logger类 -->
    <bean id="logger" class="com.liaoxiang.utils.Logger"></bean>

    <!--配置AOP-->
    <aop:config>
        <!--配置切面 -->
        <aop:aspect id="logAdvice" ref="logger">
            <!-- 配置通知的类型,并且建立通知方法和切入点方法的关联-->
            <aop:before method="printLog" pointcut="execution(* com.liaoxiang.service.impl.*.*(..))"></aop:before>
        </aop:aspect>
    </aop:config>

</beans>

测试方法

public class AOPTest {
    public static void main(String[] args) {
        //1.获取容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.获取对象
        IAccountService as = (IAccountService)ac.getBean("accountService");
        //3.执行方法
        as.saveAccount();
        as.updateAccount(1);
        as.deleteAccount();
    }
}

在这里插入图片描述
AOP配置:

spring中基于XML的AOP 配置步骤:
        1、把通知Bean也交给spring来管理
        2、使用<aop:config/> 标签 表明开始AOP 的配置
        3、使用<aop: aspect/> 标签 表明配置切面
                 id 属性: 是给切面提供一个唯一标识
                ref 属性: 是指定通知类bean的Id
        4、在 <aop:aspect/> 标签的内部使用对应标签来配置通知的类型
               我们现在示例是让printLog方法在切入点方法执行之前之前:所以是前置通知
               <aop: before/>: 表示配置前置通知
                    method属性: 用于指定Logger类中哪个方法是前置通知
                  pointcut属性: 用于指定切入点表达式,该表达式的含义指的是对业务层中哪些方法增强
        5、切入点表达式的写法:
                关键字:execution(表达式)
                表达式:
                    访问修饰符  返回值  包名.包名.包名...类名.方法名(参数列表)
                标准的表达式写法:
                    public void com.liaoxiang.service.impl.AccountServiceImpl.saveAccount()
                访问修饰符可以省略
                    void com.liaoxiang.service.impl.AccountServiceImpl.saveAccount()
                返回值可以使用通配符,表示任意返回值
                    * com.liaoxiang.service.impl.AccountServiceImpl.saveAccount()
                包名可以使用通配符,表示任意包。但是有几级包,就需要写几个*.
                    * *.*.*.*.AccountServiceImpl.saveAccount())
                包名可以使用..表示当前包及其子包
                    * *..AccountServiceImpl.saveAccount()
                类名和方法名都可以使用*来实现通配
                    * *..*.*()
                参数列表:
                    可以直接写数据类型:
                        基本类型直接写名称         int
                        引用类型写包名.类名的方式   java.lang.String
                    可以使用通配符表示任意类型,但是必须有参数
                    可以使用..表示有无参数均可,有参数可以是任意类型
                全通配写法:
                    * *..*.*(..)

                实际开发中切入点表达式的通常写法:
                    切到业务层实现类下的所有方法
                        * com.liaoxiang.service.impl.*.*(..)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值