代理模式及Spring AOP (一)
一.代理模式
在不更改源码的前提下,加入新功能,通常需要用到代理设计模式。
代理设计模式分类:
静态代理
动态代理
jdk动态代理
cglib动态代理
其中spring AOP的底层用的是动态代理。其将动态代理进行封装,方便使用。
以通过Service调用DAO,从而向数据库添加更新用户为例,添加新的事务功能。
业务模型:
package com.hdu.dao;
public interface IUserDao {
public int addrUser();
public int updateUser();
}
Interface IUserDao
package com.hdu.dao.impl;
import com.hdu.dao.IUserDao;
public class UserDaoImpl implements IUserDao {
@Override
public int addrUser() {
System.out.println("UserDaoImpl.addUser()");
return 1;
}
@Override
public int updateUser() {
System.out.println("UserDaoImpl.updateUser()");
return 1;
}
}
Class UserDaoImpl
package com.hdu.service;
public interface IUserService {
public boolean addUser();
public boolean updateUser();
}
Interface IUserService
package com.hdu.service.impl;
import com.hdu.dao.IUserDao;
import com.hdu.dao.impl.UserDaoImpl;
import com.hdu.service.IUserService;
public class UserServiceImpl implements IUserService {
//应该用spring进行解耦,本次重点在于代理,故简化操作
private IUserDao userDao = new UserDaoImpl();
@Override
public boolean addUser() {
System.out.println("UserServiceImpl.addUser()");
boolean flag = false;
int rowAffect = userDao.addrUser();
if(rowAffect==1) {
flag = true;
}
return flag;
}
@Override
public boolean updateUser() {
System.out.println("UserServiceImpl.updateUser()");
boolean flag = false;
int rowAffect = userDao.updateUser();
if(rowAffect==1) {
flag = true;
}
return flag;
}
}
Class UserServiceImpl
新增事务功能:
二.无代理(组合方式)
此时,需要改动源码,即在UserServiceImpl中加入TransactionManager,破坏了开闭原则。
package com.hdu.service.impl;
import com.hdu.dao.IUserDao;
import com.hdu.dao.impl.UserDaoImpl;
import com.hdu.other.TransactionManager;
import com.hdu.service.IUserService;
public class UserServiceImpl implements IUserService {
//应该用spring进行解耦,本次重点在于代理,故简化操作
private IUserDao userDao = new UserDaoImpl();
private TransactionManager tm = new TransactionManager();
@Override
public boolean addUser() {
tm.begin();
boolean flag = false;
System.out.println("UserServiceImpl.addUser()");
int rowAffect = userDao.addrUser();
if(rowAffect==1) {
flag = true;
tm.commit();
}
return flag;
}
@Override
public boolean updateUser() {
tm.begin();
System.out.println("UserServiceImpl.updateUser()");
boolean flag = false;
int rowAffect = userDao.updateUser();
if(rowAffect==1) {
flag = true;
tm.commit();
}
return flag;
}
}
Class UserServiceImpl
测试:
package com.hdu.test;
import org.junit.Test;
import com.hdu.service.IUserService;
import com.hdu.service.impl.UserServiceImpl;
public class TestNoProxy {
@Test
public void testMethod1() {
IUserService userService = new UserServiceImpl();
userService.addUser();
System.out.println();
userService.updateUser();
}
}
Test
三.静态代理
静态代理在编译期就已经写好了代理类,在编译期间确定了耦合关系。
优点:在无需改动目标类或目标对象的源码,不破坏开闭原则的前提下,增加了新的功能。
缺点:实现了实际的业务根新的功能解耦(UserServiceImpl和TransactionManager解耦),但用另一种方式静态代理的方式把(UserServiceImpl和TransactionManager耦合)。
代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多。而一个静态代理类只能代理一个类,代码复用度不高。
package com.hdu.proxy;
import com.hdu.other.TransactionManager;
import com.hdu.service.IUserService;
/**
* 本类是一个静态代理类,职责是把业务和新功能结合在一起,尽量降低耦合.
* 实现接口是耦合,抽象耦合,耦合的目的,约束次代理类的功能.
*private TransactionManager tm;具体耦合,用组合方式
*private IUserService userService;抽象耦合,用组合方式 完成具体业务功能
*/
public class StaticProxy implements IUserService {
private TransactionManager tm;
private IUserService userService;
public StaticProxy(TransactionManager tm, IUserService userService) {
this.tm = tm;
this.userService = userService;
}
@Override
public boolean addUser() {
boolean flag = false;
try {
tm.begin();
userService.addUser();
tm.commit();
flag = true;
}catch(Exception e) {
e.printStackTrace();
tm.rollback();
}
return flag;
}
@Override
public boolean updateUser() {
boolean flag = false;
try {
tm.begin();
userService.updateUser();
tm.commit();
flag = true;
}catch(Exception e) {
e.printStackTrace();
tm.rollback();
}
return flag;
}
}
测试:
package com.hdu.test;
import org.junit.Test;
import com.hdu.other.TransactionManager;
import com.hdu.proxy.StaticProxy;
import com.hdu.service.IUserService;
import com.hdu.service.impl.UserServiceImpl;
public class TestStaticProxy {
@Test
public void testMethod1() {
//不用静态代理的方式(无事务管理)
IUserService userService = new UserServiceImpl();
userService.addUser();
userService.updateUser();
}
@Test
//带事务管理
public void testMethod2() {
TransactionManager tm = new TransactionManager();
IUserService userService = new UserServiceImpl();
StaticProxy sp = new StaticProxy(tm, userService);
sp.addUser();
System.out.println();
sp.updateUser();
}
}
Test
无事务管理:

带事务管理:

四.动态代理
动态代理,是在运行期间来确定代理对象在运行期间确定实际业务和额外的新功能之间的关系。
jdk动态代理, 要求业务类必须有接口,没有接口无法生成代理对象;而cglib动态代理,不要求业务类必须有接口,有实现类即可。
优点:① 静态代理,每一个业务都对应一个静态的代理对象。
动态代理,InvocationHandler的子实现的复用率高。
缺点:实际业务类里的所有的方法都要添加额外的功能,不能实现只给部分方法添加额外的功能。
4.1 jdk动态代理
Proxy是JDK中用来创建代理对象的类,其中使用最多的是newProxyInstance方法来创建代理对象。
要求业务类必须有接口,没有接口无法生成代理对象。
public static Object newProxyInstance(
ClassLoader loader, //由哪个类加载器加载生成代理对象
Class<?>[] interfaces, //接口对象数组,必须传入目标对象的接口
InvocationHandler h) //表示一个invocationHandler对象,当代理对象执行方法时,会调用改InvocatonHandler对象
throws IllegalArgumentException
package com.hdu.proxy;
import java.lang.reflect.Proxy;
public class JDKProxy {
/**
* 专门用来获取代理对象的方法,是通过jdk给提供的api来获取代理对象
* @param targetObject 目标对象,就是实际业务的对象,
* 比如:UserServiceImpl类的对象 就是目标对象
* @return Object obj 目标对象的代理对象,代理对象是跟目标对象是兄弟关系,所以目标对象所对应的类,必须有接口
*/
public static Object getProxyObject(Object targetObject) {
Object obj=null;
/**
* Object obj就是代理对象
* 一参:目标对象的类加载器,获取到类路径
* 二参:目标对象的接口,根据接口造出接口的对象
* 三参:InvocationHandler接口的回调,实际就是实际业务和额外新功能的耦合类
*/
obj=Proxy.newProxyInstance(
targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(),
new TransactionHandler(targetObject));
return obj;
}
}
package com.hdu.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import com.hdu.other.TransactionManager;
public class TransactionHandler implements InvocationHandler {
private Object targetObject;
public TransactionHandler(Object targetObject) {
this.targetObject=targetObject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object obj=null;
TransactionManager tm=new TransactionManager();
try {
tm.begin();
//由目标对象调用实际的功能方法
obj=method.invoke(targetObject, args);//targetObject.addUser();
tm.commit();
}catch(Exception e) {
e.printStackTrace();
tm.rollback();
}
return obj;
}
}
测试:
package com.hdu.test;
import org.junit.Test;
import com.hdu.entity.User;
import com.hdu.proxy.JDKProxy;
import com.hdu.service.UserService;
import com.hdu.service.impl.UserServiceImpl;
public class TestDynamicProxy {
@Test
public void testMethod1() {
//不用静态代理的方式(无事务管理)
UserService targetObject=new UserServiceImpl();
targetObject.addUser(new User());
targetObject.updateUser(new User());
}
@Test
public void testMethd2() {
//targetObject根proxyObject是兄弟关系,是隶属于同一个接口
//带事务管理,用动态代理方式
UserServiceImpl targetObject=new UserServiceImpl();
//送进去目标对象,出来的是代理对象
UserService proxyObject=(UserService)JDKProxy.getProxyObject(targetObject);
System.out.println(proxyObject.getClass());
//是用代理对象调用方法
proxyObject.addUser(new User());
System.out.println();
proxyObject.updateUser(new User());
}
}
Test

4.2 cglib动态代理
不要求业务类必须有接口,有实现类即可。
需要导入jar包:

package com.hdu.proxy;
import net.sf.cglib.proxy.Enhancer;
public class CGLIBProxy {
/**
* 专门用来获取代理对象的方法,是通过jdk给提供的api来获取代理对象
* @param targetObject 目标对象,就是实际业务的对象,
* 比如:UserServiceImpl类的对象 就是目标对象
* @return Object obj 目标对象的代理对象,代理对象是跟目标对象是父子关系,目标对象是长辈, 代理对象小辈。所以目标对象所对应的类,不能是final的。
*/
public static Object getProxyObject(Object targetObject) {
Object obj=null;
Enhancer enhancer=new Enhancer();
enhancer.setSuperclass(targetObject.getClass());
enhancer.setCallback(new TransactionInterceptory(targetObject));
obj=enhancer.create();
return obj;
}
}
package com.hdu.proxy;
import java.lang.reflect.Method;
import com.hdu.other.TransactionManager;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class TransactionInterceptory implements MethodInterceptor{
private Object targetObject;
public TransactionInterceptory(Object targetObject) {
this.targetObject=targetObject;
}
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object obj=null;
try {
TransactionManager tm=new TransactionManager();
tm.begin();
obj=method.invoke(targetObject, args);
tm.commit();
}catch(Exception e) {
e.printStackTrace();
}
return obj;
}
}
测试:
package com.hdu.test;
import org.junit.Test;
import com.hdu.entity.User;
import com.hdu.proxy.CGLIBProxy;
import com.hdu.service.UserService;
import com.hdu.service.impl.UserServiceImpl;
public class TestDynamicProxy {
@Test
public void testMethod1() {
//不用静态代理的方式(无事务管理)
UserService targetObject=new UserServiceImpl();
targetObject.addUser(new User());
targetObject.updateUser(new User());
}
@Test
public void testMethd2() {
//targetObject根proxyObject是父子关系
//带事务管理,用动态代理方式
UserServiceImpl targetObject=new UserServiceImpl();
//送进去目标对象,出来的是代理对象
UserService proxyObject=(UserService)CGLIBProxy.getProxyObject(targetObject);
System.out.println(proxyObject.getClass());
//是用代理对象调用方法
proxyObject.addUser(new User());
System.out.println();
proxyObject.updateUser(new User());
}
}
Test

五.小结
|
代理实现方式 |
说明 |
|
静态代理 |
一个类一个代理,重复代码仍然较多,复用度低。 静态代理:事先写好代理对象类,在程序发布前就已经存在了。 调用真实业务方法 |
|
jdk 动态代理 |
动态代理:应用程序发布后,通过动态创建代理对象。 通过反射进行invoke回调 要求业务类必须有接口,没有接口无法生成代理对象 class com.sun.proxy.$Proxy4 |
|
cglib 动态代理 |
不要求业务类必须有接口,有实现类即可 底层采用asm字节码生成框架生成代理类的字节码,它创建慢,但一旦创建要比jdk方式快。 cglib使用继承,因此父类最好不要有final属性,final阻止继承和多态。 class com.hdu.service.impl.UserServiceImpl |
|
ASM 字节码生成框架 |
ASM是一个Java字节码生成框架,它能够以二进制形式修改已有类欧哲动态生成类。ASM可也直接产生二进制class文件,也可以在类被加载如JVM前动态改变类行为,ASM从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。不过ASM在创建class字节码的过程中,操纵的级别是底层JVM的汇报指令级别。这要求就高了,对开发者要对class的结构和JVM指令要有一定了解。 |
代理模式及Spring AOP (一)的更多相关文章
- Hibernate 延迟加载的代理模式 和 Spring AOP的代理模式
Hibernate 延迟加载的代理模式 和 Spring AOP的代理模式 主题 概念 Hibernate 延迟加载的代理模式 Spring AOP的代理模式 区别和联系 静态代理和动态代理 概念 代 ...
- 从代理模式到Spring AOP
什么是代理模式 假如我喜欢上隔壁班的翠花,但是我没胆量向她送花,这时候我需要一个铁杆哥们帮我做这件事, 很明显这哥们是个代理,是去执行任务的,但是花实际上是我"送"的,代理和我一样 ...
- 代理模式及Spring AOP (二)
一.Spring AOP 1.1 Spring AOP 底层还是用的动态代理.如果目标对象所对应的类有接口,spring就用jdk生成代理对象: 如果目标对象所对应的类没有接口,spring就用C ...
- CgLib动态代理学习【Spring AOP基础之一】
如果不了解JDK中proxy动态代理机制的可以先查看上篇文章的内容:Java动态代理学习[Spring AOP基础之一] 由于Java动态代理Proxy.newProxyInstance()的时候会发 ...
- java代理课程测试 spring AOP代理简单测试
jjava加强课程测试代码 反射. 代理 .泛型.beanUtils等 项目源码下载:http://download.csdn.net/detail/liangrui1988/6568169 热身运动 ...
- Java动态代理学习【Spring AOP基础之一】
Spring AOP使用的其中一个底层技术就是Java的动态代理技术.Java的动态代理技术主要围绕两个类进行的 java.lang.reflect.InvocationHandler java.la ...
- 基于代理类实现Spring AOP
目录 ProxyFactoryBean类介绍 基于JDK动态代理的Spring AOP实现 基于CGLIB代理的Spring AOP实现 Spring的通知类型 ProxyFactoryBean类 ...
- Spring AOP详解 、 JDK动态代理、CGLib动态代理
AOP是Aspect Oriented Programing的简称,面向切面编程.AOP适合于那些具有横切逻辑的应用:如性能监测,访问控制,事务管理以及日志记录.AOP将这些分散在各个业务逻辑中的代码 ...
- 【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理
Spring AOP详解 . JDK动态代理.CGLib动态代理 原文地址:https://www.cnblogs.com/kukudelaomao/p/5897893.html AOP是Aspec ...
随机推荐
- java开源类库pinyin4j的使用
最近CMS系统为了增加查询的匹配率,需要增加拼音检索字段,在网上找到了pinyin4j的java开源类库,提供中文转汉语拼音(并且支持多音字), 呵呵,看了看他的demo,决定就用它了,因为我在实际使 ...
- Java知识集锦
Java知识集锦 一.Java程序基础 1.1 开发和运行环境 1.2 Java语言概述 二.Java语法基础 2.1 基础类型和语法 2.2 对象和类型 2.3 包和访问控制 三.数据类型及类型转换 ...
- root登录不进去 dropbear ssh
安装好了dropbear, root 怎么也登录不进去. 看 /var/log/messages , 发觉有很多下面的消息, 网上查了一下, 发觉建个 /etc/shells 文件,然后把 /bin/ ...
- Java 写数据到文件
private boolean writeToFile(BusGpsBean gpsBean) { String dataStr = DateUtil.date2String(new Date(), ...
- English trip -- Review Unit 9 Daily living 日常生活
主要讲了一个时态:现在进行时 Be动词+Ving 需要记住的有6种规律 1.直接单词后面 + ing e.g. watch -> watching 2.是ie结尾的单词,变y ...
- 37mysql 表操作
创建表 #语法: create table 表名( 字段名1 类型[(宽度) 约束条件], 字段名2 类型[(宽度) 约束条件], 字段名3 类型[(宽度) 约束条件] ); #注意: 1. 在同一张 ...
- git 添加tag
前言 什么是tag?tag是节点的意思,一般在上线的时候使用.比如说:你在本地做了好几个功能,然后把这些功能提交到了上线的分支上,某个时刻,你想上线你的新功能,这个时候你需要你个tag来标记一下,告诉 ...
- Jenkins install
Linux CentOS 7.1 x64 Java 1.8 x64 apache-maven-3.3.9 Installation sudo wget -O /etc/yum.repos.d/jenk ...
- Mvc 学习笔记(一)
1. MVC 表示 模型-视图-控制器.MVC是一种用于开发应用程序的模式,具备良好的架构,可测试和易于维护.基于MVC应用程序中包含: Models:表示应用程序的数据,并使用验证逻辑强制执行业务规 ...
- 获取当前目录getcwd,设置工作目录chdir,获取目录信息
#include <unistd.h> #include <stdio.h> #include <limits.h> int main(int argc, char ...
EnhancerByCGLIB
12d88161