AOP讲解
组成
- Subject:抽象角色,是一个接口。该接口是真实对象和它的代理共用的接口;
- RealSubject:真实角色,被代理对象。
- Proxy:代理角色,内部含有对真实对象RealSubject的引用,从而可以操作真实对象。
静态代理
优点:可以做到在不修改目标对象的功能前提下,对目标功能扩展。
缺点 : 因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类。
Subject 抽象角色接口
public interface Subject{
void hello();
}
RealSubject 真实角色
public class RealSubject implements Subject{
@Override
public void hello(){
System.out.println("RealSubject");
}
}
Proxy 代理类
public class Proxy implements Subject {
private Subject subject = null;
@Override
public void hello(){
if(subject == null)
subject = new RealSubject();
System.out.print("Hello, I'm A Proxy, I'm invoking...");
this.subject.hello();
System.out.print("Hello, I'm A Proxy, I'm Done...");
}
}
动态代理
动态代理即代理类是程序在运行时创建的,使用动态代理,可以编写通用的代理逻辑,用于各种类型的被代理对象,而不需要为每个被代理的类型都创建一个静态代理类。
jdk动态代理
使用示例
1.创建一个实现接口InvocationHandler的类,它必须实现invoke方法
2.创建被代理的类以及接口
3.通过Proxy的静态方法
newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h)创建一个代理
4.通过代理调用方法
//接口
publicinterfaceHello{
voidsayHello();
}
//实现类
public class Amy implements Hello{
private String name;
public Amy(String name) {
this.name = name;
}
public void sayHello() {
System.out.println(name + " say hello");
}
}
//动态代理类,必须实现InvocationHandler接口
public class ProxyHello implements InvocationHandler {
private Hello hello;
public ProxyHello(Hello hello) {
this.hello = hello;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before say hello");
method.invoke(hello, args);
System.out.println("after say hello");
return null;
}
}
//测试
public class Test {
public static void main(String[] args) {
Amy amy = new Amy("Amy");
ProxyHello proxyHello = new ProxyHello(amy);
Hello helloProxy = (Hello) Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
new Class[]{Hello.class},
proxyHello);
helloProxy.sayHello();
}
}
创建代理类
JDK提供的生成代理类的方法是调用Proxy的静态方法newProxyInstance 生成代理类,它需要三个参数。
1)loader表示类加载器。
2)interfaces表示代理类要实现的接口列表,是一个数组,元素的类型只能是接口,不能是普通的类。
3)实现了InvocationHandler 的代理对象。
InvocationHandler
JDK 动态代理类必须实现反射包中的 java.lang.reflect.InvocationHandler 接口,在此接口中只有一个 invoke() 方法
invoke()方法:所有执行代理对象的方法都会被替换成执行invoke方法。
// Object proxy 代理
// Method method 方法
// Object[] args 参数
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before say hello");
method.invoke(hello, args);
System.out.println("after say hello");
}
newProxyInstance()
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h) throws IllegalArgumentException {
//验证传入的InvocationHandler不能为空
Objects.requireNonNull(h);
//复制代理类实现的所有接口
final Class<?>[] intfs = interfaces.clone();
//获取安全管理器
final SecurityManager sm = System.getSecurityManager();
//进行一些权限检验
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
// 该方法先从缓存获取代理类, 如果没有再去生成一个代理类
// 如果缓存中已经存在相应接口的代理类,直接返回;否则,使用ProxyClassFactory创建代理类
// getProxyClass0方法生成代理类的字节码文件。
Class<?> cl = getProxyClass0(loader, intfs);
try {
//进行一些权限检验
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
//获取参数类型是InvocationHandler.class的代理类构造器
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
//如果代理类是不可访问的, 就使用特权将它的构造器设置为可访问
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
//传入InvocationHandler实例去构造一个代理类的实例
//所有代理类都继承自Proxy, 因此这里会调用Proxy的构造器将InvocationHandler引用传入
return cons.newInstance(new Object[]{h});
} catch (Exception e) {
throw new InternalError(e.toString(), e);
}
}
getProxyClass0()
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
// 限定代理的接口不能超过65535个
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// 从缓冲中获取代理类,如果缓存中不存在再通过ProxyClassFactory创建
// 缓存是通过弱引用来缓存,弱引用很适合用来做缓存
// proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory())
return proxyClassCache.get(loader, interfaces);
}
ProxyClassFactory
private static final class ProxyClassFactory
implements BiFunction[], Class<?>>
{
// 代理类前缀
private static final String proxyClassNamePrefix = "$Proxy";
// 生成代理类名称的计数器
private static final AtomicLong nextUniqueNumber = new AtomicLong();
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) {
/*
* 校验类加载器是否能通过接口名称加载该类
*/
Class<?> interfaceClass = null;
try {
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}
/*
* 校验该类是否是接口类型
*/
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
/*
* 校验接口是否重复
*/
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
String proxyPkg = null; // 代理类包名
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
/*
* 非public接口,代理类的包名与接口的包名相同
*/
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL;
String name = intf.getName();
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}
if (proxyPkg == null) {
// public代理接口,使用com.sun.proxy包名
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
/*
* 为代理类生成名字
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*
* 真正生成代理类的字节码文件的地方,生成表示代理类class对象的二进制
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
// 使用类加载器将代理类的字节码文件加载到JVM中
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
throw new IllegalArgumentException(e.toString());
}
}
}
最终生成的代理类
已经继承了proxy类 所有不能多继承public final class $Proxy0 extends Proxy implements Hello {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy0(InvocationHandler paramInvocationHandler) {
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject) {
try {
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
} catch (Error|RuntimeException error) {
throw null;
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final void sayHello() {
try {
this.h.invoke(this, m3, null);
return;
} catch (Error|RuntimeException error) {
throw null;
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final String toString() {
try {
return (String)this.h.invoke(this, m2, null);
} catch (Error|RuntimeException error) {
throw null;
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final int hashCode() {
try {
return ((Integer)this.h.invoke(this, m0, null)).intValue();
} catch (Error|RuntimeException error) {
throw null;
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m3 = Class.forName("com.lhd.proxy.Hello").getMethod("sayHello", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
} catch (NoSuchMethodException noSuchMethodException) {
throw new NoSuchMethodError(noSuchMethodException.getMessage());
} catch (ClassNotFoundException classNotFoundException) {
throw new NoClassDefFoundError(classNotFoundException.getMessage());
}
}
}
通过这个代理类我们可以知道JDK动态代理为什么只能基于接口的代理,因为该代理类继承了Proxy类,而java是不支持多继承的,所有只能通过实现接口来实现代理。
jdk动态代理是通过反射来实现的。
cglib 动态代理
什么是CGLIB?
CGLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。通常可以使用Java的动态代理创建代理,但当要代理的类没有实现接口或者为了更好的性能,CGLIB是一个好的选择。
原理
CGLIB原理:动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。它比使用java反射的JDK动态代理要快。
CGLIB底层:使用字节码处理框架ASM,来转换字节码并生成新的类
使用
public class Student {
private String name = "zhangsan";
public String getStuName() {
return name;
}
}
public class CglibMethodInterceptTest {
public static void main(String[] args) {
//创建一个Enhancer对象
Enhancer enchaner = new Enhancer();
//设置被代理的类
enchaner.setSuperclass(Student.class);
//创建一个回调接口
Callback interceptor = new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)
throws Throwable {
System.err.println("原方法名是 : " + method.getName());
System.err.println("原方法声明的类为 " + method.getDeclaringClass());
System.err.println("我是 " + (String) proxy.invokeSuper(obj, args));
System.err.println("我调用结束了");
return null;
}
};
enchaner.setCallback(interceptor);
Student student = (Student) enchaner.create();
student.getStuName();
}
}
原方法名是 : getStuName
原方法声明的类为 class com.iminer.Student
我是 zhangsan
我调用结束了
Spring中的应用
AOP介绍
AOP(Aspect Orient Programming),作为面向对象编程的一种补充,广泛应用于处理一些具有横切性质的系统级服务,如日志收集、事务管理、安全检查、缓存、对象池管理等。AOP实现的关键就在于AOP框架自动创建的AOP代理,AOP代理则可分为静态代理和动态代理两大类,其中静态代理是指使用AOP框架提供的命令进行编译,从而在编译阶段就可生成 AOP 代理类,因此也称为编译时增强;而动态代理则在运行时借助于JDK动态代理、CGLIB等在内存中“临时”生成AOP动态代理类,因此也被称为运行时增强。
面向切面的编程(AOP) 是一种编程范式,旨在通过允许横切关注点的分离,提高模块化。AOP提供切面来将跨越对象关注点模块化。虽然现在可以获得许多AOP框架,但在这里我们要区分的只有两个流行的框架:Spring AOP和AspectJ。
AspectJ
AspectJ是一个面向切面的框架,他定义了AOP的一些语法,有一个专门的字节码生成器来生成遵守java规范的 class文件。
AspectJ的通知类型不仅包括我们之前了解过的三种通知:前置通知、后置通知、环绕通知,在Aspect中还有异常通知以及一种最终通知即无论程序是否正常执行,最终通知的代码会得到执行。
Spring AOP
1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP
3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
Spring AOP 与 AspectJ的关系
其实两者没有关系,当你用到Spring AOP提供的注入@Before、@After等注解时,Spring AOP和AspectJ就有了关系。Spring AOP支持注解,在使用@Aspect注解创建和配置切面时将更加方便。而使用AspectJ,需要通过.aj文件来创建切面,并且需要使用ajc(Aspect编译器)来编译代码。Spring AOP其实现原理是JDK动态代理,在运行时生成代理类。为了启用Spring对@AspectJ切面配置的支持,并保证Spring容器中的目标Bean被一个或多个切面自动增强,必须在Spring配置文件中添加如下配置
如需强制开启cglib代理则需要更改配置
Filter、Interceptor、Aspect?
filter -> interceptor -> ControllerAdvice -> aspect -> controller
Filter过滤器
过滤器可以拦截到方法的请求和响应(ServletRequest request, ServletResponse response),并对请求响应做出过滤操作。过滤器依赖于servlet容器。在实现上,基于函数回调,它可以对几乎所有请求进行过滤,一个过滤器实例只能在容器初始化时调用一次。
1.启动服务器时加载过滤器的实例,并调用init()方法来初始化实例;
2.每一次请求时都只调用方法doFilter()进行处理;
3.停止服务器时调用destroy()方法,销毁实例。
Interceptor拦截器
依赖于web框架,在SpringMVC中就是依赖于SpringMVC框架。在实现上,基于Java的反射机制,属于面向切面编程(AOP)的一种运用,就是在一个方法前,调用一个方法,或者在方法后,调用一个方法。
Aspect切片
AOP操作可以对操作进行横向的拦截,最大的优势在于他可以获取执行方法的参数,对方法进行统一的处理。常见使用日志,事务,请求参数安全验证等。