1、AOP入门案例
1.1、创建工程并引入依赖
工程为com.day03
依赖如下:
<!--依赖-->
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
<!--log4j-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
</dependency>
</dependencies>
1.2、引入applicationContext.xml和log4j.properties
applicationContext.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.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--配置相关类到applicationContext.xml-->
<bean id="customerDao" class="com.day03.dao.Impl.CustomerDaoImpl"></bean>
<!--配置切面类到applicationContext.xml-->
<bean id="aspectXml" class="com.day03.aspect.AspectXml"></bean>
<!--AOP配置-->
<aop:config>
<!--配置切入点-->
<aop:pointcut id="pointCut1" expression="execution(* com.day03.dao.Impl.CustomerDaoImpl.save(..))" />
<!--配置切面 告诉spring框架调用切面类中哪个方法来增强-->
<aop:aspect ref="aspectXml">
<aop:before method="writeLog" pointcut-ref="pointCut1" />
</aop:aspect>
</aop:config>
</beans>
log4j.properties
### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### direct messages to file mylog.log ###
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=D:/mylog.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### set log levels - for more verbose logging change 'info' to 'debug' ###
log4j.rootLogger=debug, stdout, file
1.3、编写CustomerDao.java接口和CustomerDaoImpl.java实现类
CustomerDao.java接口
package com.day03.dao;
public interface CustomerDao {
public abstract void save();
public abstract void delete();
public abstract void update();
public abstract void select();
}
CustomerDaoImpl.java
package com.day03.dao.Impl;
import com.day03.dao.CustomerDao;
public class CustomerDaoImpl implements CustomerDao {
@Override
public void save() {
System.out.println("持久层:客户保存。。。。。。。");
}
@Override
public void delete() {
System.out.println("持久层、删除用户。。。。。");
}
@Override
public void update() {
System.out.println("持久层、更新用户。。。。。。。。。");
}
@Override
public void select() {
System.out.println("持久层、查询用户..................");
}
}
1.4、配置相关类到Spring中
<?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
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--配置相关类到applicationContext.xml-->
<bean id="customerDao" class="com.day03.dao.Impl.CustomerDaoImpl"></bean>
</beans>
1.5、编写切面类AsptectXml.java并配置切面类到Spring中
AsptectXml.java切面类
package com.day03.aspect;
import org.aspectj.lang.JoinPoint;
/**
* @ Author :ShaoWei Sun.
* @ Date :Created in 20:26 2018/11/12
*/
public class AspectXml {
public void writeLog() {
System.out.println("记录日志。。。。。。");
}
}
applicationContext.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.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--配置相关类到applicationContext.xml-->
<bean id="customerDao" class="com.day03.dao.Impl.CustomerDaoImpl"></bean>
<!--配置切面类到applicationContext.xml-->
<bean id="aspectXml" class="com.day03.aspect.AspectXml"></bean>
</beans>
1.6、配置AOP
applicationContext.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.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--配置相关类到applicationContext.xml-->
<bean id="customerDao" class="com.day03.dao.Impl.CustomerDaoImpl"></bean>
<!--配置切面类到applicationContext.xml-->
<bean id="aspectXml" class="com.day03.aspect.AspectXml"></bean>
<!--AOP配置-->
<aop:config>
<!--配置切入点-->
<aop:pointcut id="pointCut1" expression="execution(* com.day03.dao.Impl.CustomerDaoImpl.save(..))" />
<!--配置切面 告诉spring框架调用切面类中哪个方法来增强-->
<aop:aspect ref="aspectXml">
<aop:before method="writeLog" pointcut-ref="pointCut1" />
</aop:aspect>
</aop:config>
</beans>
1.7、测试类
TestAOP.java
package com.day03.test;
import com.day03.dao.CustomerDao;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @ Author :ShaoWei Sun.
* @ Date :Created in 20:54 2018/11/12
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestAOP {
@Autowired
private CustomerDao customerDao;
@Test
public void test1(){
customerDao.save();
}
}
2、Spring中AOP通知类型
前置通知、后置通知、环绕通知、异常通知、最终通知
2.1、前置通知
应用: 权限控制 (权限不足,抛出异常)、 记录方法调用信息日志
2.2、后置通知
特点:在目标方法运行后,返回值后执行增强代码逻辑。
应用场景:与业务相关的,如ATM取款机取款后,自动下发短信。
2.3、环绕通知
特点:目标执行前后,都进行增强(控制目标方法执行)
应用场景:日志、缓存、权限、性能监控、事务管理
增强代码的方法要求:
接受的参数:ProceedingJoinPoint(可执行的连接点)
返回值:Object返回值
抛出Throwable异常。
说明:
ProceedingJoinPoint:表示正在执行的连接点,也就是目标方法
joinpoint.proceed表示调用目标方法
2.4、异常通知
作用:目标代码出现异常,通知执行。记录异常日志、通知管理员(短信、邮件)
应用场景:处理异常(一般不可预知),记录日志
2.5、最终通知
作用:不管目标方法是否发生异常,最终通知都会执行(类似于finally代码功能)
应用场景:释放资源 (关闭文件、 关闭数据库连接、 网络连接、 释放内存对象 )
案例:
CustomerDao.java接口
package com.day03.dao;
public interface CustomerDao {
public abstract void save();
public abstract Integer delete();
public abstract Integer update(int a , int b);
public abstract void list();
public abstract void select();
}
CustomerDaoImpl.java实现类
package com.day03.dao.Impl;
import com.day03.dao.CustomerDao;
public class CustomerDaoImpl implements CustomerDao {
@Override
public void save() {
System.out.println("持久层:客户保存。。。。。。。");
}
@Override
public Integer delete() {
System.out.println("持久层、删除用户。。。。。");
return 100;
}
@Override
public Integer update(int a, int b) {
System.out.println("持久层、更新用户。。。。。。。。。");
Integer c = a + b;
return c;
}
@Override
public void list() {
System.out.println("这是一个异常。。。。。");
//int i = 10/0;
}
@Override
public void select() {
int i =10/0;
System.out.println("持久层、查询用户..................");
}
}
切面类:AspectXml.java
package com.day03.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
/**
* @ Author :ShaoWei Sun.
* @ Date :Created in 20:26 2018/11/12
*/
public class AspectXml {
//入门案例
public void writeLog() {
System.out.println("记录日志。。。。。。");
}
//前置通知 权限控制 、权限不足、抛出异常、
public void before(JoinPoint jp) {
String username = "rose";
if (!"admin".equals(username)) {
throw new RuntimeException("你没有对"+jp.getTarget().getClass().getName()+"类中的"+jp.getSignature().getName()+"没有访问权限");
}
}
//后置通知方法 应用:ATM取款取款后、自动下发短信、参数result:被增强那个方法返回值
public void afterReturning(JoinPoint jp, Object result){
System.out.println("你取款"+result+"元");
}
//环绕通知方法 、应用、事务处理 proceedingJoinPoint 正在执行的连接点
//joinPonint表示调用目标方法
public Object around(ProceedingJoinPoint pjp){
Object proceed = null;
System.out.println("开启事务");
//获取目标方法的参数
Object[] args = pjp.getArgs();
try {
proceed = pjp.proceed(args);
System.out.println("事务提交");
// 打印目标方法里参数的结果值
System.out.println(proceed);
} catch (Throwable throwable) {
System.out.println("事务回滚");
}
return proceed;
}
//异常抛出通知
//作用:目标代码出现异常、通知执行、记录日志、通知管理员
public void afterThrowing(JoinPoint jp,Throwable ex){
System.out.println("注意、在"+jp.getTarget().getClass().getName()+"类中"+jp.getSignature().getName()+"方法中发生异常"+ex.getMessage());
}
//最终通知:不管目标方法是否发生异常、最终通知都会执行、类似于finally代码功能、
//应用:释放资源、关闭文件、关闭数据库连接、网络连接、释放内存对象
public void after(JoinPoint jp){
System.out.println("开始释放资源、连接类方法为:"+jp.getTarget().getClass().getName()+"方法:"+jp.getSignature().getName() );
}
}
applicationContext.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.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--配置相关类到applicationContext.xml-->
<bean id="customerDao" class="com.day03.dao.Impl.CustomerDaoImpl"></bean>
<!--配置切面类到applicationContext.xml-->
<bean id="aspectXml" class="com.day03.aspect.AspectXml"></bean>
<!--AOP配置-->
<aop:config>
<!--配置切入点-->
<aop:pointcut id="pointCut1" expression="execution(* com.day03.dao.Impl.CustomerDaoImpl.save(..))" />
<aop:pointcut id="pointCut2" expression="execution(* com.day03.dao.Impl.CustomerDaoImpl.delete(..))"/>
<aop:pointcut id="pointCut3" expression="execution(* com.day03.dao.Impl.CustomerDaoImpl.update(..))"/>
<aop:pointcut id="pointCut4" expression="execution(* com.day03.dao.Impl.CustomerDaoImpl.list(..))"/>
<aop:pointcut id="pointCut5" expression="execution(* com.day03.dao.Impl.CustomerDaoImpl.select(..))"/>
<!--配置切面 告诉spring框架调用切面类中哪个方法来增强-->
<aop:aspect ref="aspectXml">
<aop:before method="before" pointcut-ref="pointCut1" />
<aop:after-returning method="afterReturning" pointcut-ref="pointCut2" returning="result" />
<aop:around method="around" pointcut-ref="pointCut3"/>
<aop:after-throwing method="afterThrowing" pointcut-ref="pointCut4" throwing="ex"/>
<aop:after method="after" pointcut-ref="pointCut5"/>
</aop:aspect>
</aop:config>
</beans>
TestAOP.java测试类
package com.day03.test;
import com.day03.dao.CustomerDao;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @ Author :ShaoWei Sun.
* @ Date :Created in 20:54 2018/11/12
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestAOP {
@Autowired
private CustomerDao customerDao;
//前置
@Test
public void test1(){
customerDao.save();
}
//后置
@Test
public void test2(){
customerDao.delete();
}
//环绕
@Test
public void test3(){
customerDao.update(5,8);
}
//异常
@Test
public void test4(){
customerDao.list();
}
//最终、不管目标方法是否发生异常、最终通知都运行
@Test
public void test5(){
customerDao.select();
}
}