前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringAOP---ProxyFactoryBean

SpringAOP---ProxyFactoryBean

作者头像
小土豆Yuki
发布2020-06-15 17:40:05
6150
发布2020-06-15 17:40:05
举报
文章被收录于专栏:洁癖是一只狗洁癖是一只狗

在面试中我们经常被问到spring的AOP,今天我也说一下AOP.

AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

AOP底层采用两种动态代理模式实现:

1.JDK的动态代理

2. CGLIB动态代理

当被代理的对象没有实现接口时候,使用的是CGLIB代理,当被代理的对象实现接口时候使用的是JDK代理,当然也可以强制使用CGLIB代理。

spring有支持3中类型实现AOP

  1. 需要实现接口,手工创建代理,最原始的实现方式
  2. 使用XML配置。aop命名空间
  3. 使用注解(@AspectJ)

我们今天主要是讲解最原始的方式,手工创建代理,实现AOP.这种当时几乎不用了,主要是让大家理解AOP的作用以及原理,学习之前我们先搞清楚几个概念.

1.切面(Aspect)

交叉业务逻辑,例如事务、安全、日志等,称为切面。

2.目标对象(Target)

业务类的对象,称为目标对象。

3.织入(Weaving)

将切面插入到目标对象的目标方法的过程,称为织入。

4.连接点(JoinPoint)

目标对象中可以被切面织入的方法。

5.切入点(Pointcut)

目标对象中真正被切面织入的方法。切入点一定是连接点,但连接点不一定是切入点。被标记为final的方法是不能作用连接点与切入点的。

6.通知(Advice)

通知是切面的一种,可以完成简单的织入功能。通知可以定义切面织入的时间点,切入点定义了切面织入的位置。

7.顾问(Advisor)

顾问是切面的一种,能够将通知以更为复杂的方式织入到目标对象中,是将通知包装为 更复杂的切面的装配器。

上面的通知(Advice)和顾问(Advisor)是两种织入的方式,他们两也是有区别的,通知是给目标对象的所有方法都会织入切面,而Advisor是有选择的给目标对象的方法织入切面。我们接下来就看一下,如何基于手动创建代理方式并且使用Advisor织入来演示Spring AOP,先看一下他的关系图

我们按照上面的图实现AOP

  1. 实现classFilter 和MethodMatcher
代码语言:javascript
复制
package com.jiepi.spring.util;

import com.jiepi.spring.serviceImp.Person;
import org.springframework.aop.ClassFilter;

public class MyClassFilter implements ClassFilter {

    /*
     *  1.一个接口下会有多个实现类
     *  2.判断当前实现类是不是我们织入方式关心的目标类
     *  BaseService接口我们现在只想管理Person.
     *  参数:就是当前被拦截类:可能Person,可能Gog
     * */
    @Override
    public boolean matches(Class<?> clazz) {
        if (clazz == Person.class) {
            return true;
        }
        return false;
    }
}
代码语言:javascript
复制
package com.jiepi.spring.util;

import org.springframework.aop.MethodMatcher;

import java.lang.reflect.Method;

public class MyMethodMatcher implements MethodMatcher {


    /*
     *  被监控接口比如(BaseService),没有重载方法
     *  每一个方法名称都是以唯一
     *  此时可以采用 static检测方式,只根据方法名称判断
     * 参数:method: 接口中某一个方法
     *     targetClass: 接口中一个实现类
     *
     *  业务:只想为Person类中play方法提供织入
     */
    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        String methodName = method.getName();
        if ("play".equals(methodName)) {
            return true;
        }
        return false;
    }

    @Override
    public boolean isRuntime() {
        return false;
    }

    @Override
    public boolean matches(Method method, Class<?> targetClass, Object... args) {
        return false;
    }
}

2.实现切入点PointCut实现获取那个对象的那个方法要进行织入切面

代码语言:javascript
复制
package com.jiepi.spring.util;

import org.springframework.aop.ClassFilter;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut;

public class MyPointCut implements Pointcut {

    private ClassFilter classFilter;

    private MethodMatcher methodMatcher;

    public void setClassFilter(ClassFilter classFilter) {

        this.classFilter = classFilter;
    }

    public void setMethodMatcher(MethodMatcher methodMatcher) {

        this.methodMatcher = methodMatcher;
    }

    @Override
    public ClassFilter getClassFilter() {
        return this.classFilter;
    }

    @Override
    public MethodMatcher getMethodMatcher() {
        return this.methodMatcher;
    }
}

3.实现 切面 advice

代码语言:javascript
复制
package com.jiepi.spring.advice;

import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

public class MyAfterAdvice  implements AfterReturningAdvice {

    //切面:次要业务
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("玩完之后我要给洁癖洗澡");
    }
}

4.实现PointcutAdvisor实现绑定切入点和切面

代码语言:javascript
复制
package com.jiepi.spring.util;

import org.aopalliance.aop.Advice;
import org.springframework.aop.Pointcut;
import org.springframework.aop.PointcutAdvisor;

public class MyPointCutAdvisor implements PointcutAdvisor {

    private Pointcut pointcut;//当前拦截对象和对象调用主要业务方法 person对象.play()

    private  Advice advice; //次要业务以及次要业务与主要业务执行顺序

    public void setPointcut(Pointcut pointcut) {
        this.pointcut = pointcut;
    }

    public void setAdvice(Advice advice) {
        this.advice = advice;
    }

    @Override
    public Pointcut getPointcut() {

        return this.pointcut;
    }

    @Override
    public Advice getAdvice() {

        return this.advice;
    }

    @Override
    public boolean isPerInstance() {
        return false;
    }
}

5.目标对象

代码语言:javascript
复制
package com.jiepi.spring.service;

public interface BaseService {

    void eat();

    void play();
}
代码语言:javascript
复制
package com.jiepi.spring.serviceImp;

import com.jiepi.spring.service.BaseService;

public class Person implements BaseService {

    @Override
    public void eat() {
        System.out.println("洁癖主人吃汉堡包");
    }

    @Override
    public void play() {
        System.out.println("洁癖主人玩电脑");
    }
}

6.配置依赖注入,实现手动创建代理对象

代码语言:javascript
复制
<?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:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       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">

    <!-- 注册被监控实现类 -->
    <bean id="person" class="com.jiepi.spring.serviceImp.Person"/>
    <bean id="dog" class="com.jiepi.spring.serviceImp.Dog"/>

    <!-- 注册通知实现类 -->
    <bean id="after" class="com.jiepi.spring.advice.MyAfterAdvice"/>
    <!-- 注册类型过滤器 -->
    <bean id="classFilter" class="com.jiepi.spring.util.MyClassFilter"/>
    <!-- 注册方法匹配器 -->
    <bean id="methodMatcher" class="com.jiepi.spring.util.MyMethodMatcher"/>
    <!-- 注册切入点 -->
    <bean id="pointCut" class="com.jiepi.spring.util.MyPointCut">
        <property name="classFilter" ref="classFilter"/>
        <property name="methodMatcher" ref="methodMatcher"/>
    </bean>
    <!-- 注册顾问 -->
    <bean id="myAdvisor" class="com.jiepi.spring.util.MyPointCutAdvisor">
        <property name="advice" ref="after"/>
        <property name="pointcut" ref="pointCut"/>
    </bean>
    <!-- 注册代理对象工厂 -->
    <!--
                          此时生成代理对象,只会负责person.play方法监控
                          与Advice不同,不会对BaseService所有的方法进行监控
     -->
    <bean id="personFactory" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="target" ref="person"/>
        <property name="interceptorNames" value="myAdvisor"/>
    </bean>


</beans>

7.测试结果

代码语言:javascript
复制
package com.jiepi.spring;

import com.jiepi.spring.service.BaseService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestMain {

    @Test
    public void test() {
        ApplicationContext factory = new ClassPathXmlApplicationContext("spring_config.xml");
        BaseService service = (BaseService) factory.getBean("personFactory");
        service.play();
        System.out.println("===========下面是没有进行代理的方法================");
        service.eat();
    }
}

运行结果:
洁癖主人玩电脑
玩完之后我要给洁癖洗澡
===========下面是没有进行代理的方法================
洁癖主人吃汉堡包

可以看到我们的结果,仅仅对person目标对象中的play使用了AOP,织入了其他业务。

这个例子仅仅是让大家理解SpringAOP.并不建议使用,因为写起来太繁琐了,我们大部分工作用的都是AspectJ.其他方式的使用有机会再和大家分享.

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-09-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 洁癖是一只狗 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.切面(Aspect)
  • 2.目标对象(Target)
  • 3.织入(Weaving)
  • 4.连接点(JoinPoint)
  • 5.切入点(Pointcut)
  • 6.通知(Advice)
  • 7.顾问(Advisor)
相关产品与服务
云顾问
云顾问(Tencent Cloud Smart Advisor)是一款提供可视化云架构IDE和多个ITOM领域垂直应用的云上治理平台,以“一个平台,多个应用”为产品理念,依托腾讯云海量运维专家经验,助您打造卓越架构,实现便捷、灵活的一站式云上治理。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档