重零开始学Spring---SpringAOP

一、AOP:

是对OOP编程方式的一种补充。翻译过来为“面向切面编程”。

可以理解为一个拦截器框架,但是这个拦截器会非常武断,如果它拦截一个类,那么它就会拦截这个类中的所有方法。如对一个目标列的代理,增强了目标类的所有方法。

两个解决办法:

1.传统做法:

在添加增强时,根据方法名去判断,是否添加增强,但是这样就得一直去维护这个增强类。

2.面向切面:

将增强类和拦截条件组合在一起,然后将这个切面配置到 ProxyFactory 中,从而生成代理。

二、AOP 和 切面的关系

1.类比于 OOP 和 对象,AOP 和 切面就是这样的一种关系。

2.也可以将 切面 看成是 AOP 的一个工具。

三、几个概念

切面(Advisor):是AOP中的一个术语,表示从业务逻辑中分离出来的横切逻辑,比如性能监控,日志记录,权限控制等。

这些功能都可以从核心的业务逻辑中抽离出去。可以解决代码耦合问题,职责更加单一。封装了增强和切点。

增强(Advice):增强代码的功能的类,横切到代码中。

目标:目标方法(JDK代理)或目标类(CGLIB代理)

代理:JDK代理,CGLIB代理。或是通过 ProxyFactory 类生产。

切点:通过一个条件来匹配要拦截的类,这个条件称为切点。如拦截所有带 Controller 注解的类。增强的条件。

连接点:作为增强方法的入参,可以获取到目标方法的信息。

四、概括为一张图

重零开始学Spring---SpringAOP

SpringAOP 的底层代理的两种方式:

1.jdk动态代理方式(只对实现接口的类进行代理)

1.创建接口

package com.imooc.Demo1;

public interface usersImp {
    public void sava();

    public void update();

    public void delect();

    public void add();


}

2.创建实现类

package com.imooc.Demo1;

public class Users implements  usersImp {
    public void sava() {
        System.out.println("查询");
    }

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

    public void delect() {
        System.out.println("删除");
    }

    public void add() {
        System.out.println("添加");
    }
}


3.创建JDK动态代理

package com.imooc.Demo1;

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

/*Jdk的动态代理*/
public class jdkproxy implements InvocationHandler {

    private usersImp usersImp;

    jdkproxy(usersImp usersImp) {
        this.usersImp = usersImp;
    }

    public Object createProxy() {
//        users.getClass().getClassLoader() 类的加载器
//        users.getClass().getInterfaces() 实现的接口
//        this InvocationHandler接口 实例
        Object proxy = Proxy.newProxyInstance(usersImp.getClass().getClassLoader(), usersImp.getClass().getInterfaces(), this);
        return proxy;
    }

    //进行代理增强
//  Object proxy 代理类对象
//    Method method 代理对象的方法参数
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if ("sava".equals(method.getName())) {
            System.out.println("sava 代理增强");
            return method.invoke(usersImp, args);
        }else {

            return method.invoke(usersImp, args);
        }
    }
}

测试类

package com.imooc.Demo1;

import org.junit.Test;

public class Test2 {
    @Test
    public void test() {
        usersImp usersImp = new Users();

//        对users对象进行代理
        usersImp proxy = (usersImp) new jdkproxy(usersImp).createProxy();

        proxy.add();
        proxy.delect();
        proxy.sava();
        proxy.update();


    }

}


CGLIB 代理(字节码增强技术)

1.创建实体类

package com.imooc.Demo2;


public class Users  {
    public void sava() {
        System.out.println("查询");
    }

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

    public void delect() {
        System.out.println("删除");
    }

    public void add() {
        System.out.println("添加");
    }
}


2.创建cglib 代理类

package com.imooc.Demo2;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class Cjlib implements MethodInterceptor {
    private Users users;

    Cjlib(Users users) {

        this.users = users;
    }

    public Object createCglib() {
        //    1.创建核心类
        Enhancer enhancer = new Enhancer();

//    设置父类
        enhancer.setSuperclass(users.getClass());
//设置反转
        enhancer.setCallback(this);
//        产生代理
        Object proxy = enhancer.create();
        return proxy;
    }

    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        if ("sava".equals(method.getName())) {
            System.out.println("sava 代理增强");
            return methodProxy.invokeSuper(proxy, args);
        }
        return methodProxy.invokeSuper(proxy, args);


    }
}

3.进行测试

import org.junit.Test;

public class test {
    @Test
    public void Test2() {
        Users users = new Users();
//        创建代理类
        Users proxy = (Users) new Cjlib(users).createCglib();
        proxy.add();
        proxy.delect();
        proxy.sava();
        proxy.update();
    }
}

五、增强

1.Weaving(织入):对方法进行增强

(1)前置增强(BeforeAdvice):在目标方法前调用。

(2)后置增强(AfterAdvice):在目标方法后调用。

(3)环绕增强(AroundAdvice):将 Before 和 After ,甚至抛出增强和返回增强合到一起。

(4)返回增强(AfterReturningAdvice):在方法返回结果后执行,该增强可以接收到目标方法返回结果。

(5)抛出增强(AfterThrowingAdvice):在目标方法抛出对应的类型后执行,可以接收到对应的异常信息。

2.Introduction(引入):对类进行增强

(1)引入增强(DeclareParentsAdvice):想让程序在运行的时候动态去实现某个接口,需要引入增强。

Spring 一般切面代码实现(不带切入点)

引入Spring开发所需jar包

<?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.imooc.Springaop</groupId>
    <artifactId>springaop</artifactId>
    <version>1.0-SNAPSHOT</version>
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.0.8.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>5.1.1.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>5.0.8.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>5.0.8.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>5.0.8.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>5.0.8.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.1.1.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>aopalliance</groupId>
        <artifactId>aopalliance</artifactId>
        <version>1.0</version>
    </dependency>
   
</dependencies>

</project>

2.创建接口

package com.imooc.Demo4;

public interface StudentDao {
    public void add();

    public void delect();

    public void update();

    public void sava();
}

3.创建实现类

package com.imooc.Demo4;

public class Studaoimp implements StudentDao {
    public void add() {
        System.out.println("添加");
    }

    public void delect() {
        System.out.println("删除");
    }

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

    public void sava() {
        System.out.println("查询");
    }
}


4.创建增强类

package com.imooc.Demo4;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class MybeforAdvice implements MethodBeforeAdvice {
    //    前置通知
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("前置增强");
    }
}

5.创建spring配置文件并进行配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       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">
    <!--配置目标类-->
    <bean id="Studentdao" class="com.imooc.Demo4.Studaoimp"/>
    <!--前置通知类-->
    <bean id="mybeforAdvice" class="com.imooc.Demo4.MybeforAdvice"></bean>
       <!--是否对类进行代理,当实现类没有接口时 设置为true时 使用CGlib 代理-->
        <!-- <property name="proxyTargetClass" value="true"></property> -->
    <!--Springaop 产生代理-->
    <bean id="SpringDaopoxry" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!--配置目标类-->
        <property name="target" ref="Studentdao"/>
        <!--配置目标类实现的接口-->
        <property name="proxyInterfaces" value="com.imooc.Demo4.StudentDao"/>
        <!--配置要是增强的拦截器名称-->
        <property name="interceptorNames" value="mybeforAdvice"/>


    </bean>
</beans>

6.运行日志

前置增强
添加
前置增强
查询
前置增强
删除
前置增强
修改

SpringAOP 切点切面代码实现(带切入点)

  • 使用一般切面将目标类所有方法进行拦截增强不够灵活,常用带切点的PointcutAdvisor 实现类!

1.创建实体类

package com.imooc.Demo5;



public class Studao {
    public void add() {
        System.out.println("添加");
    }

    public void delect() {
        System.out.println("删除");
    }

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

    public void sava() {
        System.out.println("查询");
    }
}

2.创建通知类

package com.imooc.Demo5;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class MyAroundAdvice implements MethodInterceptor {

    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
//在这前就是环绕前通知
        //       执行目标方法
        System.out.println("环绕前增强");
        Object proceed = methodInvocation.proceed();
//环绕后通知
        System.out.println("环绕后增强");

        return  proceed;
    }

}

3.配置spring 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       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">
    <!--配置目标类-->
    <bean id="Studao" class="com.imooc.Demo5.Studao"></bean>
    <!--环绕通知配置-->
    <bean id="myAroundAdvice" class="com.imooc.Demo5.MyAroundAdvice"></bean>
    <!--一般切面是用通知为切面,带切入点的要先配置待遇切入点的切面-->
    <bean id="myadvice" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
       <!--配置切入点 正则表达式-->
        <property name="pattern" value=".*save.*"/>
        <!--使用哪种配置通知-->
        <property name="advice" ref="myAroundAdvice"/>

    </bean>
    <bean id="test2" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="target" ref="Studao"/>
        <property name="proxyTargetClass" value="true"/>
        <property name="interceptorNames" value="myadvice"/>

    </bean>
</beans>

4.进行测试

package com.imooc.Demo5;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import javax.annotation.Resource;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:Springaop2.xml")
public class test {
    @Resource(name = "test2")
    private  Studao studao;
    @Test
    public  void test(){
        studao.add();
        studao.delect();
        studao.update();
        studao.sava();
    }

}