Spring AOP使用的核心技术是动态代理,说到动态代理就不得不和设计模式中的代理模式联系起来,通过代理模式我们可以对目标类进行功能增强,在某个方法的执行前后增加一些操作,例如计算方法执行效率、打印日志等。

看下面的例子,我们有一个目标类Target,我们需要在目标类的test方法中增加日志打印功能,这时候我们就可以通过代理模式来实现:

package com.proxy.test;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; class Target {
public void test(){
System.out.println("Target test");
}
} class TargetProxy
{
private Target target;
private Log logger = LogFactory.getLog(TargetProxy.class);
public TargetProxy(Target target) {
this.target = target;
}
public void test()
{
logger.info("*****方法执行前**********");
target.test();
logger.info("*****方法执行后**********");
}
} public class ProxyTest{
public static void main(String[] args) {
Target target = new Target();
TargetProxy proxy = new TargetProxy(target);
proxy.test(); }
}

在上面的例子中我们通过代理类TargetProxy对目标类进行的功能增强,所谓的动态代理就是指在程序运行期间,在内存中动态的生成代理类的字节码并实例化代理对象。

实现方式有两种,一种是通过JDK自带的动态代理,另外一种则是使用Cglib实现。

1.使用JDk实现动态代理

例如在Service层,我们有两个业务逻辑类LoginServiceImpl和UserServiceImpl:

interface LoginService{
public boolean checkUser();
} class LoginServiceImpl implements LoginService{
@Override
public boolean checkUser() {
System.out.println("LoginServiceImpl checkUser");
return false;
}
} interface UserService{
public String getUserName();
} class UserServiceImpl implements UserService{ @Override
public String getUserName() {
System.out.println("UserServiceImpl getUserName");
return null;
} }

我们要对LoginServiceImpl和UserServiceImpl中的方法增加日志打印功能,可以通过Jdk动态代理实现,案例代码如下:

package com.proxy.test.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; interface LoginService{
public boolean checkUser();
} class LoginServiceImpl implements LoginService{
@Override
public boolean checkUser() {
System.out.println("LoginServiceImpl checkUser");
return false;
}
} interface UserService{
public String getUserName();
} class UserServiceImpl implements UserService{ @Override
public String getUserName() {
System.out.println("UserServiceImpl getUserName");
return null;
} } class ProxyHandler implements InvocationHandler{
private Object target;
private Log logger = LogFactory.getLog(ProxyHandler.class); public void setTarget(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] param)
throws Throwable {
logger.info("*********代理方法执行前************");
Object retObj = method.invoke(target, param);
logger.info("*********代理方法执行后************");
return retObj;
} } public class ProxyTestJDK {
public static void main(String[] args) {
//创建目标对象
LoginService loninService = new LoginServiceImpl();
UserService userService = new UserServiceImpl(); ProxyHandler proxyHandler = new ProxyHandler();
//创建LoginService代理对象
proxyHandler.setTarget(loninService);
LoginService loninService$Proxy = (LoginService) Proxy.newProxyInstance(loninService.getClass().getClassLoader(),
loninService.getClass().getInterfaces(), proxyHandler);
loninService$Proxy.checkUser(); //创建UserService代理对象
proxyHandler.setTarget(userService);
UserService userService$Proxy = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(),
userService.getClass().getInterfaces(), proxyHandler);
userService$Proxy.getUserName();
}
}

运行程序输出:

十月 17, 2015 1:37:21 下午 com.proxy.test.cglib.CglibProxy intercept
信息: *********代理方法执行前************
LoginServiceImpl checkUser
十月 17, 2015 1:37:21 下午 com.proxy.test.cglib.CglibProxy intercept
信息: *********代理方法执行后************
十月 17, 2015 1:37:21 下午 com.proxy.test.cglib.CglibProxy intercept
信息: *********代理方法执行前************
UserServiceImpl getUserName
十月 17, 2015 1:37:21 下午 com.proxy.test.cglib.CglibProxy intercept
信息: *********代理方法执行后************

2.使用Cglib动态代理实现

对于上面的需求我们也可以通过Cglib实现,具体代码如下:

package com.proxy.test.cglib;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
interface LoginService{
public boolean checkUser();
} class LoginServiceImpl implements LoginService{
@Override
public boolean checkUser() {
System.out.println("LoginServiceImpl checkUser");
return false;
}
} interface UserService{
public String getUserName();
} class UserServiceImpl implements UserService{ @Override
public String getUserName() {
System.out.println("UserServiceImpl getUserName");
return null;
} } class CglibProxy implements MethodInterceptor
{
private Log logger = LogFactory.getLog(CglibProxy.class); @Override
public Object intercept(Object proxy, Method method, Object[] params,
MethodProxy methodProxy) throws Throwable {
logger.info("*********代理方法执行前************");
Object retObj = methodProxy.invokeSuper(proxy, params);
logger.info("*********代理方法执行后************");
return retObj;
}
//返回目标对象的代理对象
public Object newProxy(Object target)
{
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
enhancer.setClassLoader(target.getClass().getClassLoader());
return enhancer.create();
}
}
public class ProxyTestCglib { public static void main(String[] args) {
//创建目标对象
LoginService loninService = new LoginServiceImpl();
UserService userService = new UserServiceImpl();
CglibProxy proxy = new CglibProxy();
//创建代理对象
LoginService loninService$Proxy = (LoginService)proxy.newProxy(loninService);
UserService userService$Proxy = (UserService)proxy.newProxy(userService);
loninService$Proxy.checkUser();
userService$Proxy.getUserName(); }
}

3.二者优缺点分析

使用JDK动态代理,目标类必须实现的某个接口,如果某个类没有实现接口则不能生成代理对象。

Cglib原理是针对目标类生成一个子类,覆盖其中的所有方法,所以目标类和方法不能声明为final类型。

从执行效率上看,Cglib动态代理效率较高。

JDK和Cglib实现动态代理实例及优缺点分析的更多相关文章

  1. JDK 和 CGLib 实现动态代理和区别

    JDK 和 CGLib 实现动态代理和区别 在日常的开发中,Spring AOP 是一个非常常用的功能.谈到 AOP,自然离不开动态代理. 那么,基于 JDK 和 CGLib 如何实现动态代理,他们之 ...

  2. JDK和CGLIB生成动态代理类的区别

     关于动态代理和静态代理 当一个对象(客户端)不能或者不想直接引用另一个对象(目标对象),这时可以应用代理模式在这两者之间构建一个桥梁--代理对象. 按照代理对象的创建时期不同,可以分为两种: 静态代 ...

  3. 【4】JDK和CGLIB生成动态代理类的区别

    当一个对象(客户端)不能或者不想直接引用另一个对象(目标对象),这时可以应用代理模式在这两者之间构建一个桥梁--代理对象. 按照代理对象的创建时期不同,可以分为两种: 静态代理:事先写好代理对象类,在 ...

  4. JDK和CGLIB生成动态代理类的区别(转)

     关于动态代理和静态代理 当一个对象(客户端)不能或者不想直接引用另一个对象(目标对象),这时可以应用代理模式在这两者之间构建一个桥梁--代理对象. 按照代理对象的创建时期不同,可以分为两种: 静态代 ...

  5. jdk与cglib的动态代理

    JDK动态代理中包含一个类和一个接口: InvocationHandler接口: public interface InvocationHandler { public Object invoke(O ...

  6. java面试题之spring aop中jdk和cglib哪个动态代理的性能更好?

    在jdk6和jdk7的时候,jdk比cglib要慢: 在jdk8的时候,jdk性能得到提升比cglib要快很多: 结论出自:https://www.cnblogs.com/xuliugen/p/104 ...

  7. JDK动态代理实现源码分析

    JDK动态代理实现方式 在Spring框架中经典的AOP就是通过动态代理来实现的,Spring分别采用了JDK的动态代理和Cglib动态代理,本文就来分析一下JDK是如何实现动态代理的. 在分析源码之 ...

  8. cglib实现动态代理简单使用

    Boss: package proxy.cglib; public class Boss{ public void findPerson() { System.out.println("我要 ...

  9. jdk自带的动态代理

    package com.stone.dp.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Met ...

随机推荐

  1. CorelDRAW中内置的视频教程在哪里?

    CorelDRAW中内置了很多教学内容和视频教程,可以帮助用户快速学习和掌握CorelDRAW的使用方法,创作出个性化的作品.很多小伙伴表示找不到软件自带学习视频,现在小编就来告诉你. 用户可以通过两 ...

  2. 团体程序设计天梯赛-练习集-L1-037. A除以B

    L1-037. A除以B 真的是简单题哈 —— 给定两个绝对值不超过100的整数A和B,要求你按照“A/B=商”的格式输出结果. 输入格式: 输入在第一行给出两个整数A和B(-100 <= A, ...

  3. MySQL NULL 值如何处理?

    我们已经知道 MySQL 使用 SQL SELECT 命令及 WHERE 子句来读取数据表中的数据,但是当提供的查询条件字段为 NULL 时,该命令可能就无法正常工作. 为了处理这种情况,MySQL提 ...

  4. mysql主主同步

    Mysql 主主同步方案 第一台机器主 [root@master ~]# vim /etc/my.cnf [mysqld] server-id=1 log-bin=mysql-binlog log-s ...

  5. 【剑指Offer】64、滑动窗口的最大值

      题目描述:   给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值.例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{ ...

  6. USACO 4.1 Fence Loops

    Fence Loops The fences that surround Farmer Brown's collection of pastures have gotten out of contro ...

  7. SQL第一节课

    phpmyadmin create table 表名( 列名 数据类型 是否为空 (是否主键|是否唯一|外键关系), 列名 数据类型...(最后一列不加逗号)) create database 数据库 ...

  8. 域名IP绑定

    该文为阿里云域名举例 首先具备3个前提: 买服务器并搭建环境:阿里云官网购买阿里云的服务器(我购买的是window系统,ECS服务器). 在自己的云服务器上布置上jdk,配置环境变量:安装上tomca ...

  9. 目录-Linux

    Linux文件系统: Linux: glibc 程序编译方式: 动态链接 静态编译 进程的类型: 终端:硬件设备,关联一个用户接口 与终端相关:通过终端启动 与终端无关:操作引导启动过程当中自动启动 ...

  10. node源码详解(四)

    本作品采用知识共享署名 4.0 国际许可协议进行许可.转载保留声明头部与原文链接https://luzeshu.com/blog/nodesource4 本博客同步在https://cnodejs.o ...