AOP面向切面编程

1.什么是面向切面编程
AspectOrientedProgramming(AOP),面向切面编程,主要面对的处理过程中的某个步骤或阶段,已获得逻辑过程中各部分之间低耦合性的隔离效果。
(即是把某个事物在某个方面的功能提取出来与一批对象进行隔离,这样就与一批对象之间的耦合性降低了,可以就某个功能进行编程)
2.AOP中的面向切面编程
(1)Join point(连接点):程序执行期间的某一个点,例如执行方法或处理异常时候的点,连接点便是方法的执行。
(2)Adivice(通知):指一个切面在特定时间要做的事情。
(3)Aspect(切面):它是一个跨越多个类的模块化的关注点,它是通知(Advice)和切点(Pointcut)合起来的抽象,它定义了一个切点(Pointcut)用来匹配连接点(Join point),也就是需要对需要拦截的那些方法进行定义;它定义了一系列的通知(Advice)用来对拦截到的方法进行增强;(是切入点的集合)
(4)Target object(目标对象):被一个或者多个切面(Aspect)通知的对象,也就是需要被 AOP 进行拦截对方法进行增强(使用通知)的对象,也称为被通知的对象。
(5)AOP Proxy(AOP代理):为了实现切面(Aspect)功能使用 AOP 框架创建一个对象,在 Spring 框架里面一个 AOP 代理要么指 JDK 动态代理,要么指 CgLIB 代理。
(6)Weaving(织入):是将切面应用到目标对象的过程,这个过程可以是在编译时(例如使用 AspectJ 编译器),类加载时,运行时完成。
(7)Joinpoint(切入点):描述拦截的术语。
(8)Introduction(引介):动态添加一些方法,放在拦截目标前后执行。
()
示例
下面定义一个接口,定义了几个假设的功能(因为动态代理是根据接口生成的。所以功能类一定要定义一个接口)

package edu.xalead.AOP;

public interface PersonInterface {
    void lvyou();

    void shangban();

    void tanqin();
}

实现接口

package edu.xalead.AOP;

public class Person implements PersonInterface {
    public void lvyou(){
        System.out.println("旅游");
    }
    public void shangban(){
        System.out.println("上班");
    }
    public void tanqin(){
        System.out.println("探亲");
    }
}

假设一个服务类要调用上面的功能

package edu.xalead.AOP;

import org.springframework.stereotype.Component;

import javax.annotation.Resource;

@Component//将依赖注入到一起
public class PersonService1 {
    @Resource//从容器中自动获取bean
    private Person1 person1;
    public void service(){
        person1.lvyou();
        person1.shangban();
        person1.tanqin();

    }
}

测试类

package edu.xalead.AOP;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test1 {
    public static void main(String[] args) {
        BeanFactory factory = new ClassPathXmlApplicationContext("applicationContext.xml");
        PersonService1 personService1 = factory.getBean(PersonService1.class);
        personService1.service();
    }
}

输出结果:
在这里插入图片描述
我们在旅游、上班、探亲途中都需要做一些共同的事情,比如:穿衣服、吃早饭等,在此我们就可以用面向切面的方式将穿衣服、迟早饭等通用功能抽取出来
下面创建一个通用功能的类

package edu.xalead.AOP;

import org.springframework.stereotype.Component;

@Component("www")
public class Server {
    public  void chuanyifu(){
        System.out.println("穿衣服");
    }
    public  void chizaofan(){
        System.out.println("吃早饭");
    }
}

输出结果:
在这里插入图片描述
3.Spring_AOP配置
实例
(1)添加maven包

 <!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
    <dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>5.1.9.RELEASE</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.4</version>
    </dependency>
    </dependencies>

(2)定义一个接口

package edu.xalead;

public interface MyThing {
    public void lvyou();
}

(3)写一个实现类,实现刚才的接口
(就是我们所要拦截的方法)

package edu.xalead;

import org.springframework.stereotype.Component;

//target
@Component("myThing")
public class MyThingImpl implements MyThing {
    //JoinPoint(切入点 要拦截的方法)
    public void lvyou() {
        System.out.println("旅游");
    }
}

(4)写一个引介(就是我们在在旅游前后要做的事情)

package edu.xalead;

import org.springframework.stereotype.Component;

/**
 * 引介(要做的事情)
 */
@Component
public class CommonThing {
    public void qichuang(){
        System.out.println("起床");
    }
    public void chifan(){
        System.out.println("吃饭");
    }
    public void chuanyi(){
        System.out.println("穿衣");
    }

    public void tuoyi(){
        System.out.println("脱衣");
    }
    public void shuijiao(){
        System.out.println("睡觉");
    }
}

(5)织入(将引介与目标对象织入到一起)
切面配置
注意:一定要将引介和目标对象利用@Component注解将它们放进bean工厂里面,才能织入

 <aop:config>
        <!--要拦截的类-->
        <aop:aspect ref="commonThing">
            <!--配置切入点(以旅游为切入点)-->
            <!--第一个* 表示任意返回值类型
                第二个* 表示以任意名字开头的package. 如 com.xx.
                第三个* 表示以任意名字开头的class的类名 如TestService
                第四个* 表示 通配 *service下的任意class
                最后二个.. 表示通配 方法可以有0个或多个参数-->
            <aop:pointcut id="aa" expression="execution(* edu.xalead.MyThingImpl.*())"/>
            <!--配置前置通知(在切入点之前拦截-->
            <aop:before method="qichuang" pointcut-ref="aa"/>
            <aop:before method="chuanyi" pointcut-ref="aa"/>
            <aop:before method="chifan" pointcut-ref="aa"/>
            <!--配置后置通知(在切入点之后拦截)-->
            <!--after-returning在after后面执行-->
            <aop:after-returning method="shuijiao" pointcut-ref="aa"/>
            <aop:after method="tuoyi" pointcut-ref="aa"/>
        </aop:aspect>
    </aop:config>

(6)测试

package edu.xalead;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        BeanFactory factory = new ClassPathXmlApplicationContext("applicationContext.xml");
        MyThing myThing = (MyThing) factory.getBean("myThing");
        myThing.lvyou();
    }
}

4.Spring通知类型

<aop:pointcut id="aa" expression="execution(* edu.xalead.MyThingImpl.*())"/>

(1)前置通知(before :在目标方法执行前的通知)

            <aop:before method="qichuang" pointcut-ref="aa"/>

(2)后置通知(after-returning:在目标方法执行后通知)

 <aop:after-returning method="shuijiao" pointcut-ref="aa"/>

(3)最终通知(after:在目标方法执行后执行的通知)
注意:最终通=通知与后置通知的不同之处在于,后置通知是在方法正常返回后执行的通知,如果方法没有正常返回(例如抛出异常),则后置通知不会执行

<aop:after method="tuoyi" pointcut-ref="aa"/>

(4)异常通知

            <aop:after-throwing method="yiwai" pointcut-ref="aa" throwing = "e" />

引介方法

    public void yiwai(Exception e){
        System.out.println("出现意外" + e.getMessage());
    }

拦截的方法(代码中加入抛出异常时候的代码,异常抛出的时候执行)

if(1 == 1){
            throw new RuntimeException("意外");
        }

(5)环绕通知(在切入点前后都要执行的方法)

 <aop:around method="around" pointcut-ref="aa"/>

引介方法

 public void around(ProceedingJoinPoint joinPoint){
        //调用方法之前要做的事
        heshui();
        try {
            joinPoint.proceed();//真正调用的方法
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        //调用方法之后要做的事
        shuaya();
    }

五种通知执行的顺序
(1)在目标方法没有抛出异常的情况下

前置通知

环绕通知的调用目标方法之前的代码

目标方法

环绕通知的调用目标方法之后的代码

后置通知

最终通知

(2)在目标方法抛出异常的情况下

前置通知

环绕通知的调用目标方法之前的代码

目标方法 抛出异常 异常通知

最终通知

5.基于注解配置AOP
直接在引介的方法的类中去配
配置方法:
(1)首先要在applicationContext.xml里面加aop:aspectj-autoproxy/配置 启用aspectj自动代理

<aop:aspectj-autoproxy/>

(2)用@Aspect注解来配置切面
(3)利用 @Pointcut注解配置切入点

 @Pointcut("execution(* edu.xalead.MyThingImpl.*())")
    private void aaa(){
        
    }

完整代码

package edu.xalead;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * 引介(要做的事情)
 */
@Component
@Aspect
public class CommonThing {
    @Pointcut("execution(* edu.xalead.MyThingImpl.*())")
    private void aaa(){

    }
    @Before("aaa()")
    public void qichuang(){
        System.out.println("起床");
    }
    @Before("aaa()")
    public void chifan(){
        System.out.println("吃饭");
    }
    @Before("aaa()")
    public void chuanyi(){
        System.out.println("穿衣");
    }
    @After("aaa()")
    public void shuijiao(){
        System.out.println("睡觉");
    }
    @AfterReturning("aaa()")
    public void tuoyi(){
        System.out.println("脱衣");
    }

    public void heshui(){
        System.out.println("喝水");
    }

    public void shuaya(){
        System.out.println("刷牙");
    }
    @AfterThrowing(value = "aaa()",throwing = "e")
    public void yiwai(Exception e){
        System.out.println("出现意外" + e.getMessage());
    }
    @Around("aaa()")
    public void around(ProceedingJoinPoint joinPoint){
        //调用方法之前要做的事
        heshui();
        try {
            joinPoint.proceed();//真正调用的方法
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        //调用方法之后要做的事
        shuaya();
    }
}

6.用配置类代替xml文件(applicationContext.xml)
利用@Configuration注解 声明配置类(创建工厂的时候指定的配置类可以省略这个注解)
创建配置类

package edu.config;

import com.alibaba.druid.pool.DruidDataSource;

import org.springframework.context.annotation.*;

import javax.sql.DataSource;
import java.nio.channels.Channel;
import java.sql.Connection;
//@Configuration
public class SpringConfig {
    private Connection conn = null;
    private  String driverClass = "com.mysql.jdbc.Driver";
    private String url = "jdbc:mysql://localhost:3306/cms";
    private String username = "root";
    private  String password = "root";
    @Bean("druidDataSource")//把 druidDataSource()方法返回的对象放入bean工厂
    @Scope("prototype")
    public DruidDataSource druidDataSource(){

        DruidDataSource ds = new DruidDataSource();
        ds.setUrl(url);
        ds.setDriverClassName(driverClass);
        ds.setUsername(username);
        ds.setPassword(password);
        return ds;
    }
}

测试类

import edu.config.SpringConfig;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import javax.sql.DataSource;

public class Test1 {
    @Test
    public void test1(){
        BeanFactory factory = new AnnotationConfigApplicationContext(SpringConfig.class);
        DataSource ds = (DataSource) factory.getBean("druidDataSource");
    }
}

几个注解
(1)@ComponetScan注解 它可以代替xml配置中的

<context:component-scan base-package="edu.xalead"/>

(2)@EnableAspectJAutoProxy当配置了组件扫描之后,当扫描到aspectj的注解的时候,自动创建aspectj代理,来启动AOP
代替xml配置中的aop:aspectj-autoproxy/>
注意,如果有多个配置类,其它的配置类要标注@Configuration,并保证组件扫描时要可以扫描到这些配置类,这些配置类才可以起作用

package edu.config;

import org.springframework.context.annotation.*;
/**
 * 创建配置类
 */
//@Configuration
//组件扫描 配置包扫描(将有component的注解实例化出来放进工厂)
@ComponentScan(basePackages = "edu")
//开启Aop
@EnableAspectJAutoProxy
//之间将配置类导入进来
//@Import(OtherConfig.class)
public class SpringConfig {

}

(3)@Import注解
如果其它的配置类也想省略@Configuration注解,则必须在工厂默认使用的配置类中使用@Import注解把配置类引用进来

在这里插入图片描述

package edu.config;

import org.springframework.context.annotation.*;
/**
 * 创建配置类
 */
//@Configuration
//组件扫描 配置包扫描(将有component的注解实例化出来放进工厂)
@ComponentScan(basePackages = "edu")
//开启Aop
@EnableAspectJAutoProxy
//之间将配置类导入进来
@Import(OtherConfig.class)
public class SpringConfig {

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值