论坛首页 Java企业应用论坛

从JDK动态代理到SPRING AOP

浏览 7319 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2012-11-03   最后修改:2012-11-05

代理模式
   代理模式是常用的java设计模式,它的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。
   代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类对象的相关方法,来提供特定的服务。
代理分类
   静态代理:由程序员创建或特定工具自动生成源代码。在程序运行前,代理类的.class文件就已经存在了。 
   动态代理:在程序运行时,运用反射机制动态创建而成。
静态代理代码示例

首先创建接口:

package org.iti.wxl.staticproxy;
/**
 * 接口
 */
public interface UserService {
             public void saveUser();
             public void deleteUser();
}

 

对接口的实现:(这里就不连接数据库了,只做简单的实例)

package org.iti.wxl.staticproxy;

public class UserServiceImpl implements UserService {
	@Override
	public void saveUser() {
		System.out.println("==user saved!==");
	}
	@Override
	public void deleteUser() {
		System.out.println("==user deleted!==");
	}
}

 

代理类:

package org.iti.wxl.staticproxy;

public class UserServiceImplProxy implements UserService {
	
	private UserServiceImpl userServiceImpl;
	public UserServiceImplProxy(UserServiceImpl userServiceImpl) {
		this.userServiceImpl = userServiceImpl;
	}

	@Override
	public void saveUser() {
		System.out.println("save begin!");
		userServiceImpl.saveUser();
		System.out.println("save end!");
	}
	
	@Override
	public void deleteUser() {
		System.out.println("delete begin!");
		userServiceImpl.deleteUser();
		System.out.println("delete end!");
	}
}

      代理类同样实现了UserService接口,代理类持有UserServiceImpl,并且重写了UserService接口里的方法,在方法里添加了逻辑。

 

测试方法:

package org.iti.wxl.staticproxy;

public class StaticProxy {
	//静态代理方法测试
	public static void main(String[] args) {
		UserServiceImpl userServiceImpl = new UserServiceImpl();
		UserServiceImplProxy userServiceImplProxy = 
				new UserServiceImplProxy(userServiceImpl);
		userServiceImplProxy.saveUser();
		System.out.println("=============");
		userServiceImplProxy.deleteUser();
	}
}

把创建的UserServiceImpl交给代理类UserServiceImplProxy,由它的实例执行数据逻辑操作。

 

方法运行结果如下:

save begin!
==user saved!==
save end!
=============
delete begin!
==user deleted!==
delete end!

  代理类中添加的逻辑在方法运行时被执行了。 

 

    观察代码可以发现每一个代理类只能为一个接口服务,这样一来程序开发中必然会产生过多的代理,而且,所有的代理操作除了调用的方法不一样之外,其他的操作都一样,则此时肯定是重复代码。

    解决这一问题最好的做法是可以通过一个代理类完成全部的代理功能,那么此时就必须使用动态代理完成。

 

 jdk动态代理

    jdk动态代理中包含一个类和一个接口,用到了java的反射机制:

public interface InvocationHandler { 
public Object invoke(Object proxy,Method method,Object[] args);
}

  参数说明:
      Object proxy:代理类对象。   
      Method method:要调用的方法  
      Object[] args:方法调用时所需要的参数

    下面用代码说明jdk动态代理的使用

接口类:

package org.iti.wxl.dynamicproxy;

public interface UserService {
	public void addUser();
	public void deleteUSer();
}

 

对接口的实现:

package org.iti.wxl.dynamicproxy;

public class UserServiceImpl implements UserService {
	@Override
	public void addUser() {
		System.out.println("==add user==");
	}
	@Override
	public void deleteUSer() {
		System.out.println("==delete user==");
	}
}

 

代理类:

package org.iti.wxl.dynamicproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class UserServiceImplProxy implements InvocationHandler {
	private Object target;
	
	public Object bind(Object target){
		this.target = target;
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), 
				target.getClass().getInterfaces(), this);
	}
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) 
			throws Throwable {
		Object result = null;
		System.out.println("user add begin!");
		result = method.invoke(target, args);
		System.out.println("user add end!");
		return result;
	}
}

 代理类主要负责两项功能:1、把Object转化成代理类的一个实现;2、为方法添加逻辑(如日志,性能测试等)。

 

测试类:

package org.iti.wxl.dynamicproxy;

public class DynamicProxy {
	public static void main(String[] args) {
		UserServiceImplProxy proxy = new UserServiceImplProxy();
		UserService userServiceProxy = (UserService)proxy.
				bind(new UserServiceImpl());
		userServiceProxy.addUser();
		System.out.println("========");
		userServiceProxy.deleteUSer();
	}
}

 测试结果:

user add begin!
==add user==
user add end!
========
user add begin!
==delete user==
user add end!

 

分析:

可以将InvocationHandler接口的子类想象成一个代理的最终操作类。 
Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类,此类提供了操作方法: newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 
参数说明: 
  ClassLoader loader:类加载器
  Class<?>[] interfaces:全部的接口
  InvocationHandler h:InvocationHandler接口的子类实例

在Proxy类中的newProxyInstance()方法中需要一个ClassLoader类的实例,ClassLoader实际上对应的是类加载器,在Java中主要有一下三种类加载器:
Booststrap ClassLoader:此加载器采用C++编写,一般开发中是看不到的。
Extendsion ClassLoader:用来进行扩展类的加载,一般对应的是jre\lib\ext目录中的类。
AppClassLoader:(默认)加载classpath指定的类,是最常使用的是一种加载器。

这三个类的继承关系是Extendsion ClassLoader继承Booststrap ClassLoader,AppClassLoader继承Booststrap ClassLoader,每加载一个类都现有父类加载,父类没有再用其孩子类加载,保证了安全性。另外我们也可以写自己的类加载器,为类加载器加密等。

 

动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java 反射机制可以生成任意类型的动态代理类。java.lang.reflect 包中的Proxy类和InvocationHandler 接口提供了生成动态代理类的能力。 

 

spring AOP

AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。Spring主要通过代理来实现AOP。

切面(Aspect):对横切关注点的抽象(类似类对对象的抽象)
连接点(JoinPoint):被拦截到的点,泛指方法。
切入点(CutPoint):对哪些连接点进行拦截的定义。

通知(Advice):在特定的连接点,AOP框架执行的动作.前置/后置/例外/最终/环绕通知。
引入(Introduction): 添加方法或字段到被通知的类。Spring允许引入新的接口到任何被通知的对象。
目标对象(Target Object): 包含连接点的对象。也被称作被通知或被代理对象。
AOP代理(AOP Proxy): AOP框架创建的对象,包含通知。在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。
织入(Weaving): 组装切面来创建一个被通知对象。这可以在编译时完成也可以在运行时完成。Spring在运行时完成织入。

示例代码:

接口类:

package org.iti.wxl.springaop;

public interface UserService {
	public String getUser(String userId);
	public void addUser();
}

 

对接口的实现:

package org.iti.wxl.springaop;

public class UserServiceImpl implements UserService {
	@Override
	public String getUser(String userId) {
		System.out.println("this is getUser() method!");
		return "user";
	}
	@Override
	public void addUser() {
		System.out.println("this is addUser() method!");
	}
}

 

定义切面:

package org.iti.wxl.springaop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect; 
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class MyInterceptor {
	@Pointcut("execution(* org.iti.wxl.springaop..*.*(..))")
	private void pointCutmethod(){
	}
	@Before("pointCutmethod() && args(userId)")
	public void doBefore(String userId){
		System.out.println("前置通知 " + userId);
	}
	@AfterReturning("pointCutmethod()")
	public void doAfterReturning(){
		System.out.println("后置通知");
	}
	@AfterThrowing("pointCutmethod()")
	public void doAfterException(){
		System.out.println("异常通知");
	}
	@After("pointCutmethod()")
	public void doAfter(){
		System.out.println("最终通知");
	}
	@Around("pointCutmethod()")
	public Object doAround(ProceedingJoinPoint pjp) throws Throwable{
		System.out.println("环绕开始");
		Object object = pjp.proceed();
		System.out.println("环绕结束");
		return object;
	}
}

说明:

第一个*表示方法的返回值,这里使用通配符,只有返回值符合条件的才拦截(!void表示有返回值)。 
第一个..表示com.royzhou.aop包及其子包。 
倒数第二个*表示包下的所有Java类都被拦截。 
最后一个*表示类的所有方法都被拦截。
(..)表示方法的参数可以任意多个,如[(java.lang.String,java.lang.Integer)表示第一个参数是String,第二个参数是int的方法才会被拦截]  

 

配置文件:

<?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:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-3.0.xsd
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
	<!-- 使用 annotation -->
	<context:annotation-config />
	<!-- 启用aop -->
	<aop:aspectj-autoproxy proxy-target-class="true" />
	<bean id="myInterceptor" class="org.iti.wxl.springaop.MyInterceptor"/>
	<bean id="userService" class="org.iti.wxl.springaop.UserServiceImpl"/>
</beans>

 测试类:

package org.iti.wxl.springaop;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringAOP {

	public static void main(String[] args) {
		ApplicationContext cxt = new ClassPathXmlApplicationContext("bean.xml");
		UserService userService = (UserService)cxt.getBean("userService");
		userService.getUser("user1");
		System.out.println("===========");
		userService.addUser();
	}
}

 运行结果:

前置通知 user1
环绕开始
this is getUser() method!
后置通知
最终通知
环绕结束
===========
环绕开始
this is addUser() method!
后置通知
最终通知
环绕结束

  在项目开发中,AOP主要用来配置spring的声明式事务管理,它还可用在日志管理,性能监测等。它为程序员编程提供了一种新思路,我们可以不断去发现它的新用途。

 

 

 

 

 

 

   发表时间:2012-11-03  

第一次在论坛发帖子,有不妥的地方还请多多包涵!

0 请登录后投票
   发表时间:2012-11-05  
静态代理,一般在需要代理对象数量较少时,可以使用。至于动态代理,是spring AOP实现的其中一种方法,另外一种方法,是通过生成继承类的方式来实现。
如果可以的话,能否对帖子中的内容再进一步说明,比如类加载机制是怎么个实现过程之类的。
(附;楼主的贴子内容和我曾经看过的一本书上的内容,有很大的类同哦。希望是巧合^_^)
0 请登录后投票
   发表时间:2012-11-05  

类的加载机制可以看看这两个帖子:http://www.iteye.com/topic/117628http://www.blogjava.net/hao446tian/archive/2011/03/14/346183.html,希望对你有帮助!

0 请登录后投票
   发表时间:2012-11-05  

   JVM装载类时使用“全盘负责委托机制”,“全盘负责”是指在当一个ClassLoader装载一个类时,除非显示的使用另一个ClassLoader,该类所依赖及应用的类也由这个ClassLoader载入;“委托机制”是指先委托父装载器寻找目标类,只有在找不到的情况下才从自己的类路径中查找并装载目标类。这一点是从安全角度考虑的,试想如果有人编写了一个恶意的基础类(如java.lang.String)并装载到JVM中将会引起多么可怕的后果。但是由于有了全盘负责制,java.lang.String永远是有根装载器来装载的,这样就避免了上述事件的发生。(来自《精通Spring2.0-企业应用开发详解》)

0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics