• spring-AOP


    AOP

    简介:

    概念

    AOP:Aspect Oriented Programming 面向对象编程,是OOP面向对象的一种补充。
    将程序中交叉业务逻辑(事物、日志)代码提取出来,封装成切面,由AOP在合适的位置将它封装的切面动态的织入到具体业务逻辑中。
    AOP不是spring特有的

    原理;

    spring原理就是使用动态代理

    • 对于实现接口的目标类,使用的是jdk动态代理
    • 对于没有实现任何接口的目标列,使用的是cglib动态代理

    应用的场合

    适用于具有很切逻辑的场合,比如事物管理、日志记录、性能检测、异常通知、访问控制等。

    作用:

    • 在不改变原有代码基础上动态添加新功能。
    • 模块化

    术语

    • 连接点 JoinPoint
      程序执行的某个特定位置,如方法调用前、方法调用后、方法抛出异常时、方法调用前后等。
    • 切入点 Pointcut
      定位查找到需要连接的点,即切点
    • 增强Advice 也成为通知
      在切点上执行一段代码程序,用来实现某些功能
    • 目标对象 Traget
      将执行增强处理的目标类
    • 织入Weaving
      将增强添加到目标类具体切入点上的过程
    • 代理 Proxy
      一个类被织入增强后,会产生一个代理类
    • 切面 Aspect
      切面和增强的组合
    • 引介 Introduction 也成为引入

    实现原理:

    代理模式回顾

    • 概念:
      为其他对象提供一种代理,以控制对这个对象的访问,起到中介的作用。通过代理对象访问目标对象,可以增强额外的操作,扩展目标对象的功能。

    代理三要素

    • 目标类的接口
    • 目标类的实例
    • 交叉业务逻辑,要执行的操作

    代理分类

    静态代理

    • 解释:
      代理类是程序员创建或者工具生成。所谓静态代理就是程序运行之前你在代理类的字节码文件
    • 缺点:
      代理对象需要和目标对象实现相同的接口,如果接口增加方法,目标对象和代理对象都要维护。
    • 静态代理实操:

    service 接口:

    
    public interface UserService {
    
    
        void  login(String username, String password) throws NoSuchMethodException;
        public String logout() throws NoSuchMethodException;
    }
    

    service 接口实现类:

    
    public class UserServiceImpl implements UserService {
        @Override
        public void login(String username, String password) {
            
              System.out.println("username:"+username+"password:"+password);
            
        }
    
        @Override
        public String logout() {
              System.out.println("UserServiceImpl.logout");
            return "bytebyte";
        }
    }
    

    代理类:

    
    public class UserServiceProxy implements UserService { //目标类的接口
    
        private    UserService userService=new UserServiceImpl();  //代理目标实例
    
        @Override
        public void login(String username, String password) throws NoSuchMethodException {
    
             /* System.out.println("login start at:["+new Date().getTime()+"]");
               userService.login(username, password);*/
            invoke(userService.getClass().getMethod("login",String.class,String.class),username,password); //具体业务逻辑
            
            
    
    
        }
    
        @Override
        public String logout() throws NoSuchMethodException {
    
           /* System.out.println("logout start at:["+new Date().getTime()+"]");
             return userService.logout();*/
           return invoke(userService.getClass().getMethod("logout")).toString();
        }
        //封装到公共方法中
        private Object invoke(Method method, Object... args)
        {
            //1.打印日记
              System.out.println(method.getName()+"login start at:["+new Date().getTime()+"]");
            try {
               return method.invoke(userService,args);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
            return null;
        }
    }
    
    

    main入口

    public static void main(String[] args) {
    
        UserService userservice=new UserServiceProxy();
        try {
            System.out.println(userservice.logout());
            userservice.login("zhangsan","里斯");
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    
    
    }
    
    

    动态代理

    代理类是程序在运行期间由JVM根据反射等机制动态生成的,自动生成代理类和代理对象。所谓动态代理就是程序在运行前不存在代理类的字节码文件。

    动态代理的三种技术:

    Proxy
    特点

    JDK自带技术,使用Jdk自带的Proxy必须实现一个或多个接口,如果没有实现任何接口,则无法使用jdk自带的Proxy技术。

    用法
    • 语法:
    
        Proxy.newProxyInstance{
                                      classLoader,//目标类的加载器
                                      interfaces,// 目标累的接口
                                      InvocationHandler 交叉业务逻辑
                   }
    
    • 实现步骤:
    • UserService
    public interface UserService {
    
    
        void  login(String username, String password) throws NoSuchMethodException;
        public String logout() throws NoSuchMethodException;
    }
    
    • UserServiceImpl
    public class UserServiceImpl implements UserService {
        @Override
        public void login(String username, String password) {
            
              System.out.println("username:"+username+"password:"+password);
            
        }
    
        @Override
        public String logout() {
              System.out.println("UserServiceImpl.logout");
            return "bytebyte";
        }
    }
    
    
    • main:
    
    public static void main(String[] args) throws NoSuchMethodException {
    
        UserService userservice = (aop02.service.UserService)Proxy.newProxyInstance(
                UserServiceImpl.class.getClassLoader(),//目标类的加载器
                new Class[]{UserService.class},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                      System.out.println("方法名:" + method.getName());
                     try{
                         System.out.println("开始事务");
                         Object obj=method.invoke(UserServiceImpl.class, args);
                           System.out.println("提交事务");
                         return obj;
                     }
                     catch (Exception ex)
                     {
                           System.out.println("回滚事务");
                     }
                        return null;
                    }
    
                }
    
    
        );
          System.out.println(userservice.logout());
        userservice.login("张三", "123456");
    
    }
    
    cglib
    解释

    如果没有实现接口,可以使用cglib,它是通过继承来实现。

    语法
    Enhancer.create(
            Class type,
            Callback callback
    )
    
    实现步骤
    • 添加jar包
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.1</version>
    </dependency>
    
    • 使用cglib
    
    HelloWorld helloWorld = (HelloWorld) Enhancer.create(
            HelloWorld.class, //目标类的类型
            new InvocationHandler() { //交叉业务逻辑
                @Override
                public Object invoke(Object o, Method method, Object[] args) throws Throwable {
                    System.out.println(method.getName() + " start at:[" + new Date().getTime() + "]");
                    return method.invoke(new HelloWorld(), args);
                }
            }
    );
    
    helloWorld.sayHello();
    

    Spring AOP配置方式

    Spring 增强的类型

    通知类型 实现接口 描述
    前置通知 MethodBeforeAdvice 在方法执行前添加功能
    后置通知 AfterReturnAdvice 在方法执行后添加功能
    环绕通知 MethedInterceptor 在方法前后执行
    异常通知 ThrowsAdvice 异常通知
    引入通知 IntroductionInterceptor 在目标类添加新方法和属性
    注:多个通知之间不允许有耦合,即多个Advice之间不允许有业务交叉。像事务处理就必须使用环绕通知

    Spring AOP1.x

    • 使用ProxyFactoryBean 手动代理

    使用步骤:

    • 添加jar 包
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-expression</artifactId>
    </dependency>
    
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
    </dependency>
    
    • 定义接口
    public interface UserService {
    
        public void login(String username, String password) throws ClassNotFoundException;
    
        public String logout();
    }
    
    • 实现接口
    public class UserServiceImpl implements UserService {
        @Override
        public void login(String username, String password) {
            
              System.out.println("username:"+username+"password:"+password);
              System.out.println("UserServiceImpl.login");
            
        }
    
        @Override
        public String logout() {
              System.out.println("UserServiceImpl.logout");
            return "bytebyte";
        }
    }
    
    • 配置Advice 实现相应的接口
    public class logAdvice implements MethodBeforeAdvice {
        @Override
        public void before(Method method, Object[] args, Object target) throws Throwable {
            System.out.println(method.getName()+" start at:["+new Date().getTime()+"],args:"+ Arrays.toString(args)+",target:"+target);
    
    
        }
    }
    
    • xml文件配置:
    
    <bean class="aop05.service.impl.UserServiceImpl " id="UserServicetarget"/>
    
    <bean class="aop05.Advice.logAdvice " id="logAdvice"/>
    
    
    <bean class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor" id="logAdvisor">
        <property name="advice" ref="logAdvice"/>
        <property name="mappedNames">
            <list>
                <value>login</value>
                <value>logout</value>
            </list>
        </property>
    </bean>
    
    <bean class="org.springframework.aop.framework.ProxyFactoryBean" id="calcService">
        
        <property name="target" ref="UserServicetarget"/>
        <property name="interfaces">
            <list>
                <value>aop05.service.UserService </value>
            </list>
        </property>
        <property name="interceptorNames">
            <list>
                <value>logAdvisor</value>
            </list>
        </property>
        
    </bean>
    
    • 使用
    public class test {
          public static void main(String[] args) {
    
              ApplicationContext ac = new ClassPathXmlApplicationContext("ioc04/spring.xml");
              UserService userService = (UserService) ac.getBean("userService");
              try {
                  userService.login("sss", "ffff");
                  System.out.println(userService.logout());
                  System.out.println(userService.getClass());
              } catch (Exception ex) {
                  ex.printStackTrace();
    
              }
          }
        
    }
    

    Spring AOP 2.x 配置

    简介

    基于命名空间的配置,原来是使用后处理器,更简单

    特点:

    • 简化配置
    • 非入侵性:编写通知时不需要实现任何接口
    • 使用AspectJ表达式定义切点
      介绍:
      一种表达式,用来定义切点位置
      两种用法
      • within(包名.类型)匹配类中的所有方法
      • execution(返回值类型 包名.类名.方法名(参数类型)) 可使用通配符*和..
    execution(void aop06.service.impl.UserServiceImpl.login(String,String))
    

    四种写法

    方法名 说明
    public void 方法名(JoinPoint) 一般用于前置通知
    public void 方法名(JoinPoint,Object) 有返回值,一般用户后置通知
    public void 方法名(JoinPoint,Exception)
    异常通知
    public void 方法名(ProceedingJoinPoint)
    环绕通知

    步骤:

    • 定义接口
    public interface UserService {
    
    
        public void login(String username, String password) throws ClassNotFoundException;
    
        public String logout();
    }
    
    • 实现接口
    public class UserServiceImpl implements UserService {
        @Override
        public void login(String username, String password) {
    
              System.out.println("username:"+username+"password:"+password);
              System.out.println("UserServiceImpl.login");
            
        }
    
        @Override
        public String logout() {
              System.out.println("UserServiceImpl.logout");
            return "bytebyte";
        }
    }
    
    • 定义增强类,不需要实现任何接口。
    public class logAdvice {
    
        public  void log(JoinPoint joinPoint)
        {
              System.out.println("joinPoint.getArgs():"+ Arrays.toString(joinPoint.getArgs()));
              System.out.println("joinPoint.getClass():"+joinPoint.getClass());
              System.out.println("joinPoint.getThis():"+joinPoint.getThis());
                System.out.println("joinPoint.getTarget():"+joinPoint.getTarget());
            Signature signature = joinPoint.getSignature();
            MethodSignature signaturemethod=(MethodSignature)signature;
            Method method = signaturemethod.getMethod();
            System.out.println("method.getName():"+method.getName());
        }   
    }
    
    • 配置
        <!---Spring 2.x配置 -->
    
        <!---配置目標對象 -->
        <bean id="userService" class="aop06.service.impl.UserServiceImpl"/>
        <!---配置增強類 -->
    
        <bean id="logAdvice" class="aop06.advice.logAdvice"/>
    
        <!---配置pointCut 并织入 -->
        <aop:config>
            <!---配置pointCut -->
         <!--   <aop:pointcut id="pc" expression="within(aop06.service.impl.UserServiceImpl)"/>-->
         <!--   <aop:pointcut id="pc" expression="within(aop06.service.impl.*ServiceImpl)"/>-->
    
            <aop:pointcut id="pc" expression="execution(void aop06.service.impl.UserServiceImpl.login(String,String))"/>
            <aop:aspect ref="logAdvice">
                <!---將logAdvice中的log方法以前入通知的方式织入到pc中-->
               <!-- <aop:before method="log"  pointcut-ref="pc"/>-->
               <!-- <aop:after-returning method="log2" pointcut-ref="pc" returning="returnValue"/>-->
                <!--<aop:after-throwing method="log3" pointcut-ref="pc" throwing="ex"/>-->
                <aop:around method="log4" pointcut-ref="pc"/>
            </aop:aspect>
        </aop:config>
    </beans>
    
    
    • 测试
    
    public static void main(String[] args) {
    
            ApplicationContext ac=new ClassPathXmlApplicationContext("aop06/spring.xml");
        UserService userServicetarget = (UserService)ac.getBean("userService");
        try {
            userServicetarget.login("zhanghua","lisi");
            userServicetarget.logout();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    
  • 相关阅读:
    js+servlet实现百度搜索栏下拉框的实现
    python time模块
    dajngo基础
    MySQL解决时区问题:datadrip com.mysql.cj.exceptions.InvalidConnectionAttributeException: The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more than one time zone.
    数据库连接池
    超星学习通绕开鼠标监控的方法
    centos7 安装python3
    docker命令
    爬虫一
    学习python的第九天笔记
  • 原文地址:https://www.cnblogs.com/lilihai/p/10142086.html
Copyright © 2020-2023  润新知