尚硅谷-Spring5.2.6-源码讲解课程-AOP篇

AOP部分的知识相比IoC部分的要少很多,也很容易理解~

AOP基本概念

AOP,面向切面编程,AOP功能的通俗理解:不修改源代码但可以实现功能新增

作用:降低耦合度。

AOP底层原理

技术:动态代理

实现:有接口的情况,通过JDK动态代理;没有接口,通过CGLIB动态代理

JDK动态代理

1.创建接口和接口对应的实现类

public interface UserDao {
    public int add(int a, int b);

    public String show(String str);
}
public class UserDaoImpl implements UserDao {
    @Override
    public int add(int a, int b) {
        System.out.println("add方法执行...");
        return a + b;
    }

    @Override
    public String show(String str) {
        System.out.println("show方法执行...");
        return str;
    }
}

2.通过实现InvocationHandler接口创建代理类 

3.调用Proxy类的静态方法newProxyInstance生成代理对象

public class JDKProxy {
    public static void main(String[] args) {
        //创建接口实现类对象
        Class[] interfaces = {UserDao.class};
        //匿名内部类实现
/*        Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                return null;
            }
        });*/

        //实际业务中,调用代理对象的那个对象通常是通过前端等方式获取的
        UserDao userDao = new UserDaoImpl();
        //实现InvocationHandler接口,创建代理类方式
        UserDao userDaoProxy = (UserDao)Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao));
        int add = userDaoProxy.add(6, 6);
        System.out.println("ending...");
    }
}

//创建代理对象
class UserDaoProxy implements InvocationHandler {

    private Object obj;

    //通过有参构造将调用代理的对象进行传参,获取对应的对象
    public UserDaoProxy(Object obj) {
        this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("AOP切面实现方法增强:核心业务逻辑执行之前... ...");
        System.out.println(method.getName() + "::" + Arrays.toString(args));
        Object result = method.invoke(obj, args);
        System.out.println("AOP切面实现方法增强:核心业务逻辑执行之后... ...");
        return result;
    }
}

AOP术语

1.连接点:可以被通知或增强的方法

2.切入点:实际被通知或增强的方法

3.通知(增强):通知或增强的方法,前置通知、后置通知、异常通知、环绕通知、最终通知...

4.切面:一个动作!把通知或增强切入到切入点的过程

AOP操作

实现:Spring框架中一般基于AspectJ框架实现,AspectJ是独立于Spring的框架。

两种实现方式

基于xml配置或注解方式(开发中实际使用的一般都是注解方式)

导入依赖:spring-aspects、aspectj相关

切入点表达式

execution([权限修饰符][返回类型][类全路径][方法名称]([参数列表]))

权限修饰符通常是*,返回类型通常省略,方法名称可以写*,参数列表通常写..

eg.execution(* com.coffeeship.dao.UserDao.*(..))

表示对com.coffeeship.dao.UserDao类中的所有方法进行增强。

AspectJ注解方式实现

1.创建需要被增强的类,定义一个方法用作切入点

@Component
public class User {
    public void add() {
        System.out.println("add... ...");
    }
}

2.创建增强类,类上添加注解@Aspect 

3.定义增强方法,方法上添加注解@Before或@After等,并使用切入点表达式

@Component
@Aspect
public class UserProxy {

    @Before("execution(* com.coffeeship.dao.User.add(..))")
    public void before() {
        System.out.println("before add ... ...");
    }
}

4.配置文件中开启生成代理对象的功能

<?xml version="1.0" encoding="UTF-8"?>
<beans default-lazy-init="true" xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       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/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">

    <!--开启注解扫描-->
    <context:component-scan base-package="com.coffeeship"/>

    <!--开启Aspect生成代理对象-->
    <aop:aspectj-autoproxy/>
</beans>

测试以下 

@Test
public void test01() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("aopconfig.xml");
    User user = applicationContext.getBean("user", User.class);
    user.add();
}

 

 其他通知或增强执行顺序

@Component
@Aspect
public class UserProxy {
    //方法执行之前执行
    @Before("execution(* com.coffeeship.dao.User.add(..))")
    public void before() {
        System.out.println("before add ... ...");
    }
    //方法执行之后执行
    @After("execution(* com.coffeeship.dao.User.add(..))")
    public void after() {
        System.out.println("after add ... ...");
    }
    //方法异常后执行
    @AfterThrowing("execution(* com.coffeeship.dao.User.add(..))")
    public void afterThrowing() {
        System.out.println("afterThrowing add ... ...");
    }
    //方法返回值之后执行
    @AfterReturning("execution(* com.coffeeship.dao.User.add(..))")
    public void afterReturning() {
        System.out.println("afterReturning add ... ...");
    }
    //环绕通知
    @Around("execution(* com.coffeeship.dao.User.add(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("around before add ... ...");
        proceedingJoinPoint.proceed();
        System.out.println("around after add ... ...");
    }
}

注意@After和@AfterReturning的执行顺序和区别

异常发生时的执行顺序:有异常时,@AfterReturning就没执行了,但@After总会执行,@After是最终通知,可以理解成try-catch-finally中的finally。

公共切入点抽取,重用切入点

@Pointcut("execution(* com.coffeeship.dao.User.add(..))")
public void pointcut() {}

//方法执行之前执行
//    @Before("execution(* com.coffeeship.dao.User.add(..))")
@Before("pointcut()")
public void before() {
    System.out.println("before add ... ...");
}

 设置增强类的优先级

@Order(优先级数),数越小,优先级越高

@Component
@Aspect
@Order(1)
public class AdminProxy {
    @Pointcut("execution(* com.coffeeship.dao.User.add(..))")
    public void pointcut() {}

    //方法执行之前执行
    //    @Before("execution(* com.coffeeship.dao.User.add(..))")
    @Before("pointcut()")
    public void before() {
        System.out.println("Admin before add ... ...");
    }
}

完全注解开发

@ComponentScan(basePackages = {"com.coffeeship"}) 表明开启组件扫描

@EnableAspectJAutoProxy(proxyTargetClass = true) 表明开启AOP生成代理对象

@Configuration
@ComponentScan(basePackages = {"com.coffeeship"})
//@EnableAspectJAutoProxy 开启AOP生成代理对象,proxyTargetClass默认是true
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AOPConfig {

}
@Test
public void test02() {
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AOPConfig.class);
    User user = applicationContext.getBean("user", User.class);
    user.add();
}

AspectJ配置文件实现(了解即可)

1.创建增强类和被增强类,定义增强类的增强方法和被增强类的被增强方法

public class Book {
    public void buy() {
        System.out.println("buy books... ...");
    }
}
public class BookProxy {
    public void before() {
        System.out.println("before buying ... ...");
    }
}

2.XML文件配置bean和AOP增强

        配置切入点 → 配置切面:增强类的bean和增强类型、切入点

<?xml version="1.0" encoding="UTF-8"?>
<beans default-lazy-init="true" 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">

    <!--创建对象-->
    <bean id="book" class="com.coffeeship.dao.Book"/>
    <bean id="bookProxy" class="com.coffeeship.aop.BookProxy"/>

    <!--配置AOP增强-->
    <aop:config>
        <aop:pointcut id="pointcut" expression="execution(* com.coffeeship.dao.Book.buy(..))"/>
        <aop:aspect ref="bookProxy">
            <aop:before method="before" pointcut-ref="pointcut"/>
        </aop:aspect>
    </aop:config>

</beans>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值