• 搞懂aop一(基础)


    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

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

  • 相关阅读:
    C# 本地文件夹上传至网络服务器中(待续)
    杭州"人才新政22条" 硕士来杭工作一次性补贴2万元
    12.Redis Select 命令
    HTML input 标签不可编辑的 readonly 属性
    让时间处理简单化 【第三方扩展类库org.apache.commons.lang.time】
    java 给指定时间加上天数or给当前日期加天数
    java事务处理
    基于注解的SpringMVC整合JPA
    Could not open JPA EntityManager for transaction; nested exception is javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: Cannot open connection
    <%@ include file=""%>与<jsp:include page=""/>两种方式的作用
  • 原文地址:https://www.cnblogs.com/mao-yan/p/13530690.html
Copyright © 2020-2023  润新知