首页 > 代码库 > 重温Spring之旅5——AOP代理对象、JDK动态代理、使用cglib生产代理

重温Spring之旅5——AOP代理对象、JDK动态代理、使用cglib生产代理

AOP——代理对象

代理模式:代理模式的英文叫做Proxy或Surrogate,中文都可译为”代理“,所谓代理,就是一个人或者一个机构代表另一个人或者另一个机构采取行动。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用.
抽象主题角色:声明了真实主题和代理主题的共同接口,这样一来在任何可以使用真实主题的地方都可以是使用代理主题.
代理主题(Proxy)角色:代理主题角色内部含有对真实主题的引用,从而可以在任何时候操作真实主题对象;代理主题角色提供一个与真实主题角色相同的接口,以便可以在任何时候都可以替代真实主题控制对真实主题的引用,负责在需要的时候创建真实主题对象(和删除真实主题对象);代理角色通常在将客户端调用传递给真实的主题之前或之后,都要执行某个操作,而不是单纯地将调用传递给真实主题对象.
真实主题角色:定义了代理角色所代表地真实对象.

代理模式示意图

技术分享
Subject.java
package com.xbmu.a.aop;

public interface Subject {
	String request();
}
RealSubject.java
package com.xbmu.a.aop;
/**
 * 真实主题
 * @author bitaotao
 *
 */
public class RealSubject implements Subject{

	@Override
	public String request() {
		return "请收取1000元整";
	}

}
ProxySubject.java
package com.xbmu.a.aop;

/**
 * 代理主题
 * @author bitaotao
 *
 */
public class ProxySubject implements Subject{

	private Subject subject;
	private String str;

	@Override
	public String request() {
		if(subject == null){
			subject  = new RealSubject();
		}
		str = subject.request();
		//添加的业务逻辑
		str = str.replaceAll("1000", "2000");
		return str;
	}

}
App.java
package com.xbmu.a.aop;

public class App {
	public static void main(String[] args) {
		Subject subject = new RealSubject();
		System.out.println("真实主题:"+subject.request());
		System.out.println("===================");
		Subject proxySubject = new ProxySubject();
		System.out.println("代理主题:"+proxySubject.request());
	}
}

静态代理:

IUserManager.java
package com.xbmu.b.staticproxy;

public interface IUserManager {
	void addUser(String username,String passwd);
	void deleteUser(String username,String passwd);
	void modifyUser(String username,String passwd);
	String findUser(String username);
}
UserManagerImpl.java
package com.xbmu.b.staticproxy;

public class UserManagerImpl implements IUserManager {

	@Override
	public void addUser(String username, String passwd) {
		System.out.println("这是UserManagerImpl类中的addUser方法");
	}

	@Override
	public void deleteUser(String username, String passwd) {
		System.out.println("这是UserManagerImpl类中的deleteUser方法");

	}

	@Override
	public void modifyUser(String username, String passwd) {
		System.out.println("这是UserManagerImpl类中的modifyUser方法");
	}

	@Override
	public String findUser(String username) {
		System.out.println("这是UserManagerImpl类中的findUser方法");
		return "btt";
	}

}
UserManagerProxy.java
package com.xbmu.b.staticproxy;

public class UserManagerProxy implements IUserManager {

	private IUserManager userManager;
	public UserManagerProxy() {
		super();
	}
	public UserManagerProxy(IUserManager userManageImpl) {
		super();
		if(userManager == null){
			this.userManager = userManageImpl;
		}
	}
	@Override
	public void addUser(String username, String passwd) {
		checkSecurity();
		userManager.addUser(username, passwd);

	}

	@Override
	public void deleteUser(String username, String passwd) {
		checkSecurity();
		userManager.deleteUser(username, passwd);

	}

	@Override
	public void modifyUser(String username, String passwd) {
		checkSecurity();
		userManager.modifyUser(username, passwd);

	}

	@Override
	public String findUser(String username) {
		checkSecurity();
		userManager.findUser(username);
		return "bitaotao";
	}
	private void checkSecurity(){
		System.out.println("检查用户信息");
	}

}
App.java
package com.xbmu.b.staticproxy;

public class App {
	public static void main(String[] args) {
		IUserManager userManager = new UserManagerImpl();
		userManager.addUser("btt", "123");
		System.out.println("==========================");
		IUserManager userManagerProxy = new UserManagerProxy(userManager);
		userManagerProxy.addUser("btt", "123");
	}
}

JDK动态代理:

IUserManager.java
package com.xbmu.c.jdkProxy;

public interface IUserManager {
	void saveUser(String username,String password);
	void updateUser(String username,String password);
	void deleteUser(String username);
	String findUser(String username);
}
UserManagerImpl.java
package com.xbmu.c.jdkProxy;

public class UserManagerImpl implements IUserManager{

	@Override
	public void saveUser(String username, String password) {
		System.out.println("UserManagerImpl---saveUser");
		
	}

	@Override
	public void updateUser(String username, String password) {
		System.out.println("UserManagerImpl---updateUser");
		
	}

	@Override
	public void deleteUser(String username) {
		System.out.println("UserManagerImpl---deleteUser");
	}

	@Override
	public String findUser(String username) {
		System.out.println("UserManagerImpl---findUser");
		return username;
	}

}
JdkProxy.java
package com.xbmu.c.jdkProxy;

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

/**Aspect(切面): 通知要放置到一个类中,那么该类就是切面,JdkProxy*/
public class JdkProxy implements InvocationHandler {

	Object userManage = null;
	
	/**
	 * 百度解释:
	 *	Weaving(织入):
	 *	织入,AOP术语。把切面(aspect)连接到其它的应用程序类型或对象上,
	 *  并创建一个被通知 (advised)的对象,这样的行为叫做织入。
	 * Weaving(织入):
	 *  是指把切面应用到目标对象来创建新的代理对象的过程.切面在指定的连接点织入到目标对象。
	 *  织入相当于将目标对象和代理对象关联起来
	 * */
	public Object createProxyInstance(Object userManage) {
		//将真实对象传给代理
		this.userManage = userManage;
	     /*
	      * 第一个参数设置代码使用的类加载器,一般采用跟目标类相同的类加载器
	      * 第二个参数设置代理类实现的接口,跟目标类使用相同的接口
	      * 第三个参数设置回调对象,当代理对象的方法被调用时,会调用该参数指定对象的invoke方法
	      */
		return Proxy.newProxyInstance(this.userManage.getClass().getClassLoader(), 
				                     this.userManage.getClass().getInterfaces(), 
				                     this);
	}

	/**
     * @param proxy   目标对象的代理类实例
     * @param method  对应于在代理实例上调用接口方法的Method实例
     * @param args 传入到代理实例上方法参数值的对象数组
     * @return 方法的返回值 没有返回值时是null
     */
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		/**
		 * Pointcut(切入点):目标对象中存在多个方法,只需要对目标对象中多个方法的某些方法进行拦截,
		 *                 此时该操作,就叫做切入点
		 * */
		if(method.getName().startsWith("save") || method.getName().startsWith("find")){
			checkSecurity();
		}
		System.out.println("代理类:"+proxy.getClass());
		System.out.println("方法的名称:"+method.getName());
		if(args!=null && args.length>0){
			for(Object arg:args){
				System.out.println("方法的参数:"+arg);
			}
		}
		//调用目标对象的方法
		Object returnValue = http://www.mamicode.com/method.invoke(this.userManage, args);>App.java
package com.xbmu.c.jdkProxy;

public class App {
	public static void main(String[] args) {
		IUserManager userManager = new UserManagerImpl();
//		userManager.saveUser("zhangsan", "123");
		
		JdkProxy jdkProxy = new JdkProxy();
		IUserManager userManageProxy = (IUserManager) jdkProxy.createProxyInstance(userManager);
		userManageProxy.saveUser("lisi", "456");
		System.out.println("/***********************************/");
		String user = userManageProxy.findUser("wangwu");
		System.out.println("查询用户:"+user);
		System.out.println("/***********************************/");
		userManageProxy.updateUser("zhaoliu", "789");
		
	}
}

使用cglib生产代理:

CGlib是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。
CglibProxy.java
package com.xbmu.d.cglibProxy;

import java.lang.reflect.Method;

import net.sf.cglib.core.Signature;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

/**切面*/
public class CglibProxy implements MethodInterceptor {

	private Object userManager = null;
	
	/**织入:将相当于将目标对象和代理对象关联起来*/
	public Object createProxyInstance(Object userManage) {
		this.userManager = userManage;
		Enhancer enhancer = new Enhancer();
		//设置目标对象的父类
		enhancer.setSuperclass(this.userManager.getClass());
		//开始调用回调函数,调用intercept的方法
		enhancer.setCallback(this);
		//创建代理对象
		return enhancer.create();
	}

	 /**
	    * @param obj  目标对象代理类的实例
	    * @param method 代理实例上调用父类方法的Method实例
	    * @param args  传入到代理实例上方法参数值的对象数组
	    * @param methodProxy 使用它调用父类的方法
	    * @return
	    * @throws Throwable
	    */
	public Object intercept(Object proxy, Method method, Object[] args,
			MethodProxy methodPrxoy) throws Throwable {
		/**
		 * Pointcut(切入点):目标对象中存在多个方法,只需要对目标对象中多个方法的某些方法进行拦截,
		 *                 此时该操作,就叫做切入点
		 * */
		if(method.getName().startsWith("save") || method.getName().startsWith("find")){
			checkSecurity();
		}
		System.out.println("代理类:"+proxy.getClass());
		System.out.println("方法的名称:"+method.getName());
		if(args!=null && args.length>0){
			for(Object arg:args){
				System.out.println("方法的参数:"+arg);
			}
		}
		//methodPrxoy方法的使用
		Signature signature = methodPrxoy.getSignature();//获取签名
		System.out.println("methodPrxoy:"+signature.getName() + "      "
				               + signature.getReturnType() + "       " + signature.getArgumentTypes());
		
		//调用目标对象的方法
		Object returnValue = http://www.mamicode.com/method.invoke(this.userManager, args);>App.java
package com.xbmu.d.cglibProxy;

public class App {
	public static void main(String[] args) {
		/**目标对象:表示访问的真实对象*/
		IUserManager userManager = new UserManagerImpl();
		userManager.saveUser("张三", "123");
		/****************************************************/
		//使用jdk代理对象访问
		/**
		 * 代理对象:在访问真实对象的前后可以添加业务逻辑,添加业务逻辑的类,称之为代理对象,
		 * 简而言之,通过代理对象来访问真实对象
		 */
		CglibProxy cglibProxy = new CglibProxy();
		IUserManager userManageProxy = (IUserManager) cglibProxy.createProxyInstance(userManager);
		/**joinpoint(连接点):程序访问目标的方法,就是连接点,saveUser,findUser,updateUser*/
		userManageProxy.saveUser("李四", "456");
		String user = userManageProxy.findUser("赵六");
		System.out.println("查询用户:"+user);
		System.out.println("/**********************************/");
		userManageProxy.updateUser("王五", "789");
		
		System.out.println("/**********************************/");
		
	}
}

AOP中的概念以及代理总结:

Aspect(切面): 是通知和切入点的结合,通知和切入点共同定义了关于切面的全部内容---它的功能、在何时和何地完成其功能
joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因spring只支持方法类型的连接点.
Pointcut(切入点):所谓切入点是指我们要对哪些joinpoint进行拦截的定义.
通知定义了切面的”什么”和”何时”,切入点就定义了”何地”.

Advice(通知):所谓通知是指拦截到joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
Target(目标对象):代理的目标对象
Weaving(织入):是指把切面应用到目标对象来创建新的代理对象的过程.切面在指定的连接点织入到目标对象
Introduction(引入):在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field.



spring在运行期创建代理,不需要特殊的编译器.
spring有两种代理方式:

1.若目标对象实现了若干接口,spring使用JDK的java.lang.reflect.Proxy类代理。

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

使用该方式时需要注意:
1.对接口创建代理优于对类创建代理,因为会产生更加松耦合的系统。对类代理是让遗留系统或无法实现接口的第三方类库同样可以得到通知,这种方式应该是备用方案。
2.标记为final的方法不能够被通知。spring是为目标类产生子类。任何需要
被通知的方法都被复写,将通知织入。final方法是不允许重写的。

spring只支持方法连接点:不提供属性接入点,spring的观点是属性拦截破坏了
封装。面向对象的概念是对象自己处理工作,其他对象只能通过方法调用的得到的
结果。



重温Spring之旅5——AOP代理对象、JDK动态代理、使用cglib生产代理