之前我谈到过动态代理:Spring学习-动态代理
动态代理里面很好地体现了AOP的思想,即面向切面编程:在不修改代码影响原有业务逻辑时,新增其他功能。
那么,在Spring中,如何实现AOP呢?
方法一:使用Spring的API接口
首先,需要新导入一个依赖:
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
接着,我们编写一个接口及其实现类:
package com.yyl.service;
/**
* @Author: LongLongA
* @Description:
* @Date: Created in 9:51 2020/12/14
*/
public interface UserService {
void add();
void delete();
void update();
void select();
}
package com.yyl.service;
/**
* @Author: LongLongA
* @Description:
* @Date: Created in 9:51 2020/12/14
*/
public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("add...");
}
@Override
public void delete() {
System.out.println("delete...");
}
@Override
public void update() {
System.out.println("update...");
}
@Override
public void select() {
System.out.println("select...");
}
}
这时,我们设想一个应用场景:需要一个日志类,在每次接口方法被调用时,打印日志。
之前的时候,我们可以自己写一个关于接口的代理类来完成上述业务,与此同时,Spring框架也实现了相关接口:
首先,编写一个日志类,然后实现Spring中提供的接口:
package com.yyl.log;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
/**
* @Author: LongLongA
* @Description:
* @Date: Created in 10:08 2020/12/14
*/
public class MyLog implements MethodBeforeAdvice {
@Override
/*
* method : 要执行的目标对象的方法
* args : 方法所需参数
* target: 被执行的目标(被切入的目标)
*
* */
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"的"+method.getName()+"被调用");
}
}
这里,MethodBeforeAdvice接口表明该日志类需要执行的方法在指定业务之前。声明后,before里面的函数会在target对象中的method方法执行之前被调用。
之后我们再编写一个日志类:
package com.yyl.log;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
/**
* @Author: LongLongA
* @Description:
* @Date: Created in 10:18 2020/12/14
*/
public class MyLog2 implements AfterReturningAdvice {//该接口用于在target执行之后调用
@Override
// returnValue : 该参数用于接收返回值
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行了"+method.getName()+"方法,返回结果为:"+returnValue);
}
}
以上事情做完后,我们需要在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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--注册Bean-->
<bean id="userServiceImpl" class="com.yyl.service.UserServiceImpl"/>
<bean id="myLog" class="com.yyl.log.MyLog"/>
<bean id="myLog2" class="com.yyl.log.MyLog2"/>
<!--配置AOP:需要导入aop的约束-->
<aop:config>
<!--
定义切入点,即需要在哪个位置执行
execution(要执行的类的位置)
-->
<aop:pointcut id="pointcut1" expression="execution(* com.yyl.service.UserServiceImpl.*(..))"/>
<!--
定义切入的方法,将其与切入点关联
advice-ref属性值为切入方法的Id
pointcut-ref属性值为切入点的id
-->
<aop:advisor advice-ref="myLog" pointcut-ref="pointcut1"/>
<aop:advisor advice-ref="myLog2" pointcut-ref="pointcut1"/>
</aop:config>
</beans>
下面是测试代码:
import com.yyl.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @Author: LongLongA
* @Description:
* @Date: Created in 11:35 2020/12/14
*/
public class MyTest {
public static void main(String[] args) {
//获取Spring配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//重点!!!:动态代理代理的是接口,所以返回值必须为接口,而不是它的实现类!!!
UserService userServiceImpl = (UserService)context.getBean("userServiceImpl");
userServiceImpl.add();
userServiceImpl.delete();
userServiceImpl.update();
userServiceImpl.select();
}
}
测试结果:
重点来了!!!!!
动态代理代理的是接口,所以返回值必须为接口,而不是它的实现类!!!
所以在此代码:UserService userServiceImpl = (UserService)context.getBean("userServiceImpl");
返回值一定要是UserService接口,而不是它的实现类!!!
以后只要记住一点:“动态代理代理的是接口,而不是实现类”