AOP
Aspect Oriented Programing 面向切面编程
连接点 可以被增强的方法
切入点 连接点中被选中要增强的方法
通知/增强 拦截到切入点并执行一段代码 通知有五种类型
目标对象 被代理(增强)的对象
织入 把增强应用到被代理对象 从而创建代理对象的过程
代理 被代理类被AOP增强后产生的类
切面 增强类 切入点和通知的结合 里面定义了拦截到切入点后执行的方法(通知)
JDK动态代理
JDK动态代理,必须面向接口,生成代理对象 ,如果目标对象没有实现接口,无法使用JDK动态代理 !
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 对目标对象 生成Jdk动态代理
public class JdkProxyFactory implements InvocationHandler {
// 被代理目标对象
private Object target;
// 1、 构造器 ,传入目标对象
public JdkProxyFactory(Object target) {
this.target = target;
}
// 2、提供创建代理对象的方法
public Object createProxy() {
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
}
@Override
// 3、编写回调函数,拦截目标对象所有方法,都会执行invoke方法
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("记录日志:" + method.getName() + "方法被执行了...");
return method.invoke(target, args);
}
}
Cglib动态代理
Spring AOP 优先对接口创建代理 ,对接口代理使用JDK动态代理
如果目标对象没有接口,使用cglib动态代理
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
// 工具类,对目标进行cglib代理
public class CglibProxyFactory implements MethodInterceptor {
// 被代理目标对象
private Object target;
// 1、 通过构造器,传入被代理目标对象
public CglibProxyFactory(Object target) {
this.target = target;
}
// 2、 提供生成代理对象的方法
public Object createProxy() {
// 用于生成代理 API类
Enhancer enhancer = new Enhancer();
// 设置目标类,根据类生成子类代理
enhancer.setSuperclass(target.getClass());
// 设置回调函数
enhancer.setCallback(this);
// 生成代理返回
return enhancer.create();
}
@Override
// 3、 编写回调拦截函数,拦截目标对象所有方法
public Object intercept(Object proxy, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
System.out.println("记录日志:" + method.getName() + "方法被调用....");
return methodProxy.invokeSuper(proxy, args);// 调用父类(真实对象)的方法
// return method.invoke(target, args);
}
}
传统的AOP
主要用于spring2.0之前 (1.2)年代,这个版本AOP编程比较复杂
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
// 自定义 环绕通知(Spring 传统AOP Advice)
public class MyMethodInterceptor implements MethodInterceptor {
@Override
// 拦截目标对象方法,mi用于执行目标方法
public Object invoke(MethodInvocation invocation) throws Throwable {
long start = System.currentTimeMillis();
Object returnVal = invocation.proceed(); // 执行目标方法
long end = System.currentTimeMillis();
System.out.println("=====================================");
System.out.println("类 :" + invocation.getThis().getClass().getSimpleName());
System.out.println("方法:" + invocation.getMethod().getName());
System.out.println("耗时:" + (end - start) + "毫秒!");
System.out.println("=====================================");
return returnVal;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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="com.hrh"/>
<aop:config proxy-target-class="false">
<aop:pointcut id="myPointcut" expression="bean(*Dao)"/>
<aop:pointcut id="myPointcut2" expression="within(com.hrh..*)"/>
<!--<aop:pointcut id="myPointcut3" expression="execution(*com.hrh.a.UserDaoImpl.a*())"/>-->
<aop:advisor advice-ref="myMethodInterceptor" pointcut-ref="myPointcut"/>
</aop:config>
</beans>
切入点语法说明
4.3.1 语法一: execution(修饰符? 返回值 方法名(参数) 异常?)
execution(* *(..)) 匹配所有spring管理对象所有方法, 第一个*任意返回值 ,第二个*任意方法, .. 任意参数
execution(* cn.itcast.spring.*..*(..)) 匹配cn.itcast.spring包中所有对象所有方法
execution(* cn.itcast.spring.CustomerService.s*(..)) 匹配CustomerService中s开头方法
4.3.2 语法二:bean(beanName) 匹配目标Bean所有方法
bean(*Service) 匹配所有以Service结尾BeanName 的对象
4.3.3 语法三:within(包.*) 匹配包下所有类的所有方法
within(cn.itcast.spring..*) 匹配spring包及其子包中类所有方法
注意: 一个
.
代表子目录; 两个点..
表示后代目录
AspectJ AOP切面编程(XML)
try{
前置增强...
// 执行目标方法
后置增强
}catch{
抛出增强
}finally{
最终通知
}
Dao和Service(被代理类)
public class AccountDao {
public void in(String inUser, double money) {
System.out.println(inUser + "收到 " + money );
int j = 1/0;
}
public void out(String outUser, double money) {
System.out.println(outUser + "转出 " + money);
}
}
public class AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public void transfer(String outUser, String inUser, double money) {
accountDao.in(inUser, money);
accountDao.out(outUser, money);
}
}
切面类
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
// 在一个切面类,可以提供多个 advice方法
public class MyAspectJ {
// 环绕通知: 返回object
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
Object returnVal = null;
try {
// 1 开启事务
System.out.println("================1 开启事务");
// 2 执行一组sql语句
returnVal = joinPoint.proceed();
// 3.1 提交事务
System.out.println("================3.1 提交事务");
} catch (Exception ex) {
ex.printStackTrace();
// 3.2 回滚事务
System.out.println("================3.2 回滚事务");
} finally {
// 4 释放资源
System.out.println("================4 释放资源");
}
return returnVal;
}
// 前置通知
public void before01(JoinPoint joinPoint) {
System.out.println("--------------------- 前置通知: 开启事务 ... ..." );
}
// 后置增强
public void afterRegurning(JoinPoint joinPoint, Object returnObject) {
System.out.println("--------------------- 后置通知: 提交事务 ... ..." );
}
public void afterThrowing(JoinPoint joinPoint, Throwable ex) {
System.out.println("--------------------- 异常通知: 回滚事务 ... ..." );
}
public void after(JoinPoint joinPoint) {
System.out.println("--------------------- 最终通知: 释放资源 ... ..." );
}
}
}
测试类
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:beans_aspect.xml")
public class SpringTest {
@Value("#{accountService}")
private AccountService accountService;
@Test
public void demo01() {
accountService.transfer("张三", "李四", 10000);
}
}
XML
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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
">
<!--1 配置目标类-->
<bean id="accountDao" class="cn.hrh.AccountDao"/>
<bean id="accountService" class="cn.hrh.AccountService">
<property name="accountDao" ref="accountDao"/>
</bean>
<!--2 配置增强类-->
<bean id="myAspectJ" class="cn.hrh.MyAspectJ"/>
<!--3 配置切入点和切面-->
<aop:config proxy-target-class="false">
<!--3.1 ref引入通知方法所在的类-->
<aop:aspect ref="myAspectJ">
<!--3.2 配置六种通知类型和切入点-->
<aop:pointcut id="myPointcut" expression="bean(*Service)"/>
<!--环绕增强-->
<!--<aop:around method="around" pointcut-ref="myPointcut"/>-->
<!--前置增强-->
<aop:before method="before01" pointcut-ref="myPointcut"/>
<!--后置增强-->
<aop:after-returning method="afterRegurning" pointcut-ref="myPointcut" returning="returnObject"/>
<!--异常通知-->
<aop:after-throwing method="afterThrowing" pointcut-ref="myPointcut" throwing="ex"/>
<!--最终通知-->
<aop:after method="after" pointcut-ref="myPointcut" />
</aop:aspect>
</aop:config>
</beans>
AspectJ AOP注解编程
Dao和Service
import org.springframework.stereotype.Repository;
@Repository("accountDao")
public class AccountDao {
public void in(String inUser, double money) {
System.out.println(inUser + "收到 " + money );
//int j = 1/0;
}
public void out(String outUser, double money) {
System.out.println(outUser + "转出 " + money);
}
}
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
@Service("accountService")
public class AccountService {
@Value("#{accountDao}")
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public void transfer(String outUser, String inUser, double money) {
accountDao.in(inUser, money);
accountDao.out(outUser, money);
}
}
切面类
// 切面类
@Component("myAspectJ")
@Aspect
// @Aspect 说明这个类是切面类,里面包含增强方法
public class MyAspect {
//@Pointcut("execution(* *(..))")
@Pointcut("within(com.hrh..*)")
private void myAspect(){}
//@Around("bean(*Service)")
public Object around(ProceedingJoinPoint proceedingJoinPoint){
Object proceed=null;
try{
System.out.println("开启事务");
proceed = proceedingJoinPoint.proceed();
System.out.println("提交事务");
}catch(Throwable e){
System.out.println("回滚事务");
}finally {
System.out.println("释放资源");
}
return proceed;
}
@Before(value = "myAspect()")
public void before(JoinPoint joinPoint){
System.out.println("1 前置通知啊");
}
@AfterReturning(value = "myAspect()",returning = "returnVal")
public void afterReturning(JoinPoint joinPoint,Object returnVal){
System.out.println("2 后置通知啊");
}
@After(value = "myAspect()")
public void after(JoinPoint joinPoint){
System.out.println("3 最终通知啊");
}
@AfterThrowing(value = "myAspect()",throwing = "ex")
public void afterThrowing(JoinPoint joinPoint,Throwable ex){
System.out.println("2 异常通知啊");
}
}
测试类
package com.hrh;
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;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class test {
@Autowired
private UserService userService;
@Test
public void test(){
userService.transfer("王五","赵六",88888);
}
}
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:context="http://www.springframework.org/schema/context"
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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.hrh"/>
<aop:aspectj-autoproxy proxy-target-class="false"/>
</beans>