利用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. 浅谈ASP.NET Core中IOC与DI的理解和使用

    说起IOC和DI,使用过ASP.NET Core的人对这两个概念一定不陌生,早前,自己也有尝试过去了解这两个东西,但是一直觉得有点很难去理解,总觉得对其还是模糊不清,所以,趁着今天有空,就去把两个概念 ...

  2. Python 爬虫从入门到进阶之路(九)

    之前的文章我们介绍了一下 Python 中的正则表达式和与爬虫正则相关的 re 模块,本章我们就利用正则表达式和 re 模块来做一个案例,爬取<糗事百科>的糗事并存储到本地. 我们要爬取的 ...

  3. Django中信号signals简单使用

    在平时的开发过程中,我们会遇到一些特殊的应用场景,如果你想要在执行某种操作之前或者之后你能够得到通知,并对其进行一些你想要的操作时,你就可以用Django中的信号(signals).Django 提供 ...

  4. Storm 学习之路(二)—— Storm核心概念详解

    一.Storm核心概念 1.1 Topologies(拓扑) 一个完整的Storm流处理程序被称为Storm topology(拓扑).它是一个是由Spouts 和Bolts通过Stream连接起来的 ...

  5. 使用事件注册器进行swoole代码封装

    在使用swoole的时候,事件回调很难维护与编写,写起来很乱.特别在封装一些代码的时候,使用这种注册,先注册用户自己定义的,然后注册些默认的事件函数. Server.php class Server ...

  6. SpringMVC_One

    SpringMVC_One SpringMVC的优势 (面试) 清晰的角色划分: 前端控制器(DispatcherServlet) 请求到处理器映射器(HandlerMapping) 处理器适配器(H ...

  7. Mybatisの常见面试题

    Mybatis -面试问题 最近准备系统的学一下Mybatis,之前只有粗略的看了下,选了十个常见的面试题 1. #{}和${}的区别是什么? #{}是预编译处理,${}是字符串替换. Mybatis ...

  8. 教你用VMware Workstation Pro 12 安装XP系统

    转     https://jingyan.baidu.com/article/ff42efa9102da3c19e220219.html

  9. 正确写网站title、keywords、description

    一.title title,也就是标题,是在浏览器上面显示出来的,方便用户了解这个页面的内容;特别是搜索引擎判断你网页内容的主要根据.搜索引擎就很大部分是依靠网站title来判断你网站是关于什么内容的 ...

  10. django基础知识之URLconf:

    URLconf 在settings.py文件中通过ROOT_URLCONF指定根级url的配置 urlpatterns是一个url()实例的列表 一个url()对象包括: 正则表达式 视图函数 名称n ...