AOP:面向切面编程.指的是将项目中的切面(横切逻辑)单独抽离出来,进行统一编程处理.
-
1:切面:一些零散的,散落在系统各处,不得不处理,但是又与核心业务无关横切逻辑叫切面. eg:异常,日志,事务
-
2:面向对象编程(OOP)主要作用:是处理核心业务逻辑,面向切面编程(AOP)主要作用是处理切面横切逻辑).面向切面编程是面向对象编程的有益补充,或者说是面向对象辅助.
-
3:面向切面编程底层用的就是动态代理模式.
-
4:面向切面编程目地:让程序员专注写业务.
代理模式:代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。
作用:保护原对象,增加原对象的功能.
动态代理:是在程序运行时通过反射机制动态创建的。两种方式:jdk实现动态代理 和 CGLib实现动态代理
jdk实现动态代理:必须依赖原对象的父接口
CGLib实现动态代理:GCLib采用底层的字节码技术,可以为一个类创建子类,在子类中采用方法拦截的技术拦截所有父类方法的调用并 顺势织入横切逻辑.
**
进入正题:AOP动态代理之CGlib注解版实现
**
1.创建maven工程(过程不啰嗦~)
2.pom.xml配置相关依赖包(不必要的可以删掉)
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.1.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.1.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.1.3.RELEASE</version>
</dependency>
<!--注解版相关包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.1.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.1.3.RELEASE</version>
</dependency>
<!--<dependency>-->
<!--<groupId>org.springframework</groupId>-->
<!--<artifactId>spring-orm</artifactId>-->
<!--<version>5.1.3.RELEASE</version>-->
<!--</dependency>-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.1.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.3.RELEASE</version>
</dependency>
<!-- 为了方便进行单元测试,添加spring-test包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.3.RELEASE</version>
</dependency>
<!--日志包-->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging-api</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
</dependency>
</dependencies>
3.创建业务接口(嫌麻烦的可以省略,直接创建一个业务类)
package Demo1;
/**
* 自我介绍的业务接口
*/
public interface peopleService {
public void showMySelf();
}
4.业务接口实现类(类上使用注解)
package Demo1;
import org.springframework.stereotype.Component;
@Component("p1")//用@Service("p1")也可以
public class peopleServiceImpl implements peopleService {
public void showMySelf() {
System.out.println("自我介绍的方法");
// int a = 8/0;
}
}
5.创建增强类,并将切点与增强方法绑定(注意:类和方法上使用了注解),此处用打印日志模拟增强方法中的业务.(嫌麻烦 可以改成sout的打印语句)
package Demo1;
import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* 增强类 MyAdvice
*/
@Aspect
@Component//这个注解不能少
public class MyAdvice {
public Logger log = Logger.getRootLogger();
/*
* xecution(* Demo1.*Impl.*(..)):表示切点 即:需要增强处理的方法
* 此处表示Demo1包下面以Impl结尾的任一方法 方法参数为动态参数(其实就是为了匹配到目标方法 这里也可以直接写方法名)
* */
@Before("execution(* Demo1.*Impl.*(..))")
public void before() {
log.info("前置增强");
}
@AfterReturning("execution(* Demo1.*Impl.*(..))")
public void afterReturn() {
log.info("后置增强");
}
@AfterThrowing("execution(* Demo1.*Impl.*(..))")
public void afterThrowing() {
log.info("异常增强");
}
@After("execution(* Demo1.*Impl.*(..))")
public void after() {
log.info("最终增强");
}
@Around("execution(* Demo1.*Impl.*(..))")
public Object around(ProceedingJoinPoint pj) throws Throwable {
log.info("环绕增强前面");
Object ob = pj.proceed();//调用切点的方法
log.info("环绕增强后面");
return ob;
}
}
6.添加log4j日志文件到resources目录 其内容:
log4j.rootLogger=info,A1
log4j.logger.org.mybatis = info
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c]-[%p] %m%n
7.配置spring.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 http://www.springframework.org/schema/context/spring-context.xsd">
<!--指定扫描注解的包-->
<context:component-scan base-package="Demo1"/>
<!--开启aop扫描-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<!--设置代理模式为CGlib-->
<aop:config proxy-target-class="true"></aop:config>
</beans>
8.进行单元测试
package entity;
import Demo1.peopleService;
import Demo1.peopleServiceImpl;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* aop注解版
*/
public class TestPeopleSevrice {
/**
* 传统方法 业务对象调方法
*/
@Test
public void TestShowMySelf1(){
peopleService p = new peopleServiceImpl();
p.showMySelf();
}
/**
* AOP注解版测试
*/
@Test
public void TestShowMySelf2(){
ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
peopleServiceImpl p1 = ac.getBean("p1", peopleServiceImpl.class);
p1.showMySelf();
}
}
执行注解版测试方法:
当程序异常(将业务实现类的该行代码取消注释:// int a = 8/0;)再次执行注解版测试方法 触发异常抛出增强:
通过看打印的日志,可以看出各个增强方法在相对于切点的调用时机.
附带本次项目结构的截图: