Spring AOP(面向切面编程)

导读:本篇文章讲解 Spring AOP(面向切面编程),希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

一、代理模式

1.理解

为某一个对象(委托类)提供一个代理(代理类),用来控制对这个对象的访问。委托类和代理类有一个共同繁荣父类或父接口。代理类会对请求做预处理、过滤、将请求分配给指定对象。

​ 生活常见的代理情况: 租房中介、婚庆公司等

2.代理模式的二个设计原则

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

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

3.常见代理模式

​ 静态代理和动态代理

二、静态代理

1.静态代理的三要素

a.有共同的行为 – 接口

b.目标角色 – 实现行为

c.代理角色 –实现行为 增强目标对象行为

2.静态代理的特点

1.在应用程序执行前得到目标角色

2.代理对象会增强目标对象的行为

3.目标角色固定

4.有可能存在多个代理 引起“类爆炸”(缺点)

3.静态代理的实现

1.定义共同行为 定义接口
/**
 * 定义行为
 */
public interface Marry {
    public void toMarry();
}
2.目标对象(实现行为)
/**
 * 静态代理 ——> 目标对象
 */
public class You implements  Marry {
    // 实现行为
    @Override
    public void toMarry() {
        System.out.println("我要结婚了...");
    }
}
3.代理对象(实现行为、增强目标对象的行为)
/**
 * 静态代理 ——> 代理对象
 */
public class MarryCompanyProxy implements Marry {

    // 目标对象
    private Marry marry;
    // 通过构造器将目标对象传入
    public MarryCompanyProxy(Marry marry) {
        this.marry = marry;
    }

    // 实现行为
    @Override
    public void toMarry() {
        // 增强行为
        before();
                
        // 执行目标对象中的方法
        marry.toMarry();
        
        // 增强行为
        after();
    }

    /**
     * 增强行为
     */
    private void after() {
        System.out.println("新婚快乐!");
    }

    /**
     * 增强行为
     */
    private void before() {
        System.out.println("场地正在布置中...");
    }
}
4.通过代理对象实现目标对象的功能
// 目标对象
You you = new You();
// 代理对象
MarryCompanyProxy marryCompanyProxy = new MarryCompanyProxy(you);
// 通过代理对象调用目标对象中的方法
marryCompanyProxy.toMarry();

三、动态代理

1.动态代理的特点

1)在应用程序执行时动态创建目标对象

2)代理对象会增强目标对象的行为

3)目标对象不固定

注:JDK动态代理的目标对象必须是接口实现

2、JDK动态代理

1.获取代理对象,实现InvocationHandler接口
public class JdkHandler implements InvocationHandler {

    // 目标对象
    private Object target; // 目标对象的类型不固定,创建时动态生成
    // 通过构造器将目标对象赋值
    public JdkHandler(Object target) {
        this.target = target;
    }

    /**
     *  1、调用目标对象的方法(返回Object)
     *  2、增强目标对象的行为
     * @param proxy 代理类的实例对象
     * @param method  目标对象的方法
     * @param args  目标对象的方法形参
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        // 增强行为
        System.out.println("==============方法前执行");

        // 调用目标对象的方法(返回Object)
        Object result = method.invoke(target,args);

        // 增强行为
        System.out.println("方法后执行==============");

        return result;
    }


    /**
   		 返回一个指定接口的代理类的实例方法调用分派到指定的调用处理程序。 (返回代		 理对象)
     * 得到代理对象
     * public static Object newProxyInstance(ClassLoader loader,
     *                                       Class<?>[] interfaces,
     *                                       InvocationHandler h)
     *      loader:类加载器
     * 	    interfaces:对象实现的接口数组
     * 	    h:InvocationHandler接口
     *
     *
     * @return
     */
    public Object getProxy() {
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }
}

2.通过代理对象实现目标对象的功能
// 目标对象
You you = new You();
// 获取代理对象
JdkHandler jdkHandler = new JdkHandler(you);
Marry marry = (Marry) jdkHandler.getProxy();
// 通过代理对象调用目标对象中的方法
marry.toMarry();

3.CGLIB动态代理

动态代理:继承思想

1.pom.xml文件中引入cglib的相关依赖
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2.2</version>
</dependency>
2.定义类,实现MethodInterceptor接口
public class CglibInterceptor implements MethodInterceptor {

    // 目标对象
    private Object target;
    // 通过构造器传入目标对象
    public CglibInterceptor(Object target) {
        this.target = target;
    }

    /**
     * 1、目标对象的方法调用
     * 2、增强行为
     * @param o
     * @param method
     * @param objects
     * @param methodProxy
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

        // 增强行为
        System.out.println("==============方法前执行");

        // 调用目标对象的方法(返回Object)
        Object result = methodProxy.invoke(target,objects);

        // 增强行为
        System.out.println("方法后执行==============");

        return result;
    }
    
    /**
     * 获取代理对象
     * @return
     */
    public Object getProxy() {
        // 通过Enhancer对象的create()方法可以生成一个类
        Enhancer enhancer = new Enhancer();
        // 设置父类 (将目标类作为其父类)
        enhancer.setSuperclass(target.getClass());
        // 设置回调,调用回调对象中的
        enhancer.setCallback(this);
        return enhancer.create();
    }

}
3.通过代理对象实现目标对象的功能
// 目标对象
You you = new You();
CglibInterceptor cglibInterceptor = new CglibInterceptor(you);
Marry marry = (Marry) cglibInterceptor.getProxy();
marry.toMarry();
//没有接口
User user = new User();
CglibInterceptor cglibInterceptor = new CglibInterceptor(user);
User u = (User) cglibInterceptor.getProxy();
u.test();

4.JDK动态代理和Cglib动态代理

1)JDK动态代理实现接口,Cglib动态代理继承思想

2)JDK动态代理(目标对象存在接口时)执行效率高于Cglib

3)如果目标对象有接口实现,选择JDK动态代理,如果没有接口实现选Cglib动态代理

四、Spring AOP(面向切面编程)

1.什么是AOP?

AOP:Aspect Oriented Programming 面向切面编程

AOP关注不是某一个类或某些方法;控制大量资源,关注的是大量的类和方法。

2.AOP能做什么?

​ AOP主要应用于日志记录,性能统计,安全控制,事务处理等方面,实现公共功能性重复使用。

3.AOP的特点

1)降低模块与模块之间的耦合度,提高业务代码的聚合度。(高内聚低耦合)

2)提高了代码的复用性

3)提高系统的扩展性。(高版本兼容低版本)

4)可以在不影响原有的功能基础上添加新的功能

4.AOP底层实现

​ 动态代理(JDK+CGLIB)

5.AOP基本概念

1)Joinpoint(连接点)

​ 匹配到的每一个方法,spring aop一个连接点即代表一个方法的执行

2)Pointcut(切入点)

​ 匹配规则。规定什么方法被拦截、需要处理什么方法

3)Advice(通知)

​ 拦截到每一个方法(每一个连接点)所执行的操作

​ 前置通知:在方法被执行之前通知

​ 最终通知:无论方法执行过程中是否出现异常,最后都会通知

​ 返回通知:当方法(无异常)执行完毕后通知

​ 异常通知:当方法执行时出现异常通知

​ 环绕通知:可以替代上面四种 有返回值得通知

4)Aspect(切面)

​ 切入点和通知的抽象(与面向对象中的类 相似)

​ 定义切入点和通知(切入点定义了要拦截哪些类的哪些方法,通知则定义了拦截过方法后要做什么)

5)Target(目标对象)

​ 被代理的目标对象

6)Weave(织入)

​ 将切面应用到目标对象 将切面应用到目标对象并生成代理对象的这个过程即为织入

7)Introduction(引入)

​ 在不修改原有应用程序代码的情况下,在程序运行期为类动态添加方法或者字段的过程称为引入

五、Spring AOP实现

1.Spring AOP环境搭建

a.jar包坐标引入Spring AOP
<!--Spring AOP-->
<dependency>
	<groupId>org.aspectj</groupId>
	<artifactId>aspectjweaver</artifactId>
	<version>1.8.9</version>
</dependency>

b.添加spring.xml的配置
xmlns:aop="http://www.springframework.org/schema/aop"

 http://www.springframework.org/schema/aop
 http://www.springframework.org/schema/aop/spring-aop.xsd

2.注解实现

a.定义切面
/**
 * 切面
 *  切入点和通知的抽象   (与面向对象中的 类 相似)
 * 	定义 切入点和通知 (切入点定义了要拦截哪些类的哪些方法,通知则定义了拦截过方法后要做什么)
 */
@Component // 将对象交给IOC容器去实例化
@Aspect // 声明当前类是一个切面
public class LogCut {

    /**
     *  切入点:
     *      匹配规则。规定什么方法被拦截、需要处理什么方法
     *  定义切入点
     *    @Pointcut("匹配规则")
     *  语法:
     *    @Pointcut("execution (* com.shsxt.service..*.*(..) )")
     *    第一个*  代表的是方法的修饰范围 (private、protected、public) *表示所有范围
     *    com.shsxt.service..*.*(..)
     *       匹配com.shsxt.service包下及子包下所有的类的所有方法
     *    com.shsxt.service.*.*(..)
     *       匹配com.shsxt.service包下所有的类的所有方法
     *
     */
    @Pointcut("execution (* com.shsxt.service..*.*(..) )")
    public void cut(){}

    /**
     * 声明前置通知   并将通知应用到定义的切入点上
     * 目标类方法执行前 执行该通知
     *
     */
   @Before(value = "cut()")
    public void before() {
        System.out.println("前置通知.....");
    }

    /**
     * 声明返回通知   并将通知应用到定义的切入点上
     * 目标类方法(无异常)执行后 执行该通知
     *
     */
   @AfterReturning(value = "cut()")
    public void afterReturn() {
        System.out.println("返回通知.....");
    }

    /**
     * 声明最终通知   并将通知应用到定义的切入点上
     * 目标类方法(无异常或有异常)执行后 执行该通知
     *
     */
    @After(value = "cut()")
    public void after() {
        System.out.println("最终通知.....");
    }

    /**
     * 声明异常通知   并将通知应用到定义的切入点上
     * 目标类方法出现异常时 执行该通知
     */
    @AfterThrowing(value="cut()",throwing = "e")
    public void afterThrow(Exception e) {
        System.out.println("异常通知....." + "  异常原因:" + e.getCause());
    }

    /**
     *  声明环绕通知  并将通知应用到切入点上
     *  方法执行前后  通过环绕通知定义相应处理
     *      需要通过显式调用对应的方法,否则无法访问指定方法 (pjp.proceed();)
     * @param pjp
     * @return
     */
    @Around(value = "cut()")
    public Object around(ProceedingJoinPoint pjp) {
        System.out.println("前置通知...");

        Object object = null;
        try {
            object = pjp.proceed();
            System.out.println(pjp.getTarget() + "======" + pjp.getSignature());
            // System.out.println("返回通知...");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("异常通知...");
        }
        System.out.println("最终通知...");

        return object;
    }

}
b.配置文件(spring.xml)
  <!--开启自动化扫描范围-->
   <context:component-scan base-package="com.shsxt"/>
   <!--配置AOP代理-->
   <aop:aspectj-autoproxy/>
c.加载资源
public class Starter {
    public static void main(String[] args) {
        BeanFactory factory=new ClassPathXmlApplicationContext("spring.xml");
        UserService userService= (UserService) factory.getBean("userService");
        userService.test();

    }
}

3.XML实现

a.定义切面
/**
 * 切面
 *  切入点和通知的抽象   (与面向对象中的 类 相似)
 * 	定义 切入点和通知 (切入点定义了要拦截哪些类的哪些方法,通知则定义了拦截过方法后要做什么)
 */
@Component // 将对象交给IOC容器去实例化
public class LogCut02 {


    public void cut(){}


    /**
     * 声明前置通知   并将通知应用到定义的切入点上
     * 目标类方法执行前 执行该通知
     *
     */

    public void before() {
        System.out.println("前置通知.....");
    }

    /**
     * 声明返回通知   并将通知应用到定义的切入点上
     * 目标类方法(无异常)执行后 执行该通知
     *
     */

    public void afterReturn() {
        System.out.println("返回通知.....");
    }

    /**
     * 声明最终通知   并将通知应用到定义的切入点上
     * 目标类方法(无异常或有异常)执行后 执行该通知
     *
     */

    public void after() {
        System.out.println("最终通知.....");
    }

    /**
     * 声明异常通知   并将通知应用到定义的切入点上
     * 目标类方法出现异常时 执行该通知
     */

    public void afterThrow(Exception e) {
        System.out.println("异常通知....." + "  异常原因:" + e.getCause());
    }


    /**
     *  声明环绕通知  并将通知应用到切入点上
     *  方法执行前后  通过环绕通知定义相应处理
     *      需要通过显式调用对应的方法,否则无法访问指定方法 (pjp.proceed();)
     * @param pjp
     * @return
     */

    public Object around(ProceedingJoinPoint pjp) {
        System.out.println("前置通知...");

        Object object = null;
        try {
            object = pjp.proceed();
            System.out.println(pjp.getTarget() + "======" + pjp.getSignature());
            // System.out.println("返回通知...");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("异常通知...");
        }
        System.out.println("最终通知...");

        return object;
    }

}
b.配置文件spring2.xml
  <!--开启自动化扫描范围-->
   <context:component-scan base-package="com.shsxt"/>
<!--aop相关配置--><aop:config>  
     <!--aop切面-->   
    <aop:aspect ref="logCut02">       
    	 <!-- 定义aop 切入点 -->        <aop:pointcut id="cut" 	expression="execution(* com.shsxt.service..*.*(..))"/>     
    	 <!-- 配置前置通知 指定前置通知方法名  并引用切入点定义 -->       	 		<aop:before method="before" pointcut-ref="cut"/>      
    	 <!-- 配置返回通知 指定返回通知方法名  并引用切入点定义 -->        		<aop:after-returning method="afterReturn" pointcut-ref="cut"/>        <!-- 配置异常通知  指定异常通知方法名  并引用切入点定义 -->        <aop:after-throwing method="afterThrow" throwing="e" pointcut-ref="cut"/>       
    	 <!-- 配置最终通知  指定最终通知方法名  并引用切入点定义 -->        		<aop:after method="after" pointcut-ref="cut"/>      
     	<!-- 配置环绕通知  指定环绕通知方法名  并引用切入点定义 -->       		   <aop:around method="around" pointcut-ref="cut"/>   
     </aop:aspect>
</aop:config>
c.加载资源
public class Starter {
    public static void main(String[] args) {
        BeanFactory factory=new ClassPathXmlApplicationContext("spring2.xml");
        UserService userService= (UserService) factory.getBean("userService");
        userService.test();

    }
}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/121463.html

(0)
seven_的头像seven_bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!