一.代理模式

在不更改源码的前提下,加入新功能,通常需要用到代理设计模式。

代理设计模式分类:

静态代理

动态代理

jdk动态代理

cglib动态代理

其中spring AOP的底层用的是动态代理。其将动态代理进行封装,方便使用。

以通过Service调用DAO,从而向数据库添加更新用户为例,添加新的事务功能。

业务模型:

 package com.hdu.dao;

 public interface IUserDao {
     public int addrUser();
     public int updateUser();
 }

Interface IUserDao

 package com.hdu.dao.impl;

 import com.hdu.dao.IUserDao;

 public class UserDaoImpl implements IUserDao {

     @Override
     public int addrUser() {
         System.out.println("UserDaoImpl.addUser()");
         return 1;
     }

     @Override
     public int updateUser() {
         System.out.println("UserDaoImpl.updateUser()");
         return 1;
     }
 }

Class UserDaoImpl

 package com.hdu.service;

 public interface IUserService {
     public boolean addUser();
     public boolean updateUser();

 }

Interface IUserService

 package com.hdu.service.impl;

 import com.hdu.dao.IUserDao;
 import com.hdu.dao.impl.UserDaoImpl;
 import com.hdu.service.IUserService;

 public class UserServiceImpl implements IUserService {
     //应该用spring进行解耦,本次重点在于代理,故简化操作
     private IUserDao userDao = new UserDaoImpl();

     @Override
     public boolean addUser() {
         System.out.println("UserServiceImpl.addUser()");
         boolean flag = false;
         int rowAffect = userDao.addrUser();
         if(rowAffect==1) {
             flag = true;
         }
         return flag;
     }

     @Override
     public boolean updateUser() {
         System.out.println("UserServiceImpl.updateUser()");
         boolean flag = false;
         int rowAffect = userDao.updateUser();
         if(rowAffect==1) {
             flag = true;
         }
         return flag;
     }

 }

Class UserServiceImpl

新增事务功能:

TransactionManager

二.无代理(组合方式)

此时,需要改动源码,即在UserServiceImpl中加入TransactionManager,破坏了开闭原则。

 package com.hdu.service.impl;

 import com.hdu.dao.IUserDao;
 import com.hdu.dao.impl.UserDaoImpl;
 import com.hdu.other.TransactionManager;
 import com.hdu.service.IUserService;

 public class UserServiceImpl implements IUserService {
     //应该用spring进行解耦,本次重点在于代理,故简化操作
     private IUserDao userDao = new UserDaoImpl();
     private TransactionManager tm = new TransactionManager();

     @Override
     public boolean addUser() {
         tm.begin();
         boolean flag = false;
         System.out.println("UserServiceImpl.addUser()");
         int rowAffect = userDao.addrUser();
         if(rowAffect==1) {
             flag = true;
             tm.commit();
         }
         return flag;
     }

     @Override
     public boolean updateUser() {
         tm.begin();
         System.out.println("UserServiceImpl.updateUser()");
         boolean flag = false;
         int rowAffect = userDao.updateUser();
         if(rowAffect==1) {
             flag = true;
             tm.commit();
         }
         return flag;
     }

 }

Class UserServiceImpl

测试:

 package com.hdu.test;

 import org.junit.Test;

 import com.hdu.service.IUserService;
 import com.hdu.service.impl.UserServiceImpl;

 public class TestNoProxy {
     @Test
     public void testMethod1() {
         IUserService userService = new UserServiceImpl();
         userService.addUser();
         System.out.println();
         userService.updateUser();
     }
 }

Test

三.静态代理

  静态代理在编译期就已经写好了代理类,在编译期间确定了耦合关系。

    优点:在无需改动目标类或目标对象的源码,不破坏开闭原则的前提下,增加了新的功能。

   缺点:实现了实际的业务根新的功能解耦(UserServiceImpl和TransactionManager解耦),但用另一种方式静态代理的方式把(UserServiceImpl和TransactionManager耦合)。

         代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多。而一个静态代理类只能代理一个类,代码复用度不高。

 package com.hdu.proxy;

 import com.hdu.other.TransactionManager;
 import com.hdu.service.IUserService;

 /**
  * 本类是一个静态代理类,职责是把业务和新功能结合在一起,尽量降低耦合.
  * 实现接口是耦合,抽象耦合,耦合的目的,约束次代理类的功能.
  *private TransactionManager tm;具体耦合,用组合方式
  *private IUserService userService;抽象耦合,用组合方式  完成具体业务功能
  */
 public class StaticProxy implements IUserService {
     private TransactionManager tm;
     private IUserService userService;
     public StaticProxy(TransactionManager tm, IUserService userService) {
         this.tm = tm;
         this.userService = userService;
     }
     @Override
     public boolean addUser() {
         boolean flag = false;
         try {
             tm.begin();
             userService.addUser();
             tm.commit();
             flag = true;
         }catch(Exception e) {
             e.printStackTrace();
             tm.rollback();
         }
         return flag;
     }

     @Override
     public boolean updateUser() {
         boolean flag = false;
         try {
             tm.begin();
             userService.updateUser();
             tm.commit();
             flag = true;
         }catch(Exception e) {
             e.printStackTrace();
             tm.rollback();
         }
         return flag;
     }

 }

测试:

 package com.hdu.test;

 import org.junit.Test;

 import com.hdu.other.TransactionManager;
 import com.hdu.proxy.StaticProxy;
 import com.hdu.service.IUserService;
 import com.hdu.service.impl.UserServiceImpl;

 public class TestStaticProxy {
     @Test
     public void testMethod1() {
         //不用静态代理的方式(无事务管理)
         IUserService userService = new UserServiceImpl();
         userService.addUser();
         userService.updateUser();
     }

     @Test
     //带事务管理
     public void testMethod2() {
         TransactionManager tm = new TransactionManager();
         IUserService userService = new UserServiceImpl();

         StaticProxy sp = new StaticProxy(tm, userService);
         sp.addUser();
         System.out.println();
         sp.updateUser();

     }
 }

Test

无事务管理:

带事务管理:

四.动态代理

动态代理,是在运行期间来确定代理对象在运行期间确定实际业务和额外的新功能之间的关系。

jdk动态代理, 要求业务类必须有接口,没有接口无法生成代理对象;而cglib动态代理,不要求业务类必须有接口,有实现类即可。

优点:① 静态代理,每一个业务都对应一个静态的代理对象。

动态代理,InvocationHandler的子实现的复用率高。

缺点:实际业务类里的所有的方法都要添加额外的功能,不能实现只给部分方法添加额外的功能。

4.1 jdk动态代理

Proxy是JDK中用来创建代理对象的类,其中使用最多的是newProxyInstance方法来创建代理对象。

要求业务类必须有接口,没有接口无法生成代理对象。

public static Object newProxyInstance(
        ClassLoader loader,       //由哪个类加载器加载生成代理对象
        Class<?>[] interfaces,    //接口对象数组,必须传入目标对象的接口
        InvocationHandler h)      //表示一个invocationHandler对象,当代理对象执行方法时,会调用改InvocatonHandler对象
throws IllegalArgumentException    
package com.hdu.proxy;

import java.lang.reflect.Proxy;

public class JDKProxy {
    /**
     * 专门用来获取代理对象的方法,是通过jdk给提供的api来获取代理对象
     * @param targetObject 目标对象,就是实际业务的对象,
     *                     比如:UserServiceImpl类的对象 就是目标对象
     * @return Object obj 目标对象的代理对象,代理对象是跟目标对象是兄弟关系,所以目标对象所对应的类,必须有接口
     */
    public static Object getProxyObject(Object targetObject) {
        Object obj=null;
        /**
         * Object obj就是代理对象
         * 一参:目标对象的类加载器,获取到类路径
         * 二参:目标对象的接口,根据接口造出接口的对象
         * 三参:InvocationHandler接口的回调,实际就是实际业务和额外新功能的耦合类
         */
        obj=Proxy.newProxyInstance(
                targetObject.getClass().getClassLoader(),
                targetObject.getClass().getInterfaces(),
                new TransactionHandler(targetObject));

        return obj;
    }
}
package com.hdu.proxy;

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

import com.hdu.other.TransactionManager;

public class TransactionHandler implements InvocationHandler {
    private Object targetObject;
    public TransactionHandler(Object targetObject) {
        this.targetObject=targetObject;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object obj=null;
        TransactionManager tm=new TransactionManager();
        try {
            tm.begin();
            //由目标对象调用实际的功能方法
            obj=method.invoke(targetObject, args);//targetObject.addUser();
            tm.commit();
        }catch(Exception e) {
            e.printStackTrace();
            tm.rollback();
        }
        return obj;
    }

}

测试:

 package com.hdu.test;

 import org.junit.Test;

 import com.hdu.entity.User;
 import com.hdu.proxy.JDKProxy;
 import com.hdu.service.UserService;
 import com.hdu.service.impl.UserServiceImpl;

 public class TestDynamicProxy {
     @Test
     public void testMethod1() {
         //不用静态代理的方式(无事务管理)
         UserService targetObject=new UserServiceImpl();
         targetObject.addUser(new User());
         targetObject.updateUser(new User());
     }
     @Test
     public void testMethd2() {
         //targetObject根proxyObject是兄弟关系,是隶属于同一个接口
         //带事务管理,用动态代理方式
         UserServiceImpl targetObject=new UserServiceImpl();
         //送进去目标对象,出来的是代理对象
         UserService proxyObject=(UserService)JDKProxy.getProxyObject(targetObject);
         System.out.println(proxyObject.getClass());

         //是用代理对象调用方法
         proxyObject.addUser(new User());
         System.out.println();
         proxyObject.updateUser(new User());
     }
 }

Test

  4.2 cglib动态代理

不要求业务类必须有接口,有实现类即可。

需要导入jar包:

  

 package com.hdu.proxy;

 import net.sf.cglib.proxy.Enhancer;

 public class CGLIBProxy {
     /**
      * 专门用来获取代理对象的方法,是通过jdk给提供的api来获取代理对象
      * @param targetObject 目标对象,就是实际业务的对象,
      *                     比如:UserServiceImpl类的对象 就是目标对象
      * @return Object obj 目标对象的代理对象,代理对象是跟目标对象是父子关系,目标对象是长辈, 代理对象小辈。所以目标对象所对应的类,不能是final的。
      */
     public static Object getProxyObject(Object targetObject) {
         Object obj=null;
         Enhancer enhancer=new Enhancer();
         enhancer.setSuperclass(targetObject.getClass());
         enhancer.setCallback(new TransactionInterceptory(targetObject));
         obj=enhancer.create();
         return obj;
     }
 }
 package com.hdu.proxy;

 import java.lang.reflect.Method;

 import com.hdu.other.TransactionManager;

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

 public class TransactionInterceptory implements MethodInterceptor{
     private Object targetObject;
     public TransactionInterceptory(Object targetObject) {
         this.targetObject=targetObject;
     }
     @Override
     public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
         Object obj=null;
         try {
             TransactionManager tm=new TransactionManager();
             tm.begin();
             obj=method.invoke(targetObject, args);
             tm.commit();
         }catch(Exception e) {
             e.printStackTrace();
         }
         return obj;
     }
 }

测试:

 package com.hdu.test;

 import org.junit.Test;

 import com.hdu.entity.User;
 import com.hdu.proxy.CGLIBProxy;
 import com.hdu.service.UserService;
 import com.hdu.service.impl.UserServiceImpl;

 public class TestDynamicProxy {
     @Test
     public void testMethod1() {
         //不用静态代理的方式(无事务管理)
         UserService targetObject=new UserServiceImpl();
         targetObject.addUser(new User());
         targetObject.updateUser(new User());
     }
     @Test
     public void testMethd2() {
         //targetObject根proxyObject是父子关系
         //带事务管理,用动态代理方式
         UserServiceImpl targetObject=new UserServiceImpl();
         //送进去目标对象,出来的是代理对象
         UserService proxyObject=(UserService)CGLIBProxy.getProxyObject(targetObject);
         System.out.println(proxyObject.getClass());
         //是用代理对象调用方法
         proxyObject.addUser(new User());
         System.out.println();
         proxyObject.updateUser(new User());

     }
 }

Test

五.小结

代理实现方式

说明

静态代理

一个类一个代理,重复代码仍然较多,复用度低。

静态代理:事先写好代理对象类,在程序发布前就已经存在了。

调用真实业务方法

jdk

动态代理

动态代理:应用程序发布后,通过动态创建代理对象。

通过反射进行invoke回调

要求业务类必须有接口,没有接口无法生成代理对象

class com.sun.proxy.$Proxy4

cglib

动态代理

不要求业务类必须有接口,有实现类即可

底层采用asm字节码生成框架生成代理类的字节码,它创建慢,但一旦创建要比jdk方式快。

cglib使用继承,因此父类最好不要有final属性,final阻止继承和多态。

class com.hdu.service.impl.UserServiceImplEnhancerByCGLIB12d88161

ASM

字节码生成框架

ASM是一个Java字节码生成框架,它能够以二进制形式修改已有类欧哲动态生成类。ASM可也直接产生二进制class文件,也可以在类被加载如JVM前动态改变类行为,ASM从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。不过ASM在创建class字节码的过程中,操纵的级别是底层JVM的汇报指令级别。这要求就高了,对开发者要对class的结构和JVM指令要有一定了解。

代理模式及Spring AOP (一)的更多相关文章

  1. Hibernate 延迟加载的代理模式 和 Spring AOP的代理模式

    Hibernate 延迟加载的代理模式 和 Spring AOP的代理模式 主题 概念 Hibernate 延迟加载的代理模式 Spring AOP的代理模式 区别和联系 静态代理和动态代理 概念 代 ...

  2. 从代理模式到Spring AOP

    什么是代理模式 假如我喜欢上隔壁班的翠花,但是我没胆量向她送花,这时候我需要一个铁杆哥们帮我做这件事, 很明显这哥们是个代理,是去执行任务的,但是花实际上是我"送"的,代理和我一样 ...

  3. 代理模式及Spring AOP (二)

    一.Spring AOP   1.1 Spring AOP 底层还是用的动态代理.如果目标对象所对应的类有接口,spring就用jdk生成代理对象: 如果目标对象所对应的类没有接口,spring就用C ...

  4. CgLib动态代理学习【Spring AOP基础之一】

    如果不了解JDK中proxy动态代理机制的可以先查看上篇文章的内容:Java动态代理学习[Spring AOP基础之一] 由于Java动态代理Proxy.newProxyInstance()的时候会发 ...

  5. java代理课程测试 spring AOP代理简单测试

    jjava加强课程测试代码 反射. 代理 .泛型.beanUtils等 项目源码下载:http://download.csdn.net/detail/liangrui1988/6568169 热身运动 ...

  6. Java动态代理学习【Spring AOP基础之一】

    Spring AOP使用的其中一个底层技术就是Java的动态代理技术.Java的动态代理技术主要围绕两个类进行的 java.lang.reflect.InvocationHandler java.la ...

  7. 基于代理类实现Spring AOP

    目录 ProxyFactoryBean类介绍 基于JDK动态代理的Spring  AOP实现 基于CGLIB代理的Spring  AOP实现 Spring的通知类型 ProxyFactoryBean类 ...

  8. Spring AOP详解 、 JDK动态代理、CGLib动态代理

    AOP是Aspect Oriented Programing的简称,面向切面编程.AOP适合于那些具有横切逻辑的应用:如性能监测,访问控制,事务管理以及日志记录.AOP将这些分散在各个业务逻辑中的代码 ...

  9. 【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理

    Spring AOP详解 . JDK动态代理.CGLib动态代理  原文地址:https://www.cnblogs.com/kukudelaomao/p/5897893.html AOP是Aspec ...

随机推荐

  1. input 输入框只能输入纯数字

    1.onkeyup = "value=value.replace(/[^\d]/g,'')" 使用 onkeyup 事件,有 bug ,那就是在中文输入法状态下,输入汉字之后直接回 ...

  2. Vultr新推出3.5美元/月套餐,并且支持微信支付了

    先前Vultr重新推出了2.5美元/月的套餐,但是不支持IPv4,所以不那么受国内朋友的欢迎,迫于压力,这不最近就推出了3.5美元/月的套餐了,这个套餐是支持IPv4的,有需要的朋友可以上车了,htt ...

  3. js实现软件版本号的比较

    //js实现软件版本号的比较 //随机举两个例子 pc2.4.3 或者pc3.5.6 /** * 输入 v1,v2 * 返回true代表v1比v2的版本新,false则代表v1与v2相等或者v1< ...

  4. 20170706xlVBA根据工资汇总表生成个人工资条

    Sub NextSeven20170706001() Application.ScreenUpdating = False Application.DisplayAlerts = False Appl ...

  5. python-day47--pymysql模块

    一.安装导入 #安装 pip3 install pymysql 二.使用 1 .基本使用 import pymysql # 链接,拿到游标 conn=pymysql.connect(host='loc ...

  6. python-day5笔记

    一.python基础--基本数据类型 (无论用户输入什么内容,input 都会存成字符串格式) 1.基本数据类型 1)数字类型 整型(整数)int:年级,年纪,等级,身份证号,QQ号,手机号,leve ...

  7. hdu-6301-贪心

    Distinct Values Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)T ...

  8. 无法安装Java,以下开关中存在错误:“0”

    无法安装Java,以下开关中存在错误:“0”:. 解决方法:以管理员运行

  9. img标签中alt属性与title属性在seo的作用-摘自网友

    img标签中alt属性与title属性作用,也许大家比较迷惑,现在给大家举例说明.alt属性是图片的替换文字.title属性规定元素的额外信息,有视觉效果. 目录 alt属性 title属性 ie和f ...

  10. 109. Magic of David Copperfield II 构造 难度:2

    109. Magic of David Copperfield II time limit per test: 0.25 sec. memory limit per test: 4096 KB The ...