SpringAOP随笔

  1. 什么是aop:

  • 面向切面编程(Aspect Oriented Programming)
  • aop是一种编程方法,通过预编译和动态代理模式结合来实现。
  • 使用SpringAOP对业务逻辑的各个部分可以进行隔离,例如:日志,开关事务等,让这些和业务逻辑相关的代码和业务逻辑分离开。抽取在一个公共的组件中,然后通过我们的程序在执行中动态地织入到业务代码的合适位置。
  • SpringAOP降低程序的耦合度。而且可以大大减少代码量。还可以达到程序重用的效果。
  1. SpringAOP应用场景

  • 场景:针对非业务代码但是又不得不做的,不可能针对每个模块都编写一次。

  • 解决:

    • 如开启和关闭事务

      • 第一种实现:

        • public class UserServiceImpl implements UserService {
          
            @Override
            public void updateUser(User user) {
                beginTransaction();
                System.out.println(user);
                endTransaction();
            }
          
            @Override
            public void addUser(User user) {
                beginTransaction();
                System.out.println(user);
                endTransaction();
            }
          
            public void beginTransaction(){
                System.out.println("开启事务!");
            }
          
            public void endTransaction(){
                System.out.println("关闭事务!");
            }
          }
          
      • 第二种实现

        • public class TransactionHandler {
            public void beginTransaction(){
                System.out.println("开启事务!");
            }
          
            public void endTransaction(){
                System.out.println("关闭事务!");
            }
          }
          public class UserServiceImplHandler extends TransactionHandler implements UserService {
            @Override
            public void addUser(User user) {
                this.beginTransaction();
                System.out.println(user);
                this.endTransaction();
            }
          
            @Override
            public void updateUser(User user) {
                this.beginTransaction();
                System.out.println(user);
                this.endTransaction();
            }
          }
          
      • 第三种:动态代理的方式

        • 在这里插入图片描述

        • 动态代理区分两种

          • JDK动态代理

            • 代理对象和目标对象必需实现相同的接口

            • package com.fxyh.spring.proxy;
              
              import java.lang.reflect.InvocationHandler;
              import java.lang.reflect.Method;
              import java.lang.reflect.Proxy;
              
              public class TransactionHandlerWithJDKDynamicProxy implements InvocationHandler {
              
                private Object targetObject;
              
                public TransactionHandlerWithJDKDynamicProxy() {
                }
              
                public TransactionHandlerWithJDKDynamicProxy(Object targetObject) {
                    this.targetObject = targetObject;
                }
              
                public Object createProxyInstance(){
                    return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),
                            this.targetObject.getClass().getInterfaces(),
                            this);
                }
              
                /**
                 *
                 * @param proxy     代理对象
                 * @param method    目标对象的方法
                 * @param args      方法的参数
                 */
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    Object returnObj;
                    if (proxy == null)
                        throw new IllegalArgumentException("");
                    if (method == null)
                        throw new IllegalArgumentException("");
              
                    String methodName = method.getName();
                    boolean flag = methodName.startsWith("add") || methodName.startsWith("delete") || methodName.startsWith("update");
                    if (flag){
                        beginTransaction();
                    }
                    returnObj = method.invoke(this.targetObject,args);
                    if (flag){
                        endTransaction();
                    }
                    return returnObj;
                }
              
                private void endTransaction() {
                    System.out.println("JDK关闭事务!");
                }
              
                private void beginTransaction() {
                    System.out.println("JDK开启事务!");
                }
              }
              
              
            • package com.fxyh.spring.service.impl;
              
              import com.fxyh.spring.entity.User;
              import com.fxyh.spring.proxy.TransactionHandlerWithCGlibDynamicProxy;
              import com.fxyh.spring.service.UserService;
              import org.junit.Before;
              import org.junit.Test;
              
              import static org.junit.Assert.*;
              
              public class UserServiceImplHandlerWithCGlibDynamicProxyTest {
              
                private UserService userService;
              
                private TransactionHandlerWithCGlibDynamicProxy transactionHandlerWithCGlibDynamicProxy;
              
                @Before
                public void setUp() {
                    this.transactionHandlerWithCGlibDynamicProxy = new TransactionHandlerWithCGlibDynamicProxy(new UserServiceImplHandlerWithCGlibDynamicProxy());
                    this.userService = (UserService) this.transactionHandlerWithCGlibDynamicProxy.createProxyInstance();
                }
              
                @Test
                public void addUser() {
                    User user = new User();
                    user.setUsername("zhangsan");
                    user.setPassword("123456");
                    this.userService.addUser(user);
                }
              
              }
              
            • Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),
              this.targetObject.getClass().getInterfaces(),
              this);

            • 这也是为啥jdk动态代理要实现相同的接口的原因。

          • CGLib动态代理

            • 代理对象和目标对象不需要实现相同的接口,只需要维护父子关系

            • package com.fxyh.spring.proxy;
              
              import org.springframework.cglib.proxy.Enhancer;
              import org.springframework.cglib.proxy.InvocationHandler;
              
              import java.lang.reflect.Method;
              
              public class TransactionHandlerWithCGlibDynamicProxy implements InvocationHandler {
              
                private Object targetObject;
              
                public TransactionHandlerWithCGlibDynamicProxy() {
                }
              
                public TransactionHandlerWithCGlibDynamicProxy(Object targetObject) {
                    this.targetObject = targetObject;
                }
              
                public Object createProxyInstance(){
                    Enhancer enhancer = new Enhancer();
                    enhancer.setSuperclass(this.targetObject.getClass());
                    enhancer.setCallback(this);
                    return enhancer.create();
                }
              
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    Object returnObj;
                    if (proxy == null)
                        throw new IllegalArgumentException("");
                    if (method == null)
                        throw new IllegalArgumentException("");
              
                    String methodName = method.getName();
                    boolean flag = methodName.startsWith("add") || methodName.startsWith("delete") || methodName.startsWith("update");
                    if (flag){
                        beginTransaction();
                    }
                    returnObj = method.invoke(this.targetObject,args);
                    if (flag){
                        endTransaction();
                    }
                    return returnObj;
                }
              
                private void endTransaction() {
                    System.out.println("CGlib关闭事务!");
                }
              
                private void beginTransaction() {
                    System.out.println("CGlib开启事务!");
                }
              }
              
            • package com.fxyh.spring.service.impl;
              
              import com.fxyh.spring.entity.User;
              import com.fxyh.spring.proxy.TransactionHandlerWithCGlibDynamicProxy;
              import com.fxyh.spring.service.UserService;
              import org.junit.Before;
              import org.junit.Test;
              
              import static org.junit.Assert.*;
              
              public class UserServiceImplHandlerWithCGlibDynamicProxyTest {
              
                private UserService userService;
              
                private TransactionHandlerWithCGlibDynamicProxy transactionHandlerWithCGlibDynamicProxy;
              
                @Before
                public void setUp() {
                    this.transactionHandlerWithCGlibDynamicProxy = new TransactionHandlerWithCGlibDynamicProxy(new UserServiceImplHandlerWithCGlibDynamicProxy());
                    this.userService = (UserService) this.transactionHandlerWithCGlibDynamicProxy.createProxyInstance();
                }
              
                @Test
                public void addUser() {
                    User user = new User();
                    user.setUsername("zhangsan");
                    user.setPassword("123456");
                    this.userService.addUser(user);
                }
              
                @Test
                public void updateUser() {
                }
              }
              
  1. SpringAOP相关术语

  • 切面(Aspect)
    • 横切关注点
    • 将非业务代码抽取出来放到一个组件类中,这个组件就称为切面。
  • 通知(Advice)
    • 非业务代码建成的方法就称为通知
    • 通知分为:前通知,后通知,返回通知,环绕通知和异常通知。
  • 切入点(PointCut)
    • 将通知用在目标对象上的位置(细粒度控制使用在哪些类的哪些方法上),也就是定义的哪些连接点会得到通知。
    • com.fxyh.service.*.*(…)
  • 连接点(JoinPoint)
    • 通知织入的位置
  • 目标对象(TargetObject)
  • 通知者(Advisor)
    • 通知的增强
    • Advisor = Advice + PointCut
  • 织入(Weaving)
    • 把切面应用到目标对象来创建新的代理对象的过程
  • 导入(Introduction)
    • 允许我们向现有的类添加新方法属性。就是把切面用到目标类中。
  • 在这里插入图片描述
  1. SpringAOP的实现

    • 使用proxyFactoryBean的方式

      • <?xml version="1.0" encoding="UTF-8"?>
        <beans xmlns="http://www.springframework.org/schema/beans"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
        
        
           <bean id="userService" class="com.fxyh.spring.service.impl.UserServiceImpl"/>
        
           <bean id="transactionHandler" class="com.fxyh.spring.handler.TransactionHandler"/>
        
           <bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
               <property name="interfaces">
                   <array>
                       <value>com.fxyh.spring.service.UserService</value>
                   </array>
               </property>
               <property name="interceptorNames">
                   <array>
                       <value>transactionHandler</value>
                   </array>
               </property>
               <property name="targetName" value="userService"/>
           </bean>
        
        </beans>
        
      • package com.fxyh.spring.handler;
        
        
        import org.aopalliance.intercept.MethodInterceptor;
        import org.aopalliance.intercept.MethodInvocation;
        
        import java.lang.reflect.Method;
        
        public class TransactionHandler implements MethodInterceptor {
           public void beginTransaction(){
               System.out.println("开始事务!");
           }
        
           public void endTransaction(){
               System.out.println("关闭事务!");
           }
        
           @Override
           public Object invoke(MethodInvocation invocation) throws Throwable {
               Method method = invocation.getMethod();
               String methodName = method.getName();
               boolean isUse = methodName.startsWith("save") || methodName.startsWith("delete") || methodName.startsWith("update");
               if (isUse){
                   beginTransaction();
               }
               //调用目标方法
               Object returnObj = invocation.proceed();
        
               if (isUse){
                   endTransaction();
               }
               return returnObj;
           }
        }
        
      • package com.fxyh.spring.service;
        
        import com.fxyh.spring.domain.User;
        import org.junit.Before;
        import org.junit.Test;
        import org.springframework.context.ApplicationContext;
        import org.springframework.context.support.ClassPathXmlApplicationContext;
        
        import static org.junit.Assert.*;
        
        public class UserServiceTest {
        
           private ApplicationContext context;
        
           private UserService userService;
        
           @Before
           public void setUp() throws Exception {
               this.context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
               this.userService = (UserService) this.context.getBean("proxyFactoryBean");
           }
        
           @Test
           public void saveUser() {
               User user = new User();
               user.setId(1);
               user.setUsername("zhangsan");
               user.setAge(18);
               userService.saveUser(user);
           }
        
        }
        
    • 使用aop命名空间方式

      • <?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:aop="http://www.springframework.org/schema/aop"
              xmlns:context="http://www.springframework.org/schema/context"
              xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
               http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
               http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
        
        
           <bean id="userService" class="com.fxyh.spring.service.impl.UserServiceImpl"/>
        
           <bean id="transactionHandlerAOP" class="com.fxyh.spring.handler.TransactionHandlerAOP"/>
        
           <aop:config>
               <!--
                   第一个*号表示返回值,此时为任意返回值也可没有返回值
                   第二个*号表示所有的类或接口
                   第三个*号表示所有的方法
                   ..表示方法为任意参数
               -->
               <aop:pointcut id="AllMethod" expression="execution(* com.fxyh.spring.service.*.add*(..)) ||
                           execution(* com.fxyh.spring.service.*.delete*(..))"/>
               <aop:aspect id="transactionHandlerAspect" ref="transactionHandlerAOP">
                   <aop:before method="beginTransaction" pointcut-ref="AllMethod"/>
                   <aop:after method="endTransaction" pointcut-ref="AllMethod"/>
               </aop:aspect>
           </aop:config>
        
        </beans>
        
        
      - 
         ```xml
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>4.3.21.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>4.3.21.RELEASE</version>
        </dependency>
      
      • package com.fxyh.spring.handler;
        
        public class TransactionHandlerAOP {
           public void beginTransaction(){
               System.out.println("开始事务!");
           }
        
           public void endTransaction(){
               System.out.println("关闭事务!");
           }
        }
        
      • package com.fxyh.spring.service;
        
        import com.fxyh.spring.domain.User;
        import org.junit.Before;
        import org.junit.Test;
        import org.springframework.context.ApplicationContext;
        import org.springframework.context.support.ClassPathXmlApplicationContext;
        
        public class UserServiceAOPTest {
        
           private ApplicationContext context;
        
           private UserService userService;
        
           @Before
           public void setUp() throws Exception {
               this.context = new ClassPathXmlApplicationContext("classpath:applicationContext-aop.xml");
               this.userService = this.context.getBean(UserService.class);
           }
        
           @Test
           public void saveUser() {
               User user = new User();
               user.setId(1);
               user.setUsername("zhangsan");
               user.setAge(18);
               userService.saveUser(user);
           }
         	
         	@Test
         public void findUserById() {
               userService.findUserById(1);
           }
        }
        
      • 这里要注意要导入spring-aop和spring-aspects两个包。

      • 这里定义的只有add和delete开头的方法才会开启和关闭事务。findUserById测试的时候就不会开启事务。

    • 使用注解方式

      • package com.fxyh.spring.handler;
        
        import com.fxyh.spring.exception.CustomException;
        import org.aspectj.lang.JoinPoint;
        import org.aspectj.lang.Signature;
        import org.aspectj.lang.annotation.*;
        import org.springframework.stereotype.Component;
        
        @Aspect
        @Component
        public class TransactionHandlerAutoAOP {
        
           /*
            * 定义一个公共的切点
            */
           @Pointcut("execution(* com.fxyh.spring.service.*.*(..))")
           public void pointcut() {}
        
           /**
            * 前置通知
            * @param joinPoint
            */
           @Before(value = "execution(* com.fxyh.spring.service.*.*(..))")
           public void beginTransaction(JoinPoint joinPoint){
               Object[] args = joinPoint.getArgs();
               Object target = joinPoint.getTarget();
               String kind = joinPoint.getKind();
               Signature signature = joinPoint.getSignature();
               System.out.println("注解方式开始事务!");
           }
        
           /**
            * 后置通知
            */
           @After(value = "pointcut()")
           public void endTransaction(){
               System.out.println("注解方式关闭事务!");
           }
        
           /**
            * 异常通知
            * @param joinpoint
            * @param exception
            */
           @AfterThrowing(value = "pointcut()", throwing = "exception")
           public void logException(JoinPoint joinpoint, Exception exception){
               System.out.println(exception.getMessage());
           }
        
           /**
            * 自定义异常通知
            * @param joinPoint
            * @param exception
            */
           @AfterThrowing(value = "pointcut()", throwing = "exception")
           public void logException(JoinPoint joinPoint, CustomException exception){
               System.out.println(exception.getMessage());
           }
        }
        
      • package com.fxyh.spring.exception;
        
        public class CustomException extends RuntimeException {
           private static final long serialVersionUID = 5100311675985804166L;
        
           private String message;
        
           public CustomException(){}
        
           public CustomException(String message){
               super(message);
               this.message = message;
           }
        
           @Override
           public String getMessage() {
               return message;
           }
        
           public void setMessage(String message) {
               this.message = message;
           }
        }
        
      • 配置:

        • xml方式

          • <?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:aop="http://www.springframework.org/schema/aop"
                  xmlns:context="http://www.springframework.org/schema/context"
                  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                   http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
                   http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
            
               <context:component-scan base-package="com.fxyh.spring"/>
            
               <aop:aspectj-autoproxy />
            
            </beans>
            
        • JavaConfig方式

          • package com.fxyh.spring;
            
            import org.springframework.context.annotation.ComponentScan;
            import org.springframework.context.annotation.Configuration;
            import org.springframework.context.annotation.EnableAspectJAutoProxy;
            
            @Configuration
            @ComponentScan("com.fxyh.spring")
            @EnableAspectJAutoProxy
            public class AppAOPConfig {
            }
            
      • 测试:

        • package com.fxyh.spring.service;
          
          import com.fxyh.spring.domain.User;
          import org.junit.Before;
          import org.junit.Test;
          import org.springframework.context.ApplicationContext;
          import org.springframework.context.support.ClassPathXmlApplicationContext;
          
          public class UserServiceAutoAOPTest {
          
           private ApplicationContext context;
          
           private UserService userService;
          
           @Before
           public void setUp() throws Exception {
             //xml配置方式
               this.context = new ClassPathXmlApplicationContext("classpath:applicationContext-autoAop.xml");
             //JavaConfig方式
             //this.context = new AnnotationConfigApplicationContext(AppAOPConfig.class);
               this.userService = this.context.getBean(UserService.class);
           }
          
           @Test
           public void saveUser() {
               User user = new User();
               user.setId(1);
               user.setUsername("zhangsan");
               user.setAge(18);
               userService.saveUser(user);
           }
          
           @Test
           public void findUserById() {
               userService.findUserById(1);
           }
          }
          
    • 需要完整的源码可以去我的GitHub下载:https://github.com/fxyh9712/SpringStudy

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值