利用IOC DI实现软件分层,虽然解决了耦合问题,但是很多地方仍然存在非该层应该实现的功能,造成了无法“高内聚”的现象,同时存在大量重复的代码,开发效率低下。

 @Service
public class UserServiceImpl implements UserService {
        @Autowired
        private UserDao userDao;         @Override
        public void registUser(User user) {
                try {
                        System.out.println("校验权限。。。");
                        System.out.println("开启事务。。。");
                        System.out.println("记录日志。。。");
                        userDao.addUser(user);
                        System.out.println("提交事务。。。");
                } catch (Exception e) {
                        System.out.println("回滚事务");
                        e.printStackTrace();
                }
        }         @Override
        public void upToVIP(User user) {
                try {
                        System.out.println("校验权限。。。");
                        System.out.println("开启事务。。。");
                        System.out.println("记录日志。。。");
                        userDao.updateUser(user);
                        System.out.println("提交事务。。。");
                } catch (Exception e) {
                        System.out.println("回滚事务");
                        e.printStackTrace();
                }
                
        }         @Override
        public void removeUser(User user) {
                try {
                        System.out.println("校验权限。。。");
                        System.out.println("开启事务。。。");
                        System.out.println("记录日志。。。");
                        userDao.deleteUser(user.getId());
                        System.out.println("提交事务。。。");
                } catch (Exception e) {
                        System.out.println("回滚事务");
                        e.printStackTrace();
                }
        } }

  此时,可以通过代理设计模式,将这部分代码提取到代理者中,简化层中的代码。

一、静态代理模式

  优点:结构清晰,便于理解

  缺点:如果被代理者有多个方法,则代理者也需要开发多个方法,其中往往存在大量重复代码,仍然存在代码重复。

  静态代码设计模式解决了软件分层过程中 额外的功能代码侵入模块的问题,将额外的功能代码提取到了代理者中进行,但是静态代理实现的代理者中存在大量重复代码,并没有解决代码重复问题。所以在开发中 --包括spring的底层,基本不会使用静态代理。

 package cn.tedu.staticproxy;
public interface SJSkill {
        public void 吃();
        public void 唱歌();
} package cn.tedu.staticproxy;
public class FBB implements SJSkill{
        public void 吃(){
                System.out.println("fbb吃饭。。。");
        }
        public void 唱歌(){
                System.out.println("fbb唱歌。。。");
        }
} package cn.tedu.staticproxy;
public class JJRStaticProxy implements SJSkill{         private FBB fbb = new FBB();
        
        @Override
        public void 吃() {
                System.out.println("权限认证:你谁啊????");
                fbb.吃();
                System.out.println("记录日志:等我,我记一下来访记录");
        }         @Override
        public void 唱歌() {
                System.out.println("权限认证:你谁啊????");
                fbb.唱歌();
                System.out.println("记录日志:等我,我记一下来访记录");
        } } package cn.tedu.staticproxy;
import org.junit.Test;
public class StaticProxyTest {
        @Test
        public void test01(){
                JJRStaticProxy jjr = new JJRStaticProxy();
                jjr.吃();
                jjr.唱歌();
        }
}

二、动态代理-jdk内置的动态代理

  在jdk提供了动态代理实现的工具类,直接使用该工具类就可以创建出代理者,并且可以通过内置的回调函数指定代理在工作时的执行逻辑,从而实现基于jdk原生api的动态代理机制。

  java动态代理的特点:

    优点:不需要像静态代理一样被代理方法都要实现一遍,而只需要在回调函数中进行处理就可以了,重复代码只需编写一次。

    缺点:java的动态代理是通过代理者实现和被代理者相同的接口来保证两者具有相同的方法的,如果被代理者想要被代理的方法不属于任何接口,则生成的代理者自然无法具有这个方法,也就无法实现对该方法的代理。所以java的动态代理机制是基于接口进行的,受限于要代理的方法是否有接口的支持。

  案例:

 package cn.tedu.javaproxy;

 import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; import org.junit.Test; public class JavaProxyTest {
        @Test
        public void test01(){
        
                //被代理者
                final FBB fbb = new FBB();
                
                //java动态代理方式 生成fbb的代理者
                /**
                 * classLoader:用来生成代理者类的类加载器,通常可以传入被代理者类的类加载器
                 * interfaces: 要求生成的代理者实现的接口们,通常就是实现和被代理者相同的接口,保证具有和被代理者相同的方法
                 * invocationHandler: 用来设定回调函数的回调接口,使用者需要写一个类实现此接口,从而实现其中的invoke方法,
                 * 在其中编写代码处理代理者调用方法时的回调过程,通常在这里调用真正对象身上的方法,并且在方法之前或之后做额外操作。
                 */
                SJSkill proxy = (SJSkill) Proxy.newProxyInstance(FBB.class.getClassLoader(),FBB.class.getInterfaces()
                                ,new InvocationHandler() {
                                        @Override
                                        /**
                                         * proxy: 代理者
                                         * method:当前调用的方法对象
                                         * args:挡墙调用的方法的参数数组
                                         */
                                        public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
                                                if("拍电影".equals(method.getName())){
                                                        System.out.println("不好意思,给多少钱不拍了~~");
                                                        return null;
                                                }else{
                                                        System.out.println("检验权限。。。。");
                                                        Object returnObj = method.invoke(fbb, args);
                                                        System.out.println("记录日志。。。。");
                                                        return returnObj;
                                                }
                                        }
                                });
                //从此之后,不允许直接调用被代理者身上的方法,而是要通过代理者来调用
                //fbb.吃();
                //fbb.唱歌();
                proxy.吃();
                proxy.唱歌();
                proxy.拍电影();
        }
}

    java动态代理原理图:

    

三、动态代理-第三方包cglib实现的动态代理

    CGLIB是第三方提供的动态代理的实现工具,不管有没有接口都可以实现动态代理。

    CGLIB实现动态代理的原理:生成的动态代理是被代理者的子类,所以代理者具有和父类即被代理者相同的方法,从而实现代理。

    CGLIB动态代理的特点:

      优点:无论是否有接口都可以实现动态代理,使用场景基本不受限制

      缺点:第三方提供的动态代理机制不是原生的,需要导入第三方的开发包才可以使用。

  案例:

      先导入CGLIB相关包:spring-core-3.2.3.RELEASE.jar

        如果导入了spring包就包含了CGLIB

 package cn.tedu.cglibproxy;

 import java.lang.reflect.Method;

 import org.junit.Test;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy; public class CglibProxyTest {
        @Test
        public void test01(){
                final FBB fbb = new FBB();
                
                //增强器
                Enhancer enhancer = new Enhancer();
                
                //设定接口 -- 此方法要求生成的动态代理额外实现指定接口们 ,单cglib动态代理不是靠接口实现的,所以可以不设置
                enhancer.setInterfaces(fbb.getClass().getInterfaces());
                
                //设定父类 -- 此处要传入被代理者的类,cglib是通过集成被代理者的类来持有和被代理者相同的方法的,此方法必须设置
                enhancer.setSuperclass(fbb.getClass());
                
                //设定回调函数 -- 为增强器设定回调函数,之后通过增强器生成的代理对象调用任何方法都会走到此回调函数中,实现调用真正被代理对象的方法的效果
                enhancer.setCallback(new MethodInterceptor() {
                        @Override
                        public Object intercept(Object proxy, Method method, Object[] args,
                                        MethodProxy methodProxy) throws Throwable {
                                if("拍电影".equals(method.getName())){
                                        System.out.println("对不起,不拍了~~~");
                                        return null;
                                }else{
                                        System.out.println("检查权限。。。");
                                        Object returnObj = method.invoke(fbb, args);
                                        System.out.println("记录日志。。。");
                                        return returnObj;
                                }
                        }
                });
                
                //生成代理对象
                FBB proxy = (FBB) enhancer.create();
                proxy.吃();
                proxy.唱歌();
                proxy.拍电影();
        }
}

  CGLIB动态代理原理图:

将功能代码提取到代理者中,实现 “高内聚” 的效果。

 package cn.tedu.em.service;

 import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import cn.tedu.em.dao.UserDao;
import cn.tedu.em.domain.User; @Service
public class UserServiceImpl implements UserService { @Autowired
private UserDao userDao; public void upToVIP(User user){
userDao.updateUser(user);
} public void removeUser(User user){
userDao.deleteUser(5);
} public void registUser(User user){
userDao.addUser(user);
}
} package cn.tedu.em.service; import java.lang.reflect.Method; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.stereotype.Service; @Service
public class UserServiceImplCglibProxy { @Autowired
@Qualifier("userServiceImpl")
private UserService userService; public UserServiceImplCglibProxy() { } public UserService getCglibProxy() {
Enhancer enhancer = new Enhancer();
enhancer.setInterfaces(userService.getClass().getInterfaces());
enhancer.setSuperclass(userService.getClass());
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object proxy, Method method, Object[] args,
MethodProxy mproxy) throws Throwable {
try {
System.out.println("校验权限。。。");
System.out.println("开启事务。。。");
System.out.println("记录日志。。。"); Object returnObj = method.invoke(userService, args); System.out.println("提交事务。。。");
return returnObj;
} catch (Exception e) {
System.out.println("回滚事务");
e.printStackTrace();
throw new RuntimeException(e);
}
}
});
return (UserService) enhancer.create();
}
} package cn.tedu.em.web; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller; import cn.tedu.em.domain.User;
import cn.tedu.em.service.UserServiceImplCglibProxy; @Controller
public class RegistServlet { @Autowired
//private UserService userService;
//private UserServiceImplJavaProxy proxy;
private UserServiceImplCglibProxy proxy;
public void regist(){
User user = new User(1,"LK","断桥残雪","4192@qq.com");
//proxy.getJavaProxy().upToVIP(user);
proxy.getCglibProxy().upToVIP(user);
}
}

JAVA-Spring AOP基础 - 代理设计模式的更多相关文章

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

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

  2. [Spring框架]Spring AOP基础入门总结二:Spring基于AspectJ的AOP的开发.

    前言: 在上一篇中: [Spring框架]Spring AOP基础入门总结一. 中 我们已经知道了一个Spring AOP程序是如何开发的, 在这里呢我们将基于AspectJ来进行AOP 的总结和学习 ...

  3. [Spring框架]Spring AOP基础入门总结一.

    前言:前面已经有两篇文章讲了Spring IOC/DI 以及 使用xml和注解两种方法开发的案例, 下面就来梳理一下Spring的另一核心AOP. 一, 什么是AOP 在软件业,AOP为Aspect ...

  4. Java Spring AOP用法

    Java Spring AOP用法 Spring AOP Java web 环境搭建 Java web 项目搭建 Java Spring IOC用法 spring提供了两个核心功能,一个是IoC(控制 ...

  5. 死磕Spring之AOP篇 - Spring AOP自动代理(二)筛选合适的通知器

    该系列文章是本人在学习 Spring 的过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring 源码分析 GitHub 地址 进行阅读. Spring 版本:5.1 ...

  6. 死磕Spring之AOP篇 - Spring AOP自动代理(三)创建代理对象

    该系列文章是本人在学习 Spring 的过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring 源码分析 GitHub 地址 进行阅读. Spring 版本:5.1 ...

  7. 从零开始学 Java - Spring AOP 实现用户权限验证

    每个项目都会有权限管理系统 无论你是一个简单的企业站,还是一个复杂到爆的平台级项目,都会涉及到用户登录.权限管理这些必不可少的业务逻辑.有人说,企业站需要什么权限管理阿?那行吧,你那可能叫静态页面,就 ...

  8. 从零开始学 Java - Spring AOP 实现主从读写分离

    深刻讨论为什么要读写分离? 为了服务器承载更多的用户?提升了网站的响应速度?分摊数据库服务器的压力?就是为了双机热备又不想浪费备份服务器?上面这些回答,我认为都不是错误的,但也都不是完全正确的.「读写 ...

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

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

随机推荐

  1. OVS实现VXLAN隔离

    一.实验环境 1.准备3个CentOS7 mini版本的虚拟机,每个主机3个网卡.如图: 图中OVS-1.OVS-2.OVS-3分别为三台CentOS7 mini版虚拟机,分别配备3个虚拟网卡.如图中 ...

  2. Kafka 学习之路(一)—— Kafka简介

    一.简介 Apache Kafka是一个分布式的流处理平台.它具有以下特点: 支持消息的发布和订阅,类似于RabbtMQ.ActiveMQ等消息队列: 支持数据实时处理: 能保证消息的可靠性投递: 支 ...

  3. Redis 安装和配置(一)

    一. mac下redis的安装 1. 官网http://redis.io/ 下载最新的稳定版本,这里是3.2.0 2. sudo mv 到 /usr/local/ 3. sudo tar -zxf r ...

  4. rbash限制用户执行的命令

    rbash限制用户执行的命令 软连接 sudo ln -s /bin/bash /bin/rbash sudo bash -c 'echo "/bin/rbash" >> ...

  5. oh-my-zsh自定义配置

    oh-my-zsh主题配置 默认的zsh主题robbyrussell已经很棒了, 简洁高效, 能很好的显示git的相关信息, 比如branch信息, 修改, 删除, 添加等操作. 但是多用户的话就不能 ...

  6. Smobiler实现手机弹窗

    前言 在实际项目中有很多场景需要用到弹窗,如图1 那么这些弹窗在Smobiler中如何实现呢? 正文 Smobiler实现弹窗有两种方式:1.MessageBox.Show 2.ShowDialog和 ...

  7. asyncio源码分析之基本执行流程

    基于async关键字的原生协程 # 定义一个简单的原生协程cor async def cor(): print('enter cor') print('exit cor') print(type(co ...

  8. Linux嵌入式kgdb调试环境搭建

    ======================= 我的环境 ==========================PC 端: win7 + vmware-15 ubuntu16.04开发板:Freesca ...

  9. HDU 1286:找新朋友(欧拉函数)

    http://acm.hdu.edu.cn/showproblem.php?pid=1286 题意:中文. 思路:求欧拉函数. #include <cstdio> #include < ...

  10. 从壹开始[ 做贡献 ]之三 || 北京.Net俱乐部活动——DNT精英论坛开幕

    缘起 哈喽大家好!好久不见,可能有一部分小伙伴发现我好久没有写文章了,是不是懒惰了,并没有,这两周也是正式开始了<NetCore系列教程的视频录制>,不过还不多,预计会是每周一个视频,基本 ...