一、Spring-AOP代码实现
0、引入jar包:
spring-aop.jar
spring-aspects.jar 依赖:aspectjweaver.jar
spring-expression.jar
1、定义接口
public interface OrderService {
public void save(String name);
public void delete(String name);
public String query(String name);
}
2、定义接口的实现类
public class OrderServiceImpl implements OrderService{
@Override
public void save(String name) {
System.out.println("新增订单 "+ name);
}
@Override
public void delete(String name) {
System.out.println("删除订单 "+ name);
}
@Override
public String query(String name) {
System.out.println("查询订单 "+ name);
return name;
}
}
3、定义通知(通知其实就会额外要做的操作)
- 前置通知
package aop.advice_pointcut;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.*;
import java.lang.reflect.Method;
/**
* @program: SpringAll
* @author: weidd
* @date: 2021-04-09 23:41
* 通知的类型
* 前置通知 MethodBeforeAdvice
* 后置通知 AfterReturningAdvice
* 异常通知 ThrowsAdvice
* 环绕通知 MethodInterceptor
**/
public class OrderAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("前置通知===========");
System.out.println("method.getName()-->"+method.getName());
}
}
4、组件交给Spring管理同时建立组件和通知之间的关系(IOC+DI)
<?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
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--管理业务组件-->
<bean id="orderService" class="aop.advice_pointcut.OrderServiceImpl"></bean>
<!--配置通知类-->
<bean id="orderAdvice" class="aop.advice_pointcut.OrderAdvice"></bean>
<!--
配置切入点(<aop:config回车引入aop的标签)
-->
<aop:config>
<!--
id:切入点在工厂中的的唯一id
expression:表达式 execution(*[方法的返回值为任意值] 包名.类名.*[*代表所有的方法](..不关心参数)))
-->
<aop:pointcut id="orderPointcut" expression="execution(* aop.advice_pointcut.OrderServiceImpl.*(..))"/>
<!--组装切面-->
<aop:advisor advice-ref="orderAdvice" pointcut-ref="orderPointcut"></aop:advisor>
</aop:config>
</beans>
5、测试AOP
public class Test2 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("advice/advice.xml");
OrderService orderService = (OrderService) context.getBean("orderService");
System.out.println("===========================");
orderService.save("奔波儿");
System.out.println("===========================");
System.out.println("orderService.getClass() -->"+orderService.getClass());
/**
* ===========================
* 前置通知===========
* method.getName()-->save
* 新增订单 奔波儿
* ===========================
* orderService.getClass() -->class com.sun.proxy.$Proxy3
*/
}
}
二、AOP中重要的概念:
- Advice(通知):除目标方法以外的操作都称为通知。(额外的操作)
- pointcut(切入点):用来告诉开发的通知类应用于项目中哪些类的哪些方法。(哪些方法需要被增强)
- Aspect(切面):通知+切入点构成切面
AOP帮助我们解决了什么问题?
- 通过为项目中的某些类在程序运行的过程中动态的生成代理对象,由生成的代理对象完成项目中通用的附加操作,从而减少项目中通用代码的冗余问题。
多个切面的执行顺序?
- 切面的配置顺序就是切面的执行顺序。
- 使用
aop:advisor
中的order属性来控制切面的执行顺序。(使用order属性,配置顺序将失效。)【order是int类型的数值,数值越小越先执行】
环绕通知
- 环绕通知注意事项:需要有一个放行的操作。并且有返回值需要处理。
package aop.advice_around;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class AroundAdvice implements MethodInterceptor {
// @Override
// jdk中动态代理: 参数1:当前代理对象. 参数2:当前执行方法对象 方法3: 参数
// 参数:MethodInvocation :对象是spring对jdk动态代理的进一步封装.
// MethodInvocation中包含: 当前代理对象\当前方法\目标对象\方法执行的参数
/*public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("方法名: " + invocation.getMethod().getName());
System.out.println("方法的参数: " + invocation.getArguments());
System.out.println("目标对象" + invocation.getThis());
// 放行去执行目标方法
invocation.proceed();
System.out.println("----目标方法执行之后----");
return null;
}*/
@Override
// jdk中动态代理: 参数1:当前代理对象. 参数2:当前执行方法对象 方法3: 参数
// 参数:MethodInvocation :对象是spring对jdk动态代理的进一步封装.
// MethodInvocation中包含: 当前代理对象\当前方法\目标对象\方法执行的参数
public Object invoke(MethodInvocation invocation) throws Throwable {
// System.out.println("方法名: " + invocation.getMethod().getName());
// System.out.println("方法的参数: " + invocation.getArguments());
// System.out.println("目标对象" + invocation.getThis());
long start = System.currentTimeMillis();
// 放行去执行目标方法
Object proceed = invocation.proceed();// 重要 TODO
System.out.println("----目标方法执行之后----");
long end = System.currentTimeMillis();
System.out.println("目标方法的执行时间为--> " + (end - start));
return proceed;
}
}
三、Spring 动态代理注意事项:
spring 框架生成代理的方式:
-
两种:一种是jdk动态代理,一种是基于cglib动态代理
-
jdk proxy(默认):基于接口生成的动态代理对象,只能赋值给接口
-
Spring cglib:基于实现类生成的代理对象,技能赋值给实现类,又能赋值给接口。
-
注意: jdk proxy : Spring AOP 基于接口生成的代理对象,只能赋值给接口,不能赋值给其实现类,否则会报错。
//spring aop基于接口生成的代理对象只赋值给接口。
EmployeeService employeeService = (EmployeeService) context.getBean("employeeService");
//将代理对象赋值给接口的实现类,程序报错。
// EmployeeServiceImpl employeeService = (EmployeeServiceImpl) context.getBean("employeeService");
- 两种动态代理的切换:
<aop:config proxy-target-class="false">
默认是false
,jdk动态代理,基于接口实现。
四、Spring创建简单对象和复杂对象
- 简单对象(可以通过
new
生成的对象):①直接将对象交个Spring容器去管理; - 复杂对象(不能直接new生成的对象:如抽象类和接口):①:为需要增强的组件创建一个XXXFactoryBean,实现Spring提供的FactoryBean接口,泛型为需要增强的组件。②:将该XXXFactoryBean组件交给Spring容器去管理。
- Spring创建复杂对象代码:
public class CalendarFactory implements FactoryBean<Calendar> {
@Override
//用来创建复杂对象的创建方式
public Calendar getObject() throws Exception {
return Calendar.getInstance();
}
@Override
//复杂对象的类型
public Class<?> getObjectType() {
return Calendar.class;
}
@Override
//用来创建复杂对象的创建次数.单例
public boolean isSingleton() {
return true;
}
}
<!--将复杂类的工厂类交给spring容器管理-->
<bean id="calendar" class="factoryBean.CalendarFactory"></bean>
- Spring创建简单对象代码:
public class ProductServiceImpl implements ProductService {
@Override
public void save(String id) {
System.out.println("新增商品" + id);
}
@Override
public void update(String id) {
System.out.println("更新商品");
}
}
<!--被代理对象 taget-->
<bean id="productService" class="aop.staticproxy.service.ProductServiceImpl"></bean>