动态代理
- 特点:字节码随用随加载。
- 作用:不修改源码的基础上对方法增强。
- 分类:
基于子类:
maven依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.1_3</version>
</dependency>
创建代理对象使用Ehancer中create方法。参数:class:字节码,被代理对象的字节码。callback:增强代码:一般写改接口的子接口实现类。
要求:被代理类不能是最终类。
基于接口:使用proxy类中中的newProxyInstance方法,被代理的类最少要实现一个接口。参数:ClassLoader:类加载器,写被代理的类。Class[ ]:字节码数组,让被代理对象和代理对象有相同的方法。InvocationHandler:提供增强的代码。一都是改接口的实现类。实现类谁用谁写。
AOP
面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
优势:减少重复代码;提高开发效率;维护方便。
- AOP术语:
Joinpoint(连接点):指被拦截到的点,指的是方法。
Pointcut(切入点):指要对那些Joinponit进行连接。被增强的方法。
Advice(通知/增强):拦截到Joinpoint之后要做的事情就是通知。通知类型:前置通知,后置通知,异常通知,最终通知,环绕通知。
Introduction(引介):在运行期为类动态的添加一些方法。
Target(目标对象):代理的目标对象。
Weaving(织入):把增强应用到目标对象来创建新的代理对象的过程。
Proxy(代理):一个类被AOP织入增强后,就产生一个代理结果类。
Aspect(切面):切入点和通知(引介)的结合。 - 基于xml配置:
service:
public interface AccountService {
/**
* 模拟保存
*/
void saveAccount();
/**
* 模拟更新
* @param i
*/
void updateAccount(int i);
/**
* 删除账户
* @return
*/
int deleteAccount();
}
serviceImpl:
public class AccountServiceImpl implements AccountService {
public void saveAccount() {
System.out.println("执行了保存");
}
public void updateAccount(int i) {
System.out.println("执行了更新"+i);
}
public int deleteAccount() {
System.out.println("执行了删除");
return 0;
}
}
切面类:
public class Logger {
/**
* 打印日志:计划让其在切入点方法前执行(切入点方法就是业务层方法)
*/
public void printLog(){
System.out.println("Logger->printLog");
}
public Object arroundPrintLog(ProceedingJoinPoint proceedingJoinPoint){
Object rtValue = null;
try {
Object[] args = proceedingJoinPoint.getArgs();//得到方法执行所需的参数。
System.out.println("写在proceed前是前置通知");
rtValue = proceedingJoinPoint.proceed(args);//调用业务层(切入点)方法
System.out.println("写在proceed前是后置通知");
return rtValue;
}catch (Throwable e){
System.out.println("写在catch里面是异常通知");
throw new RuntimeException(e);
}finally {
System.out.println("写在finally里面是最终通知");
}
}
}
xml:
<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">
<!--配置spring中的IoC-->
<bean id="accountService" class="com.demo.service.Impl.AccountServiceImpl"></bean>
<!--AOP配置-->
<!--Logger类-->
<bean id="logger" class="com.demo.utils.Logger"></bean>
<!--开始配置AOP-->
<aop:config>
<!--配置切入点表达式 如果将它写在aop:aspect之前则所有切面都可用-->
<aop:pointcut id="pt" expression="execution( * com.demo.service.Impl.AccountServiceImpl.*(..))"></aop:pointcut>
<!--开始配置切面 id:给切面配置唯一路径;ref:指定通知类bean的id-->
<aop:aspect id="logAdvice" ref="logger">
<!--配置通知类型 method:用于指定某方法; pointcut:表明对业务层中那些方法增强,写法:execution(访问修饰符 返回值 包名.包名...类名.方法名(参数列表) 也可以使用通配符-->
<!--前置通知 -->
<aop:before method="printLog" pointcut-ref="pt"></aop:before>
<!--后置通知 通常和异常只能执行一个-->
<aop:after-returning method="printLog" pointcut-ref="pt"></aop:after-returning>
<!--异常通知-->
<aop:after-throwing method="printLog" pointcut-ref="pt"></aop:after-throwing>
<!--最终通知 无论切入点方法是否正常执行都会执行-->
<aop:after method="printLog" pointcut-ref="pt"></aop:after>
<!--环绕通知-->
<aop:around method="arroundPrintLog" pointcut-ref="pt"></aop:around>
</aop:aspect>
</aop:config>
</beans>
- 注解AOP
xml:
<!--注解配置AOP-->
<context:component-scan base-package="com.demo"></context:component-scan>
<!--配置spring开启注解AOP支持-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy><!--可以改成在切面类中使用@EnableAspectJAutoProxy-->
切面类:
@Component("logger")
@Aspect
@EnableAspectJAutoProxy
public class Logger {
@Pointcut("execution( * com.demo.service.Impl.AccountServiceImpl.*(..))")
private void pt(){}
/**
* 打印日志:计划让其在切入点方法前执行(切入点方法就是业务层方法)
*/
@Before("pt()")
@After("pt()")
@AfterReturning("pt()")
@AfterThrowing("pt()")
public void printLog(){
System.out.println("Logger->printLog");
}
@Around("pt()")
public Object arroundPrintLog(ProceedingJoinPoint proceedingJoinPoint){
Object rtValue = null;
try {
Object[] args = proceedingJoinPoint.getArgs();//得到方法执行所需的参数。
System.out.println("写在proceed前是前置通知");
rtValue = proceedingJoinPoint.proceed(args);//调用业务层(切入点)方法
System.out.println("写在proceed前是后置通知");
return rtValue;
}catch (Throwable e){
System.out.println("写在catch里面是异常通知");
throw new RuntimeException(e);
}finally {
System.out.println("写在finally里面是最终通知");
}
}
}