码迷,mamicode.com
首页 > 其他好文 > 详细

搞懂aop一(基础)

时间:2020-09-02 16:48:21      阅读:40      评论:0      收藏:0      [点我收藏+]

标签:代理   定义   rpo   col   注入   owa   show   aop   src   

aop术语:

1、连接点(Joinpoint): 需要增强的具体位置比如某一个方法调用前,调用后,异常后
2、切点(pointcut): 用于定位连接点。
3、增强(advice):是植入连接点的一段代码
4、目标对象(target):连接点所在的类的实例
5、引介(introduction):可以为类添加属性和方法
6、织入(weaving):将增强添加到具体的目标对象的连接点上。(编译器、类装载期、运行期)
7、代理(Proxy):被增强后的新对象,这个对象融合了原类和增强逻辑的代理类。
8、切面(Aspect): 由切点和增强组成

代理基础:

1、jdk只支持有接口的代理,这个是jdk动态代理的短板。

技术图片
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @author liangjunhui
 * @date Created in 2020-08-19 9:37
 */
public class JDKProxy<T> implements InvocationHandler {
    private T userService;
    /**
     *
     * @param proxy 代理后的对象
     * @param method 连接点对应的方法
     * @param args 方法参数
     * @return 方法返回结果
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("方法前增强");
        method.invoke(userService,args);
        System.out.println("方法后增强");
        return null;
    }

    public T getProxy(T userService){
        this.userService = userService;
        return (T)Proxy.newProxyInstance(userService.getClass().getClassLoader(),userService.getClass().getInterfaces(),this);
    }

    public static void main(String[] args) {
        JDKProxy<UserServiceImpl> jdkProxy = new JDKProxy();
        UserServiceImpl userService = new UserServiceImpl();
        UserService proxy = jdkProxy.getProxy(userService);
        proxy.test("sdfds");
    }
}
JDKProxy

2、cglib代理,可以支持对无接口对象的代理

技术图片
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * @author liangjunhui
 * @date Created in 2020-08-19 9:53
 */
public class CglibProxy<T> implements MethodInterceptor {
    private Enhancer enhancer = new Enhancer();
    public T getUserService(Class<T> cls){
        enhancer.setSuperclass(cls);
        enhancer.setCallback(this);
        return (T)enhancer.create();
    }
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("方法前增强");
        methodProxy.invokeSuper(o,objects);
        System.out.println("方法后增强");
        return null;
    }

    public static void main(String[] args) {
        CglibProxy<UserServiceImpl> cglibProxy = new CglibProxy();
        UserServiceImpl userService = cglibProxy.getUserService(UserServiceImpl.class);
        userService.test("sed");
    }
}
View Code

增强:

技术图片
/**
 * 注意这些增强只是植入了方法的执行位置,这个植入是无差别的,全部的方法都会植入
 * @author liangjunhui
 * @date Created in 2020-08-19 10:17
 */
@Configuration
public class AdviceConfig {
    @Bean
    public ProxyFactoryBean userServiceProxy(){
        ProxyFactoryBean factoryBean = new ProxyFactoryBean();
        // 引介增强需要设置接口名
        // 并强制使用cglib代理,要不然会报ClassCastException
        factoryBean.setInterfaces(Flag.class);
        factoryBean.setProxyTargetClass(true);
        factoryBean.setTargetName("userService");
        factoryBean.setInterceptorNames("afterAdvice","beforeAdvice","aroundAdive","throwingAdvice","introductionAdvice");
        return factoryBean;
    }
    @Bean
    public MethodBeforeAdvice beforeAdvice(){
        // 创建一个前置增强
        return (method, args, target) -> System.out.println("前置增强");
    }
    @Bean
    public AfterReturningAdvice afterAdvice(){
        // 后置通知这个是方法结束的时候做的出发,如果方法执行未完成则不会触发
        return (returnValue, method, args, target) -> System.out.println("后置增强");
    }
    @Bean
    public UserService userService(){
        return new UserService();
    }
    @Bean
    public MethodInterceptor aroundAdive(){
        return invocation -> {
            System.out.println("环绕通知前");
            // 执行目标方法
            invocation.proceed();
            System.out.println("环绕通知后");
            return null;
        };
    }
    @Bean
    public ThrowsAdvice throwingAdvice(){
        /**
         * 这个接口没有定义任何方法,使用该接口,方法名必须是
         *  afterThrowing,参数3个选填,一个必填(Exception)。
         */
        return new ThrowsAdvice(){
            public void afterThrowing(Method method, Object[] args, Object target, Exception ex){
                System.out.println("方法发生异常");
            }
        };
    }
    @Bean
    public IntroductionAdvice introductionAdvice(){
        return new IntroductionAdvice();
    }
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AdviceConfig.class);
        UserService userServiceProxy = (UserService)context.getBean("userServiceProxy");
        userServiceProxy.test("sdf");
        System.out.println("=========================触发引介逻辑===================================");
        ((Flag) userServiceProxy).flag(true);
        userServiceProxy.test("引介");
        System.out.println("=========================有异常出现===================================");
        userServiceProxy.test(null);

    }
}
class UserService {
    public void test(String name){
        System.out.println(name+name.length());
    }
}
interface Flag {
    Boolean flag(Boolean b);
}
class IntroductionAdvice extends DelegatingIntroductionInterceptor implements Flag{
    private ThreadLocal<Boolean> threadLocal = new ThreadLocal<>();
    @Override
    public Object invoke(MethodInvocation mi) throws Throwable {
        if(threadLocal.get()!=null && threadLocal.get()){
            System.out.println("运行期特殊处理");
        }
        return super.invoke(mi);
    }

    @Override
    public Boolean flag(Boolean b) {
        threadLocal.set(b);
        return b;
    }
}
AdviceConfig

切点pointcut

  接口定义:

ClassFilter getClassFilter();
MethodMatcher getMethodMatcher();

  连接点方法匹配

    1、静态:仅对方法签名做匹配(方法名和方法参数及顺序),这种只会判别一次
    2、动态:可以判断方法运行时的入参,这种需要每一次做判别
  切点类型(spring提供了六种)
    1、静态方法切点{@link StaticMethodMatcherPointcut}
    2、动态方法切点{@link DynamicMethodMatcherPointcut}
    3、注解式切点 {@link AnnotationMatchingPointcut}
    4、表达式切点 {@link ExpressionPointcut} 这个是为了支持Aspectj切点表达式语法定义的接口
    5、流程切丁 {@link ControlFlowPointcut} 这个是根据堆栈信息判断目标方法是否由一个方法直接或间接调用,以此判断是否为连接点
    6、复合切点 {@link ComposablePointcut} 可以定义多个切点,每个切点都是返回这个类的对象,因此这个类可以链式调用方法

切面(切点和增强):定义切点和增强,然后通过ProxyFactoryBean配置一个代理对象。这个是一个工厂bean,会为容器注入两个bean,一个是工厂bean本身,一个是工厂bean#getObject()方法获取的对象。创建代理对象是org.springframework.aop.framework.DefaultAopProxyFactory#createAopProxy这个方法决定的

技术图片
@Configuration
public class AdvisorConfig {
    private MethodBeforeAdvice beforeAdvice = (method, args, target) -> System.out.println("前置增强");
     // 静态切面2 正则匹配方法名
    @Bean
    public RegexpMethodPointcutAdvisor regexpMethodPointcutAdvisor(){
        RegexpMethodPointcutAdvisor advisor = new RegexpMethodPointcutAdvisor();
        advisor.setAdvice(beforeAdvice);
        advisor.setPattern(".*tes.*");
        return advisor;
    }
    @Bean
    public ProxyFactoryBean regexpMethodPointcutAdvisorProxy(){
        ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
        proxyFactoryBean.setTargetName("a");
        proxyFactoryBean.setInterceptorNames("regexpMethodPointcutAdvisor");
        return proxyFactoryBean;
    }
    // 动态切面 是由 DefaultPointcutAdvisor 和 DynamicMethodPointcut两个支持的
    @Bean
    public DefaultPointcutAdvisor dynamicMethodPointcutAdvisor(){
        DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
        advisor.setPointcut(new MyDynamicMethodMatcherPointcut());
        advisor.setAdvice(beforeAdvice);
        return advisor;
    }
    // 动态代理
    @Bean
    public ProxyFactoryBean dynamicMethodPointcutAdvisorProxy(){
        ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
        proxyFactoryBean.setTargetName("a");
        proxyFactoryBean.setInterceptorNames("dynamicMethodPointcutAdvisor");
        return proxyFactoryBean;
    }
}
AdvisorConfig

注意:上面是手动创建一个代理,但是这样太过于麻烦(如果有多个切面的,就需手动要配多个),一般不这样做,大多时候都是通过自动代理来实现业务功能,这里知识介绍一下怎么创建一个代理对象。更多请直接参考源码

搞懂aop一(基础)

标签:代理   定义   rpo   col   注入   owa   show   aop   src   

原文地址:https://www.cnblogs.com/mao-yan/p/13530690.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!