• 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();
        }
    }
  • 相关阅读:
    BZOJ 2212/BZOJ 3702
    BZOJ 4761 Cow Navigation
    BZOJ 3209 花神的数论题
    BZOJ 4760 Hoof, Paper, Scissors
    BZOJ 3620 似乎在梦中见过的样子
    BZOJ 3940 Censoring
    BZOJ 3942 Censoring
    BZOJ 3571 画框
    BZOJ 1937 最小生成树
    BZOJ 1058 报表统计
  • 原文地址:https://www.cnblogs.com/learnjfm/p/7135827.html
Copyright © 2020-2023  润新知