Spring框架之AOP

一.代理模式

1.静态代理模式

                 实现静态代理三要素
                         a.共同行为
                         b.代理角色
                                与目标角色拥有共同行为
                                增强目标角色行为
                                持有目标对象的引用
                         c.目标角色
                实现行为接口
                    结婚案例
                         结婚
                         婚庆公司
                         YOU

共同的行为接口:

package com.shsxt;

public interface Marry {
    /**
     * 行为定义
     */
    public  void toMarry();
}

婚庆公司类(代理角色):主要理解里面的带参构造器,传入一个marry接口.

package com.shsxt;

/**
 * 代理角色
 *   与目标角色拥有共同行为
     增强目标角色行为
     持有目标对象的引用
 */
public class MarryCompany implements Marry {

    private Marry marry;

    public MarryCompany(Marry marry) {
        this.marry = marry;
    }


    public  void before(){
        System.out.println("新郎不要着急,车队正在快速飞奔中...");
    }

    public  void after(){
        System.out.println("恭喜您,成功进入人生第二阶段...");
    }

    @Override
    public void toMarry() {
        // 前置通知
        before();
        // 拦截目标对象方法  连接点
        marry.toMarry();
        // 最终通知
        after();
    }
}

目标角色:YOU

package com.shsxt;

/**
 * 目标角色:实现行为接口
 */
public class You implements  Marry {
    @Override
    public void toMarry() {
        System.out.println("等了这么久,终于等到你...");
    }
}

测试类:

package com.shsxt;

import java.lang.reflect.Proxy;

public class Test {

    public static void main(String[] args) {
        You you=new You();
        new MarryCompany(you).toMarry();
   
    }
}

运行结果:

2.动态代理模式

相比于静态代理,动态代理在创建代理对象上更加的灵活,它会根据需要通过反射机制在程序运行期动态的为目标对象创建代理对象,代理的行为可以代理多个方法, 即满足生产需要的同时又达到代码通用的目的动态代理的两种实现方式:

a.jdk 实现动态代理(实现InvocationHandler接口),代理的目标角色必须要实现某个接口,且接收的时候一定用父接口来接收,否则回报类型不能转化的错误.(回调方式实现)

package com.shsxt;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JdkHandler implements InvocationHandler {
    
    // 做代理的目标对象
    private Object target;

    public JdkHandler(Object target) {
        this.target = target;
    }


    /**
     * 返回动态代理对象
     * @return
     */
    public  Object getProxy(){
        /**
         * 返回动态代理
         *   参数1:类加载器
         *   参数2:目标对象实现的接口数组
         *   参数3:实现InvocationHandler 接口的类对象
         */
         //注意函数的三个参数的含义
       return  Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }


    public  void before(){
        System.out.println("新郎不要着急,车队正在快速飞奔中...");
    }

    public  void after(){
        System.out.println("恭喜您,成功进入人生第二阶段...");
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result=null;
        before();
        result= method.invoke(target,args);
        after();
        return result;
    }
}

测试代码(运行结果同上)

JdkHandler jdkHandler=new JdkHandler(new You());
      Marry marry= (Marry) jdkHandler.getProxy();
      marry.toMarry();

b.cglib 动态代理实现(采用继承的方式实现)代理的目标角色可以不要实现某个接口,接收用父类接收即可

引入cglib的依赖

<dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib-nodep</artifactId>
      <version>2.2</version>
</dependency>

代码实现

package com.shsxt;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CglibInterceptor implements MethodInterceptor {
    
    //定义个Object来接收目标对象 
    private Object target;

    //带参数的构造器,传入的是目标参数 
    public CglibInterceptor(Object target) {
        this.target = target;
    }

    //获取Proxy代理对象
    public  Object getProxy(){
        Enhancer enhancer=new Enhancer();
        enhancer.setCallback(this);
        enhancer.setSuperclass(target.getClass());
        return enhancer.create();
    }


    public  void before(){
        System.out.println("新郎不要着急,车队正在快速飞奔中...");
    }

    public  void after(){
        System.out.println("恭喜您,成功进入人生第二阶段...");
    }
     //主要参数的含义
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        Object result=null;
        before();
        result= methodProxy.invoke(target,objects);
        after();
        return result;
    }
}

测试代码:

CglibInterceptor cglibInterceptor = new CglibInterceptor(new You());
        You you= (You) cglibInterceptor.getProxy();
        you.toMarry();

没有继承方法的类:

package com.shsxt;

public class AccountService {

    public  void test(){
        System.out.println("AccountService.test..");
    }
}

测试代码:

CglibInterceptor cglibInterceptor = new CglibInterceptor(new AccountService());
        AccountService accountService= (AccountService) cglibInterceptor.getProxy();
        accountService.test();

没有继承接口的也能够被动态代理

3.二者的区别

code generator library ,操作字节码。 与 jdk 提供的代理区别是Proxy:委托类必须有接口,制作过程比较快,执行慢;而cglib:委托类可以没有接口,继承的思维来实现相似性,制作代理过程比较慢,执行快,主要解决没有接口类的代理实现。

二.七牛(一个资源存放的平台)的简单使用

首先要在IDEA中引入依赖

   <!--
        七牛坐标添加
   -->
      <!-- https://mvnrepository.com/artifact/com.qiniu/qiniu-java-sdk -->
      <dependency>
         <groupId>com.qiniu</groupId>
         <artifactId>qiniu-java-sdk</artifactId>
         <version>7.2.17</version>
      </dependency>

实现本地文件的上传

package com.shsxt;

import com.google.gson.Gson;
import com.qiniu.common.QiniuException;
import com.qiniu.common.Zone;
import com.qiniu.http.Response;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.UploadManager;
import com.qiniu.storage.model.DefaultPutRet;
import com.qiniu.util.Auth;
import org.junit.*;

public class TestQiNiu {

    @org.junit.Test
    public void test() {

        //构造一个带指定Zone对象的配置类(zone为区域)
        Configuration cfg = new Configuration(Zone.zone0());
        //...其他参数参考类注释
        UploadManager uploadManager = new UploadManager(cfg);
        //...生成上传凭证,然后准备上传(accessKey 与 secretKey 可以在个人中心查看)
        String accessKey = "3K77OJ214syThkwIbX8skAMHcEYmZJuqGcTSEEW1";
        String secretKey = "DpQoXLAfJWJJblp-kdBm4t573Tzf_cEmq6xLoFW3";
        //指定文件名
        String bucket = "shsxt-0927";
        //如果是Windows情况下,格式是 D:\\qiniu\\test.png
        String localFilePath = "C:\\Users\\lp\\Desktop\\Spring_IOC总结.xmind";
        //默认不指定key的情况下,以文件内容的hash值作为文件名
        String key = "ioc.xmind";
        Auth auth = Auth.create(accessKey, secretKey);
        String upToken = auth.uploadToken(bucket);
        try {
            Response response = uploadManager.put(localFilePath, key,upToken);
            //解析上传成功的结果
            DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
            System.out.println(putRet.key);
            System.out.println(putRet.hash);
        } catch (QiniuException ex) {
            Response r = ex.response;
            System.err.println(r.toString());
            try {
                System.err.println(r.bodyString());
            } catch (QiniuException ex2) {
                //ignore
            }
        }

    }
}

三.AOP初认识

1.Aop 是什么?

Aspect Oriented Programing 面向切面编程, 相比较 oop 面向对象编程来说, Aop关注的不再是程序代码中某个类,某些方法,而 aop 考虑的更多的是一种面到面的切入即层与层之间的一种切入,所以称之为切面。 联想大家吃的汉堡(中间夹肉)。那么 aop是怎么做到拦截整个面的功能呢?与java-web里面的 servlet urlpattern /* 的配置 ,实际上也是 aop 的实现。

2.Aop 能做什么?

AOP 主要应用于日志记录,性能统计,安全控制,事务处理等方面,实现公共功能性的重复使用。

3.Aop 带来的好处

              1. 降低模块与模块之间的耦合度,提高业务代码的聚合度。 (高内聚低耦合)
              2. 提高了代码的复用性。
              3. 提高系统的扩展性

四.Aop 基本概念

1.Joinpoint(连接点)

被拦截到的每个点, spring 中指被拦截到的每一个方法, spring aop一个连接点即代表一个方法的执行

2. Pointcut(切入点)

对连接点进行拦截的定义(匹配规则定义 规定拦截哪些方法,对哪些方法进行处理), spring 这块有专门的表达式语言定义。

3. Advice(通知)----- 拦截到每一个连接点即(每一个方法)后所要做的操作

i. 前置通知 (前置增强) --before() 执行方法前通知

           ii.返回通知(返回增强) --afterReturn 方法正常结束返回后的通知

           iii.异常抛出通知(异常抛出增强) --afetrThrow()

           iv.最终通知—after 无论方法是否发生异常,均会执行该通知。

           v.环绕通知—around 包围一个连接点(join point)的通知,如方法调用。这是最强大的一种通知类型。 环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行。

4. Aspect(切面)

切入点与通知的结合,决定了切面的定义,切入点定义了要拦截哪些类的哪些方法,通知则定义了拦截过方法后要做什么,切面则是横切关注点的抽象,与类相似,类是对物体特征的抽象,切面则是横切关注点抽象。

5. Target(目标对象) ----  被代理的目标对象

6. Weave(织入) ---了解---  将切面应用到目标对象并生成代理对象的这个过程即为织入。

7. Introduction(引入) ---了解--- 在不修改原有应用程序代码的情况下,在程序运行期为类动态添加方法或者字段的过程称为引入

五.使用 Aop 解决日志处理问题

-------------------------Aop 配置有两种方式 注解方式 与 xml 方式

加入AOP坐标

<dependency>
     <groupId>org.aspectj</groupId>
     <artifactId>aspectjweaver</artifactId>
     <version>1.8.9</version>
</dependency>

beans.xml 配置:

      添加命名空间:

xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd

       配置 aop 代理(开启aop 代理环境)

<aop:aspectj-autoproxy/>

1.注解的实现类(注解的配置方式及几种不同的通知)

package com.shsxt_02.proxy;

import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * 定义aop 切面类
 *   @Aspect:标注当前类为切面类
 */
@Component
@Aspect
public class LogCut {

    /**
     * 定义切入点
     * 匹配com.shsxt_02.service 包以及子包下的所有类的所有方法
     */
    @Pointcut("execution(* com.shsxt_02.service..*.*(..))")
    public  void cut(){}

    /**
     * 前置通知
     */
    @Before(value = "cut()")
    public  void before(){
        //throw  new RuntimeException();
        System.out.println("前置通知:目标方法执行前执行该通知...");
    }

    @AfterReturning(value = "cut()")
    public  void afterReturn(){
        System.out.println("返回通知:方法正常结束后执行该通知...");
    }

    @After(value = "cut()")
    public  void after(){
        System.out.println("最终通知:方法是否抛出异常均会执行该通知...");
    }

    @AfterThrowing(value = "cut()",throwing = "e")
    public  void afterThrow(Exception e){
        System.out.println("异常通知:方法抛出异常会执行该通知..."+e);
    }

    /**
     * 环绕通知:重点
     * @param pjp
     * @return
     * @throws Throwable
     */
    @Around(value = "cut()")
    public  Object around(ProceedingJoinPoint pjp) throws Throwable {
        // 执行目标对象的方法
        Object result=null;
        System.out.println("环绕通知开始...");
        System.out.println(pjp.getSignature());
        //Signature signature=pjp.getSignature();
        System.out.println(pjp.getTarget());
        Object[] params=pjp.getArgs();
        for(Object obj:params){
            System.out.println(obj);
        }
        System.out.println("---------");
        System.out.println(pjp.getKind());
        System.out.println(pjp.getSourceLocation());
        result= pjp.proceed();
        System.out.println("通知结束...");


        return result;
    }

}

2.配置的方式(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: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">
    <context:component-scan base-package="com.shsxt_02"/>
    <!--
       开启aop 代理环境
    -->
    <aop:aspectj-autoproxy/>


    <aop:config>
    <!--
      配置切面
    -->
    <aop:aspect ref="logCut02">
        <!--
          配置切入点
        -->
        <aop:pointcut id="cut" expression="execution(* com.shsxt_02.service..*.*(..))"></aop:pointcut>
        <!--<aop:before method="before" pointcut-ref="cut"></aop:before>
        <aop:after-returning method="afterReturn" pointcut-ref="cut"></aop:after-returning>-->
        <aop:around method="around" pointcut-ref="cut"></aop:around>
    </aop:aspect>
</aop:config>



</beans>









 


 


 

 



 


 


 


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值