Previously, aop was notified by manually creating proxy classes, but in daily development, we don't want to hard-code these proxy classes in code. 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. Configuring AOP declaratively (that is, using an xml configuration file)
1. How to use ProxyFactoryBean:
The ProxyFactoryBean class is an implementation class of FactoryBean that allows you to specify a bean as a target and provides a set of notifications and advisors for the bean that will eventually be merged into an AOP proxy. It and our previous ProxyFactory implementations are Advised implementations.
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 before Notification types and creation in SpringAOP
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()); } }
- This notification class is then managed using spring's IOC, declared 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"> <!--injection student--> <bean name="student" class="cn.lyn4ever.aop.aopconfig.Student"> </bean> <!--injection teacher--> <bean name="teacher" class="cn.lyn4ever.aop.aopconfig.Teacher"> <!--Be careful,this student If the property of the above proxy class,Instead of being able to student--> <!--<property name="student" ref="student"/>--> <property name="student" ref="proxyOne"/> </bean> <!--Inject the notification class we created--> <bean id="advice" class="cn.lyn4ever.aop.aopconfig.AuditAdvice"></bean> <!--Create a proxy class to notify with the preceding written notification so that all methods on this class are notified--> <bean name="proxyOne" class="org.springframework.aop.framework.ProxyFactoryBean" p:target-ref="student" p:interceptorNames-ref="interceptorNames"> <!--because interceptorNames The property of is a variable parameter,That is one list--> </bean> <!--Introduced above util Namespace of,Simplified writing--> <util:list id="interceptorNames"> <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(); }
No problem with running results
- This is done by creating notifications directly. Next, let's try creating an entry point (since all the methods in the class are notified above, and we use an entry point to notify only some of them), which is added as follows 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"> <!--injection student--> <bean name="student" class="cn.lyn4ever.aop.aopconfig.Student"> </bean> <!--injection teacher--> <bean name="teacherOne" class="cn.lyn4ever.aop.aopconfig.Teacher"> <!--Be careful,this student If the property of the above proxy class,Instead of being able to student--> <!--<property name="student" ref="student"/>--> <property name="student" ref="proxyOne"/> </bean> <!--Inject the notification class we created--> <bean id="advice" class="cn.lyn4ever.aop.aopconfig.AuditAdvice"></bean> <!--Create a proxy class to notify with the preceding written notification so that all methods on this class are notified--> <bean name="proxyOne" class="org.springframework.aop.framework.ProxyFactoryBean" p:target-ref="student" p:interceptorNames-ref="interceptorNames"> <!--because interceptorNames The property of is a variable parameter,That is one list--> </bean> <!--Introduced above util Namespace of,Simplified writing--> <util:list id="interceptorNames"> <value>advice</value> </util:list> <!--Here's how to use a starting point for notification,The code above is the same as the previous configuration file,No modifications--> <!--sutdent basic bean,We continue to use--> <bean name="teacherTwo" p:student-ref="proxyTwo" class="cn.lyn4ever.aop.aopconfig.Teacher"/> <bean id="proxyTwo" class="org.springframework.aop.framework.ProxyFactoryBean" p:target-ref="student" p:interceptorNames-ref="interceptorAdvisorNames"> </bean> <util:list id="interceptorAdvisorNames"> <value>advisor</value> </util:list> <!--Configure entry points bean--> <bean id="advisor" class="org.springframework.aop.support.DefaultPointcutAdvisor" p:advice-ref="advice"> <property name="pointcut"> <!--We used an anonymous entry point bean To write aspectJ Expression,Of course, there are other types of entry points,You can see this in the link above--> <bean class="org.springframework.aop.aspectj.AspectJExpressionPointcut" p:expression="execution(* talk*(..))"/> </property> </bean> </beans>
- The aspectj expression in the figure above is incorrectly written and has the correct one in the code
2. Use aop namespace
The following namespaces were introduced into the xml, and I've included additional namespaces to keep them unaffected.And then inject the first three bean s very normally
<?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 in a normal way bean--> <!--injection student--> <bean name="student" class="cn.lyn4ever.aop.aopconfig.Student"/> <!--injection teacher--> <bean name="teacherOne" class="cn.lyn4ever.aop.aopconfig.Teacher"> <property name="student" ref="student"/> </bean> <!--Inject the notification class we created--> <bean id="advice" class="cn.lyn4ever.aop.proxyfactory.BeforeAdvice"/> <aop:config> <aop:pointcut id="talkExecution" expression="execution(* talk*(..))"/> <aop:aspect ref="advice"> <!--This is the way we write in our custom notification class--> <aop:before method="beforeSaySomething" pointcut-ref="talkExecution"/> <!--Of course,Other notification types can also be configured--> </aop:aspect> </aop:config> </beans>
In this configuration, we can also configure other types of notifications, but this method property must write the method in that notification class that we customize
- The following syntax is also supported when writing expression in aop:pointcut:
<aop:pointcut id="talkExecution" expression="execution(* talk*(..)) and args(String) and bean(stu*)"/> <!-- The and in the middle can also be expressed as or or args(String) means that the parameter type is string or a custom class, followed by an example Beans (stu*) means that the id of a bean begins with a stu, and beans (*Service*) are often used to represent a service-tier bean -->
3. Use @AspectJ style annotations
Although annotation classes are declared as annotations, you still need to configure a little bit of content in the xml (you can configure it by annotation, but it's easier to use in springboot)
- For convenience, only one HighStudent has been written, and its methods are called directly, independent of external teacher instances
package cn.lyn4ever.aop.aspectj; import cn.lyn4ever.aop.aopconfig.Teacher; import org.springframework.stereotype.Component; /** * Declare that this is a SpringBean, 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() in the back entry point * @param teacher */ public void sleep(Teacher teacher) { System.out.println("I want to sleep"); } }
- Create Face 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 facet classes, that is, include tangent points and notifications */ @Component //Declarations are managed by spring @Aspect //Indicates that this is a tangent class public class AnnotatedAdvice { /* Create a starting point, or of course multiple */ @Pointcut("execution(* talk*(..))") public void talkExecution(){} @Pointcut("bean(high*)")//Why is it high here, because we are testing the bean s high Student this time public void beanPoint(){} @Pointcut("args(value)") public void argsPoint(Teacher value){} /* Create notifications, or of course multiple The parameter to this note is the entry point method name above, note that some have parameters This notification method has the same parameters as before, with or without JoinPoint */ @Before("talkExecution()") public void doSomethingBefore(JoinPoint joinPoint){ System.out.println("before: Do Something"+joinPoint.getSignature().getName()+"()"); } /** * Wrap notification with ProceedingJoinPoint parameter, which is a subclass of joinPoint * You have to add this if you want to release the method * @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"> <!--notice Spring scanning@Aspect annotation--> <aop:aspectj-autoproxy/> <!--Configure Scan Package,scanning@Component--> <context:component-scan base-package="cn.lyn4ever.aop.aspectj"/> </beans>
- Configuring scan annotations using Java annotation configuration
@Configuration //Declare that this is a configuration class @ComponentScan("cn.lyn4ever.aop.aspectj") @EnableAspectJAutoProxy(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 surrounded by notifications 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 surrounded by notifications System.out.println(); student.talk();//Before advice System.out.println(); student.walk();//Will not be notified System.out.println(); } }
The project code address, if you think it's okay, give it a star t