aop наконец-то поставили на повестку дня написать об этом.
Каталог серий
Эта серия делится на три части: первую, среднюю и вторую. Первая часть в основном знакомит с тем, как использовать АОП, и предоставляет демо и инструкции по настройке; вторая часть анализирует технические принципы реализации АОП; вторая часть в основном анализирует исходный код реализации АОП в Spring.
адрес проекта
адрес проекта:glmapper-ssm-parent
Этот проект содержит все коды следующих реализаций АОП. Заинтересованные студенты могут разветвить и запустить их. В этой демонстрации перечислены реализации 4 способов:
- подход на основе кода
- На основе чистого класса POJO
- Аспектный подход
- Внедренный подход на основе аспектов
В настоящее время мы часто используемАспектный подходПуть. Давайте рассмотрим различные формы выражения одну за другой.
агентный подход
Этот метод выглядит простым для понимания, но довольно хлопотный в настройке, друзья могут ссылаться на проект, а здесь публикуются только наиболее критичные коды процессов.
1. Сначала определите интерфейс: GoodsService
public interface GoodsService {
/**
* 查询所有商品信息
*
* @param offset 查询起始位置
* @param limit 查询条数
* @return
*/
List<Goods> queryAll(int offset,int limit);
}
2. Класс реализации GoodsService
@Service
@Qualifier("goodsService")
public class GoodsServiceImpl implements GoodsService {
@Autowired
private GoodsDao goodsDao;
public List<Goods> queryAll(int offset, int limit) {
System.out.println("执行了queryAll方法");
List<Goods> list = new ArrayList<Goods>();
return list;
}
}
3. Определите класс уведомлений LoggerHelper, который наследует MethodBeforeAdvice и AfterReturningAdvice.
//通知类 LoggerHelper
public class LoggerHelper implements MethodBeforeAdvice,
AfterReturningAdvice {
private static final Logger LOGGER = LoggerFactory.getLogger(LoggerHelper.class);
//MethodBeforeAdvice的before方法实现
public void before(Method method, Object[] objects, Object o) throws Throwable {
LOGGER.info("before current time:"+System.currentTimeMillis());
}
//AfterReturningAdvice的afterReturning方法实现
public void afterReturning(Object o, Method method,
Object[] objects, Object o1) throws Throwable {
LOGGER.info("afterReturning current time:"+System.currentTimeMillis());
}
}
4. Ключевым моментом является то, что на эту конфигурацию нужно обращать внимание. В этом проекте я настроил его в файле applicationContext.xml.
<!-- 定义被代理者 -->
<bean id="goodsServiceImpl" class="com.glmapper.framerwork.service.impl.GoodsServiceImpl"></bean>
<!-- 定义通知内容,也就是切入点执行前后需要做的事情 -->
<bean id="loggerHelper" class="com.glmapper.framerwork.aspect.LoggerHelper"></bean>
<!-- 定义切入点位置 -->
<bean id="loggerPointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="pattern" value=".*query.*"></property>
</bean>
<!-- 使切入点与通知相关联,完成切面配置 -->
<!-- 从这里可以帮助我们理解Advisor,advice和pointcut之间的关系-->
<!--adivce和pointcut是Advisor的两个属性-->
<bean id="loggerHelperAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="loggerHelper"></property>
<property name="pointcut" ref="loggerPointcut"></property>
</bean>
<!-- 设置代理 -->
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 代理的对象 ,也就是目标类-->
<property name="target" ref="goodsServiceImpl"></property>
<!-- 使用切面 -->
<property name="interceptorNames" value="loggerHelperAdvisor"></property>
<!-- 代理接口,商品接口 -->
<property name="proxyInterfaces" value="com.glmapper.framerwork.service.GoodsService"></property>
</bean>
5. Использование: метод вставки аннотаций
@Controller
@RequestMapping("/buy")
public class BuyController {
@Autowired
private OrderService orderService;
//因为我们已经在配置文件中配置了proxy,
//所以这里可以直接注入拿到我们的代理类
@Autowired
private GoodsService proxy;
@RequestMapping("/initPage")
public ModelAndView initPage(HttpServletRequest request,
HttpServletResponse response, ModelAndView view) {
//这里使用proxy执行了*query*,
List<Goods> goods = proxy.queryAll(10,10);
view.addObject("goodsList", goods);
view.setViewName("goodslist");
return view;
}
}
6. Использование: метод класса инструмента для ручного получения bean-компонентов
Этот метод предназначен для получения прокси-объекта через класс инструментов SpringContextUtil.
@RequestMapping("/initPage")
public ModelAndView initPage(HttpServletRequest request,
HttpServletResponse response, ModelAndView view) {
//这里通过工具类来拿,效果一样的。
GoodsService proxy= (GoodsService) SpringContextUtil.getBean("proxy");
List<Goods> goods = proxy.queryAll(10,10);
view.addObject("goodsList", goods);
view.setViewName("goodslist");
return view;
}
7. Определение класса SpringContextUtil
Это все еще небольшая яма. Прежде всего, SpringContextUtil наследует интерфейс ApplicationContextAware. Мы надеемся, что SpringContextUtil может напрямую управляться контейнером Spring, поэтому нам нужно использовать аннотацию @Component. После его маркировки самое главное, что его можно сканировать настроенным нами инъекционным сканированием (лично я наступил на яму, кладу под пакет, который нельзя сканировать, а отладка всегда нулевая, я чуть не разбил компьютер...)
@Component
public class SpringContextUtil implements ApplicationContextAware {
// Spring应用上下文环境
private static ApplicationContext applicationContext;
/**
* 实现ApplicationContextAware接口的回调方法,设置上下文环境
*
* @param applicationContext
*/
public void setApplicationContext(ApplicationContext applicationContext) {
SpringContextUtil.applicationContext = applicationContext;
}
/**
* @return ApplicationContext
*/
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
/**
* 获取对象
* 这里重写了bean方法,起主要作用
* @param name
* @return Object 一个以所给名字注册的bean的实例
* @throws BeansException
*/
public static Object getBean(String name) throws BeansException {
return applicationContext.getBean(name);
}
}
8. Запуск результатов
21:04:47.940 [http-nio-8080-exec-7] INFO
c.g.framerwork.aspect.LoggerHelper - before current
time:1529413487940
执行了queryAll方法
21:04:47.940 [http-nio-8080-exec-7] INFO
c.g.framerwork.aspect.LoggerHelper - afterReturning current
time:1529413487940
Вышеупомянутый наиболее классический способ заключается в реализации процесса АОП через прокси.
Чистый аспект POJOaop:config
Обратите внимание на разницу между этим и LoggerHelper, где LoggerAspect не наследует какой-либо интерфейс или абстрактный класс.
1. Определение класса POJO
/**
* @description: [描述文本]
* @email: <a href="guolei.sgl@antfin.com"></a>
* @author: guolei.sgl
* @date: 18/6/20
*/
public class LoggerAspect {
private static final Logger LOGGER =
LoggerFactory.getLogger(LoggerHelper.class);
public void before(){
LOGGER.info("before current time:"+System.currentTimeMillis());
}
public void afterReturning() {
LOGGER.info("afterReturning current time:"+System.currentTimeMillis());
}
}
2. Файл конфигурации
<!-- 定义通知内容,也就是切入点执行前后需要做的事情 -->
<bean id="loggerAspect"
class="com.glmapper.framerwork.aspect.LoggerAspect">
</bean>
<aop:config>
<!--定义切面-->
<aop:aspect ref="loggerAspect">
<aop:pointcut id="loggerPointCut" expression=
"execution(* com.glmapper.framerwork.service.impl.*.*(..)) " />
<!-- 定义 Advice -->
<!-- 前置通知 -->
<aop:before pointcut-ref="loggerPointCut" method="before" />
<!-- 后置通知 -->
<aop:after-returning pointcut-ref="loggerPointCut"
method="afterReturning"/>
</aop:aspect>
</aop:config>
Учтите, что если есть параметры before и afterReturning в LoggerAspect, их нужно обработать здесь, иначе о них будет сообщено.0 formal unbound in pointcutаномальный.
Подход на основе аннотаций @AspectJ
Этот метод является самой простой реализацией, и мы можем напрямую использовать аннотацию @Aspect для аннотирования нашего класса аспектов.
1. Определите класс аспекта и используйте @Aspect для аннотации
/**
* @description: 使用Aspect注解驱动的方式
* @email: <a href="guolei.sgl@antfin.com"></a>
* @author: guolei.sgl
* @date: 18/6/20
*/
@Aspect
public class LoggerAspectInject {
private static final Logger LOGGER = LoggerFactory.getLogger(LoggerAspectInject.class);
@Pointcut("execution(* com.glmapper.framerwork.service.impl.*.*(..))")
public void cutIn(){}
@Before("cutIn()")
public void before(){
LOGGER.info("before current time:"+System.currentTimeMillis());
}
@AfterReturning("cutIn()")
public void AfterReturning(){
LOGGER.info("afterReturning current time:"+System.currentTimeMillis());
}
}
2. Использование 1: объявить bean-компонент в файле конфигурации
<aop:aspectj-autoproxy />
<!-- 定义通知内容,也就是切入点执行前后需要做的事情 -->
<bean id="loggerAspectInject"
class="com.glmapper.framerwork.aspect.LoggerAspectInject">
</bean>
<!-- 定义被代理者 -->
<bean id="goodsServiceImpl"
class="com.glmapper.framerwork.service.impl.GoodsServiceImpl">
</bean>
3. Использование клиента:
@Controller
@RequestMapping("/buy")
public class BuyController {
@Autowired
private OrderService orderService;
@RequestMapping("/initPage")
public ModelAndView initPage(HttpServletRequest request,
HttpServletResponse response, ModelAndView view) {
//通过SpringContextUtil手动获取 代理bean
GoodsService goodsService=(GoodsService)
SpringContextUtil.getBean("goodsServiceImpl");
List<Goods> goods = goodsService.queryAll(10,10);
view.addObject("goodsList", goods);
view.setViewName("goodslist");
return view;
}
}
4. Использование 2: Используйте аннотацию @component для размещения в IOC
@Aspect
@Component //这里加上了Component注解,就不需要在xml中配置了
public class LoggerAspectInject {
private static final Logger LOGGER =
LoggerFactory.getLogger(LoggerAspectInject.class);
@Pointcut("execution(* com.glmapper.framerwork.service.impl.*.*(..))")
public void cutIn(){}
@Before("cutIn()")
public void before(){
LOGGER.info("before current time:"+System.currentTimeMillis());
}
@AfterReturning("cutIn()")
public void AfterReturning(){
LOGGER.info("afterReturning current time:"+System.currentTimeMillis());
}
}
5. Код клиента:
@Controller
@RequestMapping("/buy")
public class BuyController {
@Autowired
private OrderService orderService;
//直接注入
@Autowired
private GoodsService goodsService;
@RequestMapping("/initPage")
public ModelAndView initPage(HttpServletRequest request,
HttpServletResponse response, ModelAndView view) {
List<Goods> goods = goodsService.queryAll(10,10);
view.addObject("goodsList", goods);
view.setViewName("goodslist");
return view;
}
}
6. Относительно полный LoggerAspectInject, на который можно напрямую ссылаться в реальных проектах.
/**
* @description: aop
* @email: <a href="henugl@1992.163.com"></a>
* @author: glmapper@磊叔
* @date: 18/6/4
*/
@Aspect
@Component
public class LoggerAspectInject {
private static final Logger LOGGER= LoggerFactory.getLogger(LoggerAspectInject.class);
@Pointcut("execution(* com.glmapper.book.web.controller.*.*(..))")
public void cutIn(){
}
@Around("cutIn()") // 定义Pointcut,名称即下面的标识"aroundAdvice
public Object aroundAdvice(ProceedingJoinPoint poin){
System.out.println("环绕通知");
Object object = null;
try{
object = poin.proceed();
}catch (Throwable e){
e.printStackTrace();
}
return object;
}
// 定义 advise
//这个方法只是一个标识,相当于在配置文件中定义了pointcut的id,此方法没有返回值和参数
@Before("cutIn()")
public void beforeAdvice(){
System.out.println("前置通知");
}
@After("cutIn()")
public void afterAdvice(){
System.out.println("后置通知");
}
@AfterReturning("cutIn()")
public void afterReturning(){
System.out.println("后置返回 ");
}
@AfterThrowing("cutIn()")
public void afterThrowing(){
System.out.println("后置异常");
}
}
Об именованных точках: Метод cutIn в приведенном выше примере можно назвать именованным pointcut.На именованные pointcut могут ссылаться другие pointcut, а на анонимные pointcut - нет. Только @AspectJ поддерживает именованные точки, а стиль схемы не поддерживает именованные точки. Как показано ниже, @AspectJ ссылается на именованные точки, используя:
@Pointcut("execution(* com.glmapper.book.web.controller.*.*(..))")
public void cutIn(){
}
//引入命名切入点
@Before("cutIn()")
public void beforeAdvice(){
System.out.println("前置通知");
}
Инъекционные AspectJ Аспекты
Таким образом, я чувствую, что это комбинация второго и третьего.
1. Определите класс аспекта
/**
* @description: 注入式 也是一种通过XML方式配置的方式
* @email: <a href="guolei.sgl@antfin.com"></a>
* @author: guolei.sgl
* @date: 18/6/20
*/
public class LoggerAspectHelper {
private static final Logger LOGGER = LoggerFactory.getLogger(LoggerAspectHelper.class);
/**
* 调动方法前执行
* @param point
* @throws Throwable
*/
public void doBefore(JoinPoint point) throws Throwable {
LOGGER.info("before current time:"+System.currentTimeMillis());
}
/**
* 在调用方法前后执行
* @param point
* @return
* @throws Throwable
*/
public Object doAround(ProceedingJoinPoint point) throws Throwable
{
LOGGER.info("around current time:"+System.currentTimeMillis());
if(point.getArgs().length>0) {
return point.proceed(point.getArgs());
}else{
return point.proceed();
}
}
/**
* 在调用方法之后执行
* @param point
* @throws Throwable
*/
public void doAfter(JoinPoint point) throws Throwable
{
LOGGER.info("after current time:"+System.currentTimeMillis());
}
/**
* 异常通知
* @param point
* @param ex
*/
public void doThrowing(JoinPoint point, Throwable ex)
{
LOGGER.info("throwing current time:"+System.currentTimeMillis());
}
}
2. XML-конфигурация
<bean id="loggerAspectHelper"
class="com.glmapper.framerwork.aspect.LoggerAspectHelper">
</bean>
<aop:config>
<aop:aspect id="configAspect" ref="loggerAspectHelper">
<!--配置com.glmapper.framerwork.service.imp
包下所有类或接口的所有方法 -->
<aop:pointcut id="cutIn" expression=
"execution(* com.glmapper.framerwork.service.impl.*.*(..))" />
<aop:before pointcut-ref="cutIn" method="doBefore" />
<aop:after pointcut-ref="cutIn" method="doAfter" />
<aop:around pointcut-ref="cutIn" method="doAround" />
<aop:after-throwing pointcut-ref="cutIn"
method="doThrowing" throwing="ex" />
</aop:aspect>
</aop:config>
3. Результаты
23:39:48.756 [http-nio-8080-exec-4] INFO c.g.f.aspect.LoggerAspectHelper
- before current time:1529509188756
23:39:48.757 [http-nio-8080-exec-4] INFO c.g.f.aspect.LoggerAspectHelper
- around current time:1529509188757
excute queryAll method...
23:39:48.757 [http-nio-8080-exec-4] INFO c.g.f.aspect.LoggerAspectHelper
- after current time:1529509188757
выражение
Из приведенных выше примеров мы все используем некоторые регулярные выражения для указания наших точек. В реальном использовании используется не только исполнение, но и многие другие типы выражений. Вот некоторые из них:
1. исполнение
точки соединения для сопоставления выполнения методов;
execution(* com.glmapper.book.web.controller.*.*(..))
- Тело выражения execute();
- Первый символ «*» указывает на то, что тип возвращаемого значения произвольный;
- com.glmapper.book.web.controller Имя пакета сервиса, нарезанного АОП, то есть наша бизнес-часть
- Знак «.» после имени пакета указывает на текущий пакет и подпакеты.
- Вторая "*" представляет имя класса, т.е. все классы
- .*(..) означает любое имя метода, круглые скобки означают параметры, а две точки означают любой тип параметра
2. внутри
Используется для сопоставления выполнения метода с указанным типом;
//如果在com.glmapper.book.web.controller包或其下的任何子包中
//定义了该类型,则在Web层中有一个连接点。
within(com.glmapper.book.web.controller..*)
@Pointcut("within(com.glmapper.book.web.controller..*)")
public void cutIn(){}
@within: используется для сопоставления методов в указанном типе аннотации;
/**
* @description: 注解定义
* @email: <a href="henugl@1992.163.com"></a>
* @author: glmapper@磊叔
* @date: 18/6/4
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.FIELD})
public @interface AuthAnnotation {
}
Любой метод класса соответствующего типа целевого объекта, который содержит аннотацию AuthAnnotation; эта аннотация должна быть объявлена на целевом объекте, и объявление в интерфейсе не оказывает на него никакого влияния.
@within(com.glmapper.book.common.annotaion.AuthAnnotation)
//所有被@AdviceAnnotation标注的类都将匹配
@Pointcut("@within(com.glmapper.book.common.annotaion.AuthAnnotation)")
public void cutIn(){}
3. это
Метод выполнения, используемый для сопоставления с типом текущего прокси-объекта АОП; обратите внимание, что тип прокси-объекта АОП совпадает, поэтому он может включать импортированные интерфейсы и сопоставление типов; используемое выражение должно быть полным именем объекта тип и подстановочные знаки не поддерживаются;
//当前目标对象(非AOP对象)实现了 UserService 接口的任何方法
this(com.glmapper.book.web.service.UserService)
//用于向通知方法中传入代理对象的引用。
@Before("cutIn() && this(proxy)")
public void beforeAdvice(ProceedingJoinPoint poin,Object proxy){
System.out.println("前置通知");
}
4. цель
Метод выполнения, используемый для сопоставления с типом текущего целевого объекта; обратите внимание, что тип целевого объекта соответствует, поэтому импортированный интерфейс не включается, а тип сопоставляется; выражение, используемое в целевом объекте, должно быть полным имя типа и подстановочные знаки не поддерживаются;
//当前目标对象(非AOP对象)实现了 UserService 接口的任何方法
target(com.glmapper.book.web.service.UserService)
//用于向通知方法中传入代理对象的引用。
@Before("cutIn() && target(proxy)")
public void beforeAdvice(ProceedingJoinPoint poin,Object proxy){
System.out.println("前置通知");
}
@target: метод выполнения, используемый для сопоставления с текущим типом целевого объекта, где целевой объект содержит указанную аннотацию; любой целевой объект содержит метод класса аннотации Secure; это, как и @within, должно быть объявлено на целевом объекте, в То, что объявлено на интерфейсе, также не влияет на него.
@target(com.glmapper.book.common.annotaion.AuthAnnotation)
@Pointcut("@target(com.glmapper.book.common.annotaion.AuthAnnotation)")
public void cutIn(){}
5. аргументы
Параметр, передаваемый для соответствия текущему выполняемому методу, является методом выполнения указанного типа; параметры в списке типов параметров должны быть полным именем типа, а подстановочные знаки не поддерживаются; args относится к динамическому pointcut, который очень дорого и не лучше не использовать в особых случаях;
//任何一个以接受“传入参数类型为java.io.Serializable”开头,
//且其后可跟任意个任意类型的参数的方法执行,
//args指定的参数类型是在运行时动态匹配的
args (java.io.Serializable,..)
//用于将参数传入到通知方法中。
@Before("cutIn() && args(age,username)")
public void beforeAdvide(JoinPoint point, int age, String username){
//...
}
@args: используется для сопоставления выполнения текущего выполняемого метода с параметрами, переданными с указанной аннотацией; любой метод, который принимает только один параметр, и параметр, передаваемый при запуске метода, имеет аннотацию AuthAnnotation; динамический pointcut, аналогичный индикатор аргумента;
@args (com.glmapper.book.common.annotaion.AuthAnnotation)
@Before("@args(com.glmapper.book.common.annotaion.AuthAnnotation)")
public void beforeAdvide(JoinPoint point){
//...
}
6. @аннотация
Используйте «@annotation (тип аннотации)», чтобы сопоставить текущий выполняемый метод с указанной аннотацией; тип аннотации также должен быть полным именем типа;
//当前执行方法上持有注解 AuthAnnotation将被匹配
@annotation(com.glmapper.book.common.annotaion.AuthAnnotation)
//匹配连接点被它参数指定的AuthAnnotation注解的方法。
//也就是说,所有被指定注解标注的方法都将匹配。
@Pointcut("@annotation(com.glmapper.book.common.annotaion.AuthAnnotation)")
public void cutIn(){}
Существует также метод бобов, который никогда не использовался. Заинтересованные могут глянуть.
Примеры приведены в разделе «Основные понятия», упомянутом ниже.
Основные понятия
Часть основных понятий в основном относится к некоторым понятиям в АОП Эта часть в основном относится к некоторым пояснениям на официальном сайте.
AOP
AOP(Aspect-Oriented Programming)
, Прямо сейчасАспектно-ориентированное программирование, это сOOP
( Object-Oriented Programming
, объектно-ориентированное программирование) дополняют друг друга, обеспечиваяOOP
Перспективы различных абстрактных программных структур. существуетOOP
, мы используем класс как нашу основную единицу, иAOP
Основная единица вАспект.
Сквозные проблемы(Cross Cutting Concern
): автономные службы, такие как системный журнал. Если это не независимая услуга (то есть услуга с сильной связью с бизнесом), она не может быть сквозной. Обычно такого рода независимую услугу необходимо распределить по всем уголкам системы и бизнес-процесса.
Target Object
цель. Целевой объект для совета по плетению. целевой объект также называетсяadvised object
.
Поскольку Spring AOP использует прокси времени выполнения для реализации аспектов, советуемый объект всегда является прокси-объектом; обратите внимание, что советуемый объект относится не к исходному классу, а к прокси-классу, созданному путем объединения рекомендаций.
ткать
которыйAdvice
используется дляJoinPoint
процесс, этот процесс называется плетением. С другой точки зрения, говорят, чтоaspect
соединяться с другими объектами и создаватьadviced object
процесс.
По разным технологиям выполнения,AOP
Существует три способа плетения:
- прошивка компилятора, которая требует спец.
Java
переводчик - Время загрузки классов соткано, для чего требуется специальный загрузчик классов
- Динамическое создание прокси, добавление улучшений в целевые классы во время выполнения (
Advice
) для создания подклассов.
Spring использует динамическое переплетение прокси, а AspectJ использует переплетение компилятора и загрузку классов.
играет роль
Spring AOP по умолчанию использует стандартный динамический прокси JDK. Это позволяет проксировать любой интерфейс (или набор интерфейсов).
Spring AOP также может использовать прокси CGLIB. Если бизнес-объект не реализует интерфейс, по умолчанию используется CGLIB. Хорошей практикой является программирование интерфейсов, а не классов; бизнес-классы обычно реализуют один или несколько бизнес-интерфейсов. В некоторых особых случаях, т. е. в методах, не объявленных в интерфейсе, который необходимо уведомлять, или в методах, которым необходимо передать прокси-объект конкретному типу, можно принудительно использовать CGLIB.
Introductions
Мы знаем, что язык Java сам по себе не является динамическим, то есть после того, как наш класс скомпилирован, в него трудно добавлять новые функции. Но в примере, приведенном в начале, мы хоть и не добавили к объекту новых методов, но добавили ему новый функционал. Это относится к добавлению новых функций к существующим методам.Можем ли мы добавить новые методы к объекту? Ответ определенно да, и это может быть достигнуто с помощью введения.
введение: Динамическое добавление или удаление методов для класса. Добавьте к типу дополнительные методы или поля. Spring АОП позволяет нам目标对象
Внедрить новые интерфейсы (и соответствующие реализации).
Aspect
Аспекты: сочетание советов и подсказок.
Аспекты реализуют сквозную функцию. Наиболее распространенными являются модули протоколирования и модули, отнимающие много времени на выполнение методов.Таким образом, программа делится на несколько слоев по функциям.Если бизнес-модель наследует модуль протоколирования по традиционному наследованию, остается слишком много мест для вставлять и изменять Создавая аспект, АОП можно использовать для достижения одной и той же функции, мы можем создавать разные аспекты для разных нужд.
И собрать сквозные проблемы, разбросанные по различным бизнес-объектам, спроектировать каждый независимый повторно используемый объект, эти объекты называются аспектами; в приведенном выше примере мы определяем четыре разные формы в соответствии с различными срезами методов конфигурации.
Joinpoint
Точка или время, в которое Аспект присоединяется к бизнес-процессу при выполнении приложения, называется Точкой присоединения. В частности, это время, когда Advice вызывается и выполняется в приложении. Это время может быть до или после вызова метода (или и то, и другое). ) оба), или когда возникает исключение.
Joinpoint & ProceedingJoinPoint
Окружающее уведомление = предварительное + выполнение целевого метода + пост-уведомление, метод continue используется для запуска выполнения целевого метода.
Окружающий совет ProceedingJoinPoint выполняет метод continue, чтобы разрешить выполнение целевого метода, что также является самым большим различием между окружающим советом и методами до и после уведомления.
Происходящая точка соединения наследует точку соединения. Метод continue выставлен на основе JoinPoint. continue очень важен, это метод, выполняемый цепочкой прокси-серверов aop; если этот метод открыт, он может поддерживатьaop:aroundЭтот аспект (другие аспекты должны использовать только JoinPoint, который связан с типом аспекта) может решить, следует ли пройти через цепочку прокси или использовать другую логику, которая перехватывает саму себя.
В методе обертывания советов необходимо возвращать объект типа Object, если возвращаемый тип метода обертывания советов будет void, это вызовет некоторые непредсказуемые ситуации, такие как: 404.
Pointcut
совпадениеjoin points
предикат.Advice
Связан с выражением pointcut и работает с любой точкой соединения, которая соответствует pointcut. (например, выполнение метода с определенным именем). Концепция точки соединения, совпадающей с выражением pointcut, такова:AOP
Основной,Spring
Использовать по умолчаниюAspectJ
Язык выражений Pointcut.
существуетSpring
, все методы можно рассматривать какJoinpoint
, но мы не хотим добавлять все методыAdvice
, иPointcut
Роль состоит в том, чтобы предоставить набор правил (используяAspectJ pointcut expression language
описывать) соответствоватьJoinpoint
, для удовлетворяющих правилуJoinpoint
Добавить кAdvice
.
Pointcut и Joinpoint
существуетSpring AOP
, выполнение всех методовjoin point
. иpoint cut
описательная информация, которая изменяетjoin point
, пройти черезpoint cut
, мы можем определить, какойjoin point
можно вплести вAdvice
. следовательноjoin point
иpoint cut
По сути, это два разных измерения.
advice
вjoin point
выступил наpoint cut
что предусмотреноjoin point
что можно сделатьadvice
.
Advice
концепция
Advice — это реализация нашей функциональности аспекта, и именно здесь фактически выполняется pointcut. Например, несколько методов, таких как печать времени в предыдущем примере (все методы, аннотированные @Before и другими аннотациями, являются уведомлениями); Advice вставляет код в приложение в точке соединения.
Классификация
BeforeAdvice, AfterAdvice, разница в том, что Advice вызывается до или после целевого метода, Throw Advice означает, что Advice вызывается, когда цель ненормальна.
- перед советом: совет, который выполняется до точки соединения.Хотя совет перед выполняется до точки соединения, он не может предотвратить выполнение точки соединения, если не произойдет исключение (то есть в коде перед советом мы не можем искусственно решить, продолжать ли выполнение кода в точке соединения)
- совет после возврата: совет выполнить после нормального возврата точки соединения
- после выдачи совета: совет, который выполняется после того, как точка соединения выдает исключение
- совет после (окончательного): совет, который будет выполняться независимо от того, завершается ли точка соединения нормально или возникает исключение.
- Совет вокруг: совет, который выполняется до точки соединения и после выхода из точки соединения.Это наиболее часто используемый совет.
Advice, JoinPoint, отношения PointCut
В блоге большого человека в Интернете была найдена следующая картинка, которая может помочь нам лучше понять взаимосвязь между этими понятиями.
Выше приведено краткое изложение некоторых основных понятий, связанных с АОП, и отношений между ними.
некоторые ямы
Некоторые проблемы, зафиксированные в процессе отладки программы
1. Используйте АОП для успешного перехвата службы уровня контроллера, но страница сообщает об ошибке 404.
@Around("cutIn()")
public void aroundAdvice(ProceedingJoinPoint poin) {
System.out.println("环绕通知");
}
Здесь следует отметить, что при повторном использовании объемного совета вы должны указать методу возвращаемое значение.
@Around("cutIn()")
public Object aroundAdvice(ProceedingJoinPoint poin) throws Throwable {
System.out.println("环绕通知");
return poin.proceed();
}
2. 0 формальный несвязанный в pointcut
В spring 4.x предоставляется метод аннотации aop с параметрами. См. следующий пример:
@Pointcut(value = "execution(* com.glmapper.framerwork.service.impl.*(int,int)) && args(i,j)")
public void cutIn(int i, int j) {}
@Before(value="cutIn(i, j)",argNames = "i,j")
public void beforeMethod( int i, int j) {
System.out.println("---------begins with " + i + "-" +j);
}
Например, здесь есть два параметра типа int раньше, если мы не укажем параметры для него, когда мы его используем в это время, он выдаст:Caused by: java.lang.IllegalArgumentException: error at ::0 formal unbound in pointcutИнформация об исключении.
本来是想放在一篇里面的,但是实在太长了,就分开吧;周末更新下