利用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. Programming In Lua 第四章

    1, 2, 3, 4, 5, 6, 7,

  2. LNet代码分析

    源码版本:lustre-release 2.15.55 介绍 LNet是Lustre的网络模块,代码目录分为了lnet和lnd lnet提供了统一的接口 lnd封装了底层驱动,有socklnd(TCP ...

  3. JavaScript 基础知识 变量与数据类型

    一.区分大小写 JS中一切(变量/函数名/操作符)都是严格区分大小写的 二.标识符 变量.函数.属性的名字以及函数的参数 命名规则:1.第一个字符可以是字母.下划线.美元符号$ 2.其他的字符可以是字 ...

  4. 记一次linux通过jstack定位CPU使用过高问题或排查线上死锁问题

    一.java定位进程 在服务器中终端输入命令:top 可以看到进程ID,为5421的cpu这列100多了. 记下这个数字:5421 二.定位问题进程对应的线程 然后在服务器中终端输入命令:top -H ...

  5. CentOS 常用命令合集

    tail -f ../logs/catalina.out    在Tomcat中的bin目录下查看Tomcat日志 ps -ef|grep java                 查看Tomcat服 ...

  6. 在django中用MySQL为数据库 新建一个项目的流程

    创建mysql数据库 在cmd输入 mysql -uroot -p 启动mysql客户端并连接服务器 创建一个db_orm的数据库 mysql>CREATE DATABASE db_orm ch ...

  7. 哈工大计算机网络Week2-网络应用数据交换

    目录 网络应用数据交换 P2P应用:原理与文件分发 纯P2P架构 文件分发:客户机/服务器 vs. P2P CS 为什么是这样的?不应该传送和发出难道是并行的??? P2P P2P文件分发典型例子:B ...

  8. 多线程总结-同步之ReentrantLock

    目录 1 ReentrantLock与synchronized对比 2.示例用法 2.1 基本用法 2.2 尝试锁 2.3 可打断 2.4 公平锁 1 ReentrantLock与synchroniz ...

  9. C语言 实验设备管理系统

    实验设备信息管理系统 简单的思路,简单的算法 题目简述:实验室设备信息用文件存储,提供文件的输入输出操作:要能够完成设备的录入和修改,需要提供设备添加和修改操作:实现对设备进行分类统计,需要提供排序操 ...

  10. TCP传输协议中如何解决丢包问题

    TCP在不可靠的网络上实现可靠的传输,必然会有丢包.TCP是一个“流”协议,一个详细的包将会被TCP拆分为好几个包上传,也是将会把小的封裝成大的上传,这就是说TCP粘包和拆包难题. 但是许多人有不同的 ...