动态代理、IoC、AOP
动态代理
JDK动态代理
利用反射机制生成一个实现代理接口的类,在调用具体方法前用invokeHandler
来处理。JDK代理只能对实现接口的类生成代理。
IWork.java
package dynamicProxy;
public interface IWork {
void meeting();
int evaluate(String name);
}
Leader.java
package dynamicProxy;
import java.util.Random;
public class Leader implements IWork{
@Override
public void meeting() {
System.out.println("领导早上要组织会议");
}
@Override
public int evaluate(String name) {
int score = new Random(System.currentTimeMillis()).nextInt(20) + 80;
System.out.println(String.format("领导给%s的考评为%s分", name, score));
return score;
}
}
Test.java
package dynamicProxy;
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
Leader leader = new Leader();
//proxy是被代理后生成的对象,是通过IWork接口的字节码增强方式创建的类而构造出来的,是一个临时构造的类的对象
IWork proxy = (IWork) Proxy.newProxyInstance(Leader.class.getClassLoader(),
new Class[]{IWork.class},new WorkInvocationHandler(leader));
proxy.meeting();
proxy.evaluate("Joy");
proxy.evaluate("James");
}
}
WorkInvocationHandler.java
package dynamicProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class WorkInvocationHandler implements InvocationHandler {
private Object object;
public WorkInvocationHandler(Object o) {
this.object = o;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("object: "+object.getClass().getSimpleName());
System.out.println("proxy: "+proxy.getClass().getSimpleName());
if("meeting".equals(method.getName())) {
System.out.println("准备材料。。");
return method.invoke(object,args);
} else if("evaluate".equals(method.getName())) {
if(args[0] instanceof String) {
if("James".equals(args[0])) {
System.out.println("James 不好");
return 70;
}
}
return method.invoke(object,args);
}
return null;
}
}
输出结果:
object: Leader
proxy: $Proxy0
准备材料。。
领导早上要组织会议
object: Leader
proxy: $Proxy0
领导给Joy的考评为97分
object: Leader
proxy: $Proxy0
James 不好
从main函数看起:
Leader leader = new Leader();
//proxy是被代理后生成的对象,是通过IWork接口的字节码增强方式创建的类而构造出来的,是一个临时构造的类的对象
IWork proxy = (IWork) Proxy.newProxyInstance(Leader.class.getClassLoader(),
new Class[]{IWork.class},new WorkInvocationHandler(leader));
proxy.meeting();
通过类WorkInvocationHandler
,我们可以动态代理Leader类方法的实现。通过Proxy的静态方法newProxyInstance
来生成了一个实例对象。看下源码,解释放在注释里面:
// loader: 选用的类加载器,用哪个类加载器去加载代理对象,本文中是Leader的类加载器
// interfaces:被代理的类实现的接口,可以是多个
// h:绑定代理类的一个方法
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
// 一些判断和验证
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
// 查找或生成特定的代理类,也就是会h要处理的类
// 内部实现:如果有实现给定接口的代理类,就返回cache,
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
// 验证
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
// /* parameter types of a proxy class constructor */
//private static final Class<?>[] constructorParams =
// { InvocationHandler.class };
// getConstructor可以无参也可以有参
// 获得代理类构造器 (接收一个 InvocationHandler 参数)
// 获取Constructor类对象
// 一共有4种方法,全部都在Class类中:
// 1. getConstructors():获取类中的公共方法
// 2. getConstructor(Class[] params): 获取类的特定构造方法,params参数指定构造方法的参数类型
// 3. getDeclaredConstructors(): 获取类中所有的构造方法(public、protected、default、private)
// 4. getDeclaredConstructor(Class[] params): 获取类的特定构造方法,params参数指定构造方法的参数类型
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
// Class.newInstance() 只能够调用无参的构造函数,即默认的构造函数;
// Constructor.newInstance() 可以根据传入的参数,调用任意构造构造函数。
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
查看代理类的内容,例如这篇文章:java怎么查看jvm中动态代理class类内容。
生成代理类的内容具体如下,可以看到,除了hashCode
,toString
,equals
之外,还实现了IWork
接口的两个方法。:
public final class $Proxy0 extends Proxy implements IWork {
private static Method m1;
private static Method m4;
private static Method m2;
private static Method m3;
private static Method m0;
public $Proxy0(InvocationHandler var1) {
super(var1);
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m4 = Class.forName("cn.orzlinux.Bean.dynamicProxy.IWork").getMethod("meeting");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("cn.orzlinux.Bean.dynamicProxy.IWork").getMethod("evaluate", Class.forName("java.lang.String"));
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
public final boolean equals(Object var1) {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int evaluate(String var1) {
try {
return (Integer)super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final void meeting() {
try {
super.h.invoke(this, m4, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
}
调用则都用了InvocationHandler
的invoke方法。我们已经实现了这个方法:
invoke
的参数:
- proxy就是我们生成的
$proxy0
类:
IWork proxy = (IWork) Proxy.newProxyInstance(Leader.class.getClassLoader(),
new Class[]{IWork.class},new WorkInvocationHandler(leader));
- method是
$proxy
类中实现的那几个方法 - args是参数数组
这样就一目了然了:
在main方法中:
Leader leader = new Leader();
IWork proxy = (IWork) Proxy.newProxyInstance(Leader.class.getClassLoader(),
new Class[]{IWork.class},new WorkInvocationHandler(leader));
proxy.meeting();
先生成代理类,代理类中除了hashCode
,toString
,equals
之外,还实现了IWork
接口的两个方法。给WorkInvocationHandler
传递了一个Leader
对象。例如调用proxy.meeting()
:
去代理类中找meeting
方法,发现要到handler
的invoke
方法,而invoke
方法可以在leader
对象的基础之上加一些准备工作。也就是输出:
准备材料。。
领导早上要组织会议
至此,整体脉络就梳理完了。
CGlib动态代理
JDK动态代理是基于接口的方式,换句话来说就是代理类和目标类都实现同一个接口。
CGLib动态代理是代理类去继承目标类,然后重写其中目标类的方法,这样可以保证代理类拥有目标类的同名方法。
示例
Dog.java
public class Dog {
final public void run(String name) {
System.out.println("dog "+name+"--- run");
}
public void eat() {
System.out.println("dog --- eat");
}
}
MyMethodInterceptro.java
public class MyMethodInterceptor implements MethodInterceptor {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("对目标类进行增强");
// 方法调用,非反射
Object Object = methodProxy.invokeSuper(o,objects);
return objects;
}
}
CGlibProxy.java
public class CGlibProxy {
public static void main(String[] args) {
// 导出cglib动态类
System.setProperty( DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "F:\\java_classes");
// Enhancer,类似于JDK动态代理的Proxy
Enhancer enhancer = new Enhancer();
//设置目标类的字节码文件
enhancer.setSuperclass(Dog.class);
enhancer.setCallback(new MyMethodInterceptor());
//这里的creat方法就是正式创建代理类
Dog proxyDog = (Dog) enhancer.create();
proxyDog.eat();
}
}
代理类
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
// 代理类继承了Dog,并且实现了Factory接口(设置回调函数和返回实例化对象的方法)
public class Dog$$EnhancerByCGLIB$$b46ed1e9 extends Dog implements Factory {
// method 和 methodProxy
private boolean CGLIB$BOUND;
private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
private static final Callback[] CGLIB$STATIC_CALLBACKS;
private MethodInterceptor CGLIB$CALLBACK_0;
private static final Method CGLIB$eat$0$Method;
private static final MethodProxy CGLIB$eat$0$Proxy;
private static final Object[] CGLIB$emptyArgs;
private static final Method CGLIB$finalize$1$Method;
private static final MethodProxy CGLIB$finalize$1$Proxy;
private static final Method CGLIB$equals$2$Method;
private static final MethodProxy CGLIB$equals$2$Proxy;
private static final Method CGLIB$toString$3$Method;
private static final MethodProxy CGLIB$toString$3$Proxy;
private static final Method CGLIB$hashCode$4$Method;
private static final MethodProxy CGLIB$hashCode$4$Proxy;
private static final Method CGLIB$clone$5$Method;
private static final MethodProxy CGLIB$clone$5$Proxy;
// 为目标类的每一个方法都建立索引
static void CGLIB$STATICHOOK1() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
Class var0 = Class.forName("cn.orzlinux.Bean.cglib.Dog$$EnhancerByCGLIB$$b46ed1e9");
Class var1;
Method[] var10000 = ReflectUtils.findMethods(new String[]{"finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
CGLIB$finalize$1$Method = var10000[0];
CGLIB$finalize$1$Proxy = MethodProxy.create(var1, var0, "()V", "finalize", "CGLIB$finalize$1");
CGLIB$equals$2$Method = var10000[1];
CGLIB$equals$2$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2");
CGLIB$toString$3$Method = var10000[2];
CGLIB$toString$3$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$3");
CGLIB$hashCode$4$Method = var10000[3];
CGLIB$hashCode$4$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$4");
CGLIB$clone$5$Method = var10000[4];
CGLIB$clone$5$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5");
CGLIB$eat$0$Method = ReflectUtils.findMethods(new String[]{"eat", "()V"}, (var1 = Class.forName("cn.orzlinux.Bean.cglib.Dog")).getDeclaredMethods())[0];
CGLIB$eat$0$Proxy = MethodProxy.create(var1, var0, "()V", "eat", "CGLIB$eat$0");
}
// 调用目标类的eat方法,
final void CGLIB$eat$0() {
super.eat();
}
public final void eat() {
// 传入的方法拦截器
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
var10000.intercept(this, CGLIB$eat$0$Method, CGLIB$emptyArgs, CGLIB$eat$0$Proxy);
} else {
super.eat();
}
}
final void CGLIB$finalize$1() throws Throwable {
super.finalize();
}
// ...
public static MethodProxy CGLIB$findMethodProxy(Signature var0) {
String var10000 = var0.toString();
switch(var10000.hashCode()) {
case -1574182249:
if (var10000.equals("finalize()V")) {
return CGLIB$finalize$1$Proxy;
}
break;
case -1310345955:
if (var10000.equals("eat()V")) {
return CGLIB$eat$0$Proxy;
}
break;
case -508378822:
if (var10000.equals("clone()Ljava/lang/Object;")) {
return CGLIB$clone$5$Proxy;
}
break;
case 1826985398:
if (var10000.equals("equals(Ljava/lang/Object;)Z")) {
return CGLIB$equals$2$Proxy;
}
break;
case 1913648695:
if (var10000.equals("toString()Ljava/lang/String;")) {
return CGLIB$toString$3$Proxy;
}
break;
case 1984935277:
if (var10000.equals("hashCode()I")) {
return CGLIB$hashCode$4$Proxy;
}
}
return null;
}
public Dog$$EnhancerByCGLIB$$b46ed1e9() {
CGLIB$BIND_CALLBACKS(this);
}
public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] var0) {
CGLIB$THREAD_CALLBACKS.set(var0);
}
public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] var0) {
CGLIB$STATIC_CALLBACKS = var0;
}
private static final void CGLIB$BIND_CALLBACKS(Object var0) {
Dog$$EnhancerByCGLIB$$b46ed1e9 var1 = (Dog$$EnhancerByCGLIB$$b46ed1e9)var0;
if (!var1.CGLIB$BOUND) {
var1.CGLIB$BOUND = true;
Object var10000 = CGLIB$THREAD_CALLBACKS.get();
if (var10000 == null) {
var10000 = CGLIB$STATIC_CALLBACKS;
if (var10000 == null) {
return;
}
}
var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0];
}
}
public Object newInstance(Callback[] var1) {
CGLIB$SET_THREAD_CALLBACKS(var1);
Dog$$EnhancerByCGLIB$$b46ed1e9 var10000 = new Dog$$EnhancerByCGLIB$$b46ed1e9();
CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
return var10000;
}
public Object newInstance(Callback var1) {
CGLIB$SET_THREAD_CALLBACKS(new Callback[]{var1});
Dog$$EnhancerByCGLIB$$b46ed1e9 var10000 = new Dog$$EnhancerByCGLIB$$b46ed1e9();
CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
return var10000;
}
public Object newInstance(Class[] var1, Object[] var2, Callback[] var3) {
CGLIB$SET_THREAD_CALLBACKS(var3);
Dog$$EnhancerByCGLIB$$b46ed1e9 var10000 = new Dog$$EnhancerByCGLIB$$b46ed1e9;
switch(var1.length) {
case 0:
var10000.<init>();
CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
return var10000;
default:
throw new IllegalArgumentException("Constructor not found");
}
}
public Callback getCallback(int var1) {
CGLIB$BIND_CALLBACKS(this);
MethodInterceptor var10000;
switch(var1) {
case 0:
var10000 = this.CGLIB$CALLBACK_0;
break;
default:
var10000 = null;
}
return var10000;
}
public void setCallback(int var1, Callback var2) {
switch(var1) {
case 0:
this.CGLIB$CALLBACK_0 = (MethodInterceptor)var2;
default:
}
}
public Callback[] getCallbacks() {
CGLIB$BIND_CALLBACKS(this);
return new Callback[]{this.CGLIB$CALLBACK_0};
}
public void setCallbacks(Callback[] var1) {
this.CGLIB$CALLBACK_0 = (MethodInterceptor)var1[0];
}
static {
CGLIB$STATICHOOK1();
}
}
调用方法一的时候,在代理类中会先判断是否实现了方法拦截的接口,没实现的话直接调用目标类的方法一;如果实现了那就会被方法拦截器拦截,在方法拦截器中会对目标类中所有的方法建立索引,其实大概就是将每个方法的引用保存在数组中,我们就可以根据数组的下标直接调用方法,而不是用反射;索引建立完成之后,方法拦截器内部就会调用invoke方法(这个方法在生成的FastClass中实现),在invoke方法内就是调用CGLIB$方法一$这种方法,也就是调用对应的目标类的方法一。一般我们要添加自己的逻辑就是在方法拦截器那里。
什么时候用cglib什么时候用jdk动态代理?
- 目标对象生成了接口 默认用JDK动态代理
- 如果目标对象使用了接口,可以强制使用cglib
JDK动态代理只能对实现了接口的类生成代理,而不能针对类。Cglib是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,并覆盖其中方法的增强,但是因为采用的是继承,所以该类或方法最好不要生成final,对于final类或方法,是无法继承的。
IOC
百科解释(意会一下):
控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。
Java类和类之间的引用错综复杂:
引入IOC框架来解耦,维护类的声明周期和类的引用:
IoC框架负责维护,类的使用者只管使用。实现IoC框架和代码详见:徒手撸框架--实现IoC
BeanFactoryImpl.java
public class BeanFactoryImpl implements BeanFactory {
private static final ConcurrentHashMap<String,Object> beanMap = new ConcurrentHashMap<>();
private static final ConcurrentHashMap<String,BeanDefinition> beanDefineMap= new ConcurrentHashMap<>();
private static final Set<String> beanNameSet = Collections.synchronizedSet(new HashSet<>());
@Override
public Object getBean(String name) throws Exception {
//查找对象是否已经实例化过
Object bean = beanMap.get(name);
if(bean != null){
return bean;
}
//如果没有实例化,那就需要调用createBean来创建对象
bean = createBean(beanDefineMap.get(name));
if(bean != null) {
//对象创建成功以后,注入对象需要的参数
populatebean(bean);
//再吧对象存入Map中方便下次使用。
beanMap.put(name,bean);
}
//结束返回
return bean;
}
protected void registerBean(String name, BeanDefinition bd){
beanDefineMap.put(name,bd);
beanNameSet.add(name);
}
private Object createBean(BeanDefinition beanDefinition) throws Exception {
String beanName = beanDefinition.getClassName();
Class clz = ClassUtils.loadClass(beanName);
if(clz == null) {
throw new Exception("can not find bean by beanName");
}
List<ConstructorArg> constructorArgs = beanDefinition.getConstructorArgs();
if(constructorArgs != null && !constructorArgs.isEmpty()){
List<Object> objects = new ArrayList<>();
for (ConstructorArg constructorArg : constructorArgs) {
if (constructorArg.getValue() != null) {
objects.add(constructorArg.getValue());
} else {
objects.add(getBean(constructorArg.getRef()));
}
}
Class[] constructorArgTypes = objects.stream().map(it -> it.getClass()).collect(Collectors.toList()).toArray(new Class[]{});
Constructor constructor = clz.getConstructor(constructorArgTypes);
return BeanUtils.instanceByCglib(clz, constructor, objects.toArray());
} else {
return BeanUtils.instanceByCglib(clz, null, null);
}
}
private void populatebean(Object bean) throws Exception {
Field[] fields = bean.getClass().getSuperclass().getDeclaredFields();
if (fields != null && fields.length > 0) {
for (Field field : fields) {
String beanName = field.getName();
beanName = StringUtils.uncapitalize(beanName);
if (beanNameSet.contains(field.getName())) {
Object fieldBean = getBean(beanName);
if (fieldBean != null) {
ReflectionUtils.injectField(field,bean,fieldBean);
}
}
}
}
}
}
BeanUtils.java
// 处理对象的实例化
public class BeanUtils {
public static <T> T instanceByCglib(Class<T> clz, Constructor ctr,
Object[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clz);
//NoOp.INSTANCE,这表示一个空Callback,即如果不想对某个方法进行拦截,
// 可以在DaoFilter中返回
enhancer.setCallback(NoOp.INSTANCE);
if(ctr == null) {
return (T) enhancer.create();
} else {
return (T) enhancer.create(ctr.getParameterTypes(),args);
}
}
}
JsonApplicationContext.java
public class JsonApplicationContext extends BeanFactoryImpl {
private String fileName;
public JsonApplicationContext(String fileName) {
this.fileName = fileName;
}
public void init(){
loadFile();
}
private void loadFile(){
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName);
// ObjectMapper提供了readValue(String content, TypeReference valueTypeRef)接口,
// 第二个参数为new一个TypeReference的子类实例:
// new TypeReference<List<UserResource>>(){}。
// 泛型抽象类TypeReference用于通过子类获取完整的泛型类型信息。
List<BeanDefinition> beanDefinitions = JsonUtils.readValue(is,new TypeReference<List<BeanDefinition>>(){});
if(beanDefinitions != null && !beanDefinitions.isEmpty()) {
for (BeanDefinition beanDefinition : beanDefinitions) {
registerBean(beanDefinition.getName(), beanDefinition);
}
}
}
}
测试类:
JsonApplicationContext applicationContext = new JsonApplicationContext( "application.json" );
applicationContext.init();
Robot aiRobot = (Robot) applicationContext.getBean("robot");
aiRobot.show();
运行图:
AOP
AOP(Aspect-Oriented Programming), 即 面向切面编程, 它与 OOP( Object-Oriented Programming, 面向对象编程) 相辅相成, 提供了与 OOP 不同的抽象软件结构的视角。
在 OOP 中, 我们以类(class)作为我们的基本单元, 而 AOP 中的基本单元是 Aspect(切面)
术语
假设一下, 从前有一个叫爪哇的小县城, 在一个月黑风高的晚上, 这个县城中发生了命案. 作案的凶手十分狡猾, 现场没有留下什么有价值的线索. 不过万幸的是, 刚从隔壁回来的老王恰好在这时候无意中发现了凶手行凶的过程, 但是由于天色已晚, 加上凶手蒙着面, 老王并没有看清凶手的面目, 只知道凶手是个男性, 身高约七尺五寸. 爪哇县的县令根据老王的描述, 对守门的士兵下命令说: 凡是发现有身高七尺五寸的男性, 都要抓过来审问. 士兵当然不敢违背县令的命令, 只好把进出城的所有符合条件的人都抓了起来。
- join point --> 爪哇的小县城里的百姓: 因为根据定义, join point 是所有可能被织入 advice 的候选的点, 在 Spring AOP中, 则可以认为所有方法执行点都是 join point. 而在我们上面的例子中, 命案发生在小县城中, 按理说在此县城中的所有人都有可能是嫌疑人。
- point cut --> 男性, 身高约七尺五寸: 我们知道, 所有的方法(joint point) 都可以织入 advice, 但是我们并不希望在所有方法上都织入 advice, 而 point cut 的作用就是提供一组规则来匹配join point, 给满足规则的 join point 添加 advice. 同理, 对于县令来说, 他再昏庸, 也知道不能把县城中的所有百姓都抓起来审问, 而是根据
凶手是个男性, 身高约七尺五寸
, 把符合条件的人抓起来. 在这里凶手是个男性, 身高约七尺五寸
就是一个修饰谓语, 它限定了凶手的范围, 满足此修饰规则的百姓都是嫌疑人, 都需要抓起来审问。 - advice --> 抓过来审问, advice 是一个动作, 即一段 Java 代码, 这段 Java 代码是作用于 point cut 所限定的那些 join point 上的. 同理, 对比到我们的例子中,
抓过来审问
这个动作就是对作用于那些满足男性, 身高约七尺五寸
的爪哇的小县城里的百姓
。 - aspect: aspect 是 point cut 与 advice 的组合, 因此在这里我们就可以类比: "根据老王的线索, 凡是发现有身高七尺五寸的男性, 都要抓过来审问" 这一整个动作可以被认为是一个 aspect。
AOP的工作重心在于如何将增强织入目标对象的连接点上, 这里包含两个工作:如何通过 pointcut 和 advice 定位到特定的 joinpoint 上;如何在 advice 中编写切面代码。
可以简单地认为, 使用 @Aspect
注解的类就是切面。
在 Spring 中, 所有的方法都可以认为是 joinpoint, 但是我们并不希望在所有的方法上都添加 Advice, 而 pointcut 的作用就是提供一组规则(使用 AspectJ pointcut expression language 来描述) 来匹配joinpoint, 给满足规则的 joinpoint 添加 Advice。
例子感觉挺不错的,一堆术语放到具体的例子上就大致知道是想说什么了。
AOP的实现方式有两种:
- AOP框架在编译阶段,就对目标类进行修改,得到的class文件已经是被修改过的。生成静态的AOP代理类(生成*.class文件已经被改掉了,需要使用特定的编译器)。以AspectJ为代表 —— 静态AOP框架。
- AOP框架在运行阶段,动态生成AOP代理(在内存中动态地生成AOP代理类),以实现对目标对象的增强。它不需要特殊的编译器。以Spring AOP为代表。—— 动态AOP框架。
例子:http接口鉴权
需求:为某些http restful api提供权限验证功能,权限不符合时,返回报错。
设计:
提供一个特殊的注解
AuthChecker
,方法注解,有此注解标注的Controller需要进行权限验证。利用Spring AOP,以
@annotation
切点标志符来匹配有注解AuthChecker
所标注的 joinpoint。在 advice 中, 简单地检查调用者请求中的 Cookie 中是否有我们指定的 token, 如果有, 则认为此调用者权限合法, 允许调用, 反之权限不合法, 范围错误。
AuthChecker.java
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthChecker {
}
HttpAOPAdviseDefine.java
@Component
@Aspect
public class HttpAOPAdviseDefine {
// 定义一个 Pointcut, 使用 切点表达式函数 来描述对哪些 Join point 使用 advise.
// 之前例子中的找出七尺五寸的男子, 这里是找出AuthChecker标注的方法
@Pointcut("@annotation(cn.orzlinux.AuthChecker)")
public void pointcut() {
}
// 要把这些男子抓起来审问(对符合标注方法的动作,验证cookie)
// 定义advise
@Around("pointcut()")
public Object checkAuth(ProceedingJoinPoint joinPoint) throws Throwable {
// 在Web开发中,service层或者某个工具类中需要获取到HttpServletRequest对象还是比较常见的。
// 一种方式是将HttpServletRequest作为方法的参数从controller层一直放下传递,
// 不过这种有点费劲,且做起来不是优雅;
//还有另一种则是RequestContextHolder,直接在需要用的地方使用如下方式
//HttpServletRequest即可,使用代码如下:
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getRequest();
// 检查用户所传递的 token 是否合法
String token = getUserToken(request);
if (!token.equalsIgnoreCase("123456")) {
return "错误, 权限不合法!";
}
// 如果授权是 true 则 调用 方法( joinPoint.proceed(); )
return joinPoint.proceed();
}
private String getUserToken(HttpServletRequest request) {
Cookie[] cookies = request.getCookies();
if (cookies == null) {
return "";
}
for (Cookie cookie : cookies) {
if (cookie.getName().equalsIgnoreCase("user_token")) {
return cookie.getValue();
}
}
return "";
}
}
测试:
加上cookie之后:
解决中文???问题
在配置文件中的mvc:annotation-driven中添加如下代码:
<mvc:annotation-driven >
<!-- 消息转换器 -->
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes" value="text/html;charset=UTF-8"/>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
有什么乱七八糟的地方找不到类,试试这个: