学习AOP初级的第一天
AOP 的好处
-
降低模块之间的耦合度
-
使系统容易扩展
-
更好的代码复用
-
非业务代码更加集中,不分散,便于统一管理
AOP 是对面向对象编程的一个补充,在运行时,动态的将代码切入到类的指定方法,指定位置,的编程思想就是面向切面编程
首先 创建一个maven 工程
pom.xml 添加依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.11.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.0.11.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.11.RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
创建一个动态代理类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
public class MyInvocationHandler implements InvocationHandler {
// 接受委托对象
private Object object;
// 返回代理对象
public Object bind(Object object){
this.object = object;
return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), this);
}
// 动态代理
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method.getName()+"方法的参数"+ Arrays.toString(args));
Object result = method.invoke(this.object, args);
System.out.println(method.getName()+"结果是:"+result);
return result;
}
}
在CalImpl里实现接口Cal的方法
public class CalImpl implements Cal {
public int add(int num1, int num2) {
return num1+num2;
}
public int minus(int num1, int num2) {
return num1-num2;
}
public int multi(int num1, int num2) {
return num1*num2;
}
public int div(int num1, int num2) {
return num1/num2;
}
}
测试类Test
MyInvocationHandler myInvocationHandler = new MyInvocationHandler();
Cal cal1 =(Cal)myInvocationHandler.bind(cal);
cal1.add(1,2);
cal1.minus(2,3);
cal1.div(4,5);
cal1.multi(7,6);
上述是使用动态代理类实现AOP的过程
如果没有使用动态代理类,常规写法:
CalImpl里:
public class CalImpl implements Cal {
public int add(int num1, int num2) {
System.out.println("方法参数");
int result = num1+num2;
System.out.println("方法结果"+result);
return result;
}
public int minus(int num1, int num2) {
System.out.println("方法参数");
int result = num1-num2;
System.out.println("方法结果"+result);
return result;
}
public int multi(int num1, int num2) {
System.out.println("方法参数");
int result = num1*num2;
System.out.println("方法结果"+result);
return result;
}
public int div(int num1, int num2) {
System.out.println("方法参数");
int result = num1/num2;
System.out.println("方法结果"+result);
return result;
}
}
测试类Test:
Cal cal = new CalImpl();
cal.add(1,2);
cal.minus(2,3);
cal.div(4,5);
cal.multi(7,6);
实现接口的类里就需要写很多重复的语句,而且修改起来很麻烦,他们运许结果相同,动态代理类明显的解决了耦合作用,但是动态代理类用起来很麻烦,Spring封装了动态代理类
使用Spring
@Aspect
@Component
public class LoggerAspects {
@Before("execution(public int cn.learnbyheart.impl.CalImpl.*(..))")
public void before(JoinPoint joinPoint){ // JoinPoint 连接点
// 在执行CalImpl里的方法前,先执行before() 输出第一条日志
// System.out.println(method.getName()+"方法的参数"+Arrays.toString(args));
// 获取方法名
String name = joinPoint.getSignature().getName();
// 获取参数
String args = Arrays.toString(joinPoint.getArgs());
System.out.println(name+"参数:"+args);
}
@After("execution(public int cn.learnbyheart.impl.CalImpl.*(..))")
public void after(JoinPoint joinPoint){
// 获取方法名
String name = joinPoint.getSignature().getName();
System.out.println(name+"执行完毕");
}
// 返回结果值
@AfterReturning(value = "execution(public int cn.learnbyheart.impl.CalImpl.*(..))",returning = "result")
public void afterReturing(JoinPoint joinPoint,Object result){ // 两个要一致 否则报错
String name = joinPoint.getSignature().getName();
System.out.println("方法的结果值"+result);
}
// 抛异常
@AfterThrowing(value = "execution(public int cn.learnbyheart.impl.CalImpl.*(..))",throwing = "exception")
public void throwExp(JoinPoint joinPoint,Exception exception){
String name = joinPoint.getSignature().getName();
System.out.println(name+"方法抛出异常"+exception);
}
}
注意在返回值那里,两个参数要一致,否则报错
@Aspect:表示该类是切面类
@Component:表示该类的对象注入到IOC容器中
具体的方法注解
@Before: 表示方法执行的具体位置和时机
同时要注意,CamImpl类也需要@Component交给IOC容器来管理
resources底下创建spring-aop.xml里配置AOP
<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-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--自动扫描-->
<context:component-scan base-package="cn.learnbyheart"></context:component-scan>
<!-- aspect注解生效 为目标类自动生成代理对象-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
- context:component-scan:将cn.learnbyheart包中的所有类扫描,如果该类也有@Component注解,那就扫描进IOC容器中
- aop:aspectj-autoproxy 自动生成动态代理对象
测试类Test里:
public class Test2 {
public static void main(String[] args) {
// 加载配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-aop.xml");
// 获取代理对象
Cal proxy = (Cal) applicationContext.getBean("test");
// 如果CalImpl的@Component没有写 就用类名首字母小写代替
// Cal proxy = (Cal) applicationContext.getBean("calImpl");
proxy.add(1,2);
}
}
@Component("test") 作用相当于
<bean id="test" class="cn.learnbyheart.Impl.CalImpl"></bean>
最后:
切面:横切关注点被模块化的抽象对象
通知:切面对象完成的工作
目标:被通知的对象,即被横切的对象
代理:切面,通知,目标混合之后的对象,
连接点:通知要插入业务代码的具体位置
切点:AOP通过切点定位到连接点