Поговорим об АОП: представление и основные понятия

Java задняя часть Spring

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Информация об исключении.

本来是想放在一篇里面的,但是实在太长了,就分开吧;周末更新下