2.3.3、 根据切面中的信息创建代理对象
<?xml version="1.0" encoding="utf-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <!--代理目标--> <bean id="newsDAO" class="com.xxxx.spring.dao.impl.NewsDAOImpl"/> <bean id="xxxDAO" class="com.xxxx.spring.dao.impl.XXXDAOImpl"/> <!--定义前置增强--> <bean id="newsDAOAdvice" class="com.xxxx.spring.advice.NewsDAOAdvice"/> <!--定义环绕增强--> <bean id="newsDAOAroundAdvice" class="com.xxxx.spring.advice.NewsDAOAroundAdvice"/> <!--定义切面--> <bean id="pointCutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <!--<property name="patterns" value=".*"/>--> <property name="patterns" value="com.xxxx.spring.dao.impl.XXXDAOImpl.insert.*"/> <property name="advice" ref="newsDAOAroundAdvice"/> </bean> <!--根据切面信息创建自动代理--> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/> </beans>
3、集成AspectJ
Spring AOP 是一个简化版的 AOP 实现,并没有提供完整版的 AOP 功能。通常情况下,Spring AOP 是能够满足我们日常开发过程中的大多数场景的,但在某些情况下,我们可能需要使用 Spring AOP 范围外的某些 AOP 功能。比如Spring AOP 仅支持执行公共(public)非静态方法的调用作为连接点,如果我们需要向受保护的(protected)或私有的(private)的方法进行增强,此时就需要使用功能更加全面的 AOP 框架来实现,其中使用最多的就是 AspectJ。
3.1、需要导入包
spring-aspects-xxx.jar
aspectjweaver-xxxx.jar
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.3.0</version> </dependency> <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.6</version> </dependency>
3.2、基于XML的AspectJ AOP开发
Spring 提供了基于 XML 的 AOP 支持,并提供了一个名为“aop”的命名空间,该命名空间提供了一个 aop:config 元素。
- 在 Spring 配置中,所有的切面信息(切面、切点、通知)都必须定义在 aop:config 元素中;
- 在 Spring 配置中,可以使用多个 aop:config。
- 每一个 aop:config 元素内可以包含 3 个子元素: pointcut、advisor 和 aspect ,这些子元素必须按照这个顺序进行声明。
3.2.1、xml定义命名空间
增加spring-aop
<?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-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd "> </beans>
3.2.2、定义切面
<aop:config> <aop:aspect id="myAspect" ref="myBean"/> </aop:config>
3.2.3、定义切入点
execution 的语法格式格式为:
execution([权限修饰符] [返回值类型] [类的完全限定名] [方法名称]([参数列表]) 其中:
- 返回值类型、方法名、参数列表是必须配置的选项,而其它参数则为可选配置项。
- 返回值类型:*表示可以为任何返回值。如果返回值为对象,则需指定全路径的类名。
- 类的完全限定名:指定包名 + 类名。
- 方法名:*代表所有方法,set* 代表以 set 开头的所有方法。
- 参数列表:
(…)代表所有参数;
(*)代表只有一个参数,参数类型为任意类型;
(*,String)代表有两个参数,第一个参数可以为任何值,第二个为 String 类型的值。
3.2.4、定义通知
<!-- 前置通知 --> <aop:before pointcut-ref="myPointCut" method="..."/> <!-- 后置通知 --> <aop:after-returning pointcut-ref="myPointCut" method="..."/> <!-- 环绕通知 --> <aop:around pointcut-ref="myPointCut" method="..."/> <!-- 异常通知 --> <aop:after-throwing pointcut-ref="myPointCut" method="..."/> <!-- 最终通知 --> <aop:after pointcut-ref="myPointCut" method="..."/>
列子:
/** * 新闻信息服务接口 */ public interface NewsDAO { void insert(); void delete(); void update(); void select(); }
public class NewsDAOImpl implements NewsDAO { @Override public void insert() { System.out.println("执行UserDAOImpl的insert()方法"); } @Override public void delete() { System.out.println("执行UserDAOImpl的delete()方法"); //throw new RuntimeException("ddd"); } @Override public void update() { System.out.println("执行UserDAOImpl的update()方法"); } @Override public void select() { System.out.println("执行UserDAOImpl的select()方法"); } }
定义Apect类
public class NewsAspect { public void before() { System.out.println("前置增强……"); } public void after() { System.out.println("最终增强……"); } public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("环绕增强---前……"); proceedingJoinPoint.proceed(); System.out.println("环绕增强---后……"); } public void afterThrow(Throwable exception) { System.out.println("异常增强…… 异常信息为:" + exception.getMessage()); } public void afterReturning(Object returnValue) { System.out.println("后置返回增强…… 方法返回值为:" + returnValue); } }
定义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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd "> <!--定义bean--> <bean id="newsDAO" class="com.xxxx.spring.dao.impl.NewsDAOImpl"/> <!--定义切面--> <bean id="newsAspect" class="com.xxxx.spring.advice.NewsAspect"/> <aop:config> <!--定义切点--> <aop:pointcut id="beforePointCut" expression="execution(* com.xxxx.spring.dao.NewsDAO.insert(..))"/> <aop:pointcut id="throwPointCut" expression="execution(* com.xxxx.spring.dao.NewsDAO.update(..))"/> <aop:pointcut id="afterRerturningPointCut" expression="execution(* com.xxxx.spring.dao.NewsDAO.delete(..))"/> <aop:pointcut id="afterPointCut" expression="execution(* com.xxxx.spring.dao.NewsDAO.select(..))"/> <aop:pointcut id="aroundPointCut" expression="execution(* com.xxxx.spring.dao.NewsDAO.update(..))"/> <aop:aspect ref="newsAspect"> <aop:before method="before" pointcut-ref="beforePointCut"/> <aop:after-throwing method="afterThrow" pointcut-ref="throwPointCut" throwing="exception"/> <aop:after-returning method="afterReturning" pointcut-ref="afterRerturningPointCut" returning="returnValue"/> <aop:after method="after" pointcut-ref="afterPointCut"/> <aop:around method="around" pointcut-ref="aroundPointCut"/> </aop:aspect> </aop:config> </beans>
3.3、基于注解的AspectJ AOP开发
AspectJ 框架为 AOP 开发提供了一套 @AspectJ 注解。它允许我们直接在 Java 类中通过注解的方式对切面(Aspect)、切入点(Pointcut)和增强(Advice)进行定义。
名称 | 说明 |
@Aspect | 用于定义一个切面。 |
@Pointcut | 用于定义一个切入点。 |
@Before | 用于定义前置通知,相当于 BeforeAdvice。 |
@AfterReturning | 用于定义后置通知,相当于 AfterReturningAdvice。 |
@Around | 用于定义环绕通知,相当于 MethodInterceptor。 |
@AfterThrowing | 用于定义抛出通知,相当于 ThrowAdvice。 |
@After | 用于定义最终通知,不管是否异常,该通知都会执行。 |
@DeclareParents | 用于定义引介通知,相当于 IntroductionInterceptor |
3.3.1、启用@AspectJ注解
Java配置类或者XML配置启用
3.3.1.1、Java配置类
@Configuration @ComponentScan(basePackages = "com.xxxx.spring") @EnableAspectJAutoProxy public class AppConfig { }
3.3.1.2、XML配置
<!-- 开启注解扫描 --> <context:component-scan base-package="com.xxxx.spring"> </context:component-scan> <!--开启AspectJ 自动代理--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
3.3.2、定义切面
@Component @Aspect public class NewsAspect { @Pointcut(value = "execution(* com.xxxx.spring.dao.NewsDAO.insert(..))") public void beforePointCut(){ } @Before(value = "NewsAspect.beforePointCut()") public void before() { System.out.println("前置增强……"); } @After(value = "execution(* com.xxxx.spring.dao.NewsDAO.select(..))") public void after() { System.out.println("最终增强……"); } @Around(value="execution(* com.xxxx.spring.dao.NewsDAO.update(..))") public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println(proceedingJoinPoint.getSignature().getName()); System.out.println("环绕增强---前……"); proceedingJoinPoint.proceed(); System.out.println("环绕增强---后……"); } @AfterThrowing(value = "execution(* com.xxxx.spring.dao.NewsDAO.update(..))",throwing = "exception") public void afterThrow(Throwable exception) { System.out.println("异常增强…… 异常信息为:" + exception.getMessage()); } @AfterReturning(value = "execution(* com.xxxx.spring.dao.NewsDAO.update(..))",returning = "returnValue") public void afterReturning(Object returnValue) { System.out.println("后置返回增强…… 方法返回值为:" + returnValue); } }
3.3.3、运行
public class Test05 { public static void main(String[] args) { AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(AppConfig.class); NewsDAO newsDAO = annotationConfigApplicationContext.getBean("newsDAO", NewsDAO.class); newsDAO.update(); } }
4、aop实战练习
定义日志框架
1、src下面增加log.properties
log.file //日志存放的位置 log.format //日志格式 log.level=DEBUG //日志等级 DEBUG/INFO/WARN/ERROR log.target=console,file #日志输出
2、定义一个类,读取第一步的配置文件
3、定义一个日志生成类 XXXX
根据第二步读到配置内容,执行日志的生成逻辑 提供四个方法:debug、info、warn、error