Examples of using spring AOP in Web Application

Time:2021-7-22

preface

In the past, AOP was notified by creating proxy classes manually. However, in daily development, we are not willing to hard code these proxy classes. We prefer to use Di and IOC to manage AOP proxy classes. Spring provides us with the following ways to use the AOP framework

1、 Configure AOP declaratively (that is, using an XML configuration file)

1. How to use proxyfactorybean:

The proxyfactorybean class is an implementation class of factorybean. It allows you to specify a bean as a target and provide a set of notifications and consultants for the bean (these notifications and consultants will eventually be merged into an AOP agent). It and our previous proxyfactory are both implementations of advised.

Here is a simple example: a student and a teacher, the teacher will tell the students what to do.


public class Student {

 public void talk() {
  System.out.println("I am a boy");
 }

 public void walk() {
  System.out.println("I am walking");
 }

 public void sleep() {
  System.out.println("I want to sleep");
 }
}

Teachers


public class Teacher {

 private Student student;

 public void tellStudent(){
  student.sleep();
  student.talk();
 }

 public Student getStudent() {
  return student;
 }

 public void setStudent(Student student) {
  this.student = student;
 }
}

Let’s create a notification class, which is the same as the previous notification type in spring AOP and how to create it

package cn.lyn4ever.aop;

import org.aspectj.lang.JoinPoint;

public class AuditAdvice implements MethodBeforeAdvice {
 @Override
 public void before(Method method, Object[] objects, @Nullable Object o) throws Throwable {
  System. Out. Println ("this method has been notified" + method. Getname());
 }
}

Then use spring’s IOC to manage the notification class, and declare it in the XML configuration file as follows:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:util="http://www.springframework.org/schema/util" xmlns:p="http://www.springframework.org/schema/p"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans.xsd
  http://www.springframework.org/schema/util
  https://www.springframework.org/schema/util/spring-util.xsd">

 <!-- Inject student -- >
 <bean name="student">
 </bean>

 <!-- Inject teacher -- >
 <bean name="teacher">
  <!-- Note that the student property is the agent class above, not the student -- >
  <!--<property name="student" ref="student"/>-->
  <property name="student" ref="proxyOne"/>
 </bean>

 <!-- Inject the notification class we created -- >
 <bean></bean>

 <!-- Create a proxy class and use the notification written in front of it to notify all methods on this class -- >
 <bean name="proxyOne" p:target-ref="student"
   p:interceptorNames-ref="interceptorNames">
  <!-- Because the property of interceptornames is a variable parameter, that is, a list -- >
 </bean>

 <!-- This paper introduces util's name space to simplify the writing
 <util:list>
  <value>advice</value>
 </util:list>
</beans>

Test class


 public static void main(String[] args) {
  GenericXmlApplicationContext context = new GenericXmlApplicationContext();
  context.load("application1.xml");
  context.refresh();

  Teacher teacher = (Teacher) context.getBean("teacherOne");
  teacher.tellStudent();

 }

There is no problem with the running result

The above way is to create a notification directly. Next, we try to create a pointcut (because all methods in the class are notified, and we only use the pointcut to notify some of them). Add the following in the XML configuration file.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:util="http://www.springframework.org/schema/util" xmlns:p="http://www.springframework.org/schema/p"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans.xsd
  http://www.springframework.org/schema/util
  https://www.springframework.org/schema/util/spring-util.xsd">

 <!-- Inject student -- >
 <bean name="student">
 </bean>

 <!-- Inject teacher -- >
 <bean name="teacherOne">
  <!-- Note that the student property is the agent class above, not the student -- >
  <!--<property name="student" ref="student"/>-->
  <property name="student" ref="proxyOne"/>
 </bean>

 <!-- Inject the notification class we created -- >
 <bean></bean>

 <!-- Create a proxy class and use the notification written in front of it to notify all methods on this class -- >
 <bean name="proxyOne" p:target-ref="student"
   p:interceptorNames-ref="interceptorNames">
  <!-- Because the property of interceptornames is a variable parameter, that is, a list -- >
 </bean>

 <!-- This paper introduces util's name space to simplify the writing
 <util:list>
  <value>advice</value>
 </util:list>


 <!-- The following is the way to use pointcuts for notification. The above code is the same as the previous configuration file, and has not been modified -- >
 <!-- Sutdent basic bean, we continue to use -- >

 <bean name="teacherTwo" p:student-ref="proxyTwo"/>

 <bean
   p:target-ref="student" p:interceptorNames-ref="interceptorAdvisorNames">
 </bean>

 <util:list>
  <value>advisor</value>
 </util:list>

 <!-- Configure pointcut bean -- >
 <bean
   p:advice-ref="advice">
  <property name="pointcut">
   <!-- For this pointcut, we use an anonymous bean to write AspectJ expressions. Of course, we can also use other types of pointcuts. This can be seen in the link above -- >
   <bean
     p:expression="execution(* talk*(..))"/>
  </property>

 </bean>

</beans>

The AspectJ expression in the figure above is wrong, and there is a correct expression in the code

2. Use AOP namespace

The following namespace is introduced into XML. In order not to be affected, I use other redundant namespace. And then it’s very common to inject our previous three beans

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:aop="http://www.springframework.org/schema/aop"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans.xsd
  http://www.springframework.org/schema/aop
  http://www.springframework.org/schema/aop/spring-aop.xsd
 ">

 <!-- Inject three beans in a normal way -- >
 <!-- Inject student -- >
 <bean name="student"/>
 <!-- Inject teacher -- >
 <bean name="teacherOne">
  <property name="student" ref="student"/>
 </bean>
 <!-- Inject the notification class we created -- >
 <bean/>


 <aop:config>
  <aop:pointcut expression="execution(* talk*(..))"/>
  <aop:aspect ref="advice">
   <!-- This method is the method we wrote in the custom notification class -- >
   <aop:before method="beforeSaySomething" pointcut-ref="talkExecution"/>
   <!-- Of course, you can configure other notification types -- >
  </aop:aspect>
 </aop:config>


</beans>

In this configuration, we can also configure other types of notifications, but the method property must be written to the method in our custom notification class

When writing expression in AOP: pointcut, the following syntax is also supported:

<aop:pointcut expression="execution(* talk*(..)) and args(String) and bean(stu*)"/>
<!--
The and in the middle can also be used to express or
Args (string) means that the parameter type is string, which can also be a custom class. There are examples after this
Bean (stu *) means that the ID of a bean starts with stu. A bean (* service *) is commonly used to represent the bean of the service layer
-->

3. Use @ AspectJ style annotation method

Although the annotation class is declared by annotation, a little content needs to be configured in XML (it can also be configured by annotation, but there is a more convenient way to use it in springboot)

For convenience, we only write a highstudent and call its methods directly, independent of the external teacher instance

package cn.lyn4ever.aop.aspectj;

import cn.lyn4ever.aop.aopconfig.Teacher;
import org.springframework.stereotype.Component;

/**
 *Declare that this is a spring bean, managed by spring
 */
@Component
public class HighStudent {

 public void talk() {
  System.out.println("I am a boy");
 }

 public void walk() {
  System.out.println("I am walking");
 }

 /**
  *This method adds a teacher as a parameter to configure args ()
  * @param teacher
  */
 public void sleep(Teacher teacher) {
  System.out.println("I want to sleep");
 }
}

Create section class

package cn.lyn4ever.aop.aspectj;

import cn.lyn4ever.aop.aopconfig.Teacher;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 *Declare aspect classes, that is, including pointcuts and notifications
 */
@Component // the declaration is managed by spring
@Aspect // indicates that this is a aspect class
public class AnnotatedAdvice {

 /*
 Creating entry points, of course, can be multiple
  */
 @Pointcut("execution(* talk*(..))")
 public void talkExecution(){}

 @Pointcut ("bean (high *)) // why is it high here, because the bean we tested this time is a high student
 public void beanPoint(){}

 @Pointcut("args(value)")
 public void argsPoint(Teacher value){}

 /*
 Create notifications, of course, can be multiple
 The parameter of this annotation is the name of the pointcut method above. Note that some of them have parameters
 The parameters of this notification method are the same as before. Join point can be added or not
  */
 @Before("talkExecution()")
 public void doSomethingBefore(JoinPoint joinPoint){
  System.out.println("before: Do Something"+joinPoint.getSignature().getName()+"()");
 }

 /**
  *Please add the proceedingjoinpoint parameter, which is a subclass of joinpoint
  *Because if you want to release the method, you have to add this
  * @param joinPoint
  * @param teacher
  */
 @Around("argsPoint(teacher) && beanPoint()")
 public Object doSomethindAround(ProceedingJoinPoint joinPoint, Teacher teacher) throws Throwable {
  System.out.println("Around: Before Do Something"+joinPoint.getSignature().getName()+"()");
  Object proceed = joinPoint.proceed();
  System.out.println("Around: After Do Something"+joinPoint.getSignature().getName()+"()");

  return proceed;
 }

}

Configure open scan annotation in XML

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:aop="http://www.springframework.org/schema/aop"
  xmlns:context="http://www.springframework.org/schema/context"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans.xsd
  http://www.springframework.org/schema/aop
  http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

 <!-- Notify spring to scan @ aspect annotations -- >
 <aop:aspectj-autoproxy/>

 <!-- Configure scan package to scan @ component -- >
 <context:component-scan base-package="cn.lyn4ever.aop.aspectj"/>

</beans>

Using java annotation configuration to configure scan annotation

@Configuration // declares that this is a configuration class
@ComponentScan("cn.lyn4ever.aop.aspectj")
@Enable aspect jautoproxy (proxytargetclass = true) // equivalent to < AOP: AspectJ AutoProxy / > in XML
public class BeanConfig {
}

test method

package cn.lyn4ever.aop.aspectj;

import cn.lyn4ever.aop.aopconfig.Teacher;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

public class AspectMain {
 public static void main(String[] args) {
//  xmlConfig();
  javaConfig();

 }

 private static void javaConfig() {
  GenericApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class);
  HighStudent student = (HighStudent) context.getBean("highStudent");
  student.sleep(new Teacher());// Should be informed
  System.out.println();

  student.talk();// Before advice 
  System.out.println();

  student.walk();// Will not be notified
  System.out.println();
 }

 private static void xmlConfig(){
  GenericXmlApplicationContext context = new GenericXmlApplicationContext();
  context.load("application_aspect.xml");
  context.refresh();

  HighStudent student = (HighStudent) context.getBean("highStudent");
  student.sleep(new Teacher());// Should be informed
  System.out.println();

  student.talk();// Before advice 
  System.out.println();

  student.walk();// Will not be notified
  System.out.println();
 }
}

Project code address. If you think it’s good, give it to star

summary

The above is the whole content of this article, I hope the content of this article has a certain reference learning value for your study or work, thank you for your support to developer.

Recommended Today

MSSQL · query and sort the records of all tables in a database

Reading time |0.27 minutes word count |444 characters primary coverage |1. Introduction & background “MSSQL · query and sort the records of all tables in a database” Author | SCscHero Writing time | 2021/7/13 PM10:0 Article type |Series Degree of completion |Completed motto Every great cause has a trivial beginning. 1、 Introduction & background    Completion: […]