码迷,mamicode.com
首页 > 编程语言 > 详细

spring 之AOP

时间:2019-01-15 22:06:05      阅读:228      评论:0      收藏:0      [点我收藏+]

标签:表达式   system   cer   面向   throw   地方   代理类   private   round   

第一:什么是AOP?

AOP是面切面编程,是OOP的补充和增强,在程序开发中主要用来解决一些系统层面上的问题。比如,日志事物,权限等待。利用AOP可以对业务逻辑的各个部分进行分离,比如我们在做控制层的时候有时候会碰到需要一些验证等,而使用AOP可以将这些部分与业务逻辑分开,使我们的代码更加专注于业务逻辑的处理,这样不仅降低了逻辑部分之间的耦合性,还增强了代码的可重用性,同时也提高了开发的效率。

第二:AOP的原理是什么?

在我们学习AOP之前都碰到过这样一个问题,那就是我们设计一个项目的时候,往往这个项目的不同部分都是需要验证的,虽然我们可以把相同的代码复制到需要验证的地方,但是这样不仅增加了类之间的耦合性,还使得业务逻辑代码和验证代码混合在一起,不利于代码的维护和管理,而且还增加了大量的重复性代码。而AOP就可以很好的帮助我们解决这个问题,所谓的面向切面编程就是我们利用spring的IOC管理来告诉我们的业务逻辑代码需要插入一个验证信息了,这个时候就是利用AOP直接插入就可以了,而不需要我们在去关心需要什么样的代码这样的问题了。

技术分享图片

当我们没有使用AOP的时候,我们就需要在每个需要验证的地方添加代码,而如果使用了AOP,我们只需要写一份代码,然后让IOC管理实现我们在需要的地方插入就可以了。

AOP是基于23种设计模式之一的代理模式来实现的,因为在学习AOP之前,我们需要来学习一下代理模式。

第三:代理模式

代理模式可以分为静态代理模式和动态代理模式;其中动态代理又可以分为jdk动态代理和cglib动态代理

先来学习静态代理:下面给出一个静态代理的小案例

静态代理需要有接口以及该接口的实现类

静态代理中需要用到的包:这里除了spring的IOC需要用到的包外还多了一个aop的包,需要注意

技术分享图片

代码如下:

接口代码:

public interface SomeService {
    String doSome();
    void say();
}

接口的实现类代码:

public class SomeServiceImpl implements SomeService{
    @Override
    public String doSome() {
        // TODO Auto-generated method stub
        System.out.println("dosome");
        return "halou..";
    }
    @Override
    public void say() {
        // TODO Auto-generated method stub
        System.out.println("say....");
    }
}

代理的实现方式之一:前置切面类:就是在调用该接口的实现类之前先执行该前置切面类的方法

public class MyMethodBeforeAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        // TODO Auto-generated method stub
        System.out.println("前置通知》》》》");
    }
}

测试类的代码:

public class Test1 {
    public static void main(String[] args) {
        // 获取ApplicationContext对象 加载配置文件 反射+xml解析
        ApplicationContext ac = new ClassPathXmlApplicationContext("application.xml");
        // 从容器中获取代理对象;这里不能直接获取目标对象,那样代理就失去了作用
        SomeService service = ac.getBean("proxyFactoryBean", SomeService.class);
        String res = service.doSome();
        System.out.println(res);
        System.out.println("-----------------");
        service.say();
    }
}

配置文件代码:

<?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:p="http://www.springframework.org/schema/p"
    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/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
    
    <!-- 注册目标对象 -->
    <bean class="com.sxt.serviceimpl.SomeServiceImpl" id="someServiceImpl"/>
    
    <!-- 注册前置通知 -->
    <bean class="com.sxt.aspect.MyMethodBeforeAdvice" id="myMethodBeforeAdvice"/>
    
    <!-- AOP 配置代理类 -->
    <bean class="org.springframework.aop.framework.ProxyFactoryBean" id="proxyFactoryBean">
        <!-- 配置代理类 -->
        <property name="target" ref="someServiceImpl"/>
        <!-- 配置代理类实现的接口 -->
        <property name="interfaces" value="com.sxt.service.SomeService"/>
        <!-- 配置通知 -->
        <property name="interceptorNames">
            <list>
                <!-- 配置前置通知 -->
                <value>myMethodBeforeAdvice</value>
            </list>
        </property>
    </bean>
</beans>

这样方式较为复杂,之后会介绍使用注解的方式来实现AOP切面编程的。

测试结果如下:

技术分享图片

动态代理之jdk代理模式:

接口代码:

public interface SomeService {
    String doSome();
}

实现类代码:

public class SomeServiceImpl implements SomeService {
    @Override
    public String doSome() {
        System.out.println("dosome....");
        return "hello..";
    }
}

测试类以及代理类代码:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import com.sxt.service.SomeService;
import com.sxt.serviceimpl.SomeServiceImpl;

/**
 * @author ASUS 这是通过jdk动态代理的方式来测试
 *
 */
public class Test {
    public static void main(String[] args) {
        //实例化目标对象
        SomeService service=new SomeServiceImpl();
        //通过jdk动态代理的方式获取代理对象
        SomeService proxy=(SomeService) Proxy.newProxyInstance(service.getClass().getClassLoader(),//设置类加载器
                service.getClass().getInterfaces(),//获取目标对象的所有实现的接口
                new InvocationHandler() {
                /**
                 * proxy 代理对象
                 * method 需要执行目标对象的方法
                 * args 目标对象的参数
                 */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("方法执行之前");
                        String  invoke =(String)method.invoke(service, args);
                        System.out.println("方法执行之后");
                        return invoke.toUpperCase();
                    }
                });
        System.out.println(proxy.doSome());
    }
}

运行结果如吓:

技术分享图片

动态代理之cglib代理:在cglib代理中需要接口,只需要有对应的目标类就可以了。

目标类代码:

package com.sxt.serviceimpl;
/**
 * @author ASUS
 * cglib的目标类
 *
 */
public class SomeServiceImpl  {
    public String doSome() {
        System.out.println("dosome....");
        return "hello..";
    }
}

测试类代码:

import com.sxt.cglib.CglibProxy;
import com.sxt.serviceimpl.SomeServiceImpl;

/**
 * @author ASUS 这是通过cglib动态代理的方式来测试
 *
 */
public class Test {
    public static void main(String[] args) {
        //实例化目标对象
        SomeServiceImpl service=new SomeServiceImpl();
        //获取代理类
        SomeServiceImpl proxy=new CglibProxy(service).createProxy();
        System.out.println(proxy.doSome());
    }
}

cglib代理类代码:

import java.lang.reflect.Method;
import com.sxt.serviceimpl.SomeServiceImpl;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

/**
 * @author ASUS
 * cglib代理类
 *
 */
public class CglibProxy implements MethodInterceptor{
    //目标对象
    private SomeServiceImpl service;
    public CglibProxy(SomeServiceImpl service) {
        super();
        this.service = service;
    }
    /**
     * 创建代理对象
     * @return
     */
    public SomeServiceImpl createProxy() {
        //获取Enhancer对象
        Enhancer e=new Enhancer();
        // 设置父类对象,设置目标对象的类型
        e.setSuperclass(SomeServiceImpl.class);
        //设置callback对象,就是this
        e.setCallback(this);
        return (SomeServiceImpl)e.create();    
    }
    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        // TODO Auto-generated method stub
        System.out.println("执行之前");
        String invoke =(String) method.invoke(service, args);
        System.out.println("执行之后");
        return invoke.toUpperCase();
    }
}

注意:在cglib代理中虽然不需要接口,但是要用到一个jar包

技术分享图片

我们已经了解了代理模式的原理,现在我们来学习AOP的几种实现方式:

方式一:前置通知,顾名思义就是在目标类的方法执行执行,该前置类的方法会被执行

代码如下:

接口代码:

package com.sxt.service;
/**
 * @author ASUS
 * 静态代理的公共接口
 *
 */
public interface SomeService {
    String doSome();
    void say();
}

接口的实现类代码:

import com.sxt.service.SomeService;
/**
 * @author ASUS
 * 前置代理接口的实现类
 *
 */
public class SomeServiceImpl implements SomeService {
    @Override
    public String doSome() {
        System.out.println("dosome....");
        return "hello..";
    }
    @Override
    public void say() {
        // TODO Auto-generated method stub
        System.out.println("ssssss");
    }
}

前置代理类代码:

import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
/**
 * @author ASUS
 *
 */
public class MyMethodBeforeAdvice implements MethodBeforeAdvice{
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        // TODO Auto-generated method stub
        System.out.println("前置通知》》》");
    }
}

测试类代码:

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.sxt.service.SomeService;

/**
 * @author ASUS 测试类
 *
 */
public class Test {
    public static void main(String[] args) {
        // 获取ApplicationContext对象 加载配置文件 反射+xml解析
        ApplicationContext ac = new ClassPathXmlApplicationContext("application.xml");
        // 从容器中获取代理对象
        SomeService service = ac.getBean("proxyFactoryBean", SomeService.class);

        String res = service.doSome();
        System.out.println(res);
        System.out.println("-----------------");
        service.say();
    }
}

配置文件代码:

<?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:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- 注册目标对象 -->
    <bean class="com.sxt.serviceimpl.SomeServiceImpl" id="someServiceImpl"/>
    <!-- 注册前置通知 -->
    <bean class="com.sxt.aspect.MyMethodBeforeAdvice" id="myMethodBeforeAdvice"/>
    <!-- AOP 配置代理类 -->
    <bean class="org.springframework.aop.framework.ProxyFactoryBean" id="proxyFactoryBean">
        <!-- 配置代理类 -->
        <property name="target" ref="someServiceImpl"/>
        <!-- 配置代理类实现的接口 -->
        <property name="interfaces" value="com.sxt.service.SomeService"/>
        <!-- 配置通知 -->
        <property name="interceptorNames">
            <list>
                <!-- 配置前置通知 -->
                <value>myMethodBeforeAdvice</value>
            </list>
        </property>
    </bean>
</beans>

测试结果如下:

技术分享图片

需要用到的jar包如下:

技术分享图片

AOP实现之后置通知:也就是在目标类的方法执行之后才会执行的

接口代码:

package com.sxt.service;
/**
 * @author ASUS
 * 静态代理的公共接口
 *
 */
public interface SomeService {
    String doSome();
    void say();
}

实现类代码:

import com.sxt.service.SomeService;
/**
 * @author ASUS
 * 后置代理接口的实现类
 *
 */
public class SomeServiceImpl implements SomeService {
    @Override
    public String doSome() {
        System.out.println("dosome....");
        return "hello..";
    }
    @Override
    public void say() {
        // TODO Auto-generated method stub
        System.out.println("ssssss");
    }
}

后置代理类的代码:

package com.sxt.aspect;

import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
/**
 * @author ASUS
 *
 */
public class MyAfterReturningAdvice implements AfterReturningAdvice{
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        // TODO Auto-generated method stub
        System.out.println("后置通知"+returnValue);
        //returnValue=((String)returnValue).toUpperCase();
    }
}

测试类代码:

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.sxt.service.SomeService;
/**
 * @author ASUS 测试类
 *
 */
public class Test {
    public static void main(String[] args) {
        // 获取ApplicationContext对象 加载配置文件 反射+xml解析
        ApplicationContext ac = new ClassPathXmlApplicationContext("application.xml");
        // 从容器中获取代理对象
        SomeService service = ac.getBean("proxyFactoryBean", SomeService.class);
        String res = service.doSome();
        System.out.println(res);
        System.out.println("-----------------");
        service.say();
    }
}

配置文件代码:

<?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:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    <!-- 注册目标对象 -->
    <bean class="com.sxt.serviceimpl.SomeServiceImpl" id="someServiceImpl"/>
    <!-- 注册前置通知 -->
    <!-- <bean class="com.sxt.aspect.MyMethodBeforeAdvice" id="myMethodBeforeAdvice"/> -->
    <!-- 注册后置通知 -->
    <bean class="com.sxt.aspect.MyAfterReturningAdvice" id="myAfterReturningAdvice"/>
    <!-- AOP 配置代理类 -->
    <bean class="org.springframework.aop.framework.ProxyFactoryBean" id="proxyFactoryBean">
        <!-- 配置代理类 -->
        <property name="target" ref="someServiceImpl"/>
        <!-- 配置代理类实现的接口 -->
        <property name="interfaces" value="com.sxt.service.SomeService"/>
        <!-- 配置通知 -->
        <property name="interceptorNames">
            <list>
                <!-- 配置前置通知 -->
                <!-- <value>myMethodBeforeAdvice</value> -->
                <!-- 配置后置通知 -->
                <value>myAfterReturningAdvice</value>
            </list>
        </property>
    </bean>
</beans>

测试结果如下:

技术分享图片

AOP实现之环绕代理通知:就是在整个目标类方法执行的前后都会执行该环绕代理类的方法

接口代码:

public interface SomeService {
    String doSome();
    void say();
}

实现类代码:

public class SomeServiceImpl implements SomeService {
    @Override
    public String doSome() {
        System.out.println("dosome....");
        return "hello..";
    }
    @Override
    public void say() {
        // TODO Auto-generated method stub
        System.out.println("ssssss");
    }
}

环绕代理通知代码:

技术分享图片

测试类代码:

技术分享图片

配置文件代码:

技术分享图片

测试结果如下:

技术分享图片

异常代理:

异常代理类代码:

技术分享图片

接口代码:

技术分享图片

实现类代码:

技术分享图片

测试类代码:

技术分享图片

配置文件代码参考上面的。这里需要自己随便创造一个异常。

由于在配置文件中配置代理类太麻烦了,下面我们来介绍使用注解的方式实现代理

利用注解的方式需要用到的jar包与上面的配置文件的方式不一样;这里需要用到一个aspectj-tools的jar包

这里一共要用到6个注解《比上面多了一个最终代理类》

注解一:@Aspect这个是用在代理类的前面的,与配置文件相结合

注解二:@Before这个是前置代理类的注解,用在前置代理方法的前面

注解三:@AfterReturning后置代理类的注解

注解四:@Around环绕注解

注解五:@AfterThrowing异常代理类注解

注解六:@After最终代理类注解

这里还要在注解的后面指明代理类方法切入的位置,前面的代理实现只能指明切入的类,不能指明具体的方法,而使用注解的方式则可以指明切入的具体的方法

因此,这个最为常用

代理类代码:

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
/**
 * @author ASUS
 * @ASPECT表示该类就是一个切面
 *
 */
@Aspect
public class MyAspect {
    /**
     * execution切入点表达式:定位该通知的切入点
     * execution(* com.dpb.service.*.doSome(..))
     */
    @Before("execution(* com.sxt.serviceimpl..*(..))")
    public void before() {
        System.out.println("前置通知");
    }
    /**
     * 后置通知
     */
    @AfterReturning(value="execution(* com.sxt.service.*.doSome(..))",returning="msg")
    public void afterReturn(Object msg) {
        System.out.println("后置通知"+msg);
    }
    /**
     * 环绕通知
     * @param pjp
     * @return
     * @throws Throwable
     */
    @Around(value="execution(* com.sxt.service.*.doSome(..))")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("环绕通知之前");
        String  proceed =(String) pjp.proceed();
        System.out.println("环绕通知之后"+proceed);
        return proceed.toUpperCase();
    }
    /**
     * 异常通知
     * @param e
     */
    @AfterThrowing(value="execution(* com.sxt.service.*.doSome(..)",throwing="e")
    public void throwing(Exception e) {
        System.out.println("异常通知"+e.getMessage());
    }
    /**
     * 最终通知
     */
    @After("execution(* com.sxt.service.*.doSome(..))")
    public void after() {
        System.out.println("最终通知---");
    }
}

配置文件代码:在配置文件中需要指明目标对象,代理类对象以及开启aspectj注解

技术分享图片

接口,实现类,测试类代码参照上面的代理通知类代码

在这里我们还介绍一种使用配置文件的实现方式

就是将前面分散的代理方式的代码的配置文件合在一起就可以了。

 

spring 之AOP

标签:表达式   system   cer   面向   throw   地方   代理类   private   round   

原文地址:https://www.cnblogs.com/liyunfeng-deng/p/10274439.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!