利用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. win的cmd环境中设置***代理

    想在win的cmd环境中设置代理进行FQ安装软件,如npm等一系列. 1.配置好shadowsocks,然后编辑服务器,查看代理端口 2.打开win命令行cmd set http_proxy=http ...

  2. Redis 学习笔记(篇四):整数集合和压缩列表

    整数集合 Redis 中当一个集合(set)中只包含整数,并且元素不多时,底层使用整数集合实现,否则使用字典实现. 那么: 为什么会出现整数集合呢?都使用字典存储不行吗? 整数集合在 Redis 中的 ...

  3. chmod命令用法详解-chmod修改目录权限

    chmod用法: 用来修改某个目录或文件的访问权限.  语法: chmod [-cfvR] [--help] [--version] [who] [+ | - | =] [mode] 文件名 例子: ...

  4. zabbix报警信息设置

    报警信息 默认标题: 服务器监控报警 告警主机:{HOST.NAME} 主机IP: {HOST.IP} 告警级别:{TRIGGER.SEVERITY} 告警信息:{TRIGGER.NAME} 问题详情 ...

  5. SQL 对float类型列进行排序引发的异常

    车祸现场 要求:根据学分和完成时间获取前200名学员,当学分相同时,完成时间较早的排在前面 可以明显看到,完成时间为4.1号的记录排在了3.27号前面. 事故原因 float 表示近似数值,存在精度损 ...

  6. 安装R和Rstudio后,Rstudio出现空白和fatal error问题

    解决方法: 1.一定要以管理员方式启动Rstudio 2.只能安装一个R版本,之前安装的其他版本需要卸载干净 3.R和Rstudio需要安装在同一目录下

  7. CORS跨域djangosetting.py 配置

    1 什么是 CORS? Cross-Origin Resource Sharing(CORS)跨域资源共享是一份浏览器技术的规范,提供了 Web 服务从不同域传来沙盒脚本的方法,以避开浏览器的同源策略 ...

  8. django基础知识之HttpReqeust对象:

    HttpReqeust对象 服务器接收到http协议的请求后,会根据报文创建HttpRequest对象 视图函数的第一个参数是HttpRequest对象 在django.http模块中定义了HttpR ...

  9. shiro自定义异常无法被捕获总是抛出AuthenticationException解决方案

    这个问题我也是出的莫名其妙,刚开始好好的,然后配置多realm之后出的. 现在直入主题 在继承了 org.apache.shiro.authc.pam.ModularRealmAuthenticato ...

  10. 利用Jmeter模拟Github登录

    最近学习了Jmeter的简单操作,很想找点东西来实战一下,因为我之前写过一篇通过Python模拟登录的文章,于是便想尝试下学习通过Jmeter来模拟登录. 本人环境:Jmeter5.1.1 关于Git ...