zoukankan      html  css  js  c++  java
  • Spring AOP

    Spring AOP 

      AOP为Aspect Oriented Programming的缩写,意为:面向切面编程
      日志记录,性能统计,安全控制,事务处理,异常处理等等

    AOP与OOP区别

      OOP(面向对象编程)针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元划分。
      而AOP则是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。这两种设计思想在目标上有着本质的差异。

    AOP相关术语

      目标对象target:指的是需要被增强的对象,由于spring aop是通过代理模式实现,从而这个对象永远是被代理对象。
      连接点(join point):所谓连接点是指那些被拦截到的点,在spring中这些点指的是方法,因为spring只支持方法类型的连接点
      切入点(pointcut):简单说切入点是指我们要对哪些连接点进行拦截的定义
      通知(advice):所谓通知是指拦截到连接点之后所要做的事情就是通知,通知分为前置通知,后置通知,异常通知,最终通知,环绕通知

    Advice 定义了在 pointcut 里面定义的程序点具体要做的操作

      引介introduction:引介是一种特殊的通知,在不修改类代码的前提下,introduction可以在运行期为类动态地添加一些方法或属性
      切面aspect:是切入点和通知的结合
      织入weaving:织入是一个过程,是将切面应用到目标对象从而创建出AOP代理对象的过程,织入可以在编译期,类装载期,运行期进行。

    Spring采用动态织入,而aspectj采用静态织入

      代理Proxy:一个类被AOP织入增强后,就产生一个结果代理类

    AOP底层:

      JDK动态代理:在运行 ,在JVM内部动态生成class字节码对象(Class对象)

    Jdk动态代理只针对于接口操作

    newProxyInstance的三个参数:

      第一个参数:目标类的类加载器对象
      第二个参数:目标类的实现接口的Class[]
      第三个参数:InvocationHandler它是一个接口,它的作用是是代理实例的调用处理程序 实现的接口,接口中定义了一个方法

    下面举个例子:

    public class ProxyFactory implements InvocationHandler{
        private Object target;
        //在创建对象时,传入代理目标对象
        public ProxyFactory(Object target){
            this.target = target;
        }
        //创建代理对象
        public Object createProxy() {
            //使用proxy创建代理对象
            /*
             * 准备类加载器
             * 准备实现接口的class[]
             * 实现invocationHandler
             * */
            ClassLoader classLoader = target.getClass().getClassLoader();
            Class [] interfaces = target.getClass().getInterfaces();
            return Proxy.newProxyInstance(classLoader, interfaces, this);
        }
        //在代理对象上处理方法并返回结果
        /**
         * 参数1就是代理对象
         * 参数2就是调用方法的method对象
         * 参数3调用方法的参数
         * */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("日志操作");
            
            return method.invoke(target, args);
        }
    }
    @Service(value="userServiceImpl")
    public class UserServiceImpl implements UserService {
    
        @Override
        public void login(String username, String password) {
            System.out.println("登录操作!.....");
        }
    
        @Override
        public void regist() {
            System.out.println("注册操作!.....");
        }
    
    }
    <context:component-scan base-package="com.learn"></context:component-scan>
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations="classpath:applicationContext.xml")
    public class TestProxy {
        //准备目标对象
        @Autowired
        private UserService us;
        @Test
        public void test1() {
            //创建代理工厂
            ProxyFactory pf = new ProxyFactory(us);
            //返回代理对象赋值给目标对象
            us = (UserService) pf.createProxy();
            us.regist();
        }
    }

    CGLIB动态代理

      CGLIB(Code Generation Library)是一个开源项目,它可以在运行期扩展Java类与实现Java接口。

    CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类

    注意:jdk的动态代理只可以为接口去完成操作,而cglib它可以为没有实现接口的类去做代理,也可以为实现接口的类去做代理。

    举个例子:

    public class CGLIBFactory implements MethodInterceptor{
        //准备目标对象
        private Object target;
        //构造方法
        public CGLIBFactory(Object target) {
            this.target = target;
        } 
        //创建代理对象
        public Object createProxy() {
            //创建Enhancer
            Enhancer enhancer = new Enhancer();
            //传递目标对象的class
            enhancer.setSuperclass(target.getClass());
            //设置回调操作
            enhancer.setCallback(this);
            return enhancer.create();
            
        }
        @Override
        public Object intercept(Object arg0, Method method, Object[] arg2, MethodProxy arg3) throws Throwable {
            System.out.println("日志操作");
            
            return method.invoke(target, arg2);
        }
    }
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations="classpath:applicationContext.xml")
    public class TestCGLB {
        //准备非接口目标对象
        @Autowired
        private UserServiceImpl usi;
        @Test
        public void test() {
            //创建代理类对象,传递目标对象
            CGLIBFactory cf = new CGLIBFactory(usi);
            //获得代理类对象重新赋值给usi
            usi = (UserServiceImpl) cf.createProxy();
            usi.regist();
        }
    }

    setCallback传递的参数是Callback类型,我们使用的是MethodInterceptor
    注意:cglib它可以为没有实现接口的类做代理,也可以为接口类做代理.

    spring采用的是哪一种动态机制:

      如果目标对象,有接口,优先使用jdk动态代理
      如果目标对象,无接口,使用cglib动态代理。

    Spring AOP编程

      在传统的spring aop开发中它支持增强(advice)有五种:

    1. 前置通知 目标方法执行前增强 org.springframework.aop.MethodBeforeAdvice
    2. 后置通知 目标方法执行后增强 org.springframework.aop.AfterReturningAdvice
    3. 环绕通知 目标方法执行前后进行增强 org.aopalliance.intercept.MethodInterceptor
    4. 异常抛出通知 目标方法抛出异常后的增强 org.springframework.aop.ThrowsAdvice
    5. 引介通知 在目标类中添加一些新的方法或属性(不做介绍)org.springframework.aop.IntroductionInterceptor


    下面举个最古老的spring AOP

    //准备目标对象
    public class OrderServiceImpl implements OrderService {
    
        @Override
        public void add() {
            System.out.println("add方法执行了.....");
        }
    
        @Override
        public void update() {
            System.out.println("update方法执行了.......");
        }
    
    }
    <?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: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/context http://www.springframework.org/schema/context/spring-context.xsd">
        <!-- 目标target -->
        <bean id="orderService" class="com.learn.service.impl.OrderServiceImpl"></bean>        
        <!-- 通知advice -->
        <bean id="orderServiceAdvice" class="com.learn.utils.OrderHelper"></bean>
        <!-- 定义切点 -->
        <bean id="orderServicePointCut" class="org.springframework.aop.support.NameMatchMethodPointcut">
            <property name="mappedNames">
                <list>
                    <!-- 配置需要拦截的方法 -->
                    <value>add</value>
                    <value>update</value>
                </list>
            </property>
        </bean>
        <!-- 配置切面aspect=pointCut+advice -->
        <bean id="orderServiceAspect" class="org.springframework.aop.support.DefaultPointcutAdvisor">
            <property name="advice" ref="orderServiceAdvice"></property>
            <property name="pointcut" ref="orderServicePointCut"></property>
        </bean>
        <!-- 配置代理 -->
        <bean id="orderServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
            <property name="target" ref="orderService"></property>
            <property name="interceptorNames" value="orderServiceAspect"></property>
            <property name="proxyInterfaces" value="com.learn.service.OrderService"></property>
        </bean>
    </beans>
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations="classpath:applicationContext.xml")
    public class Aop1Test {
        @Autowired
        @Qualifier("orderServiceProxy")
        private OrderService os;
        @Test
        public void test() {
            os.update();
        }
    }

    在原有的基础上修改的Spring AOP编程

    //准备目标对象
    public class OrderServiceImpl implements OrderService {
    
        @Override
        public void add() {
            System.out.println("add方法执行了.....");
        }
    
        @Override
        public void update() {
            System.out.println("update方法执行了.......");
        }
    
    }
    <?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:context="http://www.springframework.org/schema/context"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation="
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"
            >
        <!-- 配置目标对象 -->
        <bean id="orderService" class="order"></bean>
        <!-- 配置通知 -->
        <bean id="orderServiceAdvice" class="com.learn.utils.OrderHelper"></bean>
        <!-- aop来描述切面和切点 -->
        <aop:config>
            <!-- 定义切点 -->
            <aop:pointcut expression="execution(* com.learn.service..*(..))" id="orderServicePointCut"/>
            <!-- 配置切面 -->
            <aop:advisor advice-ref="orderServiceAdvice" pointcut-ref="orderServicePointCut"/>
        </aop:config>
    </beans>
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations="classpath:applicationContext.xml")
    public class aop2Test {
        @Autowired
        @Qualifier("orderService")
        private OrderService os;
        @Test
        public void test() {
            os.update();
        }
    }

    基于aspectJ切点传统开发

    @Service("customerService")
    public class CustomerServiceImpl implements CustomerService{
    
        @Override
        public void add() {
            System.out.println("add.........");
        }
    
        @Override
        public String login() {
            System.out.println("login........");
            return "你好";
        }
    
    }
    <?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:context="http://www.springframework.org/schema/context"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation="
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"
            >
        <!-- target -->
        <bean id="userService" class="com.learn.service.impl.UserServiceImpl"></bean>
        <!-- advice -->
        <bean id="userServiceAdvice" class="com.learn.utils.UserHelper"></bean>
        <!-- aspectj配置切面 -->
        <aop:config proxy-target-class="true">
            <aop:aspect ref="userServiceAdvice">
                <aop:pointcut expression="execution(* com.learn.service..*(..))" id="delPointCut"/>
                <aop:before method="before" pointcut-ref="delPointCut"/>
                <aop:before method="before1" pointcut-ref="delPointCut"/>
                <aop:after-returning method="afterReturning" pointcut-ref="delPointCut"/>
                <aop:after-throwing method="afterThrowing" pointcut-ref="delPointCut"/>
            </aop:aspect>
        </aop:config>
    </beans>
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations="classpath:applicationContext.xml")
    public class AspectjTest {
        @Autowired
        @Qualifier("userService")
        private UserService us;
        @Test
        public void test() {
            us.regist();
        }
    }

    spring框架默认情况下,会对有接口的类使用proxy代理。没有接口的类使用cglib代理
    Proxy-target-class的值默认是false,它代表有接口使用proxy代理
    问题:如果现在对目标要使用cglib代理,只需要将proxy-target-class设置为true.

    <aop:config>来声明要对aop进行配置
    <aop:pointcut>它是用于声明切点(简单说就是对哪些方法进行拦截)
    <aop:advisor> 定义传统的aop的切面,传统的aop切面它只能包含一个切点与一个增强
    <aop:aspect>定义aspectj框架的切面.,它可以包含多个切点与多个通知

    关于切点表达式的写法:
    关于execution语法常用:

    1. execution(public * *()) 所有的public的方法
    2. execution(* cn.itheima.aop.*(..)) 所有的aop包下的所有类的方法(不包含子包)
    3. execution(* cn.itheima.aop..*(..)) 所有的aop包及其子包下的所有类的方法
    4. execution(* cn.itheima.aop.IOrderService.*(..)) IOrderService接口中定义的所有方法
    5. execution(* cn.itheima.aop.IOrderService+.*(..)) 匹配实现特定接口所有类的方法
    6. execution(* save*(..)) 区配所有的以save开头的方法

    AspectJ框架它定义的通知类型有6种

    1. 前置通知Before 相当于BeforeAdvice
    2. 后置通知AfterReturning 相当于AfterReturningAdvice
    3. 环绕通知 Around 相当于MethodInterceptor
    4. 抛出通知AfterThrowing 相当于ThrowAdvice
    5. 引介通知DeclareParents 相当于IntroductionInterceptor
    6. 最终通知After 不管是否异常,该通知都会执行相比spring 的传统AOP Advice多了一个最终通知


    下面举例用注解配置aop

    @Service("customerService")
    public class CustomerServiceImpl implements CustomerService{
    
        @Override
        public void add() {
            System.out.println("add.........");
        }
    
        @Override
        public String login() {
            System.out.println("login........");
            return "你好";
        }
    
    }
    //声明一个实体
    @Component("customerServiceAdvice")
    //声明为一个bean的切面
    @Aspect
    public class AnnotationCustomerHelper {
        //声明前置通知
        @Before("execution(* com.learn.service..*(..))")
        public void before() {
            System.out.println("前置通知");
        }
        //声明后置通知
        @AfterReturning(value="execution(* com.learn.service..*(..))",returning="value")
        public void afterReturning(JoinPoint jp,Object value) {
            System.out.println("后置通知,目标方法的返回是="+value);
        }
        //声明环绕通知
        @Around(value="execution(* com.learn.service..*(..))")
        public Object around(ProceedingJoinPoint pjp) throws Throwable{
            System.out.println("环绕前置通知");
            Object value = pjp.proceed();
            System.out.println("环绕后置通知");
            return value;
        }
        //声明异常抛出通知
        @AfterThrowing(value="execution(* com.learn.service..*(..))",throwing="exception")
        public void afterThrowing(JoinPoint jp,Throwable exception) {
            System.out.println("出问题了"+exception);
        }
        //声明最终通知
        @After("execution(* com.learn.service..*(..))")
        public void after() {
            System.out.println("最终通知");
        }
    }
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations="classpath:applicationContext.xml")
    public class AnnotationAopTest {
        @Autowired
        @Qualifier("customerService")
        private CustomerService cs;
        @Test
        public void test1() {
            //cs.add();
            cs.login();
        }
    }
  • 相关阅读:
    含有tuple的list按照tuple中的某一位进行排序
    python
    Python代码追踪(类似于bash -x的效果)
    cinder swift的区别
    C#中Main函数为什么要static
    C#编程.函数.委托
    C#编程.函数.Main()函数
    C#编程.函数.参数
    typedef int a[10];怎么解释?
    C#的DateTime得到特定日期
  • 原文地址:https://www.cnblogs.com/learnjfm/p/7135827.html
Copyright © 2011-2022 走看看