Spring_12_AOP的引出_装饰设计模式与代理机制

问题的引出

一些伪代码:

public class AccountServiceImpl implements IAccountService{	
	/* 转账操作 */
	public void transfer() {
		try{	
		// 开启事务	
		// 具体的转账业务操作
                // 提交事务
		}catch(Exception e){
			// 事务回滚	
		}finally{
			// 关闭资源
		}		
	}
}

若上述类中还存在转入、转出的方法。那么,每个方法总都必须写事务处理的代码。

在设计上存在的问题:

 1、责任不分离。

业务方法只需要关心如何完成该业务功能,不需要去关系事务管理/日志管理/权限管理等等。        

2、代码结构重复。

在开发中不要重复代码,重复就意味着维护成本增大。

 

 

装饰设计模式

在不改变源代码基础上,动态地扩展一个对象的功能。通过包裹真实的对象,对已有对象进行功能增强。

特点:

1、装饰对象和真实对象有相同的接口。这样客户端对象就能以和真实对象相同的方式和装饰对象交互。

2、装饰对象包含一个真实对象的引用(reference)

3、装饰对象接受所有来自客户端的请求。它把这些请求转发给真实的对象。

4、装饰对象可以在转发这些请求以前或以后增加一些附加功能。

这样就确保了在运行时,不用修改给定对象的结构就可以在外部增加附加的功能。

在面向对象的设计中,通常是通过继承来实现对给定类的功能扩展。

一些伪代码

public class AccountSerivceImplWapper implements IAccountService{
	// 事务对象的引用	
	public AccountSerivceImplWapper(AccountServiceImpl target) {
		// 传入真实对象的引用
	}
	public void transfer() {
		try{
			// 开启事务		
			// 真实对象调用转账方法	
            // 提交事务		
		}catch(Exception e){
			// 关闭资源
		}finally{
				
		}		
	}
	public void withdraw() {
		try{
			// 开启事务		
			// 真实对象调用取钱方法	
            // 提交事务			
		}catch(Exception e){
			// 事务回滚		
		}finally{
			// 关闭资源			
		}	
	}
}

 

小结:

可以看出,使用装饰设计模式让责任分离了。真实对象可以专注于完成业务逻辑。

但是,还是存在着代码结构重复的问题。而且,若存在多个需要增强的类,每个类都要定义一个增强类。

此外,还是暴露了真实对象,客户端可直接使用真实对象,该真实对象无事务相关的代码,仅有业务操作,很不安全。

 

静态代理模式

客户端直接使用的是代理对象,不知道真实对象是谁。代理对象在客户端和真实对象之间其中介作用。

类比现实中的房屋中介模式:租客,中介,房东。

租客不知道房东是谁,签合同、交租金都是直接与中介公司打交道。

 

特点:

1、代理对象完全包含真实对象,客户端使用的都是代理对象的方法,和真实对象没有直接关系

2、代理模式的职责:把不是真实对象该做的事情从真实对象上撇开——职责清晰

public class AccountSerivceImplWapper implements IAccountService{
	// 事务对象的引用	
        // 真实对象的引用target
	public void transfer() {
		try{
			// 开启事务		
			// 真实对象调用转账方法	
                        // 提交事务
		}catch(Exception e){
			// 事务回滚		
		}finally{
			// 关闭资源		
		}		
	}
	public void withdraw() {
		try{
			// 开启事务		
			// 真实对象调用取钱方法	
                        // 提交事务	
		}catch(Exception e){
			// 事务回滚		
		}finally{
			// 关闭资源			
		}	
	}
}

 

小结: 

静态代理模式让真实对象安全了,不会暴露在客户端。但是除此之外,仍旧存在装饰设计模式一样的问题。

 

JDK动态代理

· 静态代理与动态代理对比

静态代理:在程序运行前就已经存在代理类的字节码文件,代理对象和真实对象的关系在程序运行前就确定了。

动态代理:动态代理类是在程序运行期间由JVM通过反射等机制动态的生成的,所以不存在代理类的字节码文件。

代理对象和真实对象的关系是在程序运行时期才确定的。

 

· JDK动态代理API

1、java.lang.reflect.Proxy类

Java动态代理机制生成的所有动态代理类的父类。提供了一组静态方法,来为一组接口动态地生成代理类及其对象。

真实对象必须实现至少一个接口。

主要方法:

newProxyInstance

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
                               throws IllegalArgumentException

 

参数:

loader - 定义代理类的类加载器

interfaces - 代理类要实现的接口列表

h - 指派方法调用的调用处理程序

返回:

一个带有代理类的指定调用处理程序的代理实例,它由指定的类加载器定义,并实现指定的接口

 

2、java.lang.reflect.InvocationHandler接口

定义一个实现类实现该接口,并在实现类中编写增强的代码。

invoke

Object invoke(Object proxy, Method method,Object[] args)throws Throwable

参数:

proxy-代理对象

method-真实对象中要增强的方法

arg-真实对象中要增强的方法的参数

 

· 使用思路

1、创建增强程序类,该类实现InvocationHandler接口。

实现invoke方法,在该方法中编写对真实方法的增强代码。这个方法我们不会直接调用。

2、在增强程序类中,提供获取代理对象的方法。

通过Proxy类的newProxyInstance方法创建代理对象。

增强对象中存有对真实对象的引用,通过Spring注入,即可不暴露真实对象。

其中InvocationHandler类型的参数就是当前增强程序类的实例。

3、通过增强程序实例获取代理对象,通过代理对象增强的操作真实对象的方法。

 

一些代码 

public class TranscationManagerHandler // 增强程序类必须实现InvocationHandler接口
                implements java.lang.reflect.InvocationHandler {
	
	/* 真实对象的引用 */
	@Setter
	private Object target; 
	
	private TransactionManager tx = new TransactionManager();
	
        /* 增强程序中的具体操作  */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		Object ret = null;
		try{
			tx.begin();
			ret = method.invoke(target, args);
            tx.commit();
		}catch(Exception e){
			tx.rollback();
			e.printStackTrace();
		}finally{
			tx.close();
		}
		return ret;
	}

	/* 创建代理对象 */
	@SuppressWarnings("unchecked")
	public <T>T getProxyInstance(){
		Object ret=  Proxy.newProxyInstance(target.getClass().getClassLoader(),  // 真实对象的类加载器
				target.getClass().getInterfaces(), 	// 真实对象的类实现的接口
				this);	// 增强程序对象
		return (T)ret;
	}
}

Spring配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
      <bean id="transationManager" class="com.hanaii.common.TransactionManager"/>
       <!-- 隐藏真实对象 -->
      <bean id="accountServiceImpl" class="com.hanaii.jdk_proxy.TranscationManagerHandler">
      	<property name="target" >
      		<bean class="com.hanaii.common.AccountServiceImpl" />
      	</property>
      </bean>
</beans>

测试代码

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class JdkProxyTest {
	@Autowired
	@Qualifier("accountServiceImpl") 
	private TranscationManagerHandler handler;
	
	@Test
	public void test() throws Exception {
	 IAccountService service= handler.getProxyInstance();
	 service.transfer();
	}
}

测试结果

Transcation begin ...
service: 转账操作 ...
Transcation commit ...
Transcation close ...

 

 

· JDK动态代理存在的问题

1、代理的对象必须要实现一个接口。

2、需要为每个对象创建代理对象

3、动态代理的最小单位是类,类中的所有实现于接口的方法都会被增强。(有时候我们想要有些方法不要被增强)

 

Spring提供的动态代理:CGLIB

CGLIB提供了和JDK动态代理类似的API接口。

其原理是对指定的目标类生产一个子类,并覆写其中方法进行增强。

(类必须可继承,不能是final修饰。)

 

· API

1、org.springframework.cglib.proxy.InvocationHandler接口

该接口也存在invoke方法,使用基本同JDK动态代理相同。

另外,该接口继承了 org.springframework.cglib.proxy.Callback 接口。

 

2、org.springframework.cglib.proxy.Enhancer类

其主要方法用于创建代理对象。其使用案例如下:

Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass()); // 设置代理对象父类
enhancer.setCallback(this);     // 设置增强程序
return (T)enhancer.create();    // 创建代理对象

 

· 使用思路

基本同JDK动态代理

一些代码:

public class TranscationManagerHandler // 增强程序类必须实现InvocationHandler接口
						implements org.springframework.cglib.proxy.InvocationHandler {
	
	/* 真实对象的引用 */
	@Setter
	private Object target; 
	
	private TransactionManager tx = new TransactionManager();
	
	/* 创建代理对象 */
	@SuppressWarnings("unchecked")
	public <T>T getProxyInstance(){
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(target.getClass());
		enhancer.setCallback(this);
		return (T)enhancer.create();
	}
	
	/* 增强程序中的具体操作  */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		System.out.println("-d-");
		Object ret = null;
		try{
			tx.begin();
			ret = method.invoke(target, args);
		}catch(Exception e){
			tx.rollback();
			e.printStackTrace();
		}finally{
			tx.commit();
		}
		return ret;
	}
}

 

· 小结

1、CGLIB原理生成目标类的子类,所以目标类可以不实现接口。

2、要求类不能是final的(可继承),要拦截的方法要是非final、非static、非private的(可覆写)。

3、动态代理的最小单位是类(所有类中的方法都会被处理)。

 

Spring中的动态代理机制

若目标对象实现了若干接口,Spring就会使用JDK动态代理。

若目标对象没有实现任何接口,Spring就使用CGLIB库生成目标对象的子类。

对接口创建代理优于对类创建代理,因为会产生更加松耦合的系统,也更符合面向接口编程规则。

cglib和javassist代理的机制都是一样的,都是通过继承实现的。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值