spring-AOP

浪子不回头ぞ 提交于 2020-02-21 23:49:25

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();
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!