Commit d836622b authored by James's avatar James
Browse files

修改 Copyright 日期,换行字符由 \r\n 改为 \n

parent 5649336b
Showing with 2125 additions and 2093 deletions
+2125 -2093
/**
* Copyright (c) 2011-2021, James Zhan 詹波 (jfinal@126.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jfinal.aop;
/**
* Aop 支持在任意时空便捷使用 Aop
*
* Aop 主要功能:
* 1:Aop.get(Class) 根据 Class 去创建对象,然后对创建好的对象进行依赖注入
*
* 2:Aop.inject(Object) 对传入的对象进行依赖注入
*
* 3:Aop.inject(...) 与 Aop.get(...) 的区别是前者只针对传入的对象之中的属性进行注入。
* 而后者先要使用 Class 去创建对象,创建完对象以后对该对象之中的属性进行注入。
* 简单一句话:get(...) 比 inject(...) 多了一个目标对象的创建过程
*
* 4:AopManager.me().setSingleton(...) 用于配置默认是否为单例
*
* 5:在目标类上使用注解 Singleton 可以覆盖掉上面 setSingleton(...) 方法配置的默认值
*
*
* 基本用法:
* 1:先定义业务
* public class Service {
* @Inject
* OtherService otherSrv;
*
* @Before(Aaa.class)
* public void doIt() {
* ...
* }
* }
*
* public class OtherService {
* @Before(Bbb.class)
* public void doOther() {
* ...
* }
* }
*
*
* 2:只进行注入,对象自己创建
* Service srv = Aop.inject(new Service());
* srv.doIt();
* 由于 Service 对象是 new 出来的,不会被 AOP 代理,所以其 doIt() 方法上的 Aaa 拦截器并不会生效
* Aop.inject(...) 会对 OtherService otherSrv 进行注入,并且对 otherSrv 进行 AOP 代理,
* 所以 OtherService.doOther() 方法上的 Bbb 拦截器会生效
*
* 3:创建对象并注入
* Service srv = Aop.get(Service.class);
* srv.doIt();
* Aop.get(...) 用法对 OtherService otherSrv 的处理方式完全一样,在此基础之上 Service 自身也会被
* AOP 代理,所以 doIt() 上的 Aaa 拦截器会生效
*
* 4:小结:对象的创建交给 Aop 而不是自己 new 出来,所创建的对象才能被 AOP 代理,其上的拦截器才能生效
*
*
* 高级用法:
* 1:@Inject 注解默认注入属性自身类型的对象,可以通过如下代码指定被注入的类型:
* @Inject(UserServiceImpl.class) // 此处的 UserServiceImpl 为 UserService 的子类或实现类
* UserService userService;
*
* 2:被注入对象默认是 singleton 单例,可以通过 AopManager.me().setSingleton(false) 配置默认不为单例
*
* 3:可以在目标类中中直接配置注解 Singleton:
* @Singleton(false)
* public class MyService {...}
*
* 注意:以上代码中的注解会覆盖掉 2 中 setSingleton() 方法配置的默认值
*
* 4:如上 2、3 中的配置,建议的用法是:先用 setSingleton() 配置大多数情况,然后在个别
* 违反上述配置的情况下使用 Singleton 注解来覆盖默认配置,这样可以节省大量代码
*/
public class Aop {
static AopFactory aopFactory = new AopFactory();
public static <T> T get(Class<T> targetClass) {
return aopFactory.get(targetClass);
}
public static <T> T inject(T targetObject) {
return aopFactory.inject(targetObject);
}
/* 通过 AopManager.me().getAopFactory().inject(...) 可调用如下方法,不直接开放出来
public static <T> T inject(Class<T> targetClass, T targetObject) {
return aopFactory.inject(targetClass, targetObject);
}*/
}
/**
* Copyright (c) 2011-2023, James Zhan 詹波 (jfinal@126.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jfinal.aop;
/**
* Aop 支持在任意时空便捷使用 Aop
*
* Aop 主要功能:
* 1:Aop.get(Class) 根据 Class 去创建对象,然后对创建好的对象进行依赖注入
*
* 2:Aop.inject(Object) 对传入的对象进行依赖注入
*
* 3:Aop.inject(...) 与 Aop.get(...) 的区别是前者只针对传入的对象之中的属性进行注入。
* 而后者先要使用 Class 去创建对象,创建完对象以后对该对象之中的属性进行注入。
* 简单一句话:get(...) 比 inject(...) 多了一个目标对象的创建过程
*
* 4:AopManager.me().setSingleton(...) 用于配置默认是否为单例
*
* 5:在目标类上使用注解 Singleton 可以覆盖掉上面 setSingleton(...) 方法配置的默认值
*
*
* 基本用法:
* 1:先定义业务
* public class Service {
* @Inject
* OtherService otherSrv;
*
* @Before(Aaa.class)
* public void doIt() {
* ...
* }
* }
*
* public class OtherService {
* @Before(Bbb.class)
* public void doOther() {
* ...
* }
* }
*
*
* 2:只进行注入,对象自己创建
* Service srv = Aop.inject(new Service());
* srv.doIt();
* 由于 Service 对象是 new 出来的,不会被 AOP 代理,所以其 doIt() 方法上的 Aaa 拦截器并不会生效
* Aop.inject(...) 会对 OtherService otherSrv 进行注入,并且对 otherSrv 进行 AOP 代理,
* 所以 OtherService.doOther() 方法上的 Bbb 拦截器会生效
*
* 3:创建对象并注入
* Service srv = Aop.get(Service.class);
* srv.doIt();
* Aop.get(...) 用法对 OtherService otherSrv 的处理方式完全一样,在此基础之上 Service 自身也会被
* AOP 代理,所以 doIt() 上的 Aaa 拦截器会生效
*
* 4:小结:对象的创建交给 Aop 而不是自己 new 出来,所创建的对象才能被 AOP 代理,其上的拦截器才能生效
*
*
* 高级用法:
* 1:@Inject 注解默认注入属性自身类型的对象,可以通过如下代码指定被注入的类型:
* @Inject(UserServiceImpl.class) // 此处的 UserServiceImpl 为 UserService 的子类或实现类
* UserService userService;
*
* 2:被注入对象默认是 singleton 单例,可以通过 AopManager.me().setSingleton(false) 配置默认不为单例
*
* 3:可以在目标类中中直接配置注解 Singleton:
* @Singleton(false)
* public class MyService {...}
*
* 注意:以上代码中的注解会覆盖掉 2 中 setSingleton() 方法配置的默认值
*
* 4:如上 2、3 中的配置,建议的用法是:先用 setSingleton() 配置大多数情况,然后在个别
* 违反上述配置的情况下使用 Singleton 注解来覆盖默认配置,这样可以节省大量代码
*/
public class Aop {
static AopFactory aopFactory = new AopFactory();
public static <T> T get(Class<T> targetClass) {
return aopFactory.get(targetClass);
}
public static <T> T inject(T targetObject) {
return aopFactory.inject(targetObject);
}
/* 通过 AopManager.me().getAopFactory().inject(...) 可调用如下方法,不直接开放出来
public static <T> T inject(Class<T> targetClass, T targetObject) {
return aopFactory.inject(targetClass, targetObject);
}*/
}
/**
* Copyright (c) 2011-2021, James Zhan 詹波 (jfinal@126.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jfinal.aop;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
import com.jfinal.core.Controller;
import com.jfinal.plugin.activerecord.Model;
import com.jfinal.proxy.Proxy;
import com.jfinal.validate.Validator;
/**
* AopFactory 是工具类 Aop 功能的具体实现,详细用法见 Aop
*/
public class AopFactory {
// 单例缓存
protected ConcurrentHashMap<Class<?>, Object> singletonCache = new ConcurrentHashMap<Class<?>, Object>();
// 支持循环注入
protected ThreadLocal<HashMap<Class<?>, Object>> singletonTl = ThreadLocal.withInitial(() -> new HashMap<>());
protected ThreadLocal<HashMap<Class<?>, Object>> prototypeTl = ThreadLocal.withInitial(() -> new HashMap<>());
// 父类到子类、接口到实现类之间的映射关系
protected HashMap<Class<?>, Class<?>> mapping = null;
protected boolean singleton = true; // 默认单例
protected boolean injectSuperClass = false; // 默认不对超类进行注入
public <T> T get(Class<T> targetClass) {
try {
return doGet(targetClass);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
@SuppressWarnings("unchecked")
protected <T> T doGet(Class<T> targetClass) throws ReflectiveOperationException {
// Aop.get(obj.getClass()) 可以用 Aop.inject(obj),所以注掉下一行代码
// targetClass = (Class<T>)getUsefulClass(targetClass);
targetClass = (Class<T>)getMappingClass(targetClass);
Singleton si = targetClass.getAnnotation(Singleton.class);
boolean singleton = (si != null ? si.value() : this.singleton);
if (singleton) {
return doGetSingleton(targetClass);
} else {
return doGetPrototype(targetClass);
}
}
@SuppressWarnings("unchecked")
protected <T> T doGetSingleton(Class<T> targetClass) throws ReflectiveOperationException {
Object ret = singletonCache.get(targetClass);
if (ret != null) {
return (T)ret;
}
HashMap<Class<?>, Object> map = singletonTl.get();
int size = map.size();
if (size > 0) {
ret = map.get(targetClass);
if (ret != null) { // 发现循环注入
return (T)ret;
}
}
synchronized (this) {
try {
ret = singletonCache.get(targetClass);
if (ret == null) {
ret = createObject(targetClass);
map.put(targetClass, ret);
doInject(targetClass, ret);
singletonCache.put(targetClass, ret);
}
return (T)ret;
} finally {
if (size == 0) { // 仅顶层才需要 remove()
singletonTl.remove();
}
}
}
}
@SuppressWarnings("unchecked")
protected <T> T doGetPrototype(Class<T> targetClass) throws ReflectiveOperationException {
Object ret;
HashMap<Class<?>, Object> map = prototypeTl.get();
int size = map.size();
if (size > 0) {
ret = map.get(targetClass);
if (ret != null) { // 发现循环注入
// return (T)ret;
return (T)createObject(targetClass);
}
}
try {
ret = createObject(targetClass);
map.put(targetClass, ret);
doInject(targetClass, ret);
return (T)ret;
} finally {
if (size == 0) { // 仅顶层才需要 clear()
map.clear();
}
}
}
public <T> T inject(T targetObject) {
try {
doInject(targetObject.getClass(), targetObject);
return targetObject;
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
// 方法原型的参数测试过可以是:Class<? super T> targetClass, T targetObject
public <T> T inject(Class<T> targetClass, T targetObject) {
try {
doInject(targetClass, targetObject);
return targetObject;
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
protected void doInject(Class<?> targetClass, Object targetObject) throws ReflectiveOperationException {
targetClass = getUsefulClass(targetClass);
Field[] fields = targetClass.getDeclaredFields();
if (fields.length != 0) {
for (Field field : fields) {
Inject inject = field.getAnnotation(Inject.class);
if (inject == null) {
continue ;
}
Class<?> fieldInjectedClass = inject.value();
if (fieldInjectedClass == Void.class) {
fieldInjectedClass = field.getType();
}
Object fieldInjectedObject = doGet(fieldInjectedClass);
field.setAccessible(true);
field.set(targetObject, fieldInjectedObject);
}
}
// 是否对超类进行注入
if (injectSuperClass) {
Class<?> c = targetClass.getSuperclass();
if (c != Controller.class && c != Object.class && c != Validator.class && c != Model.class && c != null) {
doInject(c, targetObject);
}
}
}
protected Object createObject(Class<?> targetClass) throws ReflectiveOperationException {
return Proxy.get(targetClass);
}
/**
* 被 cglib、guice 增强过的类需要通过本方法获取到被增强之前的类型
* 否则调用其 targetClass.getDeclaredFields() 方法时
* 获取到的是一堆 cglib guice 生成类中的 Field 对象
* 而被增强前的原类型中的 Field 反而获取不到
*/
protected Class<?> getUsefulClass(Class<?> clazz) {
// com.demo.blog.Blog$$EnhancerByCGLIB$$69a17158
// return (Class<? extends Model>)((modelClass.getName().indexOf("EnhancerByCGLIB") == -1 ? modelClass : modelClass.getSuperclass()));
return (Class<?>)(clazz.getName().indexOf("$$EnhancerBy") == -1 ? clazz : clazz.getSuperclass());
}
/**
* 设置被注入的对象是否为单例,可使用 @Singleton(boolean) 覆盖此默认值
*/
public AopFactory setSingleton(boolean singleton) {
this.singleton = singleton;
return this;
}
public boolean isSingleton() {
return singleton;
}
/**
* 设置是否对超类进行注入
*/
public AopFactory setInjectSuperClass(boolean injectSuperClass) {
this.injectSuperClass = injectSuperClass;
return this;
}
public boolean isInjectSuperClass() {
return injectSuperClass;
}
public AopFactory addSingletonObject(Class<?> type, Object singletonObject) {
if (type == null) {
throw new IllegalArgumentException("type can not be null");
}
if (singletonObject == null) {
throw new IllegalArgumentException("singletonObject can not be null");
}
if (singletonObject instanceof Class) {
throw new IllegalArgumentException("singletonObject can not be Class type");
}
if ( ! (type.isAssignableFrom(singletonObject.getClass())) ) {
throw new IllegalArgumentException(singletonObject.getClass().getName() + " can not cast to " + type.getName());
}
// Class<?> type = getUsefulClass(singletonObject.getClass());
if (singletonCache.putIfAbsent(type, singletonObject) != null) {
throw new RuntimeException("Singleton object already exists for type : " + type.getName());
}
return this;
}
public AopFactory addSingletonObject(Object singletonObject) {
Class<?> type = getUsefulClass(singletonObject.getClass());
return addSingletonObject(type, singletonObject);
}
public synchronized <T> AopFactory addMapping(Class<T> from, Class<? extends T> to) {
if (from == null || to == null) {
throw new IllegalArgumentException("The parameter from and to can not be null");
}
if (mapping == null) {
mapping = new HashMap<Class<?>, Class<?>>(128, 0.25F);
} else if (mapping.containsKey(from)) {
throw new RuntimeException("Class already mapped : " + from.getName());
}
mapping.put(from, to);
return this;
}
public <T> AopFactory addMapping(Class<T> from, String to) {
try {
@SuppressWarnings("unchecked")
Class<T> toClass = (Class<T>)Class.forName(to.trim());
if (from.isAssignableFrom(toClass)) {
return addMapping(from, toClass);
} else {
throw new IllegalArgumentException("The parameter \"to\" must be the subclass or implementation of the parameter \"from\"");
}
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
/**
* 获取父类到子类的映射值,或者接口到实现类的映射值
* @param from 父类或者接口
* @return 如果映射存在则返回映射值,否则返回参数 from 的值
*/
public Class<?> getMappingClass(Class<?> from) {
if (mapping != null) {
Class<?> ret = mapping.get(from);
return ret != null ? ret : from;
} else {
return from;
}
}
}
/**
* Copyright (c) 2011-2023, James Zhan 詹波 (jfinal@126.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jfinal.aop;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
import com.jfinal.core.Controller;
import com.jfinal.plugin.activerecord.Model;
import com.jfinal.proxy.Proxy;
import com.jfinal.validate.Validator;
/**
* AopFactory 是工具类 Aop 功能的具体实现,详细用法见 Aop
*/
public class AopFactory {
// 单例缓存
protected ConcurrentHashMap<Class<?>, Object> singletonCache = new ConcurrentHashMap<Class<?>, Object>();
// 支持循环注入
protected ThreadLocal<HashMap<Class<?>, Object>> singletonTl = ThreadLocal.withInitial(() -> new HashMap<>());
protected ThreadLocal<HashMap<Class<?>, Object>> prototypeTl = ThreadLocal.withInitial(() -> new HashMap<>());
// 父类到子类、接口到实现类之间的映射关系
protected HashMap<Class<?>, Class<?>> mapping = null;
protected boolean singleton = true; // 默认单例
protected boolean injectSuperClass = false; // 默认不对超类进行注入
public <T> T get(Class<T> targetClass) {
try {
return doGet(targetClass);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
@SuppressWarnings("unchecked")
protected <T> T doGet(Class<T> targetClass) throws ReflectiveOperationException {
// Aop.get(obj.getClass()) 可以用 Aop.inject(obj),所以注掉下一行代码
// targetClass = (Class<T>)getUsefulClass(targetClass);
targetClass = (Class<T>)getMappingClass(targetClass);
Singleton si = targetClass.getAnnotation(Singleton.class);
boolean singleton = (si != null ? si.value() : this.singleton);
if (singleton) {
return doGetSingleton(targetClass);
} else {
return doGetPrototype(targetClass);
}
}
@SuppressWarnings("unchecked")
protected <T> T doGetSingleton(Class<T> targetClass) throws ReflectiveOperationException {
Object ret = singletonCache.get(targetClass);
if (ret != null) {
return (T)ret;
}
HashMap<Class<?>, Object> map = singletonTl.get();
int size = map.size();
if (size > 0) {
ret = map.get(targetClass);
if (ret != null) { // 发现循环注入
return (T)ret;
}
}
synchronized (this) {
try {
ret = singletonCache.get(targetClass);
if (ret == null) {
ret = createObject(targetClass);
map.put(targetClass, ret);
doInject(targetClass, ret);
singletonCache.put(targetClass, ret);
}
return (T)ret;
} finally {
if (size == 0) { // 仅顶层才需要 remove()
singletonTl.remove();
}
}
}
}
@SuppressWarnings("unchecked")
protected <T> T doGetPrototype(Class<T> targetClass) throws ReflectiveOperationException {
Object ret;
HashMap<Class<?>, Object> map = prototypeTl.get();
int size = map.size();
if (size > 0) {
ret = map.get(targetClass);
if (ret != null) { // 发现循环注入
// return (T)ret;
return (T)createObject(targetClass);
}
}
try {
ret = createObject(targetClass);
map.put(targetClass, ret);
doInject(targetClass, ret);
return (T)ret;
} finally {
if (size == 0) { // 仅顶层才需要 clear()
map.clear();
}
}
}
public <T> T inject(T targetObject) {
try {
doInject(targetObject.getClass(), targetObject);
return targetObject;
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
// 方法原型的参数测试过可以是:Class<? super T> targetClass, T targetObject
public <T> T inject(Class<T> targetClass, T targetObject) {
try {
doInject(targetClass, targetObject);
return targetObject;
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
protected void doInject(Class<?> targetClass, Object targetObject) throws ReflectiveOperationException {
targetClass = getUsefulClass(targetClass);
Field[] fields = targetClass.getDeclaredFields();
if (fields.length != 0) {
for (Field field : fields) {
Inject inject = field.getAnnotation(Inject.class);
if (inject == null) {
continue ;
}
Class<?> fieldInjectedClass = inject.value();
if (fieldInjectedClass == Void.class) {
fieldInjectedClass = field.getType();
}
Object fieldInjectedObject = doGet(fieldInjectedClass);
field.setAccessible(true);
field.set(targetObject, fieldInjectedObject);
}
}
// 是否对超类进行注入
if (injectSuperClass) {
Class<?> c = targetClass.getSuperclass();
if (c != Controller.class && c != Object.class && c != Validator.class && c != Model.class && c != null) {
doInject(c, targetObject);
}
}
}
protected Object createObject(Class<?> targetClass) throws ReflectiveOperationException {
return Proxy.get(targetClass);
}
/**
* 被 cglib、guice 增强过的类需要通过本方法获取到被增强之前的类型
* 否则调用其 targetClass.getDeclaredFields() 方法时
* 获取到的是一堆 cglib guice 生成类中的 Field 对象
* 而被增强前的原类型中的 Field 反而获取不到
*/
protected Class<?> getUsefulClass(Class<?> clazz) {
// com.demo.blog.Blog$$EnhancerByCGLIB$$69a17158
// return (Class<? extends Model>)((modelClass.getName().indexOf("EnhancerByCGLIB") == -1 ? modelClass : modelClass.getSuperclass()));
return (Class<?>)(clazz.getName().indexOf("$$EnhancerBy") == -1 ? clazz : clazz.getSuperclass());
}
/**
* 设置被注入的对象是否为单例,可使用 @Singleton(boolean) 覆盖此默认值
*/
public AopFactory setSingleton(boolean singleton) {
this.singleton = singleton;
return this;
}
public boolean isSingleton() {
return singleton;
}
/**
* 设置是否对超类进行注入
*/
public AopFactory setInjectSuperClass(boolean injectSuperClass) {
this.injectSuperClass = injectSuperClass;
return this;
}
public boolean isInjectSuperClass() {
return injectSuperClass;
}
public AopFactory addSingletonObject(Class<?> type, Object singletonObject) {
if (type == null) {
throw new IllegalArgumentException("type can not be null");
}
if (singletonObject == null) {
throw new IllegalArgumentException("singletonObject can not be null");
}
if (singletonObject instanceof Class) {
throw new IllegalArgumentException("singletonObject can not be Class type");
}
if ( ! (type.isAssignableFrom(singletonObject.getClass())) ) {
throw new IllegalArgumentException(singletonObject.getClass().getName() + " can not cast to " + type.getName());
}
// Class<?> type = getUsefulClass(singletonObject.getClass());
if (singletonCache.putIfAbsent(type, singletonObject) != null) {
throw new RuntimeException("Singleton object already exists for type : " + type.getName());
}
return this;
}
public AopFactory addSingletonObject(Object singletonObject) {
Class<?> type = getUsefulClass(singletonObject.getClass());
return addSingletonObject(type, singletonObject);
}
public synchronized <T> AopFactory addMapping(Class<T> from, Class<? extends T> to) {
if (from == null || to == null) {
throw new IllegalArgumentException("The parameter from and to can not be null");
}
if (mapping == null) {
mapping = new HashMap<Class<?>, Class<?>>(128, 0.25F);
} else if (mapping.containsKey(from)) {
throw new RuntimeException("Class already mapped : " + from.getName());
}
mapping.put(from, to);
return this;
}
public <T> AopFactory addMapping(Class<T> from, String to) {
try {
@SuppressWarnings("unchecked")
Class<T> toClass = (Class<T>)Class.forName(to.trim());
if (from.isAssignableFrom(toClass)) {
return addMapping(from, toClass);
} else {
throw new IllegalArgumentException("The parameter \"to\" must be the subclass or implementation of the parameter \"from\"");
}
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
/**
* 获取父类到子类的映射值,或者接口到实现类的映射值
* @param from 父类或者接口
* @return 如果映射存在则返回映射值,否则返回参数 from 的值
*/
public Class<?> getMappingClass(Class<?> from) {
if (mapping != null) {
Class<?> ret = mapping.get(from);
return ret != null ? ret : from;
} else {
return from;
}
}
}
/**
* Copyright (c) 2011-2021, James Zhan 詹波 (jfinal@126.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jfinal.aop;
import com.jfinal.core.Const;
/**
* AopManager
*/
public class AopManager {
private boolean injectDependency = Const.DEFAULT_INJECT_DEPENDENCY;
private static final AopManager me = new AopManager();
private AopManager() {}
public static AopManager me() {
return me;
}
/**
* 设置对 Controller、Interceptor、Validator 进行依赖注入,默认值为 false
*
* 被注入对象默认为 singleton,可以通过 AopManager.me().setSingleton(boolean) 配置
* 该默认值。
*
* 也可通过在被注入的目标类上使用 Singleton 注解覆盖上述默认值,注解配置
* 优先级高于默认配置
*
* 注意:该配置仅针对于配置 jfinal web。而 Aop.get(...)、Aop.inject(...) 默认就会进行注入,
* 无需配置
*/
public void setInjectDependency(boolean injectDependency) {
this.injectDependency = injectDependency;
}
public boolean isInjectDependency() {
return injectDependency;
}
/**
* 设置是否对超类进行注入
*/
public void setInjectSuperClass(boolean injectSuperClass) {
Aop.aopFactory.setInjectSuperClass(injectSuperClass);
}
public boolean isInjectSuperClass() {
return Aop.aopFactory.isInjectSuperClass();
}
/**
* 添加单例对象
*
* 由于 Aop 创建对象时不支持为构造方法传递参数,故添加此方法
*
* <pre>
* 示例:
* // Service 类的构造方法中传入了两个参数
* Service service = new Service(paraAaa, paraBbb);
* AopManager.me().addSingletonObject(Service.class, service);
*
* // 上面代码添加完成以后,可以在任何地方通过下面的方式获取单例对象
* service = Aop.get(Service.class);
*
* // 被添加进去的对象还可以用于注入
* @Inject
* Service service;
*
* // 在添加为单例对象之前还可以先为其注入依赖对象
* Service service = new Service(paraAaa, paraBbb);
* Aop.inject(service); // 这里是对 Service 进行依赖注入
* AopManager.me().addSingletonObject(Service.class, service);
* </pre>
*/
public void addSingletonObject(Class<?> type, Object singletonObject) {
Aop.aopFactory.addSingletonObject(type, singletonObject);
}
public void addSingletonObject(Object singletonObject) {
Aop.aopFactory.addSingletonObject(singletonObject);
}
/**
* 添加父类到子类的映射,或者接口到实现类的映射。
*
* 该方法用于为父类、抽象类、或者接口注入子类或者实现类
*
* <pre>
* 示例:
* // 定义接口
* public interface IService {
* public void justDoIt();
* }
*
* // 定义接口的实现类
* public class MyService implements IService {
* public void justDoIt() {
* ...
* }
* }
*
* // 添加接口与实现类的映射关系
* AopManager.me().addMapping(IService.class, MyService.class)
*
* public class MyController {
*
* // 由于前面添加了接口与实现类的关系,所以下面将被注入实现类 MyService 对象
* @Inject
* IService service
*
* public action() {
* service.justDoIt();
* }
* }
*
* 如上所示,通过建立了 IService 与 MyService 的映射关系,在 @Inject 注入的时候
* 就会注入映射好的实现类,当然也可以通过在 @Inject 注解中指定实现类来实现:
*
* @Inject(MyService.class)
* IService service
*
* 但是上面的的方法是写死在代码中的,不方便改变实现类
*
* </pre>
*
* @param from 父类或者接口
* @param to 父类的子类或者接口的实现类
*/
public <T> void addMapping(Class<T> from, Class<? extends T> to) {
Aop.aopFactory.addMapping(from, to);
}
/**
* 功能与 addMapping(Class<T> from, Class<? extends T> to) 相同,仅仅是第二个参数
* 由 Class 改为 String 类型,便于从外部配置文件传递 String 参数过来
*
* <pre>
* 示例:
* AopManager.me().addMapping(IService.class, "com.xxx.MyService")
*
* 以上代码的参数 "com.xxx.MyService" 可通过外部配置文件传入,便于通过配置文件切换接口的
* 实现类:
* AopManager.me().addMapping(IService.class, PropKit.get("ServiceImpl"));
*
* </pre>
*/
public <T> void addMapping(Class<T> from, String to) {
Aop.aopFactory.addMapping(from, to);
}
/**
* 设置 AopFactory,便于扩展自己的 AopFactory 实现
*/
public void setAopFactory(AopFactory aopFactory) {
if (aopFactory == null) {
throw new IllegalArgumentException("aopFactory can not be null");
}
Aop.aopFactory = aopFactory;
}
public AopFactory getAopFactory() {
return Aop.aopFactory;
}
/**
* 设置被注入的对象是否为单例,可在目标类上使用 @Singleton(boolean) 覆盖此默认值
*/
public void setSingleton(boolean singleton) {
Aop.aopFactory.setSingleton(singleton);
}
public boolean isSingleton() {
return Aop.aopFactory.isSingleton();
}
}
/**
* Copyright (c) 2011-2023, James Zhan 詹波 (jfinal@126.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jfinal.aop;
import com.jfinal.core.Const;
/**
* AopManager
*/
public class AopManager {
private boolean injectDependency = Const.DEFAULT_INJECT_DEPENDENCY;
private static final AopManager me = new AopManager();
private AopManager() {}
public static AopManager me() {
return me;
}
/**
* 设置对 Controller、Interceptor、Validator 进行依赖注入,默认值为 false
*
* 被注入对象默认为 singleton,可以通过 AopManager.me().setSingleton(boolean) 配置
* 该默认值。
*
* 也可通过在被注入的目标类上使用 Singleton 注解覆盖上述默认值,注解配置
* 优先级高于默认配置
*
* 注意:该配置仅针对于配置 jfinal web。而 Aop.get(...)、Aop.inject(...) 默认就会进行注入,
* 无需配置
*/
public void setInjectDependency(boolean injectDependency) {
this.injectDependency = injectDependency;
}
public boolean isInjectDependency() {
return injectDependency;
}
/**
* 设置是否对超类进行注入
*/
public void setInjectSuperClass(boolean injectSuperClass) {
Aop.aopFactory.setInjectSuperClass(injectSuperClass);
}
public boolean isInjectSuperClass() {
return Aop.aopFactory.isInjectSuperClass();
}
/**
* 添加单例对象
*
* 由于 Aop 创建对象时不支持为构造方法传递参数,故添加此方法
*
* <pre>
* 示例:
* // Service 类的构造方法中传入了两个参数
* Service service = new Service(paraAaa, paraBbb);
* AopManager.me().addSingletonObject(Service.class, service);
*
* // 上面代码添加完成以后,可以在任何地方通过下面的方式获取单例对象
* service = Aop.get(Service.class);
*
* // 被添加进去的对象还可以用于注入
* @Inject
* Service service;
*
* // 在添加为单例对象之前还可以先为其注入依赖对象
* Service service = new Service(paraAaa, paraBbb);
* Aop.inject(service); // 这里是对 Service 进行依赖注入
* AopManager.me().addSingletonObject(Service.class, service);
* </pre>
*/
public void addSingletonObject(Class<?> type, Object singletonObject) {
Aop.aopFactory.addSingletonObject(type, singletonObject);
}
public void addSingletonObject(Object singletonObject) {
Aop.aopFactory.addSingletonObject(singletonObject);
}
/**
* 添加父类到子类的映射,或者接口到实现类的映射。
*
* 该方法用于为父类、抽象类、或者接口注入子类或者实现类
*
* <pre>
* 示例:
* // 定义接口
* public interface IService {
* public void justDoIt();
* }
*
* // 定义接口的实现类
* public class MyService implements IService {
* public void justDoIt() {
* ...
* }
* }
*
* // 添加接口与实现类的映射关系
* AopManager.me().addMapping(IService.class, MyService.class)
*
* public class MyController {
*
* // 由于前面添加了接口与实现类的关系,所以下面将被注入实现类 MyService 对象
* @Inject
* IService service
*
* public action() {
* service.justDoIt();
* }
* }
*
* 如上所示,通过建立了 IService 与 MyService 的映射关系,在 @Inject 注入的时候
* 就会注入映射好的实现类,当然也可以通过在 @Inject 注解中指定实现类来实现:
*
* @Inject(MyService.class)
* IService service
*
* 但是上面的的方法是写死在代码中的,不方便改变实现类
*
* </pre>
*
* @param from 父类或者接口
* @param to 父类的子类或者接口的实现类
*/
public <T> void addMapping(Class<T> from, Class<? extends T> to) {
Aop.aopFactory.addMapping(from, to);
}
/**
* 功能与 addMapping(Class<T> from, Class<? extends T> to) 相同,仅仅是第二个参数
* 由 Class 改为 String 类型,便于从外部配置文件传递 String 参数过来
*
* <pre>
* 示例:
* AopManager.me().addMapping(IService.class, "com.xxx.MyService")
*
* 以上代码的参数 "com.xxx.MyService" 可通过外部配置文件传入,便于通过配置文件切换接口的
* 实现类:
* AopManager.me().addMapping(IService.class, PropKit.get("ServiceImpl"));
*
* </pre>
*/
public <T> void addMapping(Class<T> from, String to) {
Aop.aopFactory.addMapping(from, to);
}
/**
* 设置 AopFactory,便于扩展自己的 AopFactory 实现
*/
public void setAopFactory(AopFactory aopFactory) {
if (aopFactory == null) {
throw new IllegalArgumentException("aopFactory can not be null");
}
Aop.aopFactory = aopFactory;
}
public AopFactory getAopFactory() {
return Aop.aopFactory;
}
/**
* 设置被注入的对象是否为单例,可在目标类上使用 @Singleton(boolean) 覆盖此默认值
*/
public void setSingleton(boolean singleton) {
Aop.aopFactory.setSingleton(singleton);
}
public boolean isSingleton() {
return Aop.aopFactory.isSingleton();
}
}
/**
* Copyright (c) 2011-2021, James Zhan 詹波 (jfinal@126.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jfinal.aop;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Before is used to configure Interceptor and Validator.
*/
@Inherited
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Before {
Class<? extends Interceptor>[] value();
}
/**
* Copyright (c) 2011-2023, James Zhan 詹波 (jfinal@126.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jfinal.aop;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Before is used to configure Interceptor and Validator.
*/
@Inherited
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Before {
Class<? extends Interceptor>[] value();
}
/**
* Copyright (c) 2011-2021, James Zhan 詹波 (jfinal@126.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jfinal.aop;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Clear is used to clear all interceptors or the specified interceptors,
* it can not clear the interceptor which declare on method.
*
* <pre>
* Example:
* 1: clear all interceptors but InterA and InterB will be kept, because InterA and InterB declare on method
* @Clear
* @Before({InterA.class, InterB.class})
* public void method(...)
*
* 2: clear InterA and InterB, InterC and InterD will be kept
* @Clear({InterA.class, InterB.class})
* @Before({InterC.class, InterD.class})
* public void method(...)
* </pre>
*/
@Inherited
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Clear {
Class<? extends Interceptor>[] value() default {};
}
/**
* Copyright (c) 2011-2023, James Zhan 詹波 (jfinal@126.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jfinal.aop;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Clear is used to clear all interceptors or the specified interceptors,
* it can not clear the interceptor which declare on method.
*
* <pre>
* Example:
* 1: clear all interceptors but InterA and InterB will be kept, because InterA and InterB declare on method
* @Clear
* @Before({InterA.class, InterB.class})
* public void method(...)
*
* 2: clear InterA and InterB, InterC and InterD will be kept
* @Clear({InterA.class, InterB.class})
* @Before({InterC.class, InterD.class})
* public void method(...)
* </pre>
*/
@Inherited
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Clear {
Class<? extends Interceptor>[] value() default {};
}
/**
* Copyright (c) 2011-2021, James Zhan 詹波 (jfinal@126.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jfinal.aop;
import com.jfinal.proxy.Proxy;
/**
* duang duang duang
*
* <pre>
* 自 jfinal 3.5 开始,新增了更强大的 Aop 工具,建议使用 Aop.get(...) 以及
* Aop.inject(...) 来代替 Duang 的功能
*
* 下一个版本所有 Aop 功能将会被 Aop.java 取代,并且为了拦截器的整体缓存不会再支持
* Inject Interceptor 参数,所以删除了 Duang 中所有带 injectInters 参数
* 的方法
*
* 下一个版本的 Singleton 判别将由 @Singleton 注解以及 AopFactory 中的默认值决定,
* 所以删除了 Duang 中所有带 singletonKey 参数的方法
* </pre>
*/
public class Duang {
private Duang() {}
public static <T> T duang(Class<T> targetClass) {
// return (T)Enhancer.enhance(targetClass);
return Proxy.get(targetClass);
}
/**
* 下一个版本的 aop 将不再支持 inject interceptor,所以本方法被 Deprecated
@Deprecated
public static <T> T duang(Class<T> targetClass, Interceptor... injectInters) {
return (T)Enhancer.enhance(targetClass, injectInters);
} */
}
/**
* Copyright (c) 2011-2023, James Zhan 詹波 (jfinal@126.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jfinal.aop;
import com.jfinal.proxy.Proxy;
/**
* duang duang duang
*
* <pre>
* 自 jfinal 3.5 开始,新增了更强大的 Aop 工具,建议使用 Aop.get(...) 以及
* Aop.inject(...) 来代替 Duang 的功能
*
* 下一个版本所有 Aop 功能将会被 Aop.java 取代,并且为了拦截器的整体缓存不会再支持
* Inject Interceptor 参数,所以删除了 Duang 中所有带 injectInters 参数
* 的方法
*
* 下一个版本的 Singleton 判别将由 @Singleton 注解以及 AopFactory 中的默认值决定,
* 所以删除了 Duang 中所有带 singletonKey 参数的方法
* </pre>
*/
public class Duang {
private Duang() {}
public static <T> T duang(Class<T> targetClass) {
// return (T)Enhancer.enhance(targetClass);
return Proxy.get(targetClass);
}
/**
* 下一个版本的 aop 将不再支持 inject interceptor,所以本方法被 Deprecated
@Deprecated
public static <T> T duang(Class<T> targetClass, Interceptor... injectInters) {
return (T)Enhancer.enhance(targetClass, injectInters);
} */
}
/**
* Copyright (c) 2011-2021, James Zhan 詹波 (jfinal@126.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jfinal.aop;
import com.jfinal.proxy.Proxy;
/**
* Enhancer
*
* <pre>
* 自 jfinal 3.5 开始,新增了更强大的 Aop 工具,建议使用 Aop.get(...) 以及
* Aop.inject(...) 来代替 Enhancer 的功能
*
* 下一个版本所有 Aop 功能将会被 Aop.java 取代,并且为了拦截器的整体缓存不会再支持
* Inject Interceptor 参数,所以删除了 Enhancer 中所有带 injectInters 参数
* 的方法
*
* 下一个版本的 Singleton 判别将由 @Singleton 注解以及 AopFactory 中的默认值决定,
* 所以删除了 Enhancer 中所有带 singletonKey 参数的方法
* </pre>
*/
@Deprecated
public class Enhancer {
private Enhancer() {}
public static <T> T enhance(Class<T> targetClass) {
// return (T)net.sf.cglib.proxy.Enhancer.create(targetClass, new Callback());
return Proxy.get(targetClass);
}
/**
* 下一个版本的 aop 将不再支持 inject interceptor,所以本方法被 Deprecated
@Deprecated
public static <T> T enhance(Class<T> targetClass, Interceptor... injectInters) {
return (T)net.sf.cglib.proxy.Enhancer.create(targetClass, new Callback(injectInters));
} */
}
/**
* Copyright (c) 2011-2023, James Zhan 詹波 (jfinal@126.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jfinal.aop;
import com.jfinal.proxy.Proxy;
/**
* Enhancer
*
* <pre>
* 自 jfinal 3.5 开始,新增了更强大的 Aop 工具,建议使用 Aop.get(...) 以及
* Aop.inject(...) 来代替 Enhancer 的功能
*
* 下一个版本所有 Aop 功能将会被 Aop.java 取代,并且为了拦截器的整体缓存不会再支持
* Inject Interceptor 参数,所以删除了 Enhancer 中所有带 injectInters 参数
* 的方法
*
* 下一个版本的 Singleton 判别将由 @Singleton 注解以及 AopFactory 中的默认值决定,
* 所以删除了 Enhancer 中所有带 singletonKey 参数的方法
* </pre>
*/
@Deprecated
public class Enhancer {
private Enhancer() {}
public static <T> T enhance(Class<T> targetClass) {
// return (T)net.sf.cglib.proxy.Enhancer.create(targetClass, new Callback());
return Proxy.get(targetClass);
}
/**
* 下一个版本的 aop 将不再支持 inject interceptor,所以本方法被 Deprecated
@Deprecated
public static <T> T enhance(Class<T> targetClass, Interceptor... injectInters) {
return (T)net.sf.cglib.proxy.Enhancer.create(targetClass, new Callback(injectInters));
} */
}
package com.jfinal.aop;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Inject is used to inject dependent object
*/
@Inherited
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface Inject {
Class<?> value() default Void.class; // 被注入类的类型
}
/**
* Copyright (c) 2011-2023, James Zhan 詹波 (jfinal@126.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jfinal.aop;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Inject is used to inject dependent object
*/
@Inherited
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface Inject {
Class<?> value() default Void.class; // 被注入类的类型
}
/**
* Copyright (c) 2011-2021, James Zhan 詹波 (jfinal@126.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jfinal.aop;
/**
* Interceptor.
*/
public interface Interceptor {
void intercept(Invocation inv);
}
/**
* Copyright (c) 2011-2023, James Zhan 詹波 (jfinal@126.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jfinal.aop;
/**
* Interceptor.
*/
public interface Interceptor {
void intercept(Invocation inv);
}
/**
* Copyright (c) 2011-2021, James Zhan 詹波 (jfinal@126.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jfinal.aop;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import com.jfinal.core.Controller;
/**
* InterceptorManager.
* 1:管理控制层、业务层全局拦截器
* 2:缓存业务层 Class 级拦截器数组。业务层拦截器被整体缓存在 ProxyMethod 中
* 3:用于创建 Interceptor、组装 Interceptor
* 4:除手动 new 出来的拦截器以外,其它所有拦截器均为单例
*
* 无法使用 Method 或 Before 对象缓存业务层 Method 级拦截器:
* 1:不同对象或相同对象获取同一个 Class 中同一个 Method 得到的对象 id 值不相同
* 2:不同对象获取同一个 method 之上的 Before 得到的对象 id 值不相同
*/
public class InterceptorManager {
public static final Interceptor[] NULL_INTERS = new Interceptor[0];
// 控制层与业务层全局拦截器
private Interceptor[] globalActionInters = NULL_INTERS;
private Interceptor[] globalServiceInters = NULL_INTERS;
// 单例拦截器
private final ConcurrentHashMap<Class<? extends Interceptor>, Interceptor> singletonMap = new ConcurrentHashMap<Class<? extends Interceptor>, Interceptor>(32, 0.5F);
// 业务层 Class 级别拦截器缓存
private final ConcurrentHashMap<Class<?>, Interceptor[]> serviceClassInters = new ConcurrentHashMap<Class<?>, Interceptor[]>(32, 0.5F);
private static final InterceptorManager me = new InterceptorManager();
private InterceptorManager() {}
public static InterceptorManager me() {
return me;
}
// 此处不缓存控制层 Class 级拦截器,已经在 com.jfinal.core.Action 对象中缓存
public Interceptor[] createControllerInterceptor(Class<? extends Controller> controllerClass) {
return createInterceptor(controllerClass.getAnnotation(Before.class));
}
// 缓存业务层 Class 级拦截器
public Interceptor[] createServiceInterceptor(Class<?> serviceClass) {
Interceptor[] result = serviceClassInters.get(serviceClass);
if (result == null) {
result = createInterceptor(serviceClass.getAnnotation(Before.class));
serviceClassInters.put(serviceClass, result);
}
return result;
}
public Interceptor[] buildControllerActionInterceptor(Interceptor[] routesInters, Interceptor[] classInters, Class<? extends Controller> controllerClass, Method method) {
return doBuild(globalActionInters, routesInters, classInters, controllerClass, method);
}
public Interceptor[] buildServiceMethodInterceptor(Class<?> serviceClass, Method method) {
return doBuild(globalServiceInters, NULL_INTERS, createServiceInterceptor(serviceClass), serviceClass, method);
}
private Interceptor[] doBuild(Interceptor[] globalInters, Interceptor[] routesInters, Interceptor[] classInters, Class<?> targetClass, Method method) {
Interceptor[] methodInters = createInterceptor(method.getAnnotation(Before.class));
Class<? extends Interceptor>[] clearIntersOnMethod;
Clear clearOnMethod = method.getAnnotation(Clear.class);
if (clearOnMethod != null) {
clearIntersOnMethod = clearOnMethod.value();
if (clearIntersOnMethod.length == 0) { // method 级 @Clear 且不带参
return methodInters;
}
} else {
clearIntersOnMethod = null;
}
Class<? extends Interceptor>[] clearIntersOnClass;
Clear clearOnClass = targetClass.getAnnotation(Clear.class);
if (clearOnClass != null) {
clearIntersOnClass = clearOnClass.value();
if (clearIntersOnClass.length == 0) { // class 级 @clear 且不带参
globalInters = NULL_INTERS;
routesInters = NULL_INTERS;
}
} else {
clearIntersOnClass = null;
}
ArrayList<Interceptor> result = new ArrayList<Interceptor>(globalInters.length + routesInters.length + classInters.length + methodInters.length);
for (Interceptor inter : globalInters) {
result.add(inter);
}
for (Interceptor inter : routesInters) {
result.add(inter);
}
if (clearIntersOnClass != null && clearIntersOnClass.length > 0) {
removeInterceptor(result, clearIntersOnClass);
}
for (Interceptor inter : classInters) {
result.add(inter);
}
if (clearIntersOnMethod != null && clearIntersOnMethod.length > 0) {
removeInterceptor(result, clearIntersOnMethod);
}
for (Interceptor inter : methodInters) {
result.add(inter);
}
return result.toArray(new Interceptor[result.size()]);
}
private void removeInterceptor(ArrayList<Interceptor> target, Class<? extends Interceptor>[] clearInters) {
for (Iterator<Interceptor> it = target.iterator(); it.hasNext();) {
Interceptor curInter = it.next();
if (curInter != null) {
Class<? extends Interceptor> curInterClass = curInter.getClass();
for (Class<? extends Interceptor> ci : clearInters) {
if (curInterClass == ci) {
it.remove();
break;
}
}
} else {
it.remove();
}
}
}
public Interceptor[] createInterceptor(Before beforeAnnotation) {
if (beforeAnnotation == null) {
return NULL_INTERS;
}
return createInterceptor(beforeAnnotation.value());
}
public Interceptor[] createInterceptor(Class<? extends Interceptor>[] interceptorClasses) {
if (interceptorClasses == null || interceptorClasses.length == 0) {
return NULL_INTERS;
}
Interceptor[] result = new Interceptor[interceptorClasses.length];
try {
for (int i=0; i<result.length; i++) {
result[i] = singletonMap.get(interceptorClasses[i]);
if (result[i] == null) {
// 此处不能使用 Aop.get(...),避免生成代理类
result[i] = (Interceptor)interceptorClasses[i].newInstance();
if (AopManager.me().isInjectDependency()) {
Aop.inject(result[i]);
}
singletonMap.put(interceptorClasses[i], result[i]);
}
}
return result;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public void addGlobalActionInterceptor(Interceptor... inters) {
addGlobalInterceptor(true, inters);
}
public void addGlobalServiceInterceptor(Interceptor... inters) {
addGlobalInterceptor(false, inters);
}
private synchronized void addGlobalInterceptor(boolean forAction, Interceptor... inters) {
if (inters == null || inters.length == 0) {
throw new IllegalArgumentException("interceptors can not be null.");
}
for (Interceptor inter : inters) {
if (inter == null) {
throw new IllegalArgumentException("interceptor can not be null.");
}
if (singletonMap.containsKey(inter.getClass())) {
throw new IllegalArgumentException("interceptor already exists, interceptor must be singlton, do not create more then one instance of the same Interceptor Class.");
}
}
for (Interceptor inter : inters) {
if (AopManager.me().isInjectDependency()) {
Aop.inject(inter);
}
singletonMap.put(inter.getClass(), inter);
}
Interceptor[] globalInters = forAction ? globalActionInters : globalServiceInters;
Interceptor[] temp = new Interceptor[globalInters.length + inters.length];
System.arraycopy(globalInters, 0, temp, 0, globalInters.length);
System.arraycopy(inters, 0, temp, globalInters.length, inters.length);
if (forAction) {
globalActionInters = temp;
} else {
globalServiceInters = temp;
}
}
public java.util.List<Class<?>> getGlobalServiceInterceptorClasses() {
ArrayList<Class<?>> ret = new ArrayList<>(globalServiceInters.length + 3);
for (Interceptor i : globalServiceInters) {
ret.add(i.getClass());
}
return ret;
}
}
/**
* Copyright (c) 2011-2023, James Zhan 詹波 (jfinal@126.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jfinal.aop;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import com.jfinal.core.Controller;
/**
* InterceptorManager.
* 1:管理控制层、业务层全局拦截器
* 2:缓存业务层 Class 级拦截器数组。业务层拦截器被整体缓存在 ProxyMethod 中
* 3:用于创建 Interceptor、组装 Interceptor
* 4:除手动 new 出来的拦截器以外,其它所有拦截器均为单例
*
* 无法使用 Method 或 Before 对象缓存业务层 Method 级拦截器:
* 1:不同对象或相同对象获取同一个 Class 中同一个 Method 得到的对象 id 值不相同
* 2:不同对象获取同一个 method 之上的 Before 得到的对象 id 值不相同
*/
public class InterceptorManager {
public static final Interceptor[] NULL_INTERS = new Interceptor[0];
// 控制层与业务层全局拦截器
private Interceptor[] globalActionInters = NULL_INTERS;
private Interceptor[] globalServiceInters = NULL_INTERS;
// 单例拦截器
private final ConcurrentHashMap<Class<? extends Interceptor>, Interceptor> singletonMap = new ConcurrentHashMap<Class<? extends Interceptor>, Interceptor>(32, 0.5F);
// 业务层 Class 级别拦截器缓存
private final ConcurrentHashMap<Class<?>, Interceptor[]> serviceClassInters = new ConcurrentHashMap<Class<?>, Interceptor[]>(32, 0.5F);
private static final InterceptorManager me = new InterceptorManager();
private InterceptorManager() {}
public static InterceptorManager me() {
return me;
}
// 此处不缓存控制层 Class 级拦截器,已经在 com.jfinal.core.Action 对象中缓存
public Interceptor[] createControllerInterceptor(Class<? extends Controller> controllerClass) {
return createInterceptor(controllerClass.getAnnotation(Before.class));
}
// 缓存业务层 Class 级拦截器
public Interceptor[] createServiceInterceptor(Class<?> serviceClass) {
Interceptor[] result = serviceClassInters.get(serviceClass);
if (result == null) {
result = createInterceptor(serviceClass.getAnnotation(Before.class));
serviceClassInters.put(serviceClass, result);
}
return result;
}
public Interceptor[] buildControllerActionInterceptor(Interceptor[] routesInters, Interceptor[] classInters, Class<? extends Controller> controllerClass, Method method) {
return doBuild(globalActionInters, routesInters, classInters, controllerClass, method);
}
public Interceptor[] buildServiceMethodInterceptor(Class<?> serviceClass, Method method) {
return doBuild(globalServiceInters, NULL_INTERS, createServiceInterceptor(serviceClass), serviceClass, method);
}
private Interceptor[] doBuild(Interceptor[] globalInters, Interceptor[] routesInters, Interceptor[] classInters, Class<?> targetClass, Method method) {
Interceptor[] methodInters = createInterceptor(method.getAnnotation(Before.class));
Class<? extends Interceptor>[] clearIntersOnMethod;
Clear clearOnMethod = method.getAnnotation(Clear.class);
if (clearOnMethod != null) {
clearIntersOnMethod = clearOnMethod.value();
if (clearIntersOnMethod.length == 0) { // method 级 @Clear 且不带参
return methodInters;
}
} else {
clearIntersOnMethod = null;
}
Class<? extends Interceptor>[] clearIntersOnClass;
Clear clearOnClass = targetClass.getAnnotation(Clear.class);
if (clearOnClass != null) {
clearIntersOnClass = clearOnClass.value();
if (clearIntersOnClass.length == 0) { // class 级 @clear 且不带参
globalInters = NULL_INTERS;
routesInters = NULL_INTERS;
}
} else {
clearIntersOnClass = null;
}
ArrayList<Interceptor> result = new ArrayList<Interceptor>(globalInters.length + routesInters.length + classInters.length + methodInters.length);
for (Interceptor inter : globalInters) {
result.add(inter);
}
for (Interceptor inter : routesInters) {
result.add(inter);
}
if (clearIntersOnClass != null && clearIntersOnClass.length > 0) {
removeInterceptor(result, clearIntersOnClass);
}
for (Interceptor inter : classInters) {
result.add(inter);
}
if (clearIntersOnMethod != null && clearIntersOnMethod.length > 0) {
removeInterceptor(result, clearIntersOnMethod);
}
for (Interceptor inter : methodInters) {
result.add(inter);
}
return result.toArray(new Interceptor[result.size()]);
}
private void removeInterceptor(ArrayList<Interceptor> target, Class<? extends Interceptor>[] clearInters) {
for (Iterator<Interceptor> it = target.iterator(); it.hasNext();) {
Interceptor curInter = it.next();
if (curInter != null) {
Class<? extends Interceptor> curInterClass = curInter.getClass();
for (Class<? extends Interceptor> ci : clearInters) {
if (curInterClass == ci) {
it.remove();
break;
}
}
} else {
it.remove();
}
}
}
public Interceptor[] createInterceptor(Before beforeAnnotation) {
if (beforeAnnotation == null) {
return NULL_INTERS;
}
return createInterceptor(beforeAnnotation.value());
}
public Interceptor[] createInterceptor(Class<? extends Interceptor>[] interceptorClasses) {
if (interceptorClasses == null || interceptorClasses.length == 0) {
return NULL_INTERS;
}
Interceptor[] result = new Interceptor[interceptorClasses.length];
try {
for (int i=0; i<result.length; i++) {
result[i] = singletonMap.get(interceptorClasses[i]);
if (result[i] == null) {
// 此处不能使用 Aop.get(...),避免生成代理类
result[i] = (Interceptor)interceptorClasses[i].newInstance();
if (AopManager.me().isInjectDependency()) {
Aop.inject(result[i]);
}
singletonMap.put(interceptorClasses[i], result[i]);
}
}
return result;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public void addGlobalActionInterceptor(Interceptor... inters) {
addGlobalInterceptor(true, inters);
}
public void addGlobalServiceInterceptor(Interceptor... inters) {
addGlobalInterceptor(false, inters);
}
private synchronized void addGlobalInterceptor(boolean forAction, Interceptor... inters) {
if (inters == null || inters.length == 0) {
throw new IllegalArgumentException("interceptors can not be null.");
}
for (Interceptor inter : inters) {
if (inter == null) {
throw new IllegalArgumentException("interceptor can not be null.");
}
if (singletonMap.containsKey(inter.getClass())) {
throw new IllegalArgumentException("interceptor already exists, interceptor must be singlton, do not create more then one instance of the same Interceptor Class.");
}
}
for (Interceptor inter : inters) {
if (AopManager.me().isInjectDependency()) {
Aop.inject(inter);
}
singletonMap.put(inter.getClass(), inter);
}
Interceptor[] globalInters = forAction ? globalActionInters : globalServiceInters;
Interceptor[] temp = new Interceptor[globalInters.length + inters.length];
System.arraycopy(globalInters, 0, temp, 0, globalInters.length);
System.arraycopy(inters, 0, temp, globalInters.length, inters.length);
if (forAction) {
globalActionInters = temp;
} else {
globalServiceInters = temp;
}
}
public java.util.List<Class<?>> getGlobalServiceInterceptorClasses() {
ArrayList<Class<?>> ret = new ArrayList<>(globalServiceInters.length + 3);
for (Interceptor i : globalServiceInters) {
ret.add(i.getClass());
}
return ret;
}
}
/**
* Copyright (c) 2011-2021, James Zhan 詹波 (jfinal@126.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jfinal.aop;
import java.util.ArrayList;
import java.util.List;
/**
* InterceptorStack.
*/
public abstract class InterceptorStack implements Interceptor {
private Interceptor[] inters;
private List<Interceptor> interList;
public InterceptorStack() {
config();
if (interList == null)
throw new RuntimeException("You must invoke addInterceptors(...) to config your InterceptorStack");
inters = interList.toArray(new Interceptor[interList.size()]);
interList.clear();
interList = null;
}
protected InterceptorStack addInterceptors(Interceptor... interceptors) {
if (interceptors == null || interceptors.length == 0) {
throw new IllegalArgumentException("Interceptors can not be null");
}
if (interList == null) {
interList = new ArrayList<Interceptor>();
}
for (Interceptor ref : interceptors) {
if (AopManager.me().isInjectDependency()) {
Aop.inject(ref);
}
interList.add(ref);
}
return this;
}
public final void intercept(Invocation inv) {
new InvocationWrapper(inv, inters).invoke();
}
public abstract void config();
}
/**
* Copyright (c) 2011-2023, James Zhan 詹波 (jfinal@126.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jfinal.aop;
import java.util.ArrayList;
import java.util.List;
/**
* InterceptorStack.
*/
public abstract class InterceptorStack implements Interceptor {
private Interceptor[] inters;
private List<Interceptor> interList;
public InterceptorStack() {
config();
if (interList == null)
throw new RuntimeException("You must invoke addInterceptors(...) to config your InterceptorStack");
inters = interList.toArray(new Interceptor[interList.size()]);
interList.clear();
interList = null;
}
protected InterceptorStack addInterceptors(Interceptor... interceptors) {
if (interceptors == null || interceptors.length == 0) {
throw new IllegalArgumentException("Interceptors can not be null");
}
if (interList == null) {
interList = new ArrayList<Interceptor>();
}
for (Interceptor ref : interceptors) {
if (AopManager.me().isInjectDependency()) {
Aop.inject(ref);
}
interList.add(ref);
}
return this;
}
public final void intercept(Invocation inv) {
new InvocationWrapper(inv, inters).invoke();
}
public abstract void config();
}
/**
* Copyright (c) 2011-2021, James Zhan 詹波 (jfinal@126.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jfinal.aop;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import com.jfinal.proxy.Callback;
import com.jfinal.proxy.ProxyMethod;
import com.jfinal.proxy.ProxyMethodCache;
import com.jfinal.core.Action;
import com.jfinal.core.Controller;
/**
* Invocation is used to invoke the interceptors and the target method
*/
@SuppressWarnings("unchecked")
public class Invocation {
private static final Object[] NULL_ARGS = new Object[0]; // Prevent new Object[0] by jvm for args of method invoking
private Action action;
private Object target;
private Method method;
private Object[] args;
private Callback callback;
private Interceptor[] inters;
private Object returnValue;
private int index = 0;
public Invocation(Object target, Long proxyMethodKey, Callback callback, Object... args) {
this.action = null;
this.target = target;
ProxyMethod proxyMethod = ProxyMethodCache.get(proxyMethodKey);
this.method = proxyMethod.getMethod();
this.inters = proxyMethod.getInterceptors();
this.callback = callback;
this.args = args;
}
public Invocation(Object target, Long proxyMethodKey, Callback callback) {
this(target, proxyMethodKey, callback, NULL_ARGS);
}
/**
* 用于扩展 ProxyFactory
*/
public Invocation(Object target, Method method, Interceptor[] inters, Callback callback, Object[] args) {
this.action = null;
this.target = target;
this.method = method;
this.inters = inters;
this.callback = callback;
this.args = args;
}
// InvocationWrapper need this constructor
protected Invocation() {
this.action = null;
}
public Invocation(Action action, Controller controller) {
this.action = action;
this.inters = action.getInterceptors();
this.target = controller;
// this.args = NULL_ARGS;
this.args = action.getParameterGetter().get(action, controller);
}
public void invoke() {
if (index < inters.length) {
inters[index++].intercept(this);
}
else if (index++ == inters.length) { // index++ ensure invoke action only one time
try {
// Invoke the action
if (action != null) {
returnValue = action.getMethod().invoke(target, args);
}
// Invoke the callback
else {
returnValue = callback.call(args);
}
}
catch (InvocationTargetException e) {
Throwable t = e.getTargetException();
if (t == null) {t = e;}
throw t instanceof RuntimeException ? (RuntimeException)t : new RuntimeException(t);
}
catch (RuntimeException e) {
throw e;
}
catch (Throwable t) {
throw new RuntimeException(t);
}
}
}
public Object getArg(int index) {
if (index >= args.length)
throw new ArrayIndexOutOfBoundsException();
return args[index];
}
public void setArg(int index, Object value) {
if (index >= args.length)
throw new ArrayIndexOutOfBoundsException();
args[index] = value;
}
public Object[] getArgs() {
return args;
}
/**
* Get the target object which be intercepted
* <pre>
* Example:
* OrderService os = getTarget();
* </pre>
*/
public <T> T getTarget() {
return (T)target;
}
/**
* Return the method of this action.
* <p>
* You can getMethod.getAnnotations() to get annotation on action method to do more things
*/
public Method getMethod() {
if (action != null)
return action.getMethod();
return method;
}
/**
* Return the method name of this action's method.
*/
public String getMethodName() {
if (action != null)
return action.getMethodName();
return method.getName();
}
/**
* Get the return value of the target method
*/
public <T> T getReturnValue() {
return (T)returnValue;
}
/**
* Set the return value of the target method
*/
public void setReturnValue(Object returnValue) {
this.returnValue = returnValue;
}
// ---------
/**
* Return the controller of this action.
*/
public Controller getController() {
if (action == null)
throw new RuntimeException("This method can only be used for action interception");
return (Controller)target;
}
/**
* Return the action key.
* actionKey = controllerPath + methodName
*/
public String getActionKey() {
if (action == null)
throw new RuntimeException("This method can only be used for action interception");
return action.getActionKey();
}
/**
* Return the controller path.
*/
public String getControllerPath() {
if (action == null)
throw new RuntimeException("This method can only be used for action interception");
return action.getControllerPath();
}
/**
* 该方法已改名为 getControllerPath()
*/
@Deprecated
public String getControllerKey() {
return getControllerPath();
}
/**
* Return view path of this controller.
*/
public String getViewPath() {
if (action == null)
throw new RuntimeException("This method can only be used for action interception");
return action.getViewPath();
}
/**
* return true if it is action invocation.
*/
public boolean isActionInvocation() {
return action != null;
}
}
/**
* Copyright (c) 2011-2023, James Zhan 詹波 (jfinal@126.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jfinal.aop;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import com.jfinal.proxy.Callback;
import com.jfinal.proxy.ProxyMethod;
import com.jfinal.proxy.ProxyMethodCache;
import com.jfinal.core.Action;
import com.jfinal.core.Controller;
/**
* Invocation is used to invoke the interceptors and the target method
*/
@SuppressWarnings("unchecked")
public class Invocation {
private static final Object[] NULL_ARGS = new Object[0]; // Prevent new Object[0] by jvm for args of method invoking
private Action action;
private Object target;
private Method method;
private Object[] args;
private Callback callback;
private Interceptor[] inters;
private Object returnValue;
private int index = 0;
public Invocation(Object target, Long proxyMethodKey, Callback callback, Object... args) {
this.action = null;
this.target = target;
ProxyMethod proxyMethod = ProxyMethodCache.get(proxyMethodKey);
this.method = proxyMethod.getMethod();
this.inters = proxyMethod.getInterceptors();
this.callback = callback;
this.args = args;
}
public Invocation(Object target, Long proxyMethodKey, Callback callback) {
this(target, proxyMethodKey, callback, NULL_ARGS);
}
/**
* 用于扩展 ProxyFactory
*/
public Invocation(Object target, Method method, Interceptor[] inters, Callback callback, Object[] args) {
this.action = null;
this.target = target;
this.method = method;
this.inters = inters;
this.callback = callback;
this.args = args;
}
// InvocationWrapper need this constructor
protected Invocation() {
this.action = null;
}
public Invocation(Action action, Controller controller) {
this.action = action;
this.inters = action.getInterceptors();
this.target = controller;
// this.args = NULL_ARGS;
this.args = action.getParameterGetter().get(action, controller);
}
public void invoke() {
if (index < inters.length) {
inters[index++].intercept(this);
}
else if (index++ == inters.length) { // index++ ensure invoke action only one time
try {
// Invoke the action
if (action != null) {
returnValue = action.getMethod().invoke(target, args);
}
// Invoke the callback
else {
returnValue = callback.call(args);
}
}
catch (InvocationTargetException e) {
Throwable t = e.getTargetException();
if (t == null) {t = e;}
throw t instanceof RuntimeException ? (RuntimeException)t : new RuntimeException(t);
}
catch (RuntimeException e) {
throw e;
}
catch (Throwable t) {
throw new RuntimeException(t);
}
}
}
public Object getArg(int index) {
if (index >= args.length)
throw new ArrayIndexOutOfBoundsException();
return args[index];
}
public void setArg(int index, Object value) {
if (index >= args.length)
throw new ArrayIndexOutOfBoundsException();
args[index] = value;
}
public Object[] getArgs() {
return args;
}
/**
* Get the target object which be intercepted
* <pre>
* Example:
* OrderService os = getTarget();
* </pre>
*/
public <T> T getTarget() {
return (T)target;
}
/**
* Return the method of this action.
* <p>
* You can getMethod.getAnnotations() to get annotation on action method to do more things
*/
public Method getMethod() {
if (action != null)
return action.getMethod();
return method;
}
/**
* Return the method name of this action's method.
*/
public String getMethodName() {
if (action != null)
return action.getMethodName();
return method.getName();
}
/**
* Get the return value of the target method
*/
public <T> T getReturnValue() {
return (T)returnValue;
}
/**
* Set the return value of the target method
*/
public void setReturnValue(Object returnValue) {
this.returnValue = returnValue;
}
// ---------
/**
* Return the controller of this action.
*/
public Controller getController() {
if (action == null)
throw new RuntimeException("This method can only be used for action interception");
return (Controller)target;
}
/**
* Return the action key.
* actionKey = controllerPath + methodName
*/
public String getActionKey() {
if (action == null)
throw new RuntimeException("This method can only be used for action interception");
return action.getActionKey();
}
/**
* Return the controller path.
*/
public String getControllerPath() {
if (action == null)
throw new RuntimeException("This method can only be used for action interception");
return action.getControllerPath();
}
/**
* 该方法已改名为 getControllerPath()
*/
@Deprecated
public String getControllerKey() {
return getControllerPath();
}
/**
* Return view path of this controller.
*/
public String getViewPath() {
if (action == null)
throw new RuntimeException("This method can only be used for action interception");
return action.getViewPath();
}
/**
* return true if it is action invocation.
*/
public boolean isActionInvocation() {
return action != null;
}
}
/**
* Copyright (c) 2011-2021, James Zhan 詹波 (jfinal@126.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jfinal.aop;
import java.lang.reflect.Method;
import com.jfinal.core.Controller;
/**
* InvocationWrapper invoke the InterceptorStack.
*/
class InvocationWrapper extends Invocation {
private Interceptor[] inters;
private Invocation invocation;
private int index = 0;
InvocationWrapper(Invocation invocation, Interceptor[] inters) {
this.invocation = invocation;
this.inters = inters;
}
/**
* Invoke the action
*/
@Override
public final void invoke() {
if (index < inters.length)
inters[index++].intercept(this);
else if (index++ == inters.length)
invocation.invoke();
}
@Override
public Object getArg(int index) {
return invocation.getArg(index);
}
@Override
public Object[] getArgs() {
return invocation.getArgs();
}
@Override
public void setArg(int index, Object value) {
invocation.setArg(index, value);
}
/**
* Get the target object which be intercepted
* <pre>
* Example:
* OrderService os = getTarget();
* </pre>
*/
@SuppressWarnings("unchecked")
@Override
public <T> T getTarget() {
return (T)invocation.getTarget();
}
/**
* Return the method of this action.
* <p>
* You can getMethod.getAnnotations() to get annotation on action method to do more things
*/
@Override
public Method getMethod() {
return invocation.getMethod();
}
/**
* Return the method name of this action's method.
*/
@Override
public String getMethodName() {
return invocation.getMethodName();
}
/**
* Get the return value of the target method
*/
@SuppressWarnings("unchecked")
@Override
public <T> T getReturnValue() {
return (T)invocation.getReturnValue();
}
/**
* Set the return value of the target method
*/
@Override
public void setReturnValue(Object returnValue) {
invocation.setReturnValue(returnValue);
}
// ---------
/**
* Return the controller of this action.
*/
@Override
public Controller getController() {
return invocation.getController();
}
/**
* Return the action key.
* actionKey = controllerPath + methodName
*/
@Override
public String getActionKey() {
return invocation.getActionKey();
}
/**
* Return the controller path.
*/
@Override
public String getControllerPath() {
return invocation.getControllerPath();
}
/**
* Return view path of this controller.
*/
@Override
public String getViewPath() {
return invocation.getViewPath();
}
@Override
public boolean isActionInvocation() {
return invocation.isActionInvocation();
}
/*
* It should be added method below when com.jfinal.aop.Invocation add method, otherwise null will be returned.
*/
}
/**
* Copyright (c) 2011-2023, James Zhan 詹波 (jfinal@126.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jfinal.aop;
import java.lang.reflect.Method;
import com.jfinal.core.Controller;
/**
* InvocationWrapper invoke the InterceptorStack.
*/
class InvocationWrapper extends Invocation {
private Interceptor[] inters;
private Invocation invocation;
private int index = 0;
InvocationWrapper(Invocation invocation, Interceptor[] inters) {
this.invocation = invocation;
this.inters = inters;
}
/**
* Invoke the action
*/
@Override
public final void invoke() {
if (index < inters.length)
inters[index++].intercept(this);
else if (index++ == inters.length)
invocation.invoke();
}
@Override
public Object getArg(int index) {
return invocation.getArg(index);
}
@Override
public Object[] getArgs() {
return invocation.getArgs();
}
@Override
public void setArg(int index, Object value) {
invocation.setArg(index, value);
}
/**
* Get the target object which be intercepted
* <pre>
* Example:
* OrderService os = getTarget();
* </pre>
*/
@SuppressWarnings("unchecked")
@Override
public <T> T getTarget() {
return (T)invocation.getTarget();
}
/**
* Return the method of this action.
* <p>
* You can getMethod.getAnnotations() to get annotation on action method to do more things
*/
@Override
public Method getMethod() {
return invocation.getMethod();
}
/**
* Return the method name of this action's method.
*/
@Override
public String getMethodName() {
return invocation.getMethodName();
}
/**
* Get the return value of the target method
*/
@SuppressWarnings("unchecked")
@Override
public <T> T getReturnValue() {
return (T)invocation.getReturnValue();
}
/**
* Set the return value of the target method
*/
@Override
public void setReturnValue(Object returnValue) {
invocation.setReturnValue(returnValue);
}
// ---------
/**
* Return the controller of this action.
*/
@Override
public Controller getController() {
return invocation.getController();
}
/**
* Return the action key.
* actionKey = controllerPath + methodName
*/
@Override
public String getActionKey() {
return invocation.getActionKey();
}
/**
* Return the controller path.
*/
@Override
public String getControllerPath() {
return invocation.getControllerPath();
}
/**
* Return view path of this controller.
*/
@Override
public String getViewPath() {
return invocation.getViewPath();
}
@Override
public boolean isActionInvocation() {
return invocation.isActionInvocation();
}
/*
* It should be added method below when com.jfinal.aop.Invocation add method, otherwise null will be returned.
*/
}
/**
* Copyright (c) 2011-2021, James Zhan 詹波 (jfinal@126.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jfinal.aop;
/**
* PrototypeInterceptor.
*/
public abstract class PrototypeInterceptor implements Interceptor {
final public void intercept(Invocation inv) {
try {
getClass().newInstance().doIntercept(inv);
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
abstract public void doIntercept(Invocation inv);
}
/**
* Copyright (c) 2011-2023, James Zhan 詹波 (jfinal@126.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jfinal.aop;
/**
* PrototypeInterceptor.
*/
public abstract class PrototypeInterceptor implements Interceptor {
final public void intercept(Invocation inv) {
try {
getClass().newInstance().doIntercept(inv);
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
abstract public void doIntercept(Invocation inv);
}
package com.jfinal.aop;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Singleton 用于配置被注入对象是否为单例
*/
@Inherited
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface Singleton {
boolean value(); // 是否单例
}
/**
* Copyright (c) 2011-2023, James Zhan 詹波 (jfinal@126.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jfinal.aop;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Singleton 用于配置被注入对象是否为单例
*/
@Inherited
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface Singleton {
boolean value(); // 是否单例
}
/**
* Copyright (c) 2011-2021, James Zhan 詹波 (jfinal@126.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jfinal.captcha;
import java.io.Serializable;
/**
* 验证码
*/
public class Captcha implements Serializable {
private static final long serialVersionUID = -2593323370708163022L;
/**
* 验证码默认过期时长 180 秒
*/
public static final int DEFAULT_EXPIRE_TIME = 180;
/**
* 验证码 key,存放在 cookie,或者表单隐藏域中返回给客户端
*/
private String key;
/**
* 验证码值
*/
private String value;
/**
* 验证码过期时间
*/
private long expireAt;
/**
* 验证码构造
* @param key
* @param value
* @param expireTime 过期时长,单位为秒
*/
public Captcha(String key, String value, int expireTime) {
if (key == null || value == null) {
throw new IllegalArgumentException("key and value can not be null");
}
this.key = key;
this.value = value;
long et = expireTime;
this.expireAt = et * 1000 + System.currentTimeMillis();
}
public Captcha(String key, String value) {
this(key, value, DEFAULT_EXPIRE_TIME);
}
/**
* redis 反序列化需要默认构造方法
*/
public Captcha() {
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public long getExpireAt() {
return expireAt;
}
public void setExpireAt(long expireAt) {
this.expireAt = expireAt;
}
public boolean isExpired() {
return expireAt < System.currentTimeMillis();
}
public boolean notExpired() {
return !isExpired();
}
public String toString() {
return key + " : " + value;
}
}
/**
* Copyright (c) 2011-2023, James Zhan 詹波 (jfinal@126.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jfinal.captcha;
import java.io.Serializable;
/**
* 验证码
*/
public class Captcha implements Serializable {
private static final long serialVersionUID = -2593323370708163022L;
/**
* 验证码默认过期时长 180 秒
*/
public static final int DEFAULT_EXPIRE_TIME = 180;
/**
* 验证码 key,存放在 cookie,或者表单隐藏域中返回给客户端
*/
private String key;
/**
* 验证码值
*/
private String value;
/**
* 验证码过期时间
*/
private long expireAt;
/**
* 验证码构造
* @param key
* @param value
* @param expireTime 过期时长,单位为秒
*/
public Captcha(String key, String value, int expireTime) {
if (key == null || value == null) {
throw new IllegalArgumentException("key and value can not be null");
}
this.key = key;
this.value = value;
long et = expireTime;
this.expireAt = et * 1000 + System.currentTimeMillis();
}
public Captcha(String key, String value) {
this(key, value, DEFAULT_EXPIRE_TIME);
}
/**
* redis 反序列化需要默认构造方法
*/
public Captcha() {
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public long getExpireAt() {
return expireAt;
}
public void setExpireAt(long expireAt) {
this.expireAt = expireAt;
}
public boolean isExpired() {
return expireAt < System.currentTimeMillis();
}
public boolean notExpired() {
return !isExpired();
}
public String toString() {
return key + " : " + value;
}
}
/**
* Copyright (c) 2011-2021, James Zhan 詹波 (jfinal@126.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jfinal.captcha;
import java.util.Map.Entry;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
/**
* ICaptchaCache 默认实现,可用于单实例部署
* 集群部署需自行实现 ICaptchaCache 接口,并使用
* CaptchaManager.setCaptchaCache(...) 进行配置
*/
public class CaptchaCache implements ICaptchaCache {
private ConcurrentHashMap<String, Captcha> map = new ConcurrentHashMap<String, Captcha>();
private int interval = 90 * 1000; // timer 调度间隔为 90 秒
private Timer timer;
public CaptchaCache() {
autoRemoveExpiredCaptcha();
}
/**
* 定期移除过期的验证码
*/
private void autoRemoveExpiredCaptcha() {
timer = new Timer("CaptchaCache", true);
timer.schedule(
new TimerTask() {
public void run() {
for (Entry<String, Captcha> e : map.entrySet()) {
if (e.getValue().isExpired()) {
map.remove(e.getKey());
}
}
}
},
interval,
interval
);
}
public void put(Captcha captcha) {
map.put(captcha.getKey(), captcha);
}
public Captcha get(String key) {
return key != null ? map.get(key) : null;
}
public void remove(String key) {
map.remove(key);
}
public void removeAll() {
map.clear();
}
public boolean contains(String key) {
return map.containsKey(key);
}
}
/**
* Copyright (c) 2011-2023, James Zhan 詹波 (jfinal@126.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jfinal.captcha;
import java.util.Map.Entry;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
/**
* ICaptchaCache 默认实现,可用于单实例部署
* 集群部署需自行实现 ICaptchaCache 接口,并使用
* CaptchaManager.setCaptchaCache(...) 进行配置
*/
public class CaptchaCache implements ICaptchaCache {
private ConcurrentHashMap<String, Captcha> map = new ConcurrentHashMap<String, Captcha>();
private int interval = 90 * 1000; // timer 调度间隔为 90 秒
private Timer timer;
public CaptchaCache() {
autoRemoveExpiredCaptcha();
}
/**
* 定期移除过期的验证码
*/
private void autoRemoveExpiredCaptcha() {
timer = new Timer("CaptchaCache", true);
timer.schedule(
new TimerTask() {
public void run() {
for (Entry<String, Captcha> e : map.entrySet()) {
if (e.getValue().isExpired()) {
map.remove(e.getKey());
}
}
}
},
interval,
interval
);
}
public void put(Captcha captcha) {
map.put(captcha.getKey(), captcha);
}
public Captcha get(String key) {
return key != null ? map.get(key) : null;
}
public void remove(String key) {
map.remove(key);
}
public void removeAll() {
map.clear();
}
public boolean contains(String key) {
return map.containsKey(key);
}
}
/**
* Copyright (c) 2011-2021, James Zhan 詹波 (jfinal@126.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jfinal.captcha;
/**
* CaptchaManager
*/
public class CaptchaManager {
private static final CaptchaManager me = new CaptchaManager();
private volatile ICaptchaCache captchaCache = null;
private CaptchaManager() {}
public static CaptchaManager me() {
return me;
}
public void setCaptchaCache(ICaptchaCache captchaCache) {
if (captchaCache == null) {
throw new IllegalArgumentException("captchaCache can not be null");
}
this.captchaCache = captchaCache;
}
public ICaptchaCache getCaptchaCache() {
if (captchaCache == null) {
synchronized (this) {
if (captchaCache == null) {
captchaCache = new CaptchaCache();
}
}
}
return captchaCache;
}
}
/**
* Copyright (c) 2011-2023, James Zhan 詹波 (jfinal@126.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jfinal.captcha;
/**
* CaptchaManager
*/
public class CaptchaManager {
private static final CaptchaManager me = new CaptchaManager();
private volatile ICaptchaCache captchaCache = null;
private CaptchaManager() {}
public static CaptchaManager me() {
return me;
}
public void setCaptchaCache(ICaptchaCache captchaCache) {
if (captchaCache == null) {
throw new IllegalArgumentException("captchaCache can not be null");
}
this.captchaCache = captchaCache;
}
public ICaptchaCache getCaptchaCache() {
if (captchaCache == null) {
synchronized (this) {
if (captchaCache == null) {
captchaCache = new CaptchaCache();
}
}
}
return captchaCache;
}
}
/**
* Copyright (c) 2011-2021, James Zhan 詹波 (jfinal@126.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jfinal.captcha;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.QuadCurve2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
import com.jfinal.core.Controller;
import com.jfinal.kit.LogKit;
import com.jfinal.kit.StrKit;
import com.jfinal.render.Render;
import com.jfinal.render.RenderException;
/**
* CaptchaRender.
*/
public class CaptchaRender extends Render {
protected static String captchaName = "_jfinal_captcha";
protected static final Random random = new Random(System.nanoTime());
// 默认的验证码大小
protected static final int WIDTH = 108, HEIGHT = 40;
// 验证码随机字符数组
protected static final char[] charArray = "3456789ABCDEFGHJKMNPQRSTUVWXY".toCharArray();
// 验证码字体
protected static final Font[] RANDOM_FONT = new Font[] {
new Font(Font.DIALOG, Font.BOLD, 33),
new Font(Font.DIALOG_INPUT, Font.BOLD, 34),
new Font(Font.SERIF, Font.BOLD, 33),
new Font(Font.SANS_SERIF, Font.BOLD, 34),
new Font(Font.MONOSPACED, Font.BOLD, 34)
};
/*protected static final Font[] RANDOM_FONT = new Font[] {
new Font("nyala", Font.BOLD, 38),
new Font("Arial", Font.BOLD, 32),
new Font("Bell MT", Font.BOLD, 32),
new Font("Credit valley", Font.BOLD, 34),
new Font("Impact", Font.BOLD, 32),
new Font(Font.MONOSPACED, Font.BOLD, 40)
};*/
/**
* 设置 captchaName
*/
public static void setCaptchaName(String captchaName) {
if (StrKit.isBlank(captchaName)) {
throw new IllegalArgumentException("captchaName can not be blank.");
}
CaptchaRender.captchaName = captchaName;
}
/**
* 生成验证码
*/
public void render() {
Captcha captcha = createCaptcha();
CaptchaManager.me().getCaptchaCache().put(captcha);
Cookie cookie = new Cookie(captchaName, captcha.getKey());
cookie.setMaxAge(-1);
cookie.setPath("/");
response.addCookie(cookie);
response.setHeader("Pragma","no-cache");
response.setHeader("Cache-Control","no-cache");
response.setDateHeader("Expires", 0);
response.setContentType("image/jpeg");
ServletOutputStream sos = null;
try {
BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
drawGraphic(captcha.getValue(), image);
sos = response.getOutputStream();
ImageIO.write(image, "jpeg", sos);
} catch (IOException e) {
if (getDevMode()) {
throw new RenderException(e);
}
} catch (Exception e) {
throw new RenderException(e);
} finally {
if (sos != null) {
try {sos.close();} catch (IOException e) {LogKit.logNothing(e);}
}
}
}
protected Captcha createCaptcha() {
String captchaKey = getCaptchaKeyFromCookie();
if (StrKit.isBlank(captchaKey)) {
captchaKey = StrKit.getRandomUUID();
}
return new Captcha(captchaKey, getRandomString(), Captcha.DEFAULT_EXPIRE_TIME);
}
protected String getCaptchaKeyFromCookie() {
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals(captchaName)) {
return cookie.getValue();
}
}
}
return null;
}
protected String getRandomString() {
char[] randomChars = new char[4];
for (int i=0; i<randomChars.length; i++) {
randomChars[i] = charArray[random.nextInt(charArray.length)];
}
return String.valueOf(randomChars);
}
protected void drawGraphic(String randomString, BufferedImage image){
// 获取图形上下文
Graphics2D g = image.createGraphics();
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
// 图形抗锯齿
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// 字体抗锯齿
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
// 设定背景色
g.setColor(getRandColor(210, 250));
g.fillRect(0, 0, WIDTH, HEIGHT);
//绘制小字符背景
Color color = null;
for(int i = 0; i < 20; i++){
color = getRandColor(120, 200);
g.setColor(color);
String rand = String.valueOf(charArray[random.nextInt(charArray.length)]);
g.drawString(rand, random.nextInt(WIDTH), random.nextInt(HEIGHT));
color = null;
}
//设定字体
g.setFont(RANDOM_FONT[random.nextInt(RANDOM_FONT.length)]);
// 绘制验证码
for (int i = 0; i < randomString.length(); i++){
//旋转度数 最好小于45度
int degree = random.nextInt(28);
if (i % 2 == 0) {
degree = degree * (-1);
}
//定义坐标
int x = 22 * i, y = 21;
//旋转区域
g.rotate(Math.toRadians(degree), x, y);
//设定字体颜色
color = getRandColor(20, 130);
g.setColor(color);
//将认证码显示到图象中
g.drawString(String.valueOf(randomString.charAt(i)), x + 8, y + 10);
//旋转之后,必须旋转回来
g.rotate(-Math.toRadians(degree), x, y);
}
//图片中间曲线,使用上面缓存的color
g.setColor(color);
//width是线宽,float型
BasicStroke bs = new BasicStroke(3);
g.setStroke(bs);
//画出曲线
QuadCurve2D.Double curve = new QuadCurve2D.Double(0d, random.nextInt(HEIGHT - 8) + 4, WIDTH / 2, HEIGHT / 2, WIDTH, random.nextInt(HEIGHT - 8) + 4);
g.draw(curve);
// 销毁图像
g.dispose();
}
/*
* 给定范围获得随机颜色
*/
protected Color getRandColor(int fc, int bc) {
Random random = new Random();
if (fc > 255)
fc = 255;
if (bc > 255)
bc = 255;
int r = fc + random.nextInt(bc - fc);
int g = fc + random.nextInt(bc - fc);
int b = fc + random.nextInt(bc - fc);
return new Color(r, g, b);
}
/**
* 校验用户输入的验证码是否正确
* @param controller 控制器
* @param userInputString 用户输入的字符串
* @return 验证通过返回 true, 否则返回 false
*/
public static boolean validate(Controller controller, String userInputString) {
String captchaKey = controller.getCookie(captchaName);
if (validate(captchaKey, userInputString)) {
controller.removeCookie(captchaName);
return true;
}
return false;
}
/**
* 校验用户输入的验证码是否正确
* @param captchaKey 验证码 key,在不支持 cookie 的情况下可通过传参给服务端
* @param userInputString 用户输入的字符串
* @return 验证通过返回 true, 否则返回 false
*/
public static boolean validate(String captchaKey, String userInputString) {
ICaptchaCache captchaCache = CaptchaManager.me().getCaptchaCache();
Captcha captcha = captchaCache.get(captchaKey);
if (captcha != null && captcha.notExpired() && captcha.getValue().equalsIgnoreCase(userInputString)) {
captchaCache.remove(captcha.getKey());
return true;
}
return false;
}
}
/**
* Copyright (c) 2011-2023, James Zhan 詹波 (jfinal@126.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jfinal.captcha;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.QuadCurve2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
import com.jfinal.core.Controller;
import com.jfinal.kit.LogKit;
import com.jfinal.kit.StrKit;
import com.jfinal.render.Render;
import com.jfinal.render.RenderException;
/**
* CaptchaRender.
*/
public class CaptchaRender extends Render {
protected static String captchaName = "_jfinal_captcha";
protected static final Random random = new Random(System.nanoTime());
// 默认的验证码大小
protected static final int WIDTH = 108, HEIGHT = 40;
// 验证码随机字符数组
protected static final char[] charArray = "3456789ABCDEFGHJKMNPQRSTUVWXY".toCharArray();
// 验证码字体
protected static final Font[] RANDOM_FONT = new Font[] {
new Font(Font.DIALOG, Font.BOLD, 33),
new Font(Font.DIALOG_INPUT, Font.BOLD, 34),
new Font(Font.SERIF, Font.BOLD, 33),
new Font(Font.SANS_SERIF, Font.BOLD, 34),
new Font(Font.MONOSPACED, Font.BOLD, 34)
};
/*protected static final Font[] RANDOM_FONT = new Font[] {
new Font("nyala", Font.BOLD, 38),
new Font("Arial", Font.BOLD, 32),
new Font("Bell MT", Font.BOLD, 32),
new Font("Credit valley", Font.BOLD, 34),
new Font("Impact", Font.BOLD, 32),
new Font(Font.MONOSPACED, Font.BOLD, 40)
};*/
/**
* 设置 captchaName
*/
public static void setCaptchaName(String captchaName) {
if (StrKit.isBlank(captchaName)) {
throw new IllegalArgumentException("captchaName can not be blank.");
}
CaptchaRender.captchaName = captchaName;
}
/**
* 生成验证码
*/
public void render() {
Captcha captcha = createCaptcha();
CaptchaManager.me().getCaptchaCache().put(captcha);
Cookie cookie = new Cookie(captchaName, captcha.getKey());
cookie.setMaxAge(-1);
cookie.setPath("/");
response.addCookie(cookie);
response.setHeader("Pragma","no-cache");
response.setHeader("Cache-Control","no-cache");
response.setDateHeader("Expires", 0);
response.setContentType("image/jpeg");
ServletOutputStream sos = null;
try {
BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
drawGraphic(captcha.getValue(), image);
sos = response.getOutputStream();
ImageIO.write(image, "jpeg", sos);
} catch (IOException e) {
if (getDevMode()) {
throw new RenderException(e);
}
} catch (Exception e) {
throw new RenderException(e);
} finally {
if (sos != null) {
try {sos.close();} catch (IOException e) {LogKit.logNothing(e);}
}
}
}
protected Captcha createCaptcha() {
String captchaKey = getCaptchaKeyFromCookie();
if (StrKit.isBlank(captchaKey)) {
captchaKey = StrKit.getRandomUUID();
}
return new Captcha(captchaKey, getRandomString(), Captcha.DEFAULT_EXPIRE_TIME);
}
protected String getCaptchaKeyFromCookie() {
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals(captchaName)) {
return cookie.getValue();
}
}
}
return null;
}
protected String getRandomString() {
char[] randomChars = new char[4];
for (int i=0; i<randomChars.length; i++) {
randomChars[i] = charArray[random.nextInt(charArray.length)];
}
return String.valueOf(randomChars);
}
protected void drawGraphic(String randomString, BufferedImage image){
// 获取图形上下文
Graphics2D g = image.createGraphics();
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
// 图形抗锯齿
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// 字体抗锯齿
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
// 设定背景色
g.setColor(getRandColor(210, 250));
g.fillRect(0, 0, WIDTH, HEIGHT);
//绘制小字符背景
Color color = null;
for(int i = 0; i < 20; i++){
color = getRandColor(120, 200);
g.setColor(color);
String rand = String.valueOf(charArray[random.nextInt(charArray.length)]);
g.drawString(rand, random.nextInt(WIDTH), random.nextInt(HEIGHT));
color = null;
}
//设定字体
g.setFont(RANDOM_FONT[random.nextInt(RANDOM_FONT.length)]);
// 绘制验证码
for (int i = 0; i < randomString.length(); i++){
//旋转度数 最好小于45度
int degree = random.nextInt(28);
if (i % 2 == 0) {
degree = degree * (-1);
}
//定义坐标
int x = 22 * i, y = 21;
//旋转区域
g.rotate(Math.toRadians(degree), x, y);
//设定字体颜色
color = getRandColor(20, 130);
g.setColor(color);
//将认证码显示到图象中
g.drawString(String.valueOf(randomString.charAt(i)), x + 8, y + 10);
//旋转之后,必须旋转回来
g.rotate(-Math.toRadians(degree), x, y);
}
//图片中间曲线,使用上面缓存的color
g.setColor(color);
//width是线宽,float型
BasicStroke bs = new BasicStroke(3);
g.setStroke(bs);
//画出曲线
QuadCurve2D.Double curve = new QuadCurve2D.Double(0d, random.nextInt(HEIGHT - 8) + 4, WIDTH / 2, HEIGHT / 2, WIDTH, random.nextInt(HEIGHT - 8) + 4);
g.draw(curve);
// 销毁图像
g.dispose();
}
/*
* 给定范围获得随机颜色
*/
protected Color getRandColor(int fc, int bc) {
Random random = new Random();
if (fc > 255)
fc = 255;
if (bc > 255)
bc = 255;
int r = fc + random.nextInt(bc - fc);
int g = fc + random.nextInt(bc - fc);
int b = fc + random.nextInt(bc - fc);
return new Color(r, g, b);
}
/**
* 校验用户输入的验证码是否正确
* @param controller 控制器
* @param userInputString 用户输入的字符串
* @return 验证通过返回 true, 否则返回 false
*/
public static boolean validate(Controller controller, String userInputString) {
String captchaKey = controller.getCookie(captchaName);
if (validate(captchaKey, userInputString)) {
controller.removeCookie(captchaName);
return true;
}
return false;
}
/**
* 校验用户输入的验证码是否正确
* @param captchaKey 验证码 key,在不支持 cookie 的情况下可通过传参给服务端
* @param userInputString 用户输入的字符串
* @return 验证通过返回 true, 否则返回 false
*/
public static boolean validate(String captchaKey, String userInputString) {
ICaptchaCache captchaCache = CaptchaManager.me().getCaptchaCache();
Captcha captcha = captchaCache.get(captchaKey);
if (captcha != null && captcha.notExpired() && captcha.getValue().equalsIgnoreCase(userInputString)) {
captchaCache.remove(captcha.getKey());
return true;
}
return false;
}
}
/**
* Copyright (c) 2011-2021, James Zhan 詹波 (jfinal@126.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jfinal.captcha;
/**
* ICaptchaCache
*/
public interface ICaptchaCache {
void put(Captcha captcha);
Captcha get(String key);
void remove(String key);
void removeAll();
}
/**
* Copyright (c) 2011-2023, James Zhan 詹波 (jfinal@126.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jfinal.captcha;
/**
* ICaptchaCache
*/
public interface ICaptchaCache {
void put(Captcha captcha);
Captcha get(String key);
void remove(String key);
void removeAll();
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment