java---------AOP面向切面


AOP代理对象的生成

Spring提供了两种方式来生成代理对象,JDKProxy和Cglib,具体使用哪种方式生成由aopProxyFactory根据AdvisedSupport对象的配置来决定,默认的策略是如果目标类是接口,则使用JDK动态代理技术,否则使用cglib来生成代理。Spring如何使用JDK来生成代理对象,具体的生成代码放在jdkDynamicAopProxy这个类中。

Java动态代理机制:以巧妙的方式实现代理模式的设计理念,对一个对象添加代理时,可以使用动态代理,只有类型对象,没有实例对象,这时候可以使用静态代理。

java的静态代理:是代理模式的一种使用,静态代理的构建就是实现了被代理类接口的类,其中保存了一个被代理类的实例。在接口的实现方法中,调用被代理对象对应的方法,同时添加需要的其他操作。实例:

1)先定义一个接口
package com.proxy.staticclass;

public interface HelloWorld {
	public void print();

}
2)定义接口的实现类
public class HelloWorldImpl implements HelloWorld{

	@Override
	public void print() {
		// TODO Auto-generated method stub
		System.out.println("hello world");
	}

}
3)定义一个静态代理类
package com.proxy.staticclass;

public class StaticProxy implements HelloWorld {
	
	private HelloWorldImpl helloWorldImpl;
	
	public StaticProxy(HelloWorldImpl helloWorldImpl) {
		// TODO Auto-generated constructor stub
		this.helloWorldImpl = helloWorldImpl;
	}

	@Override
	public void print() {
		// TODO Auto-generated method stub
		System.out.println("before");
		
		helloWorldImpl.print();
		
		System.out.println("after");
	}

}
4)调用静态代理类
 public static void main(String[] args) {
	 HelloWorld helloWorld = new StaticProxy(new HelloWorldImpl());
	 helloWorld.print();
   }

   静态代理的缺点: 如果接口中有很多个方法,那实现代理就需要为每个方法实现代理,会存在很多重复的逻辑。不利于应用的维护。

java动态代理有两种:基于接口进行动态代理    和     基于继承进行的动态代理

java动态代理类位于java.lang.reflect包下,主要设计两个类:

1、interface InvocationHandle

该接口中只定义了一个方法:

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

在实际的使用时,第一个参数obj一般是指代理类,method是被代理的方法,args为该方法的参数数组(无参时设置为null)

这个抽象方法在代理类中动态实现。

2、Proxy

该类即为动态代理类,其中主要包含如下内容:

protected Proxy(InvocationHandle h):构造函数,用于给内部的invocation handle赋值。

static Class<?> getProxyClass(ClassLoader loader,Class<?> ...interfaces): loader是类装载器,interfaces 是真实类所拥有的全部接口的数组。

Static Class<?> newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandle h):返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类在Subject接口中声明过的方法)。

jdk动态代理的实例

1)定义一个jdk代理类实现invocationHandle接口
public class JdkProxy implements InvocationHandler {
	
	private HelloWorldImpl helloWorldImpl;
	
	public JdkProxy(HelloWorldImpl helloWorldImpl) {
		this.helloWorldImpl = helloWorldImpl;
		
		// TODO Auto-generated constructor stub
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		
		System.out.println("jdk before");
		
		Object result = null;
		
		try {
			result = method.invoke(helloWorldImpl, args);
		} catch (Exception e) {
			// TODO: handle exception
			System.out.println("error"+e.getMessage());	
			throw e;
		}finally{
			System.out.println("jdk after");
		}
		// TODO Auto-generated method stub
		return result;
	}

}
2)、调用动态代理
public static void main(String[] args) {
	HelloWorld helloWorld = (HelloWorld) Proxy.newProxyInstance(Client.class.getClassLoader(),
				new Class[]{HelloWorld.class}, new JdkProxy(new HelloWorldImpl()));
	helloWorld.sysName();
}

动态代理创建过程可分为以下四个步骤:

1、通过实现InvocationHandle接口创建自己的调用用处理器InvocatoinHandle handle = new InvocationHandleImpl(...);

2、通过为Proxy类指定ClassLoader对象和一组interface创建动态代理类Class clazz = Proxy.getProxyClass(classLoader,new Class[]{...});

3、通过反射机制获取动态代理类的构造函数,其参数类型是调用处理器类型Constructor constructor = clazz.getConstructor(new Class[]{invocationHandle.class});

4、通过构造函数创建代理类实例,此时需要将调用处理器对象作为参数被传入 Interface Proxy = (Interface) constructor.newInstance(new Object[](handle));

为了简化创建对象,Proxy类中的newInstance方法封装了2~4,只需要两步即可完成代理对象的创建。

生成的ProxySubject继承Proxy类实现Subject接口,实现的Subject的方法实际调用处理器的invoke方法,而invoke方法利用反射调用的是被代理对象的方法(Object result = method.invoke(proxied,args))

静态代理和静态代理的异同点

两种代理的使用有一个限制:被代理对象必须实现一个接口,代理类只能代理接口中的方法。

不同在于,静态代理比较简单,编写接口中所有方法的实现,在接口中方法数量很多时,代理类的实现比较繁琐,动态代理则实现java反射功能,将所有方法的实现集中在一处,更加动态。

cglib:

上述所说的动态代理和静态代理都要求被代理类实现某个接口,如果想对某个实现接口的类进行代理。

cglib是一个高性能的代码生成包。

1)、它广泛的被许多AOP的框架使用,例如Spring AOP 和dynaop,为它们提供方法的interception(拦截器);

2)、hibernate使用cglib来代理单端single-ended(多对一和一对一)关联(对集合的延迟抓取,是采取其他机制实现的);

3)、EasyMock和jMock是通过使用模板(moke)对象来测试java代码的包。

他们都是通过cglib来为那些没有接口的类创建模仿(moke)对象。

cglib包的底层是通过使用一个小而快的字节码处理框架ASM(java字节码操控框架),来转换字节码并生成新的类。除了cglib包,脚本语言例如Groovy和BeanShell,也是使用ASM来生成java字节码。当不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。所以cglib包要依赖于ASM包,

cglib动态代理的实现代码

1)定义一个cglib的动态代理类
public Object intercept(Object obj,Method method,Object[] args,MethodProxy proxy) throws Throwable{
		System.out.println("before in cglib");
		
		Object result = null;
		try {
			result = proxy.incokeSuper(obj,args);
		} catch (Exception e) {
			// TODO: handle exception
			System.out.println("ex:"+e.getMessage());
		}finally{
			System.out.println("after in cglib");
		}
		
		return result;
		
	}
2)调用cglib的的代理
public static void main(String[] args) {
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(HelloWorldImpl.class);
		enhancer.setCallback(new CglibProxy());
		HelloWorld helloWorld = enhancer.create();
		helloWorld.print();
	}

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

jdk只能针对有接口的类的接口方法进行动态代理

cglib基于继承来实现代理,无法对static、final类进行代理

jdk代理和cglib基于继承来实现代理,无法对peivate、static方法惊醒代理

spring AOP如何选择两种动态代理模式

如果目标对象实现了接口,则默认采用jdk动态代理

如果目标对象没有实现接口,则采用cglib进行动态代理

如果目标对象实现了接口,且强制cglib代理,则使用cglib代理


面向切面的aspect注解: @Aspect  @Pointcut  @Advice

Spring AOP的使用场景:

authentication 权限

caching 缓存

context passing 内容缓存

Error handling 错误处理

Lazy loading 懒加载

Debugging 调试

PerSistence持久化

Resource pooling 资源池

Synchronization 同步

Transaction 事务

logging,tracing,profilong and monitoring 记录跟踪 优化 校准

AOP的相关概念

Aspect(方面):一个关注点的模块化,这个关注点实现可能另外横切多个对象,事务管理是J2EE应用中一个很好的横切关注点,方面用spring的Advisor或拦截器实现。

Joinpoint(连接点):程序执行过程中明确的点,如方法的调用或特定的异常被抛出。

Advice(通知):在特定的连接点,AOP框架执行的动作,各种类型的通知包括“around”、“before” 和“thorws”通知。

通知类型将在下面讨论,许多AOP框架包括spring都是以拦截器做通知模型,维护一个“围绕”连接点的拦截器链。

Spring中定义了四个advice:BeforeAdvice,AfterAdvice,ThrowAdvice和DynamicIntroductionAdvice

Pointcut(切入点):指定一个通知将被引发的一系列连接点的集合。AOP框架必须允许开发者指定切入点:例如,使用正则表达式。Spring定义了 Pointcut接口,用来组合MethodMatcher和ClassFilter,可以通过名字很清楚的理解,

MethodMatcher是用来检验目标类的方法是否可以被应用此通知,而classFilter是用来检验是否应该用到目标类上

Introduction(引入):添加方法或字段到被通知的类。Spring允许引入新的接口到任何被通知的对象。例如,你可以使用一个引入使任何对象实现 IsModified接口,来化简缓存。Spring中要使用Introduction,可有通过DelegatingIntroductionInterceptor来实现通知,通过DefaultIntroductionAdvisor来配置Advice和代理类药实现的接口。

Target Object(目标对象):包含连接点的对象,也被称作被通知或被代理对象

AOP Proxy(AOP代理):AOP框架创建的对象,包含通知,在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理


如何使用Spring AOP

1):配置文件(xml)来进行,大概有四种方式:

a. 配置ProxyFactoryBean,显式地设置advisors,advice ,target等。

b. 配置AutoProxyCreator,这种方式下,还是和以前一样使用定义的bean,但是从容器中获得的已经是代理对象。

c. 通过<aop:config>类配置

d. 通过<aop:aspect-autoproxy>来配置,使用AspectJ的注解标识通知及切入点。

也可以直接使用ProxyFactory来编程的方式使用Spring AOP,通过ProxyFactory提供的方法可以设置target对象,Advisor等相关配置,最终通过getProxy()方法来获取代理对象。












猜你喜欢

转载自blog.csdn.net/qq_35504206/article/details/80914128
今日推荐