Spring学习笔记之aop动态代理(3)

1.0 静态代理模式的缺点: 1、在该系统中有多少的dao就的写多少的proxy,麻烦 2、如果目标接口有方法的改动,则proxy也需要改动。 PersonDao.java

public interface PersonDao {
    public void savePerson();
}

PersonDaoImpl.java
public class PersonDaoImpl implements PersonDao{
    public void savePerson() {
        System.out.println("save person");
    }
}

PersonDaoProxy.java

public class PersonDaoProxy implements PersonDao{
    private PersonDao personDao;
    private Transaction transaction;
    public PersonDaoProxy(PersonDao personDao,Transaction transaction){
        this.personDao = personDao;
        this.transaction = transaction;
    }
    public void savePerson() {
        this.transaction.beginTransaction();
        this.personDao.savePerson();
        this.transaction.commit();
    }
}   

Transaction.java

public class Transaction {
    public void beginTransaction(){
        System.out.println("begin transaction");
    }

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

ProxyTest.java

/*
 * 静态代码模式:
 *    1、在该系统中,有多少dao,就得写多少proxy
 *    2、如果目标接口有方法的改动,则proxy也得做相应的修改
 */
public class ProxyTest {
    @Test
    public void testProxy(){
        PersonDao personDao = new PersonDaoImpl();
        Transaction transaction = new Transaction();
        PersonDaoProxy proxy = new PersonDaoProxy(personDao, transaction);
        proxy.savePerson();
    }
}

2.0 动态代理模式–jdkproxy (优点:动态的产生代理对象,所以只需要一个拦截器就可以了。 缺点:如果invoke方法做事务的判断,将很复杂。 程序员还是写拦截器了,写拦截器的invoke方法了,所以invoke方法还需要修改) 问题: 1、拦截器的invoke方法在什么时候被调用的 在代理对象调用方法的时候,进入了拦截器中的invoke方法。 2、拦截器总的method参数是什么?在什么时候由实参传递给形参的。 代理对象的方法的名称是什么,method的参数的名称就是什么。 代理对象调用方法的时候进入了拦截器的invoke方法,这个时候传递参数。 3、生成的代理对象实现了接口,代理对象的方法体的内容是什么? 方法体的内容就是拦截器中的invoke方法体的内容。 说明:目标类和代理类实现了共同的接口。

MyInterceptor.java

/**
 * 1、引入personDao和Transaction
 * 2、完成invoke方法
 * @author zd
 *
 */
public class MyInterceptor implements InvocationHandler{
    private Object target;
    private Transaction transaction;
    public MyInterceptor(Object target,Transaction transaction){
        this.target = target;
        this.transaction = transaction;
    }


    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        if(method.getName().equals("savePerson")
                ||method.getName().equals("updatePerson")){
            this.transaction.beginTransaction();
            method.invoke(this.target, args);//调用目标类的目标方法
            this.transaction.commit();
        }else{
            method.invoke(this.target, args);//调用目标类的目标方法
        }
        return null;
    }
}   

PersonDao.java

public interface PersonDao {
    public void savePerson();
}   

PersonDaoImpl.java  
public class PersonDaoImpl implements PersonDao{
    public void savePerson() {
        System.out.println("save person");
    }
}   
/*
 *
 *   问题:
 *      1、拦截器中的invoke方法在什么时候被调用的
 *         在代理对象调用方法的时候,进入了拦截器中的invoke方法
 *      2、拦截器中的method参数是什么?在什么时候由实参传递给形参的
 *         代理对象的方法的名称是什么,method参数就是什么
 *         代理对象调用方法的时候,进入了拦截器中的invoke方法,这个时候,传递参数
 *      3、生成的代理对象实现了接口,代理对象的方法体的内容是什么?
 *         方法体的内容就是拦截器中的invoke方法体的内容
 */

Transaction.java

public class Transaction {
    public void beginTransaction(){
        System.out.println("begin transaction");
    }
    public void commit(){
        System.out.println("commit");
    }
}

ProxyTest.java

/**
 * jdkproxy的优点
 *     动态的产生代理对象,所以只需要用一个拦截器就可以了
 * jdkproxy的缺点
 *     如果在invoke方法中做事务的判断,将是一件很复杂的事情
 *     程序员还是写拦截器了,写拦截器中的invoke方法了,所以invoke方法还需要修改
 *     
 *  说明:
 *      目标类和代理类实现了共同的接口
 * @author zd
 *
 */
public class ProxyTest {
    @Test
    public void testProxy(){
        PersonDao target = new PersonDaoImpl();
        Transaction transaction = new Transaction();
        MyInterceptor interceptor = new MyInterceptor(target, transaction);
        /**
         * 第一个参数  目标类的类加载器
         * 第二个参数  目标类实现的所有的接口
         * 第三个参数  拦截器
         */
        PersonDao personDao = (PersonDao)Proxy.newProxyInstance(target.getClass().getClassLoader(), 
                    target.getClass().getInterfaces(), interceptor);
        personDao.savePerson();
    }
}

3.0 动态代理模式–cglib jar包cglib-nodep-2.1.3 说明:目标类是代理类的父类。

PersonDaoImpl.java

public class PersonDaoImpl implements PersonDao{
    public void savePerson() {
        System.out.println("save person");
    }
}



Transaction.java
public class Transaction {
    public void beginTransaction(){
        System.out.println("begin transaction");
    }

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

MyInterceptor.java

/**
 * 1、引入personDao和Transaction
 * 2、完成invoke方法
 * @author zd
 *
 */
public class MyInterceptor implements MethodInterceptor{
    private Object target;
    private Transaction transaction;
    public MyInterceptor(Object target,Transaction transaction){
        this.target = target;
        this.transaction = transaction;
    }

    public Object createProxy(){
        Enhancer enhancer = new Enhancer();
        enhancer.setCallback(this);//this代表拦截器对象
        enhancer.setSuperclass(target.getClass());//设置代理类的父类为目标类
        return enhancer.create();
    }
    /**
     * 该方法的内容和jdkpoxy中的invoke方法的内容是一样的
     */
    public Object intercept(Object arg0, Method method,  Object[] args,
            MethodProxy arg3) throws Throwable { 
        this.transaction.beginTransaction();
        method.invoke(this.target, args);
        this.transaction.commit();
        return null;
    }

}

PersonDao.java
public interface PersonDao {
    public void savePerson();
}

ProxyTest.java

/**
 * 目标类是代理类的父类
 * @author zd
 *
 */
public class ProxyTest {
    @Test
    public void testProxy(){
        PersonDaoImpl target = new PersonDaoImpl();
        Transaction transaction = new Transaction();
        MyInterceptor interceptor = new MyInterceptor(target, transaction);
        PersonDaoImpl proxy = (PersonDaoImpl)interceptor.createProxy();
        proxy.savePerson();
    }
}   

4.0 spring aop的概念 切面(Aspect):一个关注点的模块化,这个关注点可能会横切多个对象。事物管理是J2EE应用中一个横切关注点的很好的例子。 在Spring AOP中,切面可以使用基于模式或者基于@Aspect注解的方式来实现。 连接点(joinpoint):在程序执行过程中某个特定的点,比如某方法调用时候或者处理异常的时候。在Spring AOP中,一个连接点总是表示一个方法的执行。 通知(Advice):在切面的某个特定连接点执行的动作。其中包括了“around”、“before”和“after”等不同类型的通知。(通知的类型将在后面部分进行讨论)。 许多AOP框架(包括Spring)都是以拦截器做通知模型,并维护一个以连接点为中心的拦截器链。 切入点(Piontcut):匹配连接点的断言。通知和一个切入点表达式关联,并在满足这个切入点的连接点上运行。(例如,当执行某个特定名称的方法时)。 切入点表达式如何和连接点匹配是AOP的核心:Spring缺省使用Aspect切入点语法。 引入(Introduction):用来给一个模型声明额外的方法或属性(也被称为连接类型声明)(inter-type declaration))。Spring允许引入新的接口(以及一个对应的实现) 到任何被代理的对象。例如,你可以使用引入来使用一个bean实现IsModified接口,以便简化缓存机制。 目标对象(Target Object):被一个或者多个切面所通知的对象。也被称做通知(adviced)对象。既然Spring AOP是通过运行代理实现的,这个对象永远是一个被代理(Proxied)对象。 AOP代理(AOP Proxy):AOP框架创建的对象,用来实现切面契约(例如通知方法执行等等)。在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。 织入(Weaving):把切面连接到其他的应用程序类型或者对象,并创建一个被通知的对象。这些可以编译时(例如使用AspectJ编译器),类加载时和运行时完成。 Spring和其他纯Java AOP框架一样,在运行时完成织入。

JDKProxy代理                          SpringAop
目标对象                                目标对象
拦截器类                                切面
拦截器类中的方法                        通知
被拦截到的目标类中方法的集合              切入点
在客户端调用的方法(目标类目标方法)       连接点
代理类                                 AOP代理
代理类的代理方法生成的过程               织入

通知根据拦截目标类中的目标方法的位置不一样可以分为:前置通知、后置通知、最终通知、环绕通知、异常通知

说明:
1、通知就是切面中的方法;
2、代理对象的方法=通知+目标方法
3、连接点就是目标接口中的一个方法而已
4、拦截器中的invoke方法就是代理对象的方法=通知+目标方法
5、在现实的开发过程中,通知和目标方法时完全松耦合的

通知:就是切面的方法
织入:形成代理对象的方法的过程就是织入
连接点:客户端调用哪个方法,该方法就是连接点。
只有符合切入点,才让通知和目标方法结合在一起。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏DevOps时代的专栏

一张图了解 Spring Cloud 微服务架构

由上图所示微服务架构大致由上图的逻辑结构组成,其包括各种微服务、注册发现、服务网关、熔断器、统一配置、跟踪服务等。下面说说Spring cloud中的组件分别充...

25420
来自专栏Spring相关

[原创]SpringSecurity控制授权(鉴权)功能介绍

​ spring security中的除了用户登录校验相关的过滤器,最后还包含了鉴权功能的过滤器,还有匿名资源访问的过滤器链,相关的图解如下:

19630
来自专栏技术探索

<context:component-scan> 配置 —— 分库遇到问题(1)

相信有些人看到我贴出来的配置就知道我要说明什么问题了,如果你还是没有头绪的话,可以看下我遇到的问题。 这个配置文件本来是想要扫描 xxx包下面的Control...

28130
来自专栏微信公众号:Java团长

推荐 8 个 SpringBoot 精选项目

简介:支付服务:支付宝、微信、银联详细 代码案例,目前已经1800+Star。十分钟让你快速搭建一个支付服务,内附各种教程。

13120
来自专栏纯洁的微笑

百亿级企业级 RPC 框架开源了!

今天给大家介绍给一款性能卓越的 RPC 开源框架,其作者就是我推荐每个 Java 程序员都应该看的《Java 生态核心知识点整理》的原作者张玉龙。

15450
来自专栏技术探索

<dubbo:annotation >配置 —— 分库遇到问题(2)

在上篇笔记《context:component-scan 配置 —— 分库遇到问题(1)》中解决了 spring中某些实例被初始化了两次的问题, 但是紧接着又来...

35730
来自专栏SpringCloud专栏

SpringBoot2集成redis,使用lettuce客户端

Springboot集成redis大家都会用,主要就是使用RedisTemplate类来进行各种操作。可能很多人并没有注意,在Springboot2以后,底层访...

72810
来自专栏用户1337634的专栏

Class.getResource与ClassLoader.getResource的区别

可以看出来了,Class.getResource最后其实也是通过ClassLoader.getResource获取资源,只是在之前先把路径做了一次修改。修改的规...

30120
来自专栏程序猿DD

Netflix时代之后Spring Cloud微服务的未来

如果有人会问你有关Spring Cloud的问题,那么你想到的第一件事可能就是Netflix OSS的支持。对Eureka,Zuul或Ribbon等工具的支持不...

20240
来自专栏CSDN技术头条

如何能理解 Spring 框架的思想?

Spring 是一个轻量级的开源的 JavaEE 框架,由作者 Rod Johnson 创建,兴起于 2003 年。目的是为了解决企业级开发的复杂性问题,Spr...

14220

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励