Spring 为解耦而生,其中AOP(面向切面编程)是很浓重的一笔。AOP(Aspect-Oriented-Programming)指一种程序设计范型,该范型以一种称为切面(aspect)的语言构造为基础,切面是一种新的模块化机制,用来描述分散在对象、类或方法中的横切关注点(crosscutting)。html
说明:Advice在国内不少的书面资料上都被翻译为“通知”,但这个翻译没法表达其本质,有少许的读物将这个词翻译为“加强”,这个翻译是比较为准确的诠释,咱们经过AOP将横切关注功能加到原有的业务逻辑上,这就是对原有的业务逻辑进行的一种加强,这个加强能够是前置加强,后置加强,返回后加强,抛出异常时加强,和包围型加强。java
AOP是运用了动态代理。代理模式是GoF提出的23种设计模式中最为经典的模式之一,代理是对象的结构模式,他给某一个对象提供一个对象代理,并由代理控制对原对象的引用,简单的说,代理对象能够完成比原对象更多的职责,当须要为原对象添加横切关注功能时,就可使用原对象的代理对象。咱们在打开Office系列的Word文档时,若是文档中有插图,当文档刚加载时,文档中的插图都只是一个虚框的占位符,等真正用户翻到某一页要查看该图片时,才会真正加载这张图片,这其实就是代理模式的使用,代替真正的虚框就是一个虚拟代理,Hibernate的load方法也是返回一个虚拟代理对象,等真正用户须要访问对象的属性时,才向数据库发出SQL语句。程序员
按照代理建立初期,代理能够分为两种:面试
下面用一个找枪手代考的例子演示代理模式的使用数据库
package com.test.test2.interfaces;
/**
* @Author: Young
* @QQ: 403353323
* @Date: 2019/4/25 10:35
*/
public interface Candidate {
public void answerTheQuestion();
}
复制代码
package com.test.test2; import com.test.test2.interfaces.Candidate; /** * @Author: Young * @QQ: 403353323 * @Date: 2019/4/25 10:36 */ public class LazyStudent implements Candidate { private String name; public LazyStudent(String name) { this.name = name; } @Override public void answerTheQuestion() { // 懒学生只能写出本身名字不会答题 System.out.println("姓名" + name); } } 复制代码
package com.test.test2; import com.test.test2.interfaces.Candidate; /** * @Author: Young * @QQ: 403353323 * @Date: 2019/4/25 10:40 */ public class Gunman implements Candidate { private Candidate target; //被代理的对象 public Gunman(Candidate target) { this.target = target; } @Override public void answerTheQuestion() { // 抢手要写上代考学生的名字 target.answerTheQuestion(); // 抢手帮助懒学生答题并交卷 System.out.println("奋笔疾书书写正确答案"); System.out.println("交卷"); } } 复制代码
@Test public void testGunman(){ Candidate c = new Gunman(new LazyStudent("王小二")); c.answerTheQuestion(); } 复制代码
观察代理能够发现每个代理类只能为一个接口服务,这样一来程序开发中必然会产生不少的代理,并且,全部代理除了调用方法不同以外,其余操做都同样,则此时确定是重复代码,解决这一个问题最好的作法是经过一个代理类完成所有的代理功能,那么此时就必须使用动态代理了。编程
从JDK 1.3 开始,Java提供了动态技术,动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不只简化了编程工做,并且提升了软件系统的可扩展性,由于反射机制能够生成任意类型的动态代理。设计模式
java.lang.reflect
包中的Proxy类和InvocationHandler接口提供了生成动态代理的能力。数组
JDK动态代理中包含一个接口和一个类:bash
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
复制代码
参数说明:markdown
参数 | 说明 |
---|---|
Object proxy | 指被代理的对象 |
Method method | 要调用的方法 |
Object[] args | 方法调用时的参数 |
能够将InvocationHandler接口的子类想象成一个代理的最终操做类,替换掉ProxySubject。
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
/**************
*具体内容略去**
**************/
}
复制代码
参数说明:
源码中的参数说明
* @param loader the class loader to define the proxy class * @param interfaces the list of interfaces for the proxy class to implement * @param h the invocation handler to dispatch method invocations to 复制代码
参数 | 说明 |
---|---|
ClassLoader | 类加载器 |
Class<?>[] interfaces | 获得所有的接口 |
InvocationHandler h | 获得InvocationHandler接口的子类实例 |
Ps:在Proxy中类的newProxyInstance()
方法中须要一个ClassLoader类的实例,ClassLoader实际上对应的是类加载器。在Java中主要有三种类加载器:
BookFacade.java
public interface BookFacade {
public void addBook();
}
复制代码
BookFacadeImpl.java
import net.battier.dao.BookFacade; public class BookFacadeImpl implements BookFacade { @Override public void addBook() { System.out.println("增长图书方法。。。"); } } 复制代码
BookFacadeProxy.java
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * JDK动态代理代理类 */ public class BookFacadeProxy implements InvocationHandler { private Object target; /** * 绑定委托对象并返回一个代理类 * @param target * @return */ public Object bind(Object target) { this.target = target; //取得代理对象 return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); //要绑定接口(这是一个缺陷,cglib弥补了这一缺陷) } @Override /** * 调用方法 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result=null; System.out.println("事物开始"); //执行方法 result=method.invoke(target, args); System.out.println("事物结束"); return result; } } 复制代码
TestProxy.java
import net.battier.dao.BookFacade;
import net.battier.dao.impl.BookFacadeImpl;
import net.battier.proxy.BookFacadeProxy;
public class TestProxy {
public static void main(String[] args) {
BookFacadeProxy proxy = new BookFacadeProxy();
BookFacade bookProxy = (BookFacade) proxy.bind(new BookFacadeImpl());
bookProxy.addBook();
}
}
复制代码
说明:使用Java的动态代理有一个局限性就是被代理的类必须实现接口(也就是说只能对该类所实现接口中定义的方法进行代理),虽然面向接口编程是每一个优秀的Java程序员都知道的规则,但现实每每不尽人意,对于没有实现接口的代理类如何为其生成代理类呢?继承继承是最经典的扩展已有代码能力的手段。CGLib代理就是这样实现代理。
CGLib采用很是底层的字节码生成技术,经过为一个类建立子类来生成代理类,它弥补了Java动态代理的不足,所以,Spring中的动态代理和CGLib都是建立代理的重要手段,对于实现了接口的类就用动态代理为其生成代理类,而没有实现接口的类就用CGLib经过继承方式为其建立代理类。但由于采用的是继承,因此不能对final修饰的类进行代理。
intercept参数列表:
参数 | 说明 |
---|---|
Object obj | 代理的对象 |
Metho method | 表示拦截的方法 |
Object[] args | 数组表示参数列表,基本数据类型须要传入其包装类型,如int-->Integer、long-Long、double-->Double |
MethodProxy methodProxy | 表示对方法的代理,invokeSuper方法表示对被代理对象方法的调用 |
package com.test.test2.proxy; /** * @Author: Young * @QQ: 403353323 * @Date: 2019/5/3 21:32 */ public class BookFacadeImpl1 { public void addBook(){ System.out.println("增长图书的普通方法"); } } 复制代码
package com.test.test2.proxy; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; /** * @Author: Young * @QQ: 403353323 * @Date: 2019/5/3 21:34 */ /** * CGLib建立动态代理 */ public class BookFacadeProxy implements MethodInterceptor { private Object target; public Object getInstance(Object target){ this.target = target; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(this.target.getClass()); // 回调方法 enhancer.setCallback(this); //建立代理对象 return enhancer.create(); } @Override // 回调方法 public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("开始事务"); methodProxy.invokeSuper(obj, args); System.out.println("事务结束"); return null; } } 复制代码
public class TestCglib {
public static void main(String[] args) {
BookFacadeCglib cglib=new BookFacadeCglib();
BookFacadeImpl1 bookCglib=(BookFacadeImpl1)cglib.getInstance(new BookFacadeImpl1());
bookCglib.addBook();
}
}
复制代码