细说java动态代理和cglib的动态代理

          提到代理,想必大家对设计模式中的静态代理和动态代理都比较熟悉,小编之前在博客中对动态和静态代理进行了对比,这篇博文就只探讨java动态代理和cglib动态代理之间的区别;

♚  静态代理的温习

        在我们使用静态代理的时候,每一个代理类只能为一个接口提供服务,这这样一来在程序开发中会产生过多的代理,而且所有的代理操作除了调用的方法不一样之外,其他的操作都是相同的,这样就会造成过多的重复代码;

       为了解决上述问题,我们使用一个代理类来完成所有的代理功能,而这就需要引入动态代理;

♚  动态代理

       与静态代理相对的是动态代理,动态代理类的字节码在程序运行时由java反射机制动态生成,无需程序员手工编写它的源代码。动态代理不仅简化了编程工作,而且提高了软件系统的可扩展性,因为java反射机制可以生成任意类型的动态代理类。

      下面我们以一个示例来说明动态代理:该示例中以一个checkSecurity()的例子来演示动态代理:

UserManager.java:

<span style="font-size:18px;">package com.ysc.spring;

/**
 * 要实现的接口
 * @author root
 *
 */
public interface UserManager {
	
	public void addUser(String username,String password);
	
	public void delUser(int userId);
	
	public String findUserById(int userId);
	
	public void modifyUser(int userId,String username,String passwordString);
}</span>

UserManagerImpl.java

<span style="font-size:18px;">package com.ysc.spring;

public class UserManagerImpl  implements UserManager {

	@Override
	public void addUser(String username, String password) {
		//checkSecurity();
		System.out.println("-------------userManager.add()---------");
	}

	@Override
	public void delUser(int userId) {
		//checkSecurity();
		System.out.println("-------------userManager.delUser()---------");
	}
		
	@Override
	public String findUserById(int userId) {
		//checkSecurity();
		System.out.println("-------------userManager.findUserById()---------");
		return "张三";
	}

	@Override
	public void modifyUser(int userId, String username, String passwordString) {
		//checkSecurity();
		System.out.println("-------------userManager.modifyUser()---------");
	}
	
}</span>

     由于检查安全性的方法是可以独立于当前接口中其他业务方法的,所以我们可以把这个独立的服务拿出来,放到一个代理类中,使用动态代理把检查安全性的方法动态的加入到每一个方法中,而这些代理的工作需要由一个单独的代理类来完成:

SecurityHandler.java:

<span style="font-size:18px;">package com.ysc.spring;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class SecurityHandler implements InvocationHandler {

	private Object targetObject;
	
	public Object createProxyInstanceObject(Object targetobObject){
		this.targetObject = targetobObject;

		return Proxy.newProxyInstance(targetobObject.getClass().getClassLoader(), 
						targetobObject.getClass().getInterfaces(),
						this);
		
	}
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		checkSecurity();
		
		//调用目标方法
		Object ret = method.invoke(targetObject, args);
		
		return ret;
	}
	
	private void checkSecurity(){
		System.out.println("-----------checkSecurity----------");
	}

}</span>

     这样,我们就可以直接在客户端进行调用执行了:

Client.java:

<span style="font-size:18px;">package com.ysc.spring;

/**
 * 
 * 动态代理可以将系统中一些独立的服务(这些独立的服务具有横切性)
 * 而将这些独立的服务拿出来,使用动态代理就可以在运行时将服务自动加入到里头;
 * @author root
 *
 */
public class Client {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		SecurityHandler handler = new SecurityHandler();
		
		UserManager userManager = (UserManager)handler.createProxyInstanceObject(new UserManagerImpl());
		
		userManager.addUser("zhangsan", "123");
	}

}</span>

          通过上面的例子可以看出,通过动态代理我们实现了动态的将检查安全性切入到每一个方法中,但是动态代理依靠接口实现,那么这样的话就会导致一些没有接口的类,就不能通过jdk实现java的动态代理,而这个时候我们就需要转向cglib的动态代理:

♚  cglib动态代理

       cglib是针对类来实现代理的,它的原理是对指定的目标类生成一个类,并覆盖其中方法实现增强,看上述例子的cglib动态代理的实现:

       与动态代理不同,由于是针对类来实现,所以上述UserManagerImpl类不能直接在java代码中实现UserManager接口,所以我们需要一个配置文件来配置,且完成对哪些方法添加检查安全性的方法,ApplicationContext.xml 如下:

<span style="font-size:18px;"><?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
		http://www.springframework.org/schema/beans/spring-beans-3.2.xsd 
		http://www.springframework.org/schema/mvc 
		http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd 
		http://www.springframework.org/schema/context 
		http://www.springframework.org/schema/context/spring-context-3.2.xsd 
		http://www.springframework.org/schema/aop 
		http://www.springframework.org/schema/aop/spring-aop-3.2.xsd 
		http://www.springframework.org/schema/tx 
		http://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">
	
	<!-- 表示使用cglib代理 -->
	<aop:aspectj-autoproxy proxy-target-class="true"/>
	 
	<bean id="userManager" class="com.ysc.spring.UserManagerImpl"></bean>
	<bean id="securityHandler" class="com.ysc.spring.SecurityHandler"></bean>
	
	<!-- 检查安全性的方法要附加在哪些方法上的配置 -->
	<aop:config>
		<aop:aspect id="securityAspect" ref="securityHandler">
			<aop:pointcut expression="execution(* add*(..))" id="addAddMethod"/>
			<aop:before method="checkSecurity" pointcut-ref="addAddMethod"/>
		</aop:aspect>
	</aop:config>
</beans></span>

       动态代理中例子保持Client.java , Security.java , UserManager.java不变,只对UserManagerImpl.java 做出如下的修改:

<span style="font-size:18px;">package com.ysc.spring;

public class UserManagerImpl  {
//implements UserManager {

	
	public void addUser(String username, String password) {
		//checkSecurity();
		System.out.println("-------------userManager.add()---------");
	}

	public void delUser(int userId) {
		//checkSecurity();
		System.out.println("-------------userManager.delUser()---------");
	}
		
	public String findUserById(int userId) {
		//checkSecurity();
		System.out.println("-------------userManager.findUserById()---------");
		return "张三";
	}

	public void modifyUser(int userId, String username, String passwordString) {
		//checkSecurity();
		System.out.println("-------------userManager.modifyUser()---------");
	}

}</span>

 ✎   总结

 spring 默认使用的jdk的动态代理;

1.如果目标对象实现了接口,在默认情况下采用jdk的动态代理实现aop

2.如果目标对象实现了接口,也可以强制使用cglib生成代理实现aop

3.如果目标对象没有实现接口,那么必须引入cglib,spring会在jdk的动态代理和cglib代理之间切换

如何使用cglib强制生成代理;

* 加入cglib类库,cglib/*.jar

* 加入如下配置,表示强制使用cglib代理

<aop:aspectj-autoproxy proxy-target-class="true"/>

jdk动态代理和cglib代理的区别:

* jdk动态代理对实现了接口的类进行代理

* cglib代理可以对类代理,主要对指定的类生成一个子类,因为是继承,所以我们的目标最好不要使用使用final声明;

      到这里,jdk动态代理和cglib的动态代理之间的对比就已经结束了,希望能帮大家更深刻的理解静态代理、jdk动态代理和cglib动态代理。如果有更好的理解和建议,请留言哦!!

       


评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值